strcut device_type
struct device_type {
const char *name;
const struct attribute_group **groups;
int (*uevent)(struct device *dev, struct kobj_uevent_env *env);
char *(*devnode)(struct device *dev, mode_t *mode);
void (*release)(struct device *dev);
const struct dev_pm_ops *pm;
};
struct device
首先是指向父节点的指针parent,kobj是内嵌在device中的kbojcet,还包括device匹配的驱动,属于哪条总线。其中比较重要的是release函数,当这个设备被删去时,内核调用该方法,所有注册到内核的设备结构必须有一个release方法。
struct device {
struct device *parent; /*指向父节点的指针*/
struct device_private *p;
struct kobject kobj; /*用它联系到sysfs中*/
const char *init_name; /*初始化设备的名字 */
const struct device_type *type; /*device属性文件*/
struct mutex mutex; /* mutexto synchronize calls to
* its driver.
*/
struct bus_type *bus; /*属于哪条总线 */
struct device_driver *driver; /*哪个驱动能匹配该设备*/
void *platform_data;
struct dev_pm_info power;
struct dev_pm_domain *pm_domain;
#ifdef CONFIG_NUMA
int numa_node; /* NUMAnode this device is close to */
#endif
u64 *dma_mask; /* dma mask (if dma'able device) */
u64 coherent_dma_mask; /* Like dma_mask, but for
alloc_coherentmappings as
not allhardware supports
64 bitaddresses for consistent
allocationssuch descriptors. */
struct device_dma_parameters *dma_parms;
struct list_head dma_pools; /* dmapools (if dma'ble) */
struct dma_coherent_mem *dma_mem; /* internal for coherent mem
override */
/* arch specific additions */
struct dev_archdata archdata;
struct device_node *of_node; /* associated device tree node */
dev_t devt; /*dev_t, creates the sysfs "dev" */
spinlock_t devres_lock;
struct list_head devres_head;
struct klist_node knode_class;
struct class *class;
const struct attribute_group **groups; /* optional groups */
void (*release)(struct device *dev);
};
struct device_private
和总线一样,device也有一个联系bus、device、driver的数据结构device_private。
struct device_private {
struct klist klist_children; //子设备的链表
struct klist_node knode_parent; //连接到父设备的klist_childern时所用的节点
struct klist_node knode_driver; //连接到驱动的设备时用到的节点
struct klist_node knode_bus; //连接到总线时所用的节点
void *driver_data; //用于在设备结构中存放相关的驱动信息
struct device *device; //指向属于的device
};
#define to_device_private_parent(obj) \
container_of(obj, structdevice_private, knode_parent)//从父设备的klist_children上节点,获取相应的private
#define to_device_private_driver(obj) \
container_of(obj, struct device_private,knode_driver)//从驱动链表上获得private
#define to_device_private_bus(obj) \
container_of(obj, struct device_private,knode_bus) //从总线上获得private
struct device_attribute
sysf中的设备入口的属性结构
/* interface for exporting device attributes */
struct device_attribute{
struct attribute attr;
ssize_t (*show)(struct device *dev, struct device_attribute *attr,
char *buf);
ssize_t (*store)(struct device *dev, struct device_attribute *attr,
const char *buf, size_t count);
};
#define DEVICE_ATTR(_name, _mode, _show, _store) \
struct device_attributedev_attr_##_name = __ATTR(_name, _mode, _show, _store)
设备初始化devices_init()
同bus过程一样,device的初始化也是在driver_init完成初始化的。这个函数就是创建在sysfs中的devices目录和dev目录,还在dev目录下创建了block和char两个子目录。
int __init devices_init(void)
{
devices_kset = kset_create_and_add("devices", &device_uevent_ops, NULL); //创建devices目录
if (!devices_kset)
return -ENOMEM;
dev_kobj =kobject_create_and_add("dev", NULL); //创建dev目录
if (!dev_kobj)
goto dev_kobj_err;
sysfs_dev_block_kobj = kobject_create_and_add("block", dev_kobj);
//创建dev/block目录
if (!sysfs_dev_block_kobj)
goto block_kobj_err;
sysfs_dev_char_kobj = kobject_create_and_add("char", dev_kobj);
//创建dev/char目录
if (!sysfs_dev_char_kobj)
goto char_kobj_err;
return 0;
char_kobj_err:
kobject_put(sysfs_dev_block_kobj);
block_kobj_err:
kobject_put(dev_kobj);
dev_kobj_err:
kset_unregister(devices_kset);
return -ENOMEM;
}
这部分用于向用户控件传递event事件。
static const struct kset_uevent_opsdevice_uevent_ops = {
.filter = dev_uevent_filter,
.name = dev_uevent_name,
.uevent = dev_uevent,
};
设备注册device_register()
下面来分析下设备的注册过程。device_register是提供给外界注册设备的接口,主要调用device_initialize完成dev结构的初始化,然后调用device_add将其加入到系统中。
int device_register(struct device *dev)
{
device_initialize(dev);
return device_add(dev);
}
device_initialize()主要是完成device结构的初始化,把device中能初始化的部分全部初始化,主要是将device_ktype加入到kobject中。
voiddevice_initialize(struct device *dev)
{
dev->kobj.kset = devices_kset;
kobject_init(&dev->kobj, &device_ktype);
INIT_LIST_HEAD(&dev->dma_pools);
mutex_init(&dev->mutex);
lockdep_set_novalidate_class(&dev->mutex);
spin_lock_init(&dev->devres_lock);
INIT_LIST_HEAD(&dev->devres_head);
device_pm_init(dev);
set_dev_node(dev, -1);
}
设置dev名字,调用kobject_add()将dev加入sysfs,调用device_create_file(dev, &uevent_attr)创建uevent属性。如果dev没有被分配设备号,调用device_create_file(dev, &devt_attr)添加dev属性,之后调用device_create_sys_dev_entry在/sys/dev下添加相应的软链接,最后调用devtmpfs_create_node在/dev下创建相应的设备文件。device_add_class_symlinks()主要是添加dev与class间的软链接,bus_add_device()将dev挂入到bus的设备链表中,之后dpm_sysfs_add增加dev下的power属性, device_pm_add将dev加入到dpm_list的链表中。kobject_uevent(&dev->kobj,KOBJ_ADD)会做kobject_uevent发布KOBJ_ADD消息,最后会进入重要的函数bus_probe_device(dev),为dev寻找合适的驱动。寻找到合适的驱动后,下面做的主要是如果有parent节点,把knode_parent挂入klist_children链表。如果dev有属于的class,也挂入到链表中,对device的分析至此结束。
int device_add(struct device *dev)
{
struct device *parent = NULL;
struct class_interface *class_intf;
int error = -EINVAL;
dev = get_device(dev); //增加dev的引用计数
if (!dev)
goto done;
if (!dev->p) {
error = device_private_init(dev);//分配和初始化dev->p
if (error)
goto done;
}
/*
* for statically allocateddevices, which should all be converted
* some day, we need toinitialize the name. We prevent reading back
* the name, and force the useof dev_name()
*/
if (dev->init_name) {
dev_set_name(dev, "%s", dev->init_name);//设置dev名字
dev->init_name = NULL;
}
if (!dev_name(dev)) {
error = -EINVAL;
goto name_error;
}
pr_debug("device: '%s':%s\n", dev_name(dev), __func__);
parent = get_device(dev->parent);
setup_parent(dev, parent);
/* use parent numa_node */
if (parent)
set_dev_node(dev, dev_to_node(parent));
/* first, register with genericlayer. */
/* we require the name to be setbefore, and pass NULL */
error = kobject_add(&dev->kobj, dev->kobj.parent, NULL); //将dev加入sysfs
if (error)
goto Error;
/* notify platform of device entry */
if (platform_notify)
platform_notify(dev);
error = device_create_file(dev, &uevent_attr);//创建uevent属性文件
if (error)
goto attrError;
if (MAJOR(dev->devt)) {
error = device_create_file(dev, &devt_attr);//如果没有设备号,就创建dev属性
if (error)
goto ueventattrError;
error =device_create_sys_dev_entry(dev); //创建软链接
if (error)
goto devtattrError;
devtmpfs_create_node(dev); //在dev下创建设备文件
}
error =device_add_class_symlinks(dev); //创建dev与class之间软链接
if (error)
goto SymlinkError;
error = device_add_attrs(dev); //添加device/type/class定义的属性集合
if (error)
goto AttrsError;
error = bus_add_device(dev); //将dev挂入到bus的设备链表中
if (error)
goto BusError;
error = dpm_sysfs_add(dev); //增加dev下的power属性
if (error)
goto DPMError;
device_pm_add(dev); //将dev加入到dpm_list的链表中
/* Notify clients of device addition.This call must come
* after dpm_sysf_add() andbefore kobject_uevent().
*/
if (dev->bus)
blocking_notifier_call_chain(&dev->bus->p->bus_notifier,
BUS_NOTIFY_ADD_DEVICE, dev);
kobject_uevent(&dev->kobj, KOBJ_ADD); //发布KOBJ_ADD消息
bus_probe_device(dev); //为dev寻找合适的驱动
if (parent)
klist_add_tail(&dev->p->knode_parent,
&parent->p->klist_children); //把knode_parent挂入klist_children链表
if (dev->class) {
mutex_lock(&dev->class->p->class_mutex);
/* tie the class to the device */
klist_add_tail(&dev->knode_class,
&dev->class->p->klist_devices);
//如果dev有属于的class,也挂入到链表中
/* notify any interfaces that thedevice is here */
list_for_each_entry(class_intf,
&dev->class->p->class_interfaces, node)
if (class_intf->add_dev)
class_intf->add_dev(dev, class_intf);
mutex_unlock(&dev->class->p->class_mutex);
}
done:
put_device(dev);
return error;
DPMError:
bus_remove_device(dev);
BusError:
device_remove_attrs(dev);
AttrsError:
device_remove_class_symlinks(dev);
SymlinkError:
if (MAJOR(dev->devt))
devtmpfs_delete_node(dev);
if (MAJOR(dev->devt))
device_remove_sys_dev_entry(dev);
devtattrError:
if (MAJOR(dev->devt))
device_remove_file(dev, &devt_attr);
ueventattrError:
device_remove_file(dev, &uevent_attr);
attrError:
kobject_uevent(&dev->kobj, KOBJ_REMOVE);
kobject_del(&dev->kobj);
Error:
cleanup_device_parent(dev);
if (parent)
put_device(parent);
name_error:
kfree(dev->p);
dev->p = NULL;
goto done;
}
device_create_file()创建属性文件。
int device_create_file(struct device *dev,
const struct device_attribute *attr)
{
int error = 0;
if (dev)
error = sysfs_create_file(&dev->kobj, &attr->attr);
return error;
}
device_add_class_symlinks()主要是添加dev与class间的软链接。
static int device_add_class_symlinks(struct device *dev)
{
int error;
if (!dev->class)
return 0;
error = sysfs_create_link(&dev->kobj,
&dev->class->p->subsys.kobj,
"subsystem");
if (error)
goto out;
if (dev->parent &&device_is_not_partition(dev)) {
error = sysfs_create_link(&dev->kobj, &dev->parent->kobj,
"device");
if (error)
goto out_subsys;
}
#ifdef CONFIG_BLOCK
/* /sys/block has directories anddoes not need symlinks */
if (sysfs_deprecated && dev->class == &block_class)
return 0;
#endif
/* link in the class directorypointing to the device */
error = sysfs_create_link(&dev->class->p->subsys.kobj,
&dev->kobj, dev_name(dev));
if (error)
goto out_device;
return 0;
out_device:
sysfs_remove_link(&dev->kobj, "device");
out_subsys:
sysfs_remove_link(&dev->kobj, "subsystem");
out:
return error;
}
下面来看看bus_add_device(),可以看出其只做了一个添加dev与bus间的软链接,并将dev挂到bus的设备链表中。
int bus_add_device(struct device *dev)
{
struct bus_type *bus = bus_get(dev->bus);
int error = 0;
if (bus) {
pr_debug("bus: '%s': adddevice %s\n", bus->name, dev_name(dev));
error = device_add_attrs(bus, dev);
if (error)
goto out_put;
error = sysfs_create_link(&bus->p->devices_kset->kobj,
&dev->kobj, dev_name(dev));
if (error)
goto out_id;
error = sysfs_create_link(&dev->kobj,
&dev->bus->p->subsys.kobj, "subsystem");//创建bus与dev的软链接
if (error)
goto out_subsys;
klist_add_tail(&dev->p->knode_bus, &bus->p->klist_devices); //挂入链表
}
return 0;
out_subsys:
sysfs_remove_link(&bus->p->devices_kset->kobj, dev_name(dev));
out_id:
device_remove_attrs(bus, dev);
out_put:
bus_put(dev->bus);
return error;
}
device_pm_add()将dev加入到dpm_list的链表中。
void device_pm_add(struct device *dev)
{
pr_debug("PM: Addinginfo for %s:%s\n",
dev->bus ? dev->bus->name : "No Bus", dev_name(dev));
mutex_lock(&dpm_list_mtx);
if (dev->parent && dev->parent->power.is_prepared)
dev_warn(dev, "parent %sshould not be sleeping\n",
dev_name(dev->parent));
list_add_tail(&dev->power.entry, &dpm_list);
dev_pm_qos_constraints_init(dev);
mutex_unlock(&dpm_list_mtx);
}
重要的函数bus_probe_device(dev)为dev寻找合适的驱动,调用device_attach()。
void bus_probe_device(struct device *dev)
{
struct bus_type *bus = dev->bus;
int ret;
if (bus && bus->p->drivers_autoprobe) {
ret = device_attach(dev);
WARN_ON(ret < 0);
}
}
当device被注册时候,会匹配drv是否有匹配的项,其匹配过程主要是调用__device_attach。
int device_attach(struct device *dev)
{
int ret = 0;
device_lock(dev);
if (dev->driver) {
if (klist_node_attached(&dev->p->knode_driver)) {
ret = 1;
goto out_unlock;
}
ret = device_bind_driver(dev);
if (ret == 0)
ret = 1;
else {
dev->driver = NULL;
ret = 0;
}
} else {
pm_runtime_get_noresume(dev);
ret = bus_for_each_drv(dev->bus, NULL, dev, __device_attach);
pm_runtime_put_sync(dev);
}
out_unlock:
device_unlock(dev);
return ret;
}
__device_attach()如果匹配成功,就会调用probe函数。
static int __device_attach(struct device_driver *drv, void *data)
{
struct device *dev = data;
if (!driver_match_device(drv, dev))
return 0;
return driver_probe_device(drv, dev);
}
先来看看匹配的过程,主要是调用bus的match函数。
static inline int driver_match_device(struct device_driver *drv,
struct device *dev)
{
return drv->bus->match ? drv->bus->match(dev, drv) : 1;
}
匹配成功后,调用probe函数,从下面可以看出如果bus的probe函数存在,调用bus的,如果不存在,则调用dev的probe函数。
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));
WARN_ON(!list_empty(&dev->devres_head));
dev->driver = drv;
if (driver_sysfs_add(dev)) {
printk(KERN_ERR "%s:driver_sysfs_add(%s) failed\n",
__func__, dev_name(dev));
goto probe_failed;
}
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;
if (ret != -ENODEV && ret != -ENXIO) {
/* driver matched but the probefailed */
printk(KERN_WARNING
"%s: probe of %s failed witherror %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;
}
module_init(my_device_init)
extern struct device my_bus;
extern struct bus_typemy_bus_type;
/* Why need this ?*/
static void my_dev_release(struct device *dev)
{
}
struct devicemy_dev = {
.bus = &my_bus_type,
.parent = &my_bus,
.release = my_dev_release,
};
/*
* Export a simple attribute.
*/
static ssize_tmydev_show(struct device *dev, char *buf)
{
return sprintf(buf, "%s\n", "This is mydevice!");
}
staticDEVICE_ATTR(dev, S_IRUGO, mydev_show, NULL);
static int __initmy_device_init(void)
{
int ret = 0;
/* 初始化设备 */
strncpy(my_dev.bus_id, "my_dev", BUS_ID_SIZE);
/*注册设备*/
device_register(&my_dev);
/*创建属性文件*/
device_create_file(&my_dev, &dev_attr_dev);
return ret;
}
static void my_device_exit(void)
{
device_unregister(&my_dev);
}
module_init(my_device_init);
module_exit(my_device_exit);