本文是基于mini2440开发板Linux版本号是linux-2.6.32.2的学习笔记
一. device结构体描述
struct device {
struct device *parent; //该设备的父设备,一般是该设备所从属的bus、controller等设备
struct device_private *p; //device的私有数据
struct kobject kobj; //该数据结构对应的struct kobject
const char *init_name; //该设备的名称
struct device_type *type;
struct semaphore sem;
struct bus_type *bus; //该device属于哪个总线
struct device_driver *driver; //该device对应的device driver
void *platform_data; //一个指针,用于保存具体的平台相关的数据
struct dev_pm_info power; //电源管理相关的逻辑
#ifdef CONFIG_NUMA
int numa_node;
#endif
u64 *dma_mask;
u64 coherent_dma_mask;
struct device_dma_parameters *dma_parms;
struct list_head dma_pools;
struct dma_coherent_mem *dma_mem;
/* arch specific additions */
struct dev_archdata archdata;
dev_t devt; //一个32位的整数,它由两个部分(Major和Minor)组成
spinlock_t devres_lock;
struct list_head devres_head;
struct klist_node knode_class;
struct class *class; //该设备属于哪个class
const struct attribute_group **groups; //该设备的默认attribute集合
void (*release)(struct device *dev);
}
二. device模块初始化
int __init devices_init(void)
{
devices_kset = kset_create_and_add("devices", &device_uevent_ops, NULL);
if (!devices_kset)
return -ENOMEM;
dev_kobj = kobject_create_and_add("dev", NULL);
if (!dev_kobj)
goto dev_kobj_err;
sysfs_dev_block_kobj = kobject_create_and_add("block", dev_kobj);
if (!sysfs_dev_block_kobj)
goto block_kobj_err;
sysfs_dev_char_kobj = kobject_create_and_add("char", dev_kobj);
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;
}
1.创建一个devices_kset,parent = NULL, kset->kobj.kset = NULL,相当于新建文件夹/sys/devices。
devices_kset = kset_create_and_add("devices", &device_uevent_ops, NULL);
uevent_ops = device_uevent_ops:
2.新建一个dev_kobj,相当于新建一个文件夹/sys/dev,然后在/sys/dev新建/sys/dev/block和/sys/dev/char文件夹。
dev_kobj = kobject_create_and_add("dev", NULL);
sysfs_dev_block_kobj = kobject_create_and_add("block", dev_kobj);
sysfs_dev_char_kobj = kobject_create_and_add("char", dev_kobj);
三. device引用计数
1.增加device引用计数
struct device *get_device(struct device *dev)
{
return dev ? to_dev(kobject_get(&dev->kobj)) : NULL;
}
2.减小device引用计数
void put_device(struct device *dev)
{
/* might_sleep(); */
if (dev)
kobject_put(&dev->kobj);
}
四. device_initialize函数
void device_initialize(struct device *dev)
{
dev->kobj.kset = devices_kset;
kobject_init(&dev->kobj, &device_ktype);
INIT_LIST_HEAD(&dev->dma_pools);
init_MUTEX(&dev->sem);
spin_lock_init(&dev->devres_lock);
INIT_LIST_HEAD(&dev->devres_head);
device_init_wakeup(dev, 0);
device_pm_init(dev);
set_dev_node(dev, -1);
}
dev->kobj.kset = devices_kset,而devices_kset = device_uevent_ops,如下所示:
static struct kset_uevent_ops device_uevent_ops = {
.filter = dev_uevent_filter,
.name = dev_uevent_name,
.uevent = dev_uevent,
};
dev_uevent_filter函数过滤kobj上抛的消息。
static int dev_uevent_filter(struct kset *kset, struct kobject *kobj)
{
struct kobj_type *ktype = get_ktype(kobj);
if (ktype == &device_ktype) {
struct device *dev = to_dev(kobj);
if (dev->bus)
return 1;
if (dev->class)
return 1;
}
return 0;
}
dev_uevent函数主要把device的信息保存在环境变量中,如主设备号,次设备号,设备名字,用户权限等。
static int dev_uevent(struct kset *kset, struct kobject *kobj, struct kobj_uevent_env *env)
{
struct device *dev = to_dev(kobj);
int retval = 0;
/* add device node properties if present */
if (MAJOR(dev->devt)) {
const char *tmp;
const char *name;
mode_t mode = 0;
add_uevent_var(env, "MAJOR=%u", MAJOR(dev->devt));
add_uevent_var(env, "MINOR=%u", MINOR(dev->devt));
name = device_get_devnode(dev, &mode, &tmp);
if (name) {
add_uevent_var(env, "DEVNAME=%s", name);
kfree(tmp);
if (mode)
add_uevent_var(env, "DEVMODE=%#o", mode & 0777);
}
}
if (dev->type && dev->type->name)
add_uevent_var(env, "DEVTYPE=%s", dev->type->name);
if (dev->driver)
add_uevent_var(env, "DRIVER=%s", dev->driver->name);
#ifdef CONFIG_SYSFS_DEPRECATED
if (dev->class) {
struct device *parent = dev->parent;
/* find first bus device in parent chain */
while (parent && !parent->bus)
parent = parent->parent;
if (parent && parent->bus) {
const char *path;
path = kobject_get_path(&parent->kobj, GFP_KERNEL);
if (path) {
add_uevent_var(env, "PHYSDEVPATH=%s", path);
kfree(path);
}
add_uevent_var(env, "PHYSDEVBUS=%s", parent->bus->name);
if (parent->driver)
add_uevent_var(env, "PHYSDEVDRIVER=%s",
parent->driver->name);
}
} else if (dev->bus) {
add_uevent_var(env, "PHYSDEVBUS=%s", dev->bus->name);
if (dev->driver)
add_uevent_var(env, "PHYSDEVDRIVER=%s",
dev->driver->name);
}
#endif
/* have the bus specific function add its stuff */
if (dev->bus && dev->bus->uevent) {
retval = dev->bus->uevent(dev, env);
if (retval)
pr_debug("device: '%s': %s: bus uevent() returned %d\n",
dev_name(dev), __func__, retval);
}
/* have the class specific function add its stuff */
if (dev->class && dev->class->dev_uevent) {
retval = dev->class->dev_uevent(dev, env);
if (retval)
pr_debug("device: '%s': %s: class uevent() "
"returned %d\n", dev_name(dev),
__func__, retval);
}
/* have the device type specific fuction add its stuff */
if (dev->type && dev->type->uevent) {
retval = dev->type->uevent(dev, env);
if (retval)
pr_debug("device: '%s': %s: dev_type uevent() "
"returned %d\n", dev_name(dev),
__func__, retval);
}
return retval;
}
device_ktype定义如下:
static struct kobj_type device_ktype = {
.release = device_release,
.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 = 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;
}
static ssize_t dev_attr_store(struct kobject *kobj, struct attribute *attr, const char *buf, size_t count)
{
struct device_attribute *dev_attr = to_dev_attr(attr);
struct device *dev = to_dev(kobj);
ssize_t ret = -EIO;
if (dev_attr->store)
ret = dev_attr->store(dev, dev_attr, buf, count);
return ret;
}
对device属性文件的读写最终调用dev_attr->show和dev_attr->store两个函数。
五. 添加一个device
int device_add(struct device *dev)
{
struct device *parent = NULL;
struct class_interface *class_intf;
int error = -EINVAL;
dev = get_device(dev);
if (!dev)
goto done;
if (!dev->p) {
error = device_private_init(dev);
if (error)
goto done;
}
/*
* for statically allocated devices, which should all be converted
* some day, we need to initialize the name. We prevent reading back
* the name, and force the use of dev_name()
*/
if (dev->init_name) {
dev_set_name(dev, "%s", dev->init_name);
dev->init_name = NULL;
}
if (!dev_name(dev))
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 generic layer. */
/* we require the name to be set before, and pass NULL */
error = kobject_add(&dev->kobj, dev->kobj.parent, NULL);
if (error)
goto Error;
/* notify platform of device entry */
if (platform_notify)
platform_notify(dev);
error = device_create_file(dev, &uevent_attr);
if (error)
goto attrError;
if (MAJOR(dev->devt)) {
error = device_create_file(dev, &devt_attr);
if (error)
goto ueventattrError;
error = device_create_sys_dev_entry(dev);
if (error)
goto devtattrError;
devtmpfs_create_node(dev);
}
error = device_add_class_symlinks(dev);
if (error)
goto SymlinkError;
error = device_add_attrs(dev);
if (error)
goto AttrsError;
error = bus_add_device(dev);
if (error)
goto BusError;
error = dpm_sysfs_add(dev);
if (error)
goto DPMError;
device_pm_add(dev);
/* Notify clients of device addition. This call must come
* after dpm_sysf_add() and before 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);
bus_probe_device(dev);
if (parent)
klist_add_tail(&dev->p->knode_parent,
&parent->p->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->class_devices);
/* notify any interfaces that the device 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))
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;
}
1.增加dev的引用计数
dev = get_device(dev);
2.dev的私有数据的内存申请和部分初始化,dev->p表示dev的私有数据。
if (!dev->p)
{
error = device_private_init(dev);
}
3.设置dev->kobj的名称,设置为dev->init_name
dev_set_name(dev, "%s", dev->init_name);
4.获得dev的parent,同时将dev->parent引用计数加1
parent = get_device(dev->parent);
5.获取父kobj
setup_parent(dev, parent);
6.把内嵌的kobject注册到设备模型中将设备加入到kobject模型中,创建sys相关目录 ,目录名字为kobj->name
error = kobject_add(&dev->kobj, dev->kobj.parent, NULL);
7.在dev目录下创建uevent文件
error = device_create_file(dev, &uevent_attr);
8.看一下下面这段代码
if (MAJOR(dev->devt)) {
error = device_create_file(dev, &devt_attr);
if (error)
goto ueventattrError;
error = device_create_sys_dev_entry(dev);
if (error)
goto devtattrError;
devtmpfs_create_node(dev);
}
MAJOR(dev->devt表示主设备是否存在。
device_create_file(dev, &devt_attr)在本文件夹内创建dev文件。
static int device_create_sys_dev_entry(struct device *dev)
{
struct kobject *kobj = device_to_dev_kobj(dev);
int error = 0;
char devt_str[15];
if (kobj) {
format_dev_t(devt_str, dev->devt);
error = sysfs_create_link(kobj, &dev->kobj, devt_str);
}
return error;
}
static struct kobject *device_to_dev_kobj(struct device *dev)
{
struct kobject *kobj;
if (dev->class)
kobj = dev->class->dev_kobj;
else
kobj = sysfs_dev_char_kobj;
return kobj;
}
device_to_dev_kobj实际上得到的是上面的sysfs_dev_char_kobj(块设备除外),该kobj所在的目录为/sys/dev/char。
format_dev_t(devt_str, dev->devt)函数,得到devt_str = “主设备:次设备”这种字符串。
device_create_sys_dev_entry函数在/sys/dev/char目录新建名称为 “主设备:次设备”的软链接,链接到/sys/devices/设备所在的目录,如图所示:
devtmpfs_create_node函数是tmp文件系统相关的,暂时不看。
9.在class目录下创建符号链接,链接device所在的目录。
error = device_add_class_symlinks(dev);
error = sysfs_create_link(&dev->kobj, &dev->class->p->class_subsys.kobj, "subsystem");
error = sysfs_create_link(&dev->class->p->class_subsys.kobj, &dev->kobj, dev_name(dev));
10.创建设备属性文件
error = device_add_attrs(dev);
error = device_add_attributes(dev, class->dev_attrs);
device_add_groups(dev, type->groups);
device_add_groups(dev, dev->groups);
这里要注意,class->class_attrs在__class_register函数中注册,bus->dev_attrs在bus_add_device函数中注册。
class->class_attrs,class->dev_attrs,bus->bus_attrs,bus->dev_attrs这几个属性比较容易混淆。
11.添加设备到总线
error = bus_add_device(dev);
int bus_add_device(struct device *dev)
{
struct bus_type *bus = bus_get(dev->bus);
int error = 0;
if (bus) {
pr_debug("bus: '%s': add device %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");
if (error)
goto out_subsys;
error = make_deprecated_bus_links(dev);
if (error)
goto out_deprecated;
klist_add_tail(&dev->p->knode_bus, &bus->p->klist_devices);
}
return 0;
out_deprecated:
sysfs_remove_link(&dev->kobj, "subsystem");
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的bus属性文件;
在/sys/bus/platform/devices目录下创建一个软链接,假设device是led,那么可以得到:
/sys/bus/platform/devices/led ------->/sys/devices/platform/led;
在/sys/devices/platform/led目录新建一个软链接,名字是“subsystem”,软链接到/sys/bus/platform;
将dev放入bus的device链表中;
12.dpm_sysfs_add和device_pm_add函数,电源管理相关的函数,暂时不看。
13.通知客户端,有新设备加入
if (dev->bus)
blocking_notifier_call_chain(&dev->bus->p->bus_notifier, BUS_NOTIFY_ADD_DEVICE, dev);
14.产生一个uevent事件,有设备加入
kobject_uevent(&dev->kobj, KOBJ_ADD);
15.给设备匹配驱动程序
bus_probe_device(dev);
16.将该设备添加到其父设备的子列表中
if (parent)
klist_add_tail(&dev->p->knode_parent, &parent->p->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->class_devices);
/* notify any interfaces that the device 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);
}
dev添加到class的klist_device链表
klist_add_tail(&dev->knode_class, &dev->class->p->class_devices);
通知有新设备加入,执行该dev的class_intf->add_dev()函数
if (class_intf->add_dev)
class_intf->add_dev(dev, class_intf);
六. 创建一个device
创建一个device调用的函数是device_create
struct device *device_create(struct class *class, struct device *parent, dev_t devt, void *drvdata, const char *fmt, ...)
dev = device_create_vargs(class, parent, devt, drvdata, fmt, vargs);
dev = kzalloc(sizeof(*dev), GFP_KERNEL);
retval = device_register(dev);
先申请一个device,然后再调用device_register注册一个device。
七. 注册一个device
注册一个device调用函数device_register
int device_register(struct device *dev)
{
device_initialize(dev);
return device_add(dev);
}
先初始化device的部分成员,然后调用上面的device_add函数。
device_register注册一个已知的device,可以被device_create调用,也可以被其他各个模块调用注册一个device。
参考博客:https://blog.csdn.net/qq_16777851/article/details/81437352