Linux设备驱动编程模型之设备篇

时间:2022-01-30 19:03:36
  设备驱动程序模型建立在几个基本数据结构上,这些结构描述了总线、设备、设备驱动、属性以及他们之间的关系。我们首先认识一下这些数据结构。  

一、数据结构

设备表述符

struct device {
struct device*parent;/*指向父设备的指针*/
/*该字段用于管理device和其他device结构,一起device
与其他结构之间的关系*/
struct device_private*p;

struct kobject kobj;/*内嵌的kobject结构*/
const char*init_name; /* initial name of the device */
struct device_type*type;

struct semaphoresem;/* semaphore to synchronize calls to
* its driver.
*/

struct bus_type*bus;/*指向所连接总线的指针*//* type of bus device is on */
struct device_driver *driver;/*指向控制设备驱动程序的指针*//* which driver has allocated this
device */
void*platform_data;/*指向遗留设备驱动程序的私有数据的指针*//* Platform specific data, device
core doesn't touch it */
struct dev_pm_infopower;

#ifdef CONFIG_NUMA
intnuma_node;/* NUMA node this device is close to */
#endif
u64*dma_mask;/* dma mask (if dma'able device) */
u64coherent_dma_mask;/* Like dma_mask, but for
alloc_coherent mappings as
not all hardware supports
64 bit addresses for consistent
allocations such descriptors. */

struct device_dma_parameters *dma_parms;

struct list_headdma_pools;/*聚集的DMA缓冲池链表的首部*//* dma pools (if dma'ble) */

struct dma_coherent_mem*dma_mem;/*指向设备所使用的一致性DMA存储器描述符的指针*//* internal for coherent mem
override */
/* arch specific additions */
struct dev_archdataarchdata;

dev_tdevt;/* dev_t, creates the sysfs "dev" */

spinlock_tdevres_lock;
struct list_headdevres_head;

struct klist_nodeknode_class;
struct class*class;
const struct attribute_group **groups;/* optional groups */

void(*release)(struct device *dev);/*释放设备描述符的回调函数*/
};

Bus描述符

struct bus_type {
const char*name;/*总线类型名称*/
/*指向对象的指针,该对象包含总线属性和用于导出此属性到sysfs文件系统的方法*/
struct bus_attribute*bus_attrs;
/*指向对象的指针,该对象包含设备属性和用于导出此属性sysfs文件系统的方法*/
struct device_attribute*dev_attrs;
/*指向对象的指针,该对象包含设备驱动程序属性和用于导出此属性到sysfs文件
的方法*/
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 bus_type_private *p;
};

设备驱动

/*设备驱动程序模型中的每个驱动程序*/
struct device_driver {
const char*name;/*设备驱动程序的名称*/
struct bus_type*bus;/*指向总线描述符的指针,总线连接所支持的设备*/

struct module*owner;/*标识实现设备程序的模块,如果有的话*/
const char*mod_name;/* used for built-in modules */

bool suppress_bind_attrs;/* disables bind/unbind via sysfs */

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;
};

类描述符

/*
* device classes
*/
struct class {
const char*name;
struct module*owner;

struct class_attribute*class_attrs;
struct device_attribute*dev_attrs;
struct kobject*dev_kobj;

int (*dev_uevent)(struct device *dev, struct kobj_uevent_env *env);
char *(*devnode)(struct device *dev, mode_t *mode);

void (*class_release)(struct class *class);
void (*dev_release)(struct device *dev);

int (*suspend)(struct device *dev, pm_message_t state);
int (*resume)(struct device *dev);

const struct dev_pm_ops *pm;

struct class_private *p;
};

下面数据结构用以描述结构间关系的

