1. usb设备模型图
1.1 kobject kset kobject_type三者关系
1.2 总线子系统内部结构
本文以usb子系统为例分析:
1.2.1 注册usb总线类型为bus_type的子系统;
1.2.2 在usb子系统上创建devices_kset设备集合;
1.2.3 在usb子系统上创建drivers_kset设备驱动集合;
关于devices_kset、drivers_kset集合,他们相当于后面热插拔挂接在usb总线上的设备和驱动的父对象,这样就形成一种树形的层次结构,我们通过如下目录/sys/bus/usb/devices/...,/sys/bus/usb/drivers/...可以看到这种关系。
1.2.4 klist_devices表示设备链表,即后续挂接在usb总线上的设备都是通过该链表连接在一起;
1.2.5 klist_drivers表示驱动链表,即后续挂接在usb总线上的驱动都是通过该链表连接在一起。
2. usb总线注册源码分析
2.1 usb总线类型 usb_bus_type
struct bus_type usb_bus_type = {
.name = "usb", //总线类型为"usb"
.match = usb_device_match, //设备和驱动在usb总线上匹配的接口函数
.uevent = usb_uevent, //usb 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)) //判断是否是usb设备驱动
return 0;
intf = to_usb_interface(dev); //通过设备获取接口
usb_drv = to_usb_driver(drv); //通过设备获取驱动
id = usb_match_id(intf, usb_drv->id_table); //将接口的字段ID与驱动的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设备和usb接口)与驱动进行匹配。
static int usb_uevent(struct device *dev, struct kobj_uevent_env *env)
{
struct usb_device *usb_dev;
if (is_usb_device(dev)) { //是否是usb设备
usb_dev = to_usb_device(dev);
} else if (is_usb_interface(dev)) { //是否是接口设备
struct usb_interface *intf = to_usb_interface(dev);
usb_dev = interface_to_usbdev(intf);
} else {
return 0;
}
if (usb_dev->devnum < 0) {
/* driver is often null here; dev_dbg() would oops */
pr_debug("usb %s: already deleted?\n", dev_name(dev));
return -ENODEV;
}
if (!usb_dev->bus) {
pr_debug("usb %s: bus removed?\n", dev_name(dev));
return -ENODEV;
}
/* per-device configurations are common */
if (add_uevent_var(env, "PRODUCT=%x/%x/%x",
le16_to_cpu(usb_dev->descriptor.idVendor),
le16_to_cpu(usb_dev->descriptor.idProduct),
le16_to_cpu(usb_dev->descriptor.bcdDevice)))
return -ENOMEM;
/* class-based driver binding models */
if (add_uevent_var(env, "TYPE=%d/%d/%d",
usb_dev->descriptor.bDeviceClass,
usb_dev->descriptor.bDeviceSubClass,
usb_dev->descriptor.bDeviceProtocol))
return -ENOMEM;
return 0;
}
usb_uevent()函数主要是增加uevent参数,关于uevent 事件机制,可以参考之前写的一篇博客:点击打开链接
2.2 usb总线注册
bus_register(&usb_bus_type); //usb_bus_type结构体在1中有分析
int bus_register(struct bus_type *bus)
{
int retval;
struct subsys_private *priv;
struct lock_class_key *key = &bus->lock_key;
priv = kzalloc(sizeof(struct subsys_private), GFP_KERNEL); //分配一个子系统
if (!priv)
return -ENOMEM;
//以下usb总线和子系统互相绑定
priv->bus = bus; //子系统的总线为bus指针,即usb
bus->p = priv; //bus->p的子系统为priv
BLOCKING_INIT_NOTIFIER_HEAD(&priv->bus_notifier); //初始化一个阻塞的通知链
retval = kobject_set_name(&priv->subsys.kobj, "%s", bus->name); //子系统usb的内核对象名称设置为usb,对应目录/sys/bus/usb,其实客观点的话,只要是涉及到内核对象kobject的创建,它就是对应/sys下的一个目录名称
if (retval)
goto out;
priv->subsys.kobj.kset = bus_kset; //绑定子系统usb的父对象为bus_kset,通过/sys/bus/usb可以看到usb在bus目录下
priv->subsys.kobj.ktype = &bus_ktype; //子系统usb内核对象操作
priv->drivers_autoprobe = 1; //设置为自动匹配,在设备和驱动的匹配过程中会判断该参数,成功将执行probe探测函数
retval = kset_register(&priv->subsys); //内核集合注册,即创建usb目录,绝对路径/sys/bus/usb,该函数内部涉及的知识点较多,我在原来的一遍博客里有分析,本文不再赘述!
if (retval)
goto out;
retval = bus_create_file(bus, &bus_attr_uevent); //内核属性创建,在/sys/bus/usb目录下会生产属性文件 drivers_autoprobe、 uevent
if (retval)
goto bus_uevent_fail;
priv->devices_kset = kset_create_and_add("devices", NULL,
&priv->subsys.kobj); //在子系统usb目录下创建devices,即/sys/bus/usb/devices
if (!priv->devices_kset) {
retval = -ENOMEM;
goto bus_devices_fail;
}
priv->drivers_kset = kset_create_and_add("drivers", NULL,
&priv->subsys.kobj); //在子系统usb目录下创建drivers, 即/sys/bus/usb/drivers
if (!priv->drivers_kset) {
retval = -ENOMEM;
goto bus_drivers_fail;
}
INIT_LIST_HEAD(&priv->interfaces);
__mutex_init(&priv->mutex, "subsys mutex", key); //为子系统usb创建互斥锁
klist_init(&priv->klist_devices, klist_devices_get, klist_devices_put); //初始化子系统usb的设备链表,在usb系统下的设备都会加入到该链表
klist_init(&priv->klist_drivers, NULL, NULL); //初始化子系统usb的驱动链表,在usb子系统下的驱动都会加入到该链表
retval = add_probe_files(bus); //在子系统usb上增加探测文件,见下分析!
if (retval)
goto bus_probe_files_fail;
retval = bus_add_attrs(bus); //为该usb子系统增加默认属性文件
if (retval)
goto bus_attrs_fail;
pr_debug("bus: '%s': registered\n", bus->name);
return 0;
bus_attrs_fail:
remove_probe_files(bus);
bus_probe_files_fail:
kset_unregister(bus->p->drivers_kset);
bus_drivers_fail:
kset_unregister(bus->p->devices_kset);
bus_devices_fail:
bus_remove_file(bus, &bus_attr_uevent);
bus_uevent_fail:
kset_unregister(&bus->p->subsys);
out:
kfree(bus->p);
bus->p = NULL;
return retval;
}
内核集合注册kset_register()涉及到的内容太多,可以参照之前写的一篇博客:点击打开链接 4.1.2内核集合注册。
通过宏定义一个bus属性结构体
#define BUS_ATTR(_name, _mode, _show, _store) \
struct bus_attribute bus_attr_##_name = __ATTR(_name, _mode, _show, _store)
static BUS_ATTR(drivers_probe, S_IWUSR, NULL, store_drivers_probe); //定义bus_attr_drivers_probe探测总线属性,同时绑定对应属性的处理接口函数
static BUS_ATTR(drivers_autoprobe, S_IWUSR | S_IRUGO,
show_drivers_autoprobe, store_drivers_autoprobe); //定义bus_attr_drivers_autoprobe自动探测属性,同时绑定对应的接口函数
这里将创建两个结构体属性bus_attr_drivers_probe、bus_attr_drivers_autoprobe给下面的add_probe_file();
在add_probe_file()子系统上增加探测文件
static int add_probe_files(struct bus_type *bus)
{
int retval;
retval = bus_create_file(bus, &bus_attr_drivers_probe); //在子系统usb上增加属性文件/sys/bus/usb/drivers_probe
if (retval)
goto out;
retval = bus_create_file(bus, &bus_attr_drivers_autoprobe); //在子系统usb上增加属性文件/sys/bus/usb/drivers_autoprobe
if (retval)
bus_remove_file(bus, &bus_attr_drivers_probe);
out:
return retval;
}
在bus_add_attrs()上增加子系统usb总线的属性
static int bus_add_attrs(struct bus_type *bus)
{
int error = 0;
int i;
if (bus->bus_attrs) {
for (i = 0; attr_name(bus->bus_attrs[i]); i++) {
error = bus_create_file(bus, &bus->bus_attrs[i]);
if (error)
goto err;
}
}
done:
return error;
err:
while (--i >= 0)
bus_remove_file(bus, &bus->bus_attrs[i]);
goto done;
}
3. usb总线注册后的设备模型
[root@cl /sys/bus/usb]#ls
devices drivers_autoprobe uevent
drivers drivers_probe
uevent是如何添加的,在子系统usb注册里暂未见到,后续在分析!!!
4. usb总线通知链注册
retval = bus_register_notifier(&usb_bus_type, &usb_bus_nb);
static struct notifier_block usb_bus_nb = {
.notifier_call = usb_bus_notify, //通知链收到消息时的回调函数,见下分析
};
struct bus_type usb_bus_type = {
.name = "usb",
.match = usb_device_match,
.uevent = usb_uevent,
};
static int usb_bus_notify(struct notifier_block *nb, unsigned long action,
void *data)
{
struct device *dev = data;
switch (action) {
case BUS_NOTIFY_ADD_DEVICE: //增加设备
if (dev->type == &usb_device_type) //是usb设备类型
(void) usb_create_sysfs_dev_files(to_usb_device(dev)); //创建descriptors文件,在sysfs中显示几个属性文件
else if (dev->type == &usb_if_device_type) //是usb接口类型
usb_create_sysfs_intf_files(to_usb_interface(dev)); //创建usb接口文件
break;
case BUS_NOTIFY_DEL_DEVICE: //删除设备
if (dev->type == &usb_device_type)
usb_remove_sysfs_dev_files(to_usb_device(dev));
else if (dev->type == &usb_if_device_type)
usb_remove_sysfs_intf_files(to_usb_interface(dev));
break;
}
return 0;
}
上面分析完了通知链结构体,接下来分析注册通知链,在分析注册通知链之前,先看下初始化通知链表头:
初始化通知链表头:
BLOCKING_INIT_NOTIFIER_HEAD(&priv->bus_notifier); //初始化一个阻塞的通知链表头
链表头初始化在bus_register()函数内部,由于bus->p = priv,所以上面初始化的是usb总线通知链;
注册通知链
int bus_register_notifier(struct bus_type *bus, struct notifier_block *nb)
{
return blocking_notifier_chain_register(&bus->p->bus_notifier, nb);
}
删除通知链:
int bus_unregister_notifier(struct bus_type *bus, struct notifier_block *nb)
{
return blocking_notifier_chain_unregister(&bus->p->bus_notifier, nb);
}
向通知链发送消息:
blocking_notifier_call_chain(&dev->bus->p->bus_notifier, BUS_NOTIFY_ADD_DEVICE, dev);
当我们增加设备到总线上时,会调用device_add()设备增加函数,该函数内部会调用blocking_notifier_call_chain()通知增加设备。
关于通知链的具体实现细节,详见:点击打开链接
5. usb主设备初始化
retval = usb_major_init();
int usb_major_init(void)
{
int error;
error = register_chrdev(USB_MAJOR, "usb", &usb_fops); //注册一个USB_MAJOR=180 的usb主设备,提供一个公共的fops
if (error)
printk(KERN_ERR "Unable to get major %d for usb devices\n",
USB_MAJOR);
return error;
}
static inline int register_chrdev(unsigned int major, const char *name,
const struct file_operations *fops)
{
return __register_chrdev(major, 0, 256, name, fops); //分配256个从设备
}
usb_fops结构体:
static const struct file_operations usb_fops = {
.owner = THIS_MODULE,
.open = usb_open,
.llseek = noop_llseek,
};
usb_open():
#define MAX_USB_MINORS 256
static const struct file_operations *usb_minors[MAX_USB_MINORS]; //分配256个file_operations
static DECLARE_RWSEM(minor_rwsem);
static int usb_open(struct inode * inode, struct file * file)
{
int minor = iminor(inode);
const struct file_operations *c;
int err = -ENODEV;
const struct file_operations *old_fops, *new_fops = NULL;
down_read(&minor_rwsem);
c = usb_minors[minor];
if (!c || !(new_fops = fops_get(c)))
goto done;
old_fops = file->f_op; //备份旧的f_op
file->f_op = new_fops; //赋值新的f_op
/* Curiouser and curiouser... NULL ->open() as "no device" ? */
if (file->f_op->open) //判定新的是否有效
err = file->f_op->open(inode,file);
if (err) {
fops_put(file->f_op);
file->f_op = fops_get(old_fops);
}
fops_put(old_fops);
done:
up_read(&minor_rwsem);
return err;
}
注册一个以USB_MAJOR=180为主设备号的cdev,不过其还分配了256个从设备号,提供一个公共的open函数,对于每一个usb设备的打开都会先调用主设备的open,而后根据不同的设备调用不同的open函数,公共的open就是提供一个对于所有的usb 设备的公共接口,真正的open其实是usb_minors中存储的fops->open。