linux下的总线设备驱动模型分析
=============================================================================================================
sysfs文件系统结构分析:
=============================================================================================================
这里仅对一个指定bus及其驱动进行结构分析:(pci)
总线及驱动:
sys-bus-pci
pci
├── devices PCI上注册的device列表
│ ├── 0000:00:00.0 -> http://www.cnblogs.com/../devices/pci0000:00/0000:00:00.0
│ ├── 0000:00:02.0 -> http://www.cnblogs.com/../devices/pci0000:00/0000:00:02.0
│ ├── 0000:00:16.0 -> http://www.cnblogs.com/../devices/pci0000:00/0000:00:16.0
│ ├── 0000:00:1a.0 -> http://www.cnblogs.com/../devices/pci0000:00/0000:00:1a.0
│ ├── 0000:00:1b.0 -> http://www.cnblogs.com/../devices/pci0000:00/0000:00:1b.0
│ ├── 0000:00:1c.0 -> http://www.cnblogs.com/../devices/pci0000:00/0000:00:1c.0
│ ├── 0000:00:1c.5 -> http://www.cnblogs.com/../devices/pci0000:00/0000:00:1c.5
│ ├── 0000:00:1d.0 -> http://www.cnblogs.com/../devices/pci0000:00/0000:00:1d.0
│ ├── 0000:00:1f.0 -> http://www.cnblogs.com/../devices/pci0000:00/0000:00:1f.0
│ ├── 0000:00:1f.2 -> http://www.cnblogs.com/../devices/pci0000:00/0000:00:1f.2
│ ├── 0000:00:1f.3 -> http://www.cnblogs.com/../devices/pci0000:00/0000:00:1f.3
│ └── 0000:02:00.0 -> http://www.cnblogs.com/../devices/pci0000:00/0000:00:1c.5/0000:02:00.0
├── drivers PCI上注册的driver列表
│ ├── ehci_hcd 一个特定的设备驱动(能同时支持两个设备)
│ ├── 0000:00:1a.0 -> http://www.cnblogs.com/http://www.cnblogs.com/devices/pci0000:00/0000:00:1a.0 支持的设备列表
│ ├── 0000:00:1d.0 -> http://www.cnblogs.com/http://www.cnblogs.com/devices/pci0000:00/0000:00:1d.0
│ ├── bind
│ ├── module -> http://www.cnblogs.com/http://www.cnblogs.com/module/ehci_hcd 所属的模块(THIS_MODULE)
│ ├── new_id 其余均为此驱动相应的ATTR
│ ├── remove_id
│ ├── uevent
│ └── unbind
│────────── drivers_autoprobe PCI相应的ATTR
│────────── drivers_probe
│────────── rescan
│────────── resource_alignment
│────────── uevent
真实的设备:
sys-device-pci0000:00-0000:00:00.0
00-0000:00:00.0
├── broken_parity_status
├── class
├── config
├── consistent_dma_mask_bits
├── device
├── dma_mask_bits
├── enable
├── irq
├── local_cpulist
├── local_cpus
├── modalias
├── msi_bus
├── numa_node
├── power
│ ├── autosuspend_delay_ms
│ ├── control
│ ├── runtime_active_time
│ ├── runtime_status
│ ├── runtime_suspended_time
│ ├── wakeup
│ ├── wakeup_active
│ ├── wakeup_active_count
│ ├── wakeup_count
│ ├── wakeup_hit_count
│ ├── wakeup_last_time_ms
│ ├── wakeup_max_time_ms
│ └── wakeup_total_time_ms
├── remove
├── rescan
├── resource
├── subsystem -> http://www.cnblogs.com/../bus/pci
├── subsystem_device
├── subsystem_vendor
├── uevent
└── vendor
除了上面bus/driver/device的链接关系外,注意到还有一个driver与module的依赖关系:
system/module/ehci_hcd
module
├── drivers
│ └── pci:ehci_hcd -> http://www.cnblogs.com/../bus/pci/drivers/ehci_hcd
└── parameters
├── hird
├── ignore_oc
├── log2_irq_thresh
└── park
上面的结构对于理解设备驱动模型具有提纲挈领的作用.整个总线/驱动/设备的注册过程就是一个建立对应sysfs的过程
=============================================================================================================
设备驱动模型属性回调流程分析:
=============================================================================================================
kobject: 在sysfs中代表一个独立的目录
struct kobject {
const char *name; 对象名
struct list_head entry; 挂接到kset列表上的钩子
struct kobject *parent; 父对象
struct kset *kset; 所属的kset
struct kobj_type *ktype; 见下
struct sysfs_dirent *sd; 见下
struct kref kref; 引用计数
unsigned int state_initialized:1; 是否初始化
unsigned int state_in_sysfs:1; 是否在sysfs中
unsigned int state_add_uevent_sent:1; 是否发送相关设备增加/移除事件
unsigned int state_remove_uevent_sent:1;
unsigned int uevent_suppress:1; 是否丢弃事件(1为丢弃)
};
kobj_type: 代表一个kobject的相关操作
struct kobj_type {
void (*release)(struct kobject *kobj); 释放相关kobject
const struct sysfs_ops *sysfs_ops; 文件系统接口
struct attribute **default_attrs; 默认attrs
const struct kobj_ns_type_operations *(*child_ns_type)(struct kobject *kobj);
const void *(*namespace)(struct kobject *kobj);
};
如:
总线kobject类型
static struct kobj_type bus_ktype = {
.sysfs_ops = &bus_sysfs_ops,
};
定义:
static const struct sysfs_ops bus_sysfs_ops = {
.show = bus_attr_show,
.store = bus_attr_store,
};
具体的函数
static ssize_t bus_attr_show(struct kobject *kobj, struct attribute *attr,
char *buf)
{
struct bus_attribute *bus_attr = to_bus_attr(attr);
struct subsys_private *subsys_priv = to_subsys_private(kobj);
ssize_t ret = 0;
if (bus_attr->show)
ret = bus_attr->show(subsys_priv->bus, buf);
return ret;
}
驱动kobject类型:
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)
ret = drv_attr->show(drv_priv->driver, buf);
return ret;
}
设备的kobject类型
static struct kobj_type device_ktype = {
.release = device_release,
.sysfs_ops = &dev_sysfs_ops,
.namespace = device_namespace,
};
定义:
static const struct sysfs_ops dev_sysfs_ops = {
.show = dev_attr_show,
.store = dev_attr_store,
};
具体的函数:
static ssize_t dev_attr_show(struct kobject *kobj, struct attribute *attr,
char *buf)
{
struct device_attribute *dev_attr = to_dev_attr(attr);
struct device *dev = kobj_to_dev(kobj);
ssize_t ret = -EIO;
if (dev_attr->show)
ret = dev_attr->show(dev, dev_attr, buf);
if (ret >= (ssize_t)PAGE_SIZE) {
print_symbol("dev_attr_show: %s returned bad count\n",
(unsigned long)dev_attr->show);
}
return ret;
}
struct sysfs_ops { 桥接文件系统的接口(namespace)
ssize_t (*show)(struct kobject *, struct attribute *,char *);
ssize_t (*store)(struct kobject *,struct attribute *,const char *, size_t);
const void *(*namespace)(struct kobject *, const struct attribute *);
};
到这里为止,总结一下关于attribute, sysfs_ops, kobj_type的关系:
拿bus作为示例,
1.首先声明一个总线属性:
static BUS_ATTR(uevent, S_IWUSR, NULL, bus_uevent_store); (bus.c)
这个属性作为bus的uevent属性,并提供的一个写接口(未提供读接口)
2.在创建对应文件的时候,附属attr于sysfs系统的sysfs_dirent的结构中:
sd->s_attr.attr = (void *)attr; (file.c)
同时这里bus下产生一个uevent文件
3.在用户空间产生对对应的属性文件的读写操作,like: echo add > uevent/echo 1 > autoprobe等等
4.由文件系统发起调用,从sysfs_ops中进入,主要根据其所在对应目录的kobj的kobj_type的类型来寻找设备模型层的接口
kobj => driver_ktype =》 driver_sysfs_ops => driver_attribute => 实际的函数
kobj => bus_ktype =》 bus_attr_show => bus_attribute => 实际的函数
5.回调成功.
=============================================================================================================
总线注册流程:(结合PCI总线进行分析)
=============================================================================================================
在整个设备驱动流程中,总线必定是第一个注册到系统中去的
总线对象
struct bus_type {
const char *name; 总线名称
const char *dev_name; 用于设备名称前缀
struct device *dev_root; 总线设备的根设备
struct bus_attribute *bus_attrs; 默认的总线/设备/驱动属性(强制性加入sysfs)
struct device_attribute *dev_attrs;
struct driver_attribute *drv_attrs;
int (*match)(struct device *dev, struct device_driver *drv);
int (*uevent)(struct device *dev, struct kobj_uevent_env *env);
int (*probe)(struct device *dev);
int (*remove)(struct device *dev);
void (*shutdown)(struct device *dev);
int (*suspend)(struct device *dev, pm_message_t state);
int (*resume)(struct device *dev);
const struct dev_pm_ops *pm;
struct iommu_ops *iommu_ops;
struct subsys_private *p;
};
总线对象的一些私有数据
struct subsys_private {
struct kset subsys;
//核心数据:描述这个节点的子系统
//subsys->kobj.kset 为本总线所属于的总线集合,由系统来统计的一个全局变量(bus_kset,这个数组域由系统在init.c中初始化)
//subsys->kobj.ktype 为bus_ktype, 代表我是一条总线
struct kset *devices_kset; devices链
struct list_head interfaces;
struct mutex mutex;
struct kset *drivers_kset; driver链
struct klist klist_devices; 从devices链中增加或移除device结构的方法
struct klist klist_drivers; 从drivers链中增加或移除driver结构的方法
struct blocking_notifier_head bus_notifier; 通知事件链
unsigned int drivers_autoprobe:1; 自动匹配
struct bus_type *bus; 指向包含自己的总线类型
struct kset glue_dirs; 防止命名空间冲突?
struct class *class;
};
详细注册流程如下:(关于kset的创建注册等稍后讨论)
int __bus_register(struct bus_type *bus, struct lock_class_key *key)
{
int retval;
struct subsys_private *priv;
//Alloc a new subsys_private struct.
priv = kzalloc(sizeof(struct subsys_private), GFP_KERNEL);
if (!priv)
return -ENOMEM;
//Connect each other
priv->bus = bus;
bus->p = priv;
//Init its rw semaphore and set notifier head to null.
BLOCKING_INIT_NOTIFIER_HEAD(&priv->bus_notifier);
//Set bus name to kobj.name
retval = kobject_set_name(&priv->subsys.kobj, "%s", bus->name);
if (retval)
goto out;
//For all buses store, created by inic.c
priv->subsys.kobj.kset = bus_kset;
//For all buses kobj type (bus_ktype). some common ops for bus. For Attr operation.
priv->subsys.kobj.ktype = &bus_ktype;
//Probe auto.
priv->drivers_autoprobe = 1;
//For kset and its kobj init and register.[Discuss later]
retval = kset_register(&priv->subsys);
if (retval)
goto out;
//Add attr file of this bus. uevent, User can be operate it from cmd like echo add > uevent and so on.
retval = bus_create_file(bus, &bus_attr_uevent);
if (retval)
goto bus_uevent_fail;
//Create kset to store devices for bus and add to the sysfs.(like the dir devices in each bus) /sys/i2c/devices/xx
priv->devices_kset = kset_create_and_add("devices", NULL,
&priv->subsys.kobj);
if (!priv->devices_kset) {
retval = -ENOMEM;
goto bus_devices_fail;
}
//Create kset to store drivers for bus and add to the sysfs.(like the dir drivers in each bus)
priv->drivers_kset = kset_create_and_add("drivers", NULL,
&priv->subsys.kobj);
if (!priv->drivers_kset) {
retval = -ENOMEM;
goto bus_drivers_fail;
}
INIT_LIST_HEAD(&priv->interfaces);
//Init mutex of it.
__mutex_init(&priv->mutex, "subsys mutex", key);
//Init the method how to get and release node(device); device_private is set in parent/bus/driver
klist_init(&priv->klist_devices, klist_devices_get, klist_devices_put);
klist_init(&priv->klist_drivers, NULL, NULL);
//Create probe and autoprobe action.
retval = add_probe_files(bus);
if (retval)
goto bus_probe_files_fail;
//Create other own files for special control.Default ATTR for bus.
retval = bus_add_attrs(bus);
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;
}
---------------------------------------------------------------------------------------------------------------------
pci 总线注册流程 (pci.h/pci-driver.c)
总线类型声明:
struct bus_type pci_bus_type = {
.name = "pci",
.match = pci_bus_match,
.uevent = pci_uevent,
.probe = pci_device_probe,
.remove = pci_device_remove,
.shutdown = pci_device_shutdown,
.dev_attrs = pci_dev_attrs,
.bus_attrs = pci_bus_attrs,
.drv_attrs = pci_drv_attrs,
.pm = PCI_PM_OPS_PTR,
};
struct device_attribute pci_dev_attrs[] = {
__ATTR_RO(resource),
__ATTR_RO(vendor),
__ATTR_RO(device),
__ATTR_RO(subsystem_vendor),
__ATTR_RO(subsystem_device),
__ATTR_RO(class),
...
struct bus_attribute pci_bus_attrs[] = {
__ATTR(rescan, (S_IWUSR|S_IWGRP), NULL, bus_rescan_store),
__ATTR_NULL
};
static struct driver_attribute pci_drv_attrs[] = {
__ATTR(new_id, S_IWUSR, NULL, store_new_id),
__ATTR(remove_id, S_IWUSR, NULL, store_remove_id),
__ATTR_NULL,
};
这里三个属性分别对应sysfs里的相关属性文件, 下面不再描述属性相关的东西.
static int __init pci_driver_init(void)
{
return bus_register(&pci_bus_type);
}
注册到系统中就可以完事了.
下面是相关注册函数的作用及规则说明:
在写某总线下的驱动时,必须熟悉相关总线的match流程,等等。
pci:
static int pci_bus_match(struct device *dev, struct device_driver *drv)
{
struct pci_dev *pci_dev = to_pci_dev(dev);
struct pci_driver *pci_drv = to_pci_driver(drv);
const struct pci_device_id *found_id;
found_id = pci_match_device(pci_drv, pci_dev);
if (found_id)
return 1;
return 0;
}
spi:
static int spi_match_device(struct device *dev, struct device_driver *drv)
{
const struct spi_device *spi = to_spi_device(dev);
const struct spi_driver *sdrv = to_spi_driver(drv);
/* Attempt an OF style match */
if (of_driver_match_device(dev, drv))
return 1;
if (sdrv->id_table)
return !!spi_match_id(sdrv->id_table, spi);
return strcmp(spi->modalias, drv->name) == 0;
}
基本的原理是相同的,获取对应的设备和驱动的结构,并进行匹配(iptable/name).
后面会详细描述SPI总线的匹配规则.
=============================================================================================================
驱动注册流程:(spi)
=============================================================================================================
struct device_driver {
const char *name; 驱动名称
struct bus_type *bus; 挂接的总线类型(spi/pci...)
struct module *owner; THIS_MODULE
//The modules' name.
const char *mod_name; /* used for built-in modules */ 对于内建模块的名称
bool suppress_bind_attrs; /* disables bind/unbind via sysfs */ 是否启用属性控制bind/unbind操作
const struct of_device_id *of_match_table; 匹配的设备列表
int (*probe) (struct device *dev); 匹配
int (*remove) (struct device *dev); 移除
void (*shutdown) (struct device *dev); 关闭
int (*suspend) (struct device *dev, pm_message_t state); 挂起
int (*resume) (struct device *dev); 恢复
const struct attribute_group **groups; 默认属性
const struct dev_pm_ops *pm; 电源管理(与设备相关)
struct driver_private *p;
};
私有数据:
struct driver_private {
struct kobject kobj; 驱动本身
struct klist klist_devices; 匹配的设备列表
struct klist_node knode_bus; 挂接到目标总线上的连接件
//The module kobj whitch handle this driver.
struct module_kobject *mkobj; module handle
struct device_driver *driver; 驱动
};
驱动注册:
int driver_register(struct device_driver *drv)
{
int ret;
struct device_driver *other;
//三个接口函数,bus与driver都要有
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);
//Driver name can not be used for twice.
//查找bus是否存在同名driver(在总线的驱动列表中查找kobj名称相同的驱动)
other = driver_find(drv->name, drv->bus);
if (other) {
printk(KERN_ERR "Error: Driver '%s' is already registered, "
"aborting...\n", drv->name);
return -EBUSY;
}
//Add the driver to the bus.
ret = bus_add_driver(drv);
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;
}
增加driver到bus上去(核心):
int bus_add_driver(struct device_driver *drv)
{
struct bus_type *bus;
struct driver_private *priv;
int error = 0;
bus = bus_get(drv->bus);
if (!bus)
return -EINVAL;
priv = kzalloc(sizeof(*priv), GFP_KERNEL);
if (!priv) {
error = -ENOMEM;
goto out_put_bus;
}
//Create klist for devices.
klist_init(&priv->klist_devices, NULL, NULL);
priv->driver = drv;
drv->p = priv;
//Belong to bus driver kset.
//反向挂接
priv->kobj.kset = bus->p->drivers_kset;
//Init its kobj and add it to sysfs.
error = kobject_init_and_add(&priv->kobj, &driver_ktype, NULL,
"%s", drv->name);
if (error)
goto out_unregister;
//Auto probe, driver to find device.
//匹配设备
if (drv->bus->p->drivers_autoprobe) {
error = driver_attach(drv);
if (error)
goto out_unregister;
}
//Add driver to the list of bus.
//正向挂接
klist_add_tail(&priv->knode_bus, &bus->p->klist_drivers);
//Connect driver to the module.
//驱动与模块挂接
module_add_driver(drv->owner, drv);
//Create event attr file for driver
//创建uevent属性
error = driver_create_file(drv, &driver_attr_uevent);
if (error) {
printk(KERN_ERR "%s: uevent attr (%s) failed\n",
__func__, drv->name);
}
//创建bus上的默认属性
//Common drv attrs for bus add to the driver.
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);
}
//Disable bind/unbind via sysfs. Wheather create bind or unbind files.
//创建bind/unbind文件
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;
}
核心在这一行:【取决于bus是否autoprobe】
error = driver_attach(drv);
跟踪进去:
int driver_attach(struct device_driver *drv)
{
return bus_for_each_dev(drv->bus, NULL, drv, __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)
return -EINVAL;
klist_iter_init_node(&bus->p->klist_devices, &i,
(start ? &start->p->knode_bus : NULL));
while ((dev = next_device(&i)) && !error)
error = fn(dev, data);
klist_iter_exit(&i);
return error;
}
真正的匹配在这一行
error = fn(dev, data);
真正的核心匹配函数:
static int __driver_attach(struct device *dev, void *data)
{
struct device_driver *drv = data;
//总线优先调用match函数进行匹配,有提前否决权
if (!driver_match_device(drv, dev))
return 0;
//Get matched. to check the matched device must have no driver to driver it.
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;
}
static int really_probe(struct device *dev, struct device_driver *drv)
{
int ret = 0;
atomic_inc(&probe_count);
pr_debug("bus: '%s': %s: probing driver %s with device %s\n",
drv->bus->name, __func__, drv->name, dev_name(dev));
//Connectted 设备区驱动挂接
dev->driver = drv;
//Add the device name in driver, like /sys/bus/i2c/drivers/xxx
if (driver_sysfs_add(dev)) {
printk(KERN_ERR "%s: driver_sysfs_add(%s) failed\n",
__func__, dev_name(dev));
goto probe_failed;
}
//Bus probe first.
//Bus的probe函数进行匹配
if (dev->bus->probe) {
ret = dev->bus->probe(dev);
if (ret)
goto probe_failed;
} else if (drv->probe) {
ret = drv->probe(dev);
if (ret)
goto probe_failed;
}
driver_bound(dev);
ret = 1;
pr_debug("bus: '%s': %s: bound device %s to driver %s\n",
drv->bus->name, __func__, dev_name(dev), drv->name);
goto done;
probe_failed:
devres_release_all(dev);
driver_sysfs_remove(dev);
dev->driver = NULL;
dev_set_drvdata(dev, NULL);
if (ret == -EPROBE_DEFER) {
/* Driver requested deferred probing */
dev_info(dev, "Driver %s requests probe deferral\n", drv->name);
driver_deferred_probe_add(dev);
} else if (ret != -ENODEV && ret != -ENXIO) {
/* driver matched but the probe failed */
printk(KERN_WARNING
"%s: probe of %s failed with error %d\n",
drv->name, dev_name(dev), ret);
} else {
pr_debug("%s: probe of %s rejects match %d\n",
drv->name, dev_name(dev), ret);
}
/*
* Ignore errors returned by ->probe so that the next driver can try
* its luck.
*/
ret = 0;
done:
atomic_dec(&probe_count);
wake_up(&probe_waitqueue);
return ret;
}
至此,整个驱动的注册过程总结如下:
1. 检测总线上没有同名驱动
2. 添加驱动到总线
初始化驱动私有数据域(设备链表等,并与driver挂接)
添加到sysfs系统中
进行匹配
遍历总线上的所有设备(不管是否在中途匹配到设备)
首先调用bus的match函数,进行匹配(两个参数:dev/driver)
匹配成功后,确定设备已经在sysfs中正常注册(存在)
挂接dev的driver,添加dev到driver的sysfs下
调用bus的probe函数,检查设备与bus的匹配性(一个参数:dev)
否则调用driver的probe函数(一般走此条路)进行匹配
匹配成功:添加dev到driver的dev链表中
匹配失败:dev->driver = NULL;转失败处理流程
添加到bus的driver列表
驱动与所属模块挂接
创建uevent/bus默认driver属性/bind(unbind)属性等
3. 触发热插拔事件
=============================================================================================================
设备注册流程
=============================================================================================================
类的相关知识:class
-------------------------------------------------------------------------------------------------------------
sys
class
vc
├── vcs -> http://www.cnblogs.com/devices/virtual/vc/vcs
├── vcs1 -> http://www.cnblogs.com/devices/virtual/vc/vcs1
├── vcs2 -> http://www.cnblogs.com/devices/virtual/vc/vcs2
├── vcs3 -> http://www.cnblogs.com/devices/virtual/vc/vcs3
├── vcs4 -> http://www.cnblogs.com/devices/virtual/vc/vcs4
在注册字符设备的时候通常使用的两句代码:
wch_class = class_create(THIS_MODULE, "wch_class");
wch_device = device_create(wch_class, NULL, dev, NULL, "wch");
这样就会为为指定dev在/dev/目录下创建对应的设备节点以及在/sys/class下创建对应的类
深入类的创建相关代码:
#define class_create(owner, name) \
({ \
static struct lock_class_key __key; \
__class_create(owner, name, &__key); \
})
核心在类的注册函数:__class_register
int __class_register(struct class *cls, struct lock_class_key *key)
{
struct subsys_private *cp;
int error;
//初始化一个子系统私有数据(注:这里只初始化了它的设备列表,没有驱动列表,变相说明只有设备属于类,见上图)
cp = kzalloc(sizeof(*cp), GFP_KERNEL);
klist_init(&cp->klist_devices, klist_class_dev_get, klist_class_dev_put);
INIT_LIST_HEAD(&cp->interfaces);
kset_init(&cp->glue_dirs); //初始化绿名目录(用于无主设备)
__mutex_init(&cp->mutex, "subsys mutex", key);
//初始化类名到目录kobj
error = kobject_set_name(&cp->subsys.kobj, "%s", cls->name);
//设置kobj:默认使用/sys/dev这个目录的kobj[后面在此类下建device,就相当于在/sys/dev下建立对应的device]
/* set the default /sys/dev directory for devices of this class */
if (!cls->dev_kobj)
cls->dev_kobj = sysfs_dev_char_kobj;
#if defined(CONFIG_BLOCK)
/* let the block class directory show up in the root of sysfs */
if (!sysfs_deprecated || cls != &block_class)
cp->subsys.kobj.kset = class_kset;
#else
cp->subsys.kobj.kset = class_kset;
#endif
cp->subsys.kobj.ktype = &class_ktype;
//挂接
cp->class = cls;
cls->p = cp;
//注册kset到sysfs中
error = kset_register(&cp->subsys);
if (error) {
kfree(cp);
return error;
}
error = add_class_attrs(class_get(cls));
class_put(cls);
return error;
}
device_create函数:
@设备类
@父设备
@设备号
@回调数据
@设备名称/dev/xxname
dev = device_create_vargs(class, parent, devt, drvdata, fmt, vargs);
struct device *device_create_vargs(struct class *class, struct device *parent,
dev_t devt, void *drvdata, const char *fmt,
va_list args)
{
struct device *dev = NULL;
int retval = -ENODEV;
if (class == NULL || IS_ERR(class))
goto error;
dev = kzalloc(sizeof(*dev), GFP_KERNEL);
if (!dev) {
retval = -ENOMEM;
goto error;
}
dev->devt = devt; //设备号
dev->class = class; //设备类
dev->parent = parent; //父设备
dev->release = device_create_release;//释放函数
dev_set_drvdata(dev, drvdata);//创建并初始化设备的私有数据 dev->p->driver_data = data;
//设置设备名称
retval = kobject_set_name_vargs(&dev->kobj, fmt, args);
if (retval)
goto error;
//初始化dev
//增加设备:回到了device_add函数中去。
retval = device_register(dev);
if (retval)
goto error;
return dev;
error:
put_device(dev);
return ERR_PTR(retval);
}
-------------------------------------------------------------------------------------------------------------
设备添加:device_add
=============================================================================================================
Kobject创建步骤
=============================================================================================================
1.分配内存
kzalloc(sizeof(*kobj), GFP_KERNEL);
2.初始化所属kobj_type
kobject_init(kobj, &dynamic_kobj_ktype);
3.设置父亲kobj及名称,以及加入kobj所属的kset的列表
kobject_add(kobj, parent, "%s", name);
三者之间的一些优先级:
kobj->kset->kobj_type > kobj->kobj_type
默认属性:
kobj_type->default_attrs
添加属性:在指定的kobj目录下创建属性attr.
sysfs_create_file(kobj, attr)
软链接:
sysfs_create_link(struct kobject *kobj, struct kobject *target, char *name);
设备的层次关系:
device->kobj->parent 等同于 device->parent->kobj
=============================================================================================================
Kset的创建与初始化及相关热插拔事件的传递原理
=============================================================================================================
首先ket需要kobject的内嵌,任何kset的初始化都包含了kobj的初始化,加上kobj_type,这里的内核驱动模型三要素就全了。
这个函数是bus用来创建drivers kset 和 devices kset.
@name: 将作为kobj的名称(也就是sysfs中的目录名称)
@uevent_ops:NULL
@parent_kobj:父亲kobj
struct kset *kset_create_and_add(const char *name,
const struct kset_uevent_ops *uevent_ops,
struct kobject *parent_kobj)
{
struct kset *kset;
int error;
kset = kset_create(name, uevent_ops, parent_kobj);
if (!kset)
return NULL;
error = kset_register(kset);
if (error) {
kfree(kset);
return NULL;
}
return kset;
}
Kset的创建过程:
static struct kset *kset_create(const char *name,
const struct kset_uevent_ops *uevent_ops,
struct kobject *parent_kobj)
{
struct kset *kset;
int retval;
//Alloc kset struct.
kset = kzalloc(sizeof(*kset), GFP_KERNEL);
if (!kset)
return NULL;
//Set name to kobj.name.
retval = kobject_set_name(&kset->kobj, name);
if (retval) {
kfree(kset);
return NULL;
}
//Handle uevent.[Discuss later]
kset->uevent_ops = uevent_ops;
//Set parent kobj.
kset->kobj.parent = parent_kobj;
/*
* The kobject of this kset will have a type of kset_ktype and belong to
* no kset itself. That way we can properly free it when it is
* finished being used.
*/
//It init to belong no kset and no kset_type.
kset->kobj.ktype = &kset_ktype;
kset->kobj.kset = NULL;
return kset;
}
Kset 注册过程:
int kset_register(struct kset *k)
{
int err;
if (!k)
return -EINVAL;
//Init kobj of kset and init ket.
kset_init(k);
//Add kobj to sysfs.
err = kobject_add_internal(&k->kobj);
if (err)
return err;
//Triggle a kobj add event.
kobject_uevent(&k->kobj, KOBJ_ADD);
return 0;
}
-----------------------------------------------------------------------------------------------------------------
这里整个过程就结束了,但是里面暗含了一个事件传递链.hotplug,热插拔事件
int kobject_uevent(struct kobject *kobj, enum kobject_action action)
{
return kobject_uevent_env(kobj, action, NULL);
}
这里,在驱动模型中device_add方法同样会调用此函数,来进行热插拔的消息通知.
相关的结构体介绍:
@kset:顶级kset
@kobj:发生事件的kobj
struct kset_uevent_ops {
int (* const filter)(struct kset *kset, struct kobject *kobj); 过滤相关事件
const char *(* const name)(struct kset *kset, struct kobject *kobj); 获取子系统名称(bus级别的),如果没有则使用kset中的kobj.name
int (* const uevent)(struct kset *kset, struct kobject *kobj, 通知事件
struct kobj_uevent_env *env);
};
这个接口是附在kset上的,用于处理相关热插拔的事件
struct kset {
struct list_head list;
spinlock_t list_lock;
struct kobject kobj;
const struct kset_uevent_ops *uevent_ops;
};
kset_create(name, uevent_ops, parent_kobj)
struct kobj_uevent_env {
char *envp[UEVENT_NUM_ENVP]; 事件号
int envp_idx;
char buf[UEVENT_BUFFER_SIZE]; 事件变量
int buflen;
};
这个相当与一个事件传递的载体.
具体的传递过程:
int kobject_uevent_env(struct kobject *kobj, enum kobject_action action,
char *envp_ext[])
{
struct kobj_uevent_env *env;
const char *action_string = kobject_actions[action];
const char *devpath = NULL;
const char *subsystem;
struct kobject *top_kobj;
struct kset *kset;
const struct kset_uevent_ops *uevent_ops;
int i = 0;
int retval = 0;
...
top_kobj = kobj;
//Get top kobj.
while (!top_kobj->kset && top_kobj->parent)
top_kobj = top_kobj->parent;
if (!top_kobj->kset) {
return -EINVAL;
}
//Get the top kset.(such as bus)
kset = top_kobj->kset;
uevent_ops = kset->uevent_ops;
//Drop it if the kobj set.
if (kobj->uevent_suppress) {
return 0;
}
//Filter it, the top kset don't care the event about this kobj.
if (uevent_ops && uevent_ops->filter)
if (!uevent_ops->filter(kset, kobj)) {
return 0;
}
//Get subsystem.
if (uevent_ops && uevent_ops->name)
subsystem = uevent_ops->name(kset, kobj);
else //Use kset's kobj.name.
subsystem = kobject_name(&kset->kobj);
if (!subsystem) {
return 0;
}
/* environment buffer */
env = kzalloc(sizeof(struct kobj_uevent_env), GFP_KERNEL);
if (!env)
return -ENOMEM;
//Get path of cur kobj.
devpath = kobject_get_path(kobj, GFP_KERNEL);
if (!devpath) {
retval = -ENOENT;
goto exit;
}
增加环境变量ACTION=<action name>
增加环境变量DEVPATH=<kobj’s path>
增加环境变量SUBSYSTEM=<subsystem name>
增加环境变量kobject_uevent_env中参数envp_ext指定的环境变量。
/* default keys */
retval = add_uevent_var(env, "ACTION=%s", action_string); //Action
if (retval)
goto exit;
retval = add_uevent_var(env, "DEVPATH=%s", devpath); //Kobj path
if (retval)
goto exit;
retval = add_uevent_var(env, "SUBSYSTEM=%s", subsystem); //subsystem name(Top)
if (retval)
goto exit;
/* keys passed in from the caller */
if (envp_ext) {
for (i = 0; envp_ext[i]; i++) {
retval = add_uevent_var(env, "%s", envp_ext[i]);
if (retval)
goto exit;
}
}
//Handled by top set first
if (uevent_ops && uevent_ops->uevent) {
retval = uevent_ops->uevent(kset, kobj, env);
if (retval) {
goto exit;
}
}
/*
* Mark "add" and "remove" events in the object to ensure proper
* events to userspace during automatic cleanup. If the object did
* send an "add" event, "remove" will automatically generated by
* the core, if not already done by the caller.
*/
//Set the current kobj uevent state.
if (action == KOBJ_ADD)
kobj->state_add_uevent_sent = 1;
else if (action == KOBJ_REMOVE)
kobj->state_remove_uevent_sent = 1;
mutex_lock(&uevent_sock_mutex);
/* we will send an event, so request a new sequence number */
retval = add_uevent_var(env, "SEQNUM=%llu", (unsigned long long)++uevent_seqnum);
if (retval) {
mutex_unlock(&uevent_sock_mutex);
goto exit;
}
mutex_unlock(&uevent_sock_mutex);
//
if (uevent_helper[0] && !kobj_usermode_filter(kobj)) {
char *argv [3];
argv [0] = uevent_helper;
argv [1] = (char *)subsystem;
argv [2] = NULL;
retval = add_uevent_var(env, "HOME=/");
if (retval)
goto exit;
retval = add_uevent_var(env,
"PATH=/sbin:/bin:/usr/sbin:/usr/bin");
if (retval)
goto exit;
retval = call_usermodehelper(argv[0], argv,
env->envp, UMH_WAIT_EXEC);
}
}
通过call_usermodehelper一个用户程序uevent_helper(path: a executable file), 并传递相关的参数,事件类型,这样通知就到达了用户空间.