/**
* struct device_private - structure to hold the private to the driver core portions of the device structure.
*
* @klist_children - klist containing all children of this device
* @knode_parent - node in sibling list
* @knode_driver - node in driver list
* @knode_bus - node in bus list
* @driver_data - private pointer for driver specific info. Will turn into a
* list soon.
* @device - pointer back to the struct class that this structure is
* associated with.
*
* Nothing outside of the driver core should ever touch these fields.
*/
struct device_private {
struct klist klist_children;
struct klist_node knode_parent;
struct klist_node knode_driver;
struct klist_node knode_bus;
void *driver_data;
struct device *device;
};
struct class_private {struct kset class_subsys;struct klist class_devices;struct list_head class_interfaces;struct kset class_dirs;struct mutex class_mutex;struct class *class;};
/** * struct bus_type_private - structure to hold the private to the driver core portions of the bus_type structure. * * @subsys - the struct kset that defines this bus.  This is the main kobject * @drivers_kset - the list of drivers associated with this bus * @devices_kset - the list of devices associated with this bus * @klist_devices - the klist to iterate over the @devices_kset * @klist_drivers - the klist to iterate over the @drivers_kset * @bus_notifier - the bus notifier list for anything that cares about things * on this bus. * @bus - pointer back to the struct bus_type that this structure is associated * with. * * This structure is the one that is the actual kobject allowing struct * bus_type to be statically allocated safely.  Nothing outside of the driver * core should ever touch these fields. */struct bus_type_private {struct kset subsys;struct kset *drivers_kset;struct kset *devices_kset;struct klist klist_devices;struct klist klist_drivers;struct blocking_notifier_head bus_notifier;unsigned int drivers_autoprobe:1;struct bus_type *bus;};struct driver_private {struct kobject kobj;struct klist klist_devices;struct klist_node knode_bus;struct module_kobject *mkobj;struct device_driver *driver;};

描述属性文件的结构

/* FIXME
* The *owner field is no longer used.
* x86 tree has been cleaned up. The owner
* attribute is still left for other arches.
*/
struct attribute {
const char*name;
struct module*owner;
mode_tmode;
};

后面有时间我会补上上面数据结构之间的关系图,便于理解。

二、原理与源码解析

解决问题我还是习惯追根溯源,我们从内核启动对设备驱动模型初始化开始看起,设备驱动中sys/文件系统的初始化工作为start_kernel()->rest_init()->

static noinline void __init_refok rest_init(void)
__releases(kernel_lock)
{
……
/*创建并启动内核线程kernel_init用于初始化一些
内核部件工作*/
kernel_thread(kernel_init, NULL, CLONE_FS | CLONE_SIGHAND);
……
}

内核线程kernel_init()用于驱动的初始化

kernel_init()->do_basic_setup()->driver_init()

/**
* driver_init - initialize driver model.
*
* Call the driver model init functions to initialize their
* subsystems. Called early from init/main.c.
*/
/*在内核初始化时的线程中调用*/
void __init driver_init(void)
{
/* These are the core pieces */
/*devtmpfs 的功用是在 Linux 核心 启动早期建立一个初步的 /dev,
令一般启动程序不用等待 udev,缩短 GNU/Linux 的开机时间。
devtmpfs 在 2009 年初被提出,并在同年年尾进出的 Linux 2.6.32 正式收录
*/
devtmpfs_init();
/*设备相关初始化*/
devices_init();
/*bus初始化*/
buses_init();
/*class初始化*/
classes_init();
/*firmware初始化*/
firmware_init();
/*虚拟化平台hypervisor初始化。虚拟化 就是通过某种方式隐藏底层物理硬件
的过程,从而让多个操作系统可以透明地使用和共享它。这种
架构的另一个更常见的名称是平台虚拟化。在典型的分层架构
中,提供平台虚拟化的层称为 hypervisor (有时称为虚拟机管理程
序 或 VMM)。来宾操作系统称为虚拟机(VM),因为对这些 VM
而言,硬件是专门针对它们虚拟化的。
*/
hypervisor_init();

/* These are also core pieces, but must come after the
* core core pieces.
*/
platform_bus_init();
/*初始化system_bus,即在sys/devices目录下创建system目录*/
system_bus_init();
/*cpu_dev初始化*/
cpu_dev_init();
/*在/sys/devices/system目录下创建memory目录文件
该函数需要定义的相关宏在本机器上没有定义
*/
memory_dev_init();
}
/*在sys文件系统中建立几个文件夹*/int __init devices_init(void){/*根目录sys下创建devices文件夹*/devices_kset = kset_create_and_add("devices", &device_uevent_ops, NULL);if (!devices_kset)return -ENOMEM;/*根目录下创建dev文件夹*/dev_kobj = kobject_create_and_add("dev", NULL);if (!dev_kobj)goto dev_kobj_err;/*在dev文件夹下创建block文件夹*/sysfs_dev_block_kobj = kobject_create_and_add("block", dev_kobj);if (!sysfs_dev_block_kobj)goto block_kobj_err;/*在dev文件夹下创建char文件夹*/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;}

