1. hub驱动流程图
2. hub驱动初始化
retval = usb_hub_init();
int usb_hub_init(void)
{
if (usb_register(&hub_driver) < 0) { //注册hub驱动到usb子系统总线上
printk(KERN_ERR "%s: can't register hub driver\n",
usbcore_name);
return -1;
}
khubd_task = kthread_run(hub_thread, NULL, "khubd"); //创建hub内核线程
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;
}
usb_hub_init()函数主要包括两个方面,1. hub驱动注册, 2. hub线程创建,接下来将主要围绕他俩进行分析。
3. hub驱动注册
if (usb_register(&hub_driver) < 0) { //注册hub驱动到usb子系统总线上
在分析usb_register()函数前先看下hub_driver结构体:
static struct usb_driver hub_driver = {
.name = "hub",
.probe = hub_probe, //hub探测接口,重要,待分析!
.disconnect = hub_disconnect,
.suspend = hub_suspend, //hub挂起,涉及到PM电源管理驱动控制
.resume = hub_resume, //hub恢复,
.reset_resume = hub_reset_resume, //通过对hub复位来引发复位
.pre_reset = hub_pre_reset, //与PM电源管理系统相关
.post_reset = hub_post_reset, //与PM电源管理系统相关
.unlocked_ioctl = hub_ioctl,
.id_table = hub_id_table, //hub id表,重要,待分析!!
.supports_autosuspend = 1,
};
hub_id_table 对应结构体 struct usb_device_id:
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;
/* Used for vendor-specific interface matches */
__u8 bInterfaceNumber;
/* not matched against */
kernel_ulong_t driver_info
__attribute__((aligned(sizeof(kernel_ulong_t))));
}
而hub_id_table 分别初始化了三个usb_device_id结构体:
static const struct usb_device_id hub_id_table[] = {
{ .match_flags = USB_DEVICE_ID_MATCH_VENDOR //
| USB_DEVICE_ID_MATCH_INT_CLASS,
.idVendor = USB_VENDOR_GENESYS_LOGIC,
.bInterfaceClass = USB_CLASS_HUB,
.driver_info = HUB_QUIRK_CHECK_PORT_AUTOSUSPEND},
{ .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 */
};
第1个结构体成员
.match_flags = USB_DEVICE_ID_MATCH_VENDOR | USB_DEVICE_ID_MATCH_INT_CLASS, //表示需匹配的字段
.idVendor = USB_VENDOR_GENESYS_LOGIC, //匹配的字段.bInterfaceClass = USB_CLASS_HUB //匹配的字段
第2个结构体成员
.match_flags = USB_DEVICE_ID_MATCH_DEV_CLASS, //表示需匹配的字段
.bDeviceClass = USB_CLASS_HUB //匹配的字段
第3个结构体成员
.match_flags = USB_DEVICE_ID_MATCH_INT_CLASS, //表示需匹配的字段
.bInterfaceClass = USB_CLASS_HUB //匹配的字段
综上所述,match_flags 表示哪些字段匹配,而对应匹配的字段就是idVendor 、bInterfaceClass ,bDeviceClass ,bInterfaceClass ,这里在下面会分析如何使用。
hub_probe()函数内容太多,我将它放到第4.节分析函数内部细节。
现在我们继续回到hub驱动注册:
#define usb_register(driver) \
usb_register_driver(driver, THIS_MODULE, KBUILD_MODNAME)
int usb_register_driver(struct usb_driver *new_driver, struct module *owner,
const char *mod_name)
{
int retval = 0;
if (usb_disabled())
return -ENODEV;
//对drvwrap USB驱动包初始化
new_driver->drvwrap.for_devices = 0;
new_driver->drvwrap.driver.name = (char *) new_driver->name; //初始化驱动名称为“hub”
new_driver->drvwrap.driver.bus = &usb_bus_type; //绑定总线类型为子系统usb
new_driver->drvwrap.driver.probe = usb_probe_interface; //重要,是接口的探针函数!!!该函数内部会调用usb_serial_probe()
new_driver->drvwrap.driver.remove = usb_unbind_interface;
new_driver->drvwrap.driver.owner = owner;
new_driver->drvwrap.driver.mod_name = mod_name;
spin_lock_init(&new_driver->dynids.lock);
INIT_LIST_HEAD(&new_driver->dynids.list);
retval = driver_register(&new_driver->drvwrap.driver); //驱动注册
if (retval)
goto out;
retval = usb_create_newid_files(new_driver);
if (retval)
goto out_newid;
pr_info("%s: registered new interface driver %s\n",
usbcore_name, new_driver->name);
out:
return retval;
out_newid:
driver_unregister(&new_driver->drvwrap.driver);
printk(KERN_ERR "%s: error %d registering interface "
" driver %s\n",
usbcore_name, retval, new_driver->name);
goto out;
}
EXPORT_SYMBOL_GPL(usb_register_driver);
usb_register_driver()函数内部主要涉及到两个函数,驱动注册driver_register()和newid文件创建usb_create_newid_files(),接下来将逐个分析:
driver_register():
int driver_register(struct device_driver *drv)
{
int ret;
struct device_driver *other;
BUG_ON(!drv->bus->p);
if ((drv->bus->probe && drv->probe) ||
(drv->bus->remove && drv->remove) ||
(drv->bus->shutdown && drv->shutdown))
printk(KERN_WARNING "Driver '%s' needs updating - please use "
"bus_type methods\n", drv->name);
other = driver_find(drv->name, drv->bus); //这里查找drv->name="hub"驱动是否已经在drv->bus总线上注册。
if (other) {
printk(KERN_ERR "Error: Driver '%s' is already registered, "
"aborting...\n", drv->name);
return -EBUSY;
}
ret = bus_add_driver(drv); //bus总线上注册驱动
if (ret)
return ret;
ret = driver_add_groups(drv, drv->groups);
if (ret) {
bus_remove_driver(drv);
return ret;
}
kobject_uevent(&drv->p->kobj, KOBJ_ADD);
return ret;
}
EXPORT_SYMBOL_GPL(driver_register);
bus_add_driver() usb总线上添加hub驱动:
int bus_add_driver(struct device_driver *drv)
{
struct bus_type *bus;
struct driver_private *priv;
int error = 0;
bus = bus_get(drv->bus); //drv->bus=&usb_bus_type, bus为usb_bus_type
if (!bus)
return -EINVAL;
pr_debug("bus: '%s': add driver %s\n", bus->name, drv->name);
priv = kzalloc(sizeof(*priv), GFP_KERNEL); //分配一个驱动私有数据driver_private
if (!priv) {
error = -ENOMEM;
goto out_put_bus;
}
klist_init(&priv->klist_devices, NULL, NULL); //初始化设备链表
//以下相互绑定
priv->driver = drv; //私有驱动数据绑定drv
drv->p = priv; //drv->p驱动绑定priv
priv->kobj.kset = bus->p->drivers_kset; //将priv->kobj.kset当前内核集合指向bus->p->drivers_kset内核集合(该集合在usb总线注册bus_register()描述)
error = kobject_init_and_add(&priv->kobj, &driver_ktype, NULL,
"%s", drv->name);
if (error)
goto out_unregister;
klist_add_tail(&priv->knode_bus, &bus->p->klist_drivers); //将当前注册的priv->knode_bus节点加入到全局bus->p->klist_drivers链表中
if (drv->bus->p->drivers_autoprobe) { //在usb总线注册时对该变量drivers_autoprobe初始化为1了
error = driver_attach(drv); //见下面,按照内核启动的流程分析,这个时候usb总线上只有hub这个驱动,没有设备,所以driver_attach函数将返回失败
if (error)
goto out_unregister;
}
module_add_driver(drv->owner, drv); //见下面,从该函数这开始,本次hub驱动将不会执行下面的函数了,不过为了分析函数内部的功能,我们继续,因为在其他驱动模块注册时会用到
error = driver_create_file(drv, &driver_attr_uevent);
if (error) {
printk(KERN_ERR "%s: uevent attr (%s) failed\n",
__func__, drv->name);
}
error = driver_add_attrs(bus, drv);
if (error) {
/* How the hell do we get out of this pickle? Give up */
printk(KERN_ERR "%s: driver_add_attrs(%s) failed\n",
__func__, drv->name);
}
if (!drv->suppress_bind_attrs) {
error = add_bind_files(drv);
if (error) {
/* Ditto */
printk(KERN_ERR "%s: add_bind_files(%s) failed\n",
__func__, drv->name);
}
}
return 0;
out_unregister:
kobject_put(&priv->kobj);
kfree(drv->p);
drv->p = NULL;
out_put_bus:
bus_put(bus);
return error;
}
3.1 hub内核对象初始化
这里重点分析下内核对象初始化的内部细节,
error = kobject_init_and_add(&priv->kobj, &driver_ktype, NULL,
"%s", drv->name);
其中,driver_ktype为驱动的内核集合类型,主要用来对内核对象的操作
static struct kobj_type driver_ktype = {
.sysfs_ops = &driver_sysfs_ops,
.release = driver_release,
};
static const struct sysfs_ops driver_sysfs_ops = {
.show = drv_attr_show,
.store = drv_attr_store,
};
static ssize_t drv_attr_show(struct kobject *kobj, struct attribute *attr,
char *buf)
{
struct driver_attribute *drv_attr = to_drv_attr(attr); //获取驱动的属性结构体
struct driver_private *drv_priv = to_driver(kobj); //获取驱动的私有数据
ssize_t ret = -EIO;
if (drv_attr->show) //驱动属性文件是否不为NULL
ret = drv_attr->show(drv_priv->driver, buf); //显示驱动
return ret;
}
static ssize_t drv_attr_store(struct kobject *kobj, struct attribute *attr,
const char *buf, size_t count)
{
struct driver_attribute *drv_attr = to_drv_attr(attr);
struct driver_private *drv_priv = to_driver(kobj);
ssize_t ret = -EIO;
if (drv_attr->store)
ret = drv_attr->store(drv_priv->driver, buf, count); //存储
return ret;
}
分析完了驱动内核对象类型操作,再回到内核对象的初始化
int kobject_init_and_add(struct kobject *kobj, struct kobj_type *ktype,
struct kobject *parent, const char *fmt, ...)
{
va_list args;
int retval;
kobject_init(kobj, ktype); //初始化内核对象(绑定内核对象kobject的内核对象类型接口为ktype)
va_start(args, fmt);
retval = kobject_add_varg(kobj, parent, fmt, args); //内核对象增加,即创建hub目录
va_end(args);
return retval;
}
EXPORT_SYMBOL_GPL(kobject_init_and_add);
void kobject_init(struct kobject *kobj, struct kobj_type *ktype)
{
char *err_str;
if (!kobj) {
err_str = "invalid kobject pointer!";
goto error;
}
if (!ktype) {
err_str = "must have a ktype to be initialized properly!\n";
goto error;
}
if (kobj->state_initialized) {
/* do not error out as sometimes we can recover */
printk(KERN_ERR "kobject (%p): tried to init an initialized "
"object, something is seriously wrong.\n", kobj);
dump_stack();
}
kobject_init_internal(kobj);
kobj->ktype = ktype; //绑定内核对象的内核类型(内核对象的操作接口)
return;
error:
printk(KERN_ERR "kobject (%p): %s\n", kobj, err_str);
dump_stack();
}
EXPORT_SYMBOL(kobject_init);
static void kobject_init_internal(struct kobject *kobj)
{
if (!kobj)
return;
kref_init(&kobj->kref);
INIT_LIST_HEAD(&kobj->entry); //表示父内核对象下挂接的子内核对象
kobj->state_in_sysfs = 0; //sysfs没有初始化(涉及到sysfs文件系统)
kobj->state_add_uevent_sent = 0; //用户层事件是否发送
kobj->state_remove_uevent_sent = 0; //用户层事件是否移除
kobj->state_initialized = 1; //kobj对象初始化完成标识
}
kobject_init_and_add()->kobject_add_varg():
static int kobject_add_varg(struct kobject *kobj, struct kobject *parent,
const char *fmt, va_list vargs)
{
int retval;
retval = kobject_set_name_vargs(kobj, fmt, vargs); //设置内核对象的名称为hub
if (retval) {
printk(KERN_ERR "kobject: can not set name properly!\n");
return retval;
}
kobj->parent = parent;
return kobject_add_internal(kobj); //内核对象增加,在sys文件系统下创建目录hub,绝对路径/sys/bus/usb/drivers/hub
}
static int kobject_add_internal(struct kobject *kobj)
{
int error = 0;
struct kobject *parent;
if (!kobj)
return -ENOENT;
if (!kobj->name || !kobj->name[0]) {
WARN(1, "kobject: (%p): attempted to be registered with empty "
"name!\n", kobj);
return -EINVAL;
}
parent = kobject_get(kobj->parent);
/* join kset if set, use it as parent if we do not already have one */
if (kobj->kset) { //kset为usb集合(因为kset集合是被内核对象绑定,所以下面可以获取内核对象)
if (!parent)
parent = kobject_get(&kobj->kset->kobj); //即为usb内核对象
kobj_kset_join(kobj);
kobj->parent = parent;
}
pr_debug("kobject: '%s' (%p): %s: parent: '%s', set: '%s'\n",
kobject_name(kobj), kobj, __func__,
parent ? kobject_name(parent) : "<NULL>",
kobj->kset ? kobject_name(&kobj->kset->kobj) : "<NULL>");
error = create_dir(kobj);
if (error) {
kobj_kset_leave(kobj);
kobject_put(parent);
kobj->parent = NULL;
/* be noisy on error issues */
if (error == -EEXIST)
WARN(1, "%s failed for %s with "
"-EEXIST, don't try to register things with "
"the same name in the same directory.\n",
__func__, kobject_name(kobj));
else
WARN(1, "%s failed for %s (error: %d parent: %s)\n",
__func__, kobject_name(kobj), error,
parent ? kobject_name(parent) : "'none'");
} else
kobj->state_in_sysfs = 1; //表示kobject对象在sysfs中的状态
return error;
}
小节,到这里完成了hub内核对象的初始化和添加,并将该节点添加到了usb总线的bus->p->klist_drivers驱动链表中,同时创建目录hub,绝对路径为/sys/bus/usb/hub;接下来将分析driver_attach()
3.2 驱动绑定driver_attach()
路径:bus_add_driver()-->driver_attach(drv)
int driver_attach(struct device_driver *drv)
{
return bus_for_each_dev(drv->bus, NULL, drv, __driver_attach); //__driver_attach见下分析
}
int bus_for_each_dev(struct bus_type *bus, struct device *start,
void *data, int (*fn)(struct device *, void *))
{
struct klist_iter i;
struct device *dev;
int error = 0;
if (!bus || !bus->p)
return -EINVAL;
klist_iter_init_node(&bus->p->klist_devices, &i,//设备通过bus_add_device(...)添加时,最终会将设备加入到bus->p->klist_devices设备链表中
(start ? &start->p->knode_bus : NULL));
while ((dev = next_device(&i)) && !error)
error = fn(dev, data); //回调接口函数__driver_attach
klist_iter_exit(&i);
return error;
}
bus_for_each_dev()函数内部主要是遍历usb总线的设备链表(由于本次注册的是hub驱动),需找与本次注册的hub驱动相匹配;
static int __driver_attach(struct device *dev, void *data)
{
struct device_driver *drv = data;
/*
* Lock device and try to bind to it. We drop the error
* here and always return 0, because we need to keep trying
* to bind to devices and some drivers will return an error
* simply if it didn't support the device.
*
* driver_probe_device() will spit a warning if there
* is an error.
*/
if (!driver_match_device(drv, dev)) //驱动和设备是否匹配,不匹配将退出,bus_for_each_dev函数继续下一个设备来匹配...
return 0;
if (dev->parent) /* Needed for USB */
device_lock(dev->parent);
device_lock(dev);
if (!dev->driver)
driver_probe_device(drv, dev); //探测函数
device_unlock(dev);
if (dev->parent)
device_unlock(dev->parent);
return 0;
}
driver_match_device():
static inline int driver_match_device(struct device_driver *drv, struct device *dev)
{
return drv->bus->match ? drv->bus->match(dev, drv) : 1;
}
通过上面的usb_register_driver()函数分析,得知这里的drv为new_driver->drvwrap.driver,再次贴上该代码
new_driver->drvwrap.driver.name = (char *) new_driver->name; //初始化驱动名称为“hub”
new_driver->drvwrap.driver.bus = &usb_bus_type; //绑定总线类型为子系统usb
new_driver->drvwrap.driver.probe = usb_probe_interface; //重要,是接口的探针函数!!!该函数内部会调用usb_serial_probe()
new_driver->drvwrap.driver.remove = usb_unbind_interface;
new_driver->drvwrap.driver.owner = owner;
new_driver->drvwrap.driver.mod_name = mod_name;
所以driver_match_device()函数内部drv->bus=&usb_bus_type,即会调用 drv->bus->match(dev, drv)
struct bus_type usb_bus_type = {
.name = "usb",
.match = usb_device_match, //见下
.uevent = usb_uevent,
};
static int usb_device_match(struct device *dev, struct device_driver *drv)
{
/* devices and interfaces are handled separately */
if (is_usb_device(dev)) { //是否是usb设备
/* interface drivers never match devices */
if (!is_usb_device_driver(drv))
return 0;
/* TODO: Add real matching code */
return 1;
} else if (is_usb_interface(dev)) { //是否是usb接口
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;
}
在usb_device_match函数内部是usb设备或接口将执行不同的操作,我们在usb_register()函数注册时并未绑定类型,所以这里直接返回0,也就不会执行相应的probe探测函数了。那么这里就有疑问,usb设备和接口是什么时候被调用呢?在接下来的章节中将详细分析, intf->dev.type = &usb_if_device_type 将在usb_set_configuration()内部初始化,dev->dev.type = &usb_device_type将在usb_alloc_dev()内部初始化,这里先留个悬疑.....
3.3 module_add_driver()驱动模块增加
Hub驱动注册到usb总线上,并且这个时候usb总线上只有hub一个驱动,所以上面调用usb_device_match函数时将无法在对应的usb总线上匹配到设备,即该函数module_add_driver将不会被执行!不过为了后续再调用到该函数时不解,这里继续分析
module_add_driver(drv->owner, drv);
void module_add_driver(struct module *mod, struct device_driver *drv)
{
char *driver_name;
int no_warn;
struct module_kobject *mk = NULL;
if (!drv)
return;
if (mod) //mod为真
mk = &mod->mkobj;
else if (drv->mod_name) { //驱动名称,假设是usb主机控制器"ehci_hcd"
struct kobject *mkobj;
/* Lookup built-in module entry in /sys/modules */
mkobj = kset_find_obj(module_kset, drv->mod_name); //根据mod_name,在module_kset内核集合上查找是否有相同的驱动
if (mkobj) {
mk = container_of(mkobj, struct module_kobject, kobj); //通过结构体成员mkobj,获取对应的module_kobject结构体指针
/* remember our module structure */
drv->p->mkobj = mk;
/* kset_find_obj took a reference */
kobject_put(mkobj);
}
}
if (!mk)
return;
/* Don't check return codes; these calls are idempotent */
no_warn = sysfs_create_link(&drv->p->kobj, &mk->kobj, "module"); //创建module符号链接,即/sys/bus/usb/drivers/hub/module
driver_name = make_driver_name(drv); //driver_name ="usb:hub"
if (driver_name) {
module_create_drivers_dir(mk); //创建目录/sys/bus/usb/drivers/hub/module/usb:hub
no_warn = sysfs_create_link(mk->drivers_dir, &drv->p->kobj,
driver_name);
kfree(driver_name);
}
}
3.5 驱动属性文件绑定
路径:linux-3.10.x\include\linux\device.h
#define DRIVER_ATTR(_name, _mode, _show, _store) \
struct driver_attribute driver_attr_##_name = \
__ATTR(_name, _mode, _show, _store)
static DRIVER_ATTR(uevent, S_IWUSR, NULL, driver_uevent_store);
static ssize_t driver_uevent_store(struct device_driver *drv,
const char *buf, size_t count)
{
enum kobject_action action;
if (kobject_action_type(buf, count, &action) == 0)
kobject_uevent(&drv->p->kobj, action);
return count;
}
所以上面创建了驱动属性文件结构体driver_attr_uevent
error = driver_create_file(drv, &driver_attr_uevent); //创建驱动的属性文件操作
error = driver_add_attrs(bus, drv); //总线上增加属性文件操作
error = add_bind_files(drv);
4. hub_probe()
hub_probe()是被主机控制器ehci注册hub root时调用,详见:点击打开链接
5. hub_thread()
当usb主机控制器检测到设备接入时,通过等待队列唤醒hub_thread线程,执行usb设备的枚举,最终调用hub_probe(),关于hub_thread()分析,详见:点击打开链接
6. 总结:
usb hub初始化换号usb_hub_init()主要完成两个工作,
1. 注册hub驱动到usb总线上,其中最重要的就是hub_probe()函数;
2. 创建hub_thread()内核线程,用来处理usb主机控制器插入设备时的枚举操作。