上面代码执行后生成的目录如下图

/sys/下的dev/和devices/目录

Linux设备驱动编程模型之设备篇

/sys/dev/下的block/和char/目录

Linux设备驱动编程模型之设备篇

总线初始化

int __init buses_init(void)
{
/*在sys文件下创建bus文件夹*/
bus_kset = kset_create_and_add("bus", &bus_uevent_ops, NULL);
if (!bus_kset)
return -ENOMEM;
return 0;
}

运行结果图,在/sys/目录下生成/bus/目录

Linux设备驱动编程模型之设备篇

Class初始化

int __init classes_init(void)
{
/*在sys文件夹下创建class文件夹*/
class_kset = kset_create_and_add("class", NULL, NULL);
if (!class_kset)
return -ENOMEM;
return 0;
}

运行结果,在/sys/目录下生成/class/目录

Linux设备驱动编程模型之设备篇

Firmware初始化

int __init firmware_init(void)
{
/*在sys文件夹下创建firmware文件夹*/
firmware_kobj = kobject_create_and_add("firmware", NULL);
if (!firmware_kobj)
return -ENOMEM;
return 0;
}

运行结果,在/sys/目录下生成/firmware/目录

Linux设备驱动编程模型之设备篇

Hypervisor初始化,该功能需要内核编译选项支持,该函数需要定义CONFIG_SYS_HYPERVISOR宏,在我机器上没有编译此功能。

int __init hypervisor_init(void)
{
/*在sys文件夹下创建hypervisor文件夹*/
hypervisor_kobj = kobject_create_and_add("hypervisor", NULL);
if (!hypervisor_kobj)
return -ENOMEM;
return 0;
}

Platform_bus初始化,下面会详细讲解

int __init platform_bus_init(void)
{
int error;
/*清空early_platform_device_list链表*/
early_platform_cleanup();
/*注册platform_bus*/
error = device_register(&platform_bus);
if (error)
return error;
/*bus注册*/
error = bus_register(&platform_bus_type);
if (error)
device_unregister(&platform_bus);
return error;
}
struct device platform_bus = {/*这个名称将变成"/sys/devices/platform"*/.init_name= "platform",};

注册platform_bus设备

platform_bus_init()->device_register()

int device_register(struct device *dev)
{
device_initialize(dev);
return device_add(dev);
}

设备初始化

/**
* device_initialize - init device structure.
* @dev: device.
*
* This prepares the device for use by other layers by initializing
* its fields.
* It is the first half of device_register(), if called by
* that function, though it can also be called separately, so one
* may use @dev's fields. In particular, get_device()/put_device()
* may be used for reference counting of @dev after calling this
* function.
*
* NOTE: Use put_device() to give up your reference instead of freeing
* @dev directly once you have called this function.
*/
/*初始化device结构*/
void device_initialize(struct device *dev)
{
/*所属为devices_kset,所以他的父kobj为/sys/devices/*/
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);/*设置power结构的wake相关两个属性为假*/
device_pm_init(dev);/*初始化dev的power属性*/
set_dev_node(dev, -1);/*CONFIG_NUMA结构下才有意义*/
}

添加设备到设备树中,我们结合实际的实现结果分析

int device_add(struct device *dev)

{

struct device *parent = NULL;

struct class_interface *class_intf;/*class接口*/

int error = -EINVAL;

dev = get_device(dev);/*增加devkobject的引用计数*/

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) {/*将名称保存到kobj*/

dev_set_name(dev, "%s", dev->init_name);

dev->init_name = NULL;

}

if (!dev_name(dev))/*kobj中获取上面设置的名称*/

goto name_error;

pr_debug("device: '%s': %s\n", dev_name(dev), __func__);

/*得到设备的父设备,并增加其引用计数*/

parent = get_device(dev->parent);

/*设置设备的obj对象树,下面详细讲*/

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 */

/*obj对象添加到对象树中,对于这里的意思即platform添加到sys目录下*/

error = kobject_add(&dev->kobj, dev->kobj.parent, NULL);

实现效果如下图中的/sys/devices/目录下生成platform文件夹


Linux设备驱动编程模型之设备篇

if (error)

goto Error;

/* notify platform of device entry */

if (platform_notify)

platform_notify(dev);

/*为文件创建dient对象(其实就是属性),并加入到dient对象树中*/

/*在对应设备下面创建uevent文件*/

error = device_create_file(dev, &uevent_attr);

如下图,在/sys/devices/platform下生成uevent文件

Linux设备驱动编程模型之设备篇

if (error)

goto attrError;

/*如果存在主设备号,在我们的情形中没有主设备号*/

if (MAJOR(dev->devt)) {

/*创建dient对象,用指定的属性,即生成一个dev文件*/

error = device_create_file(dev, &devt_attr);

如果有主设备号,即在dev->kobj下生成dev文件,例如,/sys/devices/virtual/mem/random具有的主设备号为1,次设备号为8,在/sys/devices/virtual/mem/random目录下生成dev文件:

 Linux设备驱动编程模型之设备篇

其中dev文件为文本文件,保存内容为major:minor,例子中为18

Linux设备驱动编程模型之设备篇

if (error)

goto ueventattrError;

/*创建/dev符号链接(就是在dient树种创建符号链接树)*/

/*根据设备类型,在sys/dev/charblock或其属性指定的dev_obj目录下生成链接文件,其名字的样式为major:minor,18,指向dev主目录*/

error = device_create_sys_dev_entry(dev);

继续上面的例子,在这里为在/sys/dev/char/下生成1:8/文件夹链接到/sys/devices/virtual/mem/random目录:

Linux设备驱动编程模型之设备篇

if (error)

goto devtattrError;

devtmpfs_create_node(dev);

}

/*添加链接文件,devclass存在时有效,在后面详细讲*/

error = device_add_class_symlinks(dev);

if (error)

goto SymlinkError;

/*实际负责device中的属性添加。也是几个部分的集合,

包括class中的dev_attrsdevice_type中的groups,还有device

身的groups*/

error = device_add_attrs(dev);

if (error)

goto AttrsError;

/*add device to bus*/

/*添加devicebus,建立busdevice之间的联系,后面详细将*/

error = bus_add_device(dev);

if (error)

goto BusError;

/*dev->kobj目录下生成power目录,用于电源管理*/

error = dpm_sysfs_add(dev);

dev->kobj下会生成一个power文件夹用于电源的管理,例如我们这里会在platform

Linux设备驱动编程模型之设备篇

if (error)

goto DPMError;

/*添加设备到PM链表*/

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);

}

……

}

下面为一些辅助函数的实现

int device_private_init(struct device *dev)
{
/*从cache中分配private的空间*/
dev->p = kzalloc(sizeof(*dev->p), GFP_KERNEL);
if (!dev->p)
return -ENOMEM;
dev->p->device = dev;/*设置dev private属性的device为自身*/
/*初始化klist_children属性,后面传入的两个参数为
get和put函数的实现,分别为对引用计数的增加和
减少*/
klist_init(&dev->p->klist_children, klist_children_get,
klist_children_put);
return 0;
}
static struct kobject *get_device_parent(struct device *dev, struct device *parent){int retval;if (dev->class) {/*如果device存在class*/struct kobject *kobj = NULL;struct kobject *parent_kobj;struct kobject *k;/* * If we have no parent, we live in "virtual". * Class-devices with a non class-device as parent, live * in a "glue" directory to prevent namespace collisions. */if (parent == NULL)/*如果没有父设备,创建一个'virtual'的kobj,这里可以看出,当没有父设备时,class设备可以用'virtual'(即非class设备)作为父节点kobj的树而非class设备不行*/parent_kobj = virtual_device_parent(dev);else if (parent->class)/*当父设备也是class设备时,直接返回其kobj*/return &parent->kobj;else/*父设备存在,但是不是class设备*/parent_kobj = &parent->kobj;/* find our class-directory at the parent and reference it */spin_lock(&dev->class->p->class_dirs.list_lock);list_for_each_entry(k, &dev->class->p->class_dirs.list, entry)if (k->parent == parent_kobj) {/*遍历同类中的kobj,找到符合的*/kobj = kobject_get(k);/*递增其引用计数*/break;}spin_unlock(&dev->class->p->class_dirs.list_lock);if (kobj)return kobj;/* or create a new class-directory at the parent device *//*程序运行到这里表示没有在dev->class中找到k->parent==parent_kobj的kobj,所以创建他*/k = kobject_create();if (!k)return NULL;/*初始化创建kobj的kset为dev中class->p->class_dirs*/k->kset = &dev->class->p->class_dirs;/*添加kobj到其parent的kobj树中,parent字段设置为parent_kobj*/retval = kobject_add(k, parent_kobj, "%s", dev->class->name);if (retval < 0) {kobject_put(k);return NULL;}/* do not emit an uevent for this simple "glue" directory */return k;}if (parent)/*如果parent存在,返回父设备的kobj*/return &parent->kobj;return NULL;}
static struct kobject *virtual_device_parent(struct device *dev){static struct kobject *virtual_dir = NULL;if (!virtual_dir)/*创建名为virtual的kobj加入到设备的集合树中*/virtual_dir = kobject_create_and_add("virtual",     &devices_kset->kobj);/*返回创建的kobj*/return virtual_dir;}

我们看看具体在Linux下的相关目录:
Linux设备驱动编程模型之设备篇


Linux设备驱动编程模型之设备篇

设备与class关联

static int device_add_class_symlinks(struct device *dev)

{

int error;

/*没有class的情况直接返回*/

if (!dev->class)

return 0;

/*在dev->kobj目录下生成一个名为'subsystem'的链接文件,指向其所属的classsys的目录/sys/class/***,例如,接着上面的例子

*/

error = sysfs_create_link(&dev->kobj,

  &dev->class->p->class_subsys.kobj,

  "subsystem");

生成下图:

Linux设备驱动编程模型之设备篇

……

}

device关联bus

int bus_add_device(struct device *dev)

{

struct bus_type *bus = bus_get(dev->bus);

int error = 0;

/*如果bus存在,才执行下面部分*/

if (bus) {

pr_debug("bus: '%s': add device %s\n", bus->name, dev_name(dev));

/*添加属性文件,该属性文件问bus的属性*/

error = device_add_attrs(bus, dev);

if (error)

goto out_put;

/*busdevices目录下生成到dev->kobj的链接,名称为dev->kobj的名称*/

error = sysfs_create_link(&bus->p->devices_kset->kobj,

&dev->kobj, dev_name(dev));

例如,我们看/sys/devices/platform/devices/下的serial8250,为链接到/sys/devices/platform/serial8250

Linux设备驱动编程模型之设备篇

if (error)

goto out_id;

/*dev->kobj目录下subsystem为生成的到bus 下相关

目录的链接*/

error = sysfs_create_link(&dev->kobj,

&dev->bus->p->subsys.kobj, "subsystem");

我们看是考虑前面的serial8250文件,可见其subsystem目录链接到/sys/bus/platform/

Linux设备驱动编程模型之设备篇

if (error)

goto out_subsys;

/*dev->kobj目录下的bus生成到bus主目录的链接。

在我机器上没有定义相关的宏*/

error = make_deprecated_bus_links(dev);

if (error)

goto out_deprecated;

/*添加bus的设备链表到dev下的bus链表上*/

klist_add_tail(&dev->p->knode_bus, &bus->p->klist_devices);

}

return 0;

……

}

设备与驱动关联:

/**
* bus_probe_device - probe drivers for a new device
* @dev: device to probe
*
* - Automatically probe for a driver if the bus allows it.
*/
/*在设备关联的bus存在并且该bus支持驱动动态探测的情况下才有效*/
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_attach()函数最终调用driver_sysfs_add()函数实现实际工作

static int driver_sysfs_add(struct device *dev)

{

int ret;

/*驱动目录下dev->kobj目录链接到dev->kobj*/

ret = sysfs_create_link(&dev->driver->p->kobj, &dev->kobj,

  kobject_name(&dev->kobj));

我门仍然以serial8250为例子来说明,生成的链接如下图:

Linux设备驱动编程模型之设备篇

if (ret == 0) {

        /*dev->kobj目录下的driver目录链接到其驱动目录*/

ret = sysfs_create_link(&dev->kobj, &dev->driver->p->kobj,

"driver");

生成链接结果如下图所示:

Linux设备驱动编程模型之设备篇

if (ret)

sysfs_remove_link(&dev->driver->p->kobj,

kobject_name(&dev->kobj));

}

return ret;

}

注册bus

struct bus_type platform_bus_type = {
.name= "platform",/*生成的为/sys/bus/platform文件夹*/
.dev_attrs= platform_dev_attrs,
.match= platform_match,
.uevent= platform_uevent,
.pm= &platform_dev_pm_ops,
};
int bus_register(struct bus_type *bus){int retval;struct bus_type_private *priv;/*为priv申请内存空间*/priv = kzalloc(sizeof(struct bus_type_private), GFP_KERNEL);if (!priv)return -ENOMEM;/*添加与bus之间的联系*/priv->bus = bus;bus->p = priv;BLOCKING_INIT_NOTIFIER_HEAD(&priv->bus_notifier);/*设置priv的subsys的名称为bus的名字*/retval = kobject_set_name(&priv->subsys.kobj, "%s", bus->name);if (retval)goto out;/*设置kset 与ktype,同设备,这里的parent为kset的kobj属性*/priv->subsys.kobj.kset = bus_kset;priv->subsys.kobj.ktype = &bus_ktype;priv->drivers_autoprobe = 1;/*注册对应的kset,将其添加到sd树中*/retval = kset_register(&priv->subsys);if (retval)goto out;/*创建属性文件uevent*/retval = bus_create_file(bus, &bus_attr_uevent);if (retval)goto bus_uevent_fail;/*在priv->subsys.kobj目录下生成devices目录*/priv->devices_kset = kset_create_and_add("devices", NULL, &priv->subsys.kobj);if (!priv->devices_kset) {retval = -ENOMEM;goto bus_devices_fail;}/*在priv->subsys.kobj目录下生成drivers文件目录*/priv->drivers_kset = kset_create_and_add("drivers", NULL, &priv->subsys.kobj);if (!priv->drivers_kset) {retval = -ENOMEM;goto bus_drivers_fail;} /*初始化priv的相关链接*/klist_init(&priv->klist_devices, klist_devices_get, klist_devices_put);klist_init(&priv->klist_drivers, NULL, NULL);/*在bus的目录下生成两个属性文件drivers_probe和drivers_autoprobe*/retval = add_probe_files(bus);if (retval)goto bus_probe_files_fail;/*如果该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);kfree(bus->p);out:bus->p = NULL;return retval;}

实现结果如下图,在/sys/bus/platform目录下生成driversdevices两个文件夹,uevent、drivers_probedrivers_autoprobe三个属性
Linux设备驱动编程模型之设备篇

初始化system_bus,实现结果为在/sys/devices/下创建system文件夹

int __init system_bus_init(void)
{
system_kset = kset_create_and_add("system", NULL, &devices_kset->kobj);
if (!system_kset)
return -ENOMEM;
return 0;
}

Linux设备驱动编程模型之设备篇

cpu_dev初始化

int __init cpu_dev_init(void)
{
int err;
/*注册cpu,即在/sys/devices/system目录下创建cpu目录文件*/
err = sysdev_class_register(&cpu_sysdev_class);
if (!err)
err = cpu_states_init();

#if defined(CONFIG_SCHED_MC) || defined(CONFIG_SCHED_SMT)
if (!err)
err = sched_create_sysfs_power_savings_entries(&cpu_sysdev_class);
#endif

return err;
}

实现结果如下图,在/sys/devices/system下生成cpu文件夹

Linux设备驱动编程模型之设备篇
分析了内核怎样对设备驱动模型初始化后对驱动模型的理解应该有一个宏观的概念了,涉及到实际驱动的编写,每种设备有各自的一套机制,但原理上都大同小异。

 欢迎转载,转载请注明出处