Linux设备驱动模型1
@
接下来介绍Linux内核中的驱动程序模型、sysfs文件系统、平台设备模型、设备树等内容。
1. 内核对象
1.1 kobject
kobject(内核对象)是 Linux 内核设备管理机制的最高层抽象。kobject 类似面向对象体
系中的基类,它往往被嵌入到其他结构体中,形成一个复杂的多层次结构。kobject 本身对应
着 sysfs 文件系统中的一个目录。kobject 还负责设备热插拔等事件的处理工作。kobject 结构
定义如下:
struct kobject
{
const char *name; // 名称
struct list_head entry;
struct kobject *parent; // 父对象
struct kset *kset; // 所属的 kset
struct kobj_type *ktype; // 对象的类型
struct kernfs_node *sd; /*sysfs 目录项*/
struct kref kref; // 引用计数
#ifdef CONFIG_DEBUG_KOBJECT_RELEASE
struct delayed_work release;
#endif
unsigned int state_initialized:1;
unsigned int state_in_sysfs:1;
unsigned int state_add_uevent_sent:1;
unsigned int state_remove_uevent_sent:1;
unsigned int uevent_suppress:1; // 是否发送 uevent 事件
};
kobject 对象的接口函数如下:
void kobject_init(struct kobject *kobj, struct kobj_type *ktype);//初始化 kobject
int kobject_add(struct kobject *kobj, struct kobject *parent,const char *fmt, ...);//将 kobject 加入到系统
//上面两个函数的结合
int kobject_init_and_add(struct kobject *kobj,struct kobj_type *ktype, struct kobject *parent,const char *fmt, ...);
void kobject_del(struct kobject *kobj); //将 kobject 从系统中删除
struct kobject * kobject_create(void);
struct kobject * kobject_create_and_add(const char *name,struct kobject *parent);
struct kobject *kobject_get(struct kobject *kobj); //增加对象引用次数
void kobject_put(struct kobject *kobj); //减少对象引用次数
内核中常见的 kobject 包括:
static struct kobject *dev_kobj; //设备对象
struct kobject *sysfs_dev_char_kobj; //字符设备对象
struct kobject *sysfs_dev_block_kobj; //块设备对象
struct kobject *kernel_kobj; //sysfs 下的 kernel 对象
1.2 kobj_type
kobj_type 表示内核对象的类型:
struct kobj_type {
void (*release)(struct kobject *kobj);
const struct sysfs_ops *sysfs_ops; //sysfs 操作
struct attribute **default_attrs; //默认属性
const struct kobj_ns_type_operations *(*child_ns_type)(struct kobject *kobj);
const void *(*namespace)(struct kobject *kobj);
};
sysfs_ops 为内核对象在 sysfs 文件系统中的操作接口:
struct sysfs_ops
{
ssize_t (*show)(struct kobject *, struct attribute *, char *); // 显示
ssize_t (*store)(struct kobject *, struct attribute *, const char *, size_t); // 存储
};
1.3 kset
kobject 通过 kset 组织成层次化的结构,kset 是具有相同类型的 kobject 的集合。所有属
于同一个 kset 的对象(kobject)的 parent 都指向该 kset 的 kobj 成员。
struct kset {
struct list_head list;
spinlock_t list_lock;
struct kobject kobj;
const struct kset_uevent_ops *uevent_ops;
};
kset 对象的接口函数如下
void kset_init(struct kset * k);
struct kset * kset_create_and_add(const char *name,struct kset_uevent_ops *u,struct kobject *parent_kobj);
int kset_register(struct kset *kset);
void kset_unregister(struct kset *kset);
内核中常见的 kset 包括:
struct kset *bus_kset;
struct kset *class_kset;
struct kset *system_kset;
2.设备模型层次
Linux 内核的设备模型包括设备(device)、设备驱动(device_driver)、总线(bus)、设备类
(class)等几个核心组件。
device 代表一个设备,设备结构(struct device)中包含了设备的 DMA 设置以及与体系
相关的硬件特性,具体定义如下:
struct device {
struct kobject kobj;
struct device *parent;
struct device_private *p;
const char *init_name; /* initial name of the device */
const struct device_type *type;
const 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 */
void *driver_data; /* Driver data, set and get with
dev_set_drvdata/dev_get_drvdata */
struct {
const char *name;
spinlock_t lock;
} driver_override;
struct mutex mutex; /* mutex to synchronize calls to
* its driver.
*/
struct dev_links_info links;
struct dev_pm_info power;
struct dev_pm_domain *pm_domain;
#ifdef CONFIG_ENERGY_MODEL
struct em_perf_domain *em_pd;
#endif
#ifdef CONFIG_PINCTRL
struct dev_pin_info *pins;
#endif
struct dev_msi_info msi;
#ifdef CONFIG_ARCH_HAS_DMA_OPS
const struct dma_map_ops *dma_ops;
#endif
u64 *dma_mask; /* dma mask (if dma'able device) */
u64 coherent_dma_mask;/* Like dma_mask, but for
alloc_coherent mappings as
not all hardware supports
64 bit addresses for consistent
allocations such descriptors. */
u64 bus_dma_limit; /* upstream dma constraint */
const struct bus_dma_region *dma_range_map;
struct device_dma_parameters *dma_parms;
struct list_head dma_pools; /* dma pools (if dma'ble) */
#ifdef CONFIG_DMA_DECLARE_COHERENT
struct dma_coherent_mem *dma_mem; /* internal for coherent mem
override */
#endif
#ifdef CONFIG_DMA_CMA
struct cma *cma_area; /* contiguous memory area for dma
allocations */
#endif
#ifdef CONFIG_SWIOTLB
struct io_tlb_mem *dma_io_tlb_mem;
#endif
#ifdef CONFIG_SWIOTLB_DYNAMIC
struct list_head dma_io_tlb_pools;
spinlock_t dma_io_tlb_lock;
bool dma_uses_io_tlb;
#endif
/* arch specific additions */
struct dev_archdata archdata;
struct device_node *of_node; /* associated device tree node */
struct fwnode_handle *fwnode; /* firmware device node */
#ifdef CONFIG_NUMA
int numa_node; /* NUMA node this device is close to */
#endif
dev_t devt; /* dev_t, creates the sysfs "dev" */
u32 id; /* device instance */
spinlock_t devres_lock;
struct list_head devres_head;
const struct class *class;
const struct attribute_group **groups; /* optional groups */
void (*release)(struct device *dev);
struct iommu_group *iommu_group;
struct dev_iommu *iommu;
struct device_physical_location *physical_location;
enum device_removable removable;
DECLARE_BITMAP(flags, DEV_FLAG_COUNT);
};
device 的注册与注销函数如下:
int __must_check device_register(struct device *dev);
void device_unregister(struct device *dev);
void device_initialize(struct device *dev);
int __must_check device_add(struct device *dev);
void device_del(struct device *dev);
device_driver 即设备的驱动,它对应的数据结构定义如下:
struct device_driver {
const char *name;
const 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 */
enum probe_type probe_type;
const struct of_device_id *of_match_table;
const struct acpi_device_id *acpi_match_table;
int (*probe) (struct device *dev);
void (*sync_state)(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 *const *groups;
const struct attribute_group *const *dev_groups;
const struct dev_pm_ops *pm;
void (*coredump) (struct device *dev);
struct driver_private *p;
struct {
/*
* Called after remove() but before devres entries are released.
* This is a Rust only callback.
*/
void (*post_unbind_rust)(struct device *dev);
} p_cb;
};
device_driver 的注册与注销函数原型如下:
int __must_check driver_register(struct device_driver *drv);
void driver_unregister(struct device_driver *drv);
probe 成员函数在 Linux 驱动开发中是一个很重要的接口。分析 driver_register 函数的调用关
系,可发现 driver_register->bus_add_driver-> driver_attach->__driver_attach-> driver_probe_device-> really_probe:
static int really_probe(struct device *dev, struct device_driver *drv)
{
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;
}
}
假如设备的总线没有实现 probe 成员函数,则 driver_register 函数会调用 device_driver
的 probe 成员函数。设备与设备驱动均挂载到总线上,总线完成设备、设备驱动的匹配、探测等管理工作。
struct bus_type {
const char *name;
const char *dev_name;
const struct attribute_group *const *bus_groups;
const struct attribute_group *const *dev_groups;
const struct attribute_group *const *drv_groups;
int (*match)(struct device *dev, const struct device_driver *drv);
int (*uevent)(const struct device *dev, struct kobj_uevent_env *env);
int (*probe)(struct device *dev);
void (*sync_state)(struct device *dev);
void (*remove)(struct device *dev);
void (*shutdown)(struct device *dev);
const struct cpumask *(*irq_get_affinity)(struct device *dev,
unsigned int irq_vec);
int (*online)(struct device *dev);
int (*offline)(struct device *dev);
int (*suspend)(struct device *dev, pm_message_t state);
int (*resume)(struct device *dev);
int (*num_vf)(struct device *dev);
int (*dma_configure)(struct device *dev);
void (*dma_cleanup)(struct device *dev);
const struct dev_pm_ops *pm;
bool driver_override;
bool need_parent_lock;
};
类是对设备的更高级的抽象,它更关注设备的共性:
struct class {
const char *name;
const struct attribute_group *const *class_groups;
const struct attribute_group *const *dev_groups;
int (*dev_uevent)(const struct device *dev, struct kobj_uevent_env *env);
char *(*devnode)(const struct device *dev, umode_t *mode);
void (*class_release)(const struct class *class);
void (*dev_release)(struct device *dev);
int (*shutdown_pre)(struct device *dev);
const struct kobj_ns_type_operations *ns_type;
const struct ns_common *(*namespace)(const struct device *dev);
void (*get_ownership)(const struct device *dev, kuid_t *uid, kgid_t *gid);
const struct dev_pm_ops *pm;
};
使用 class_create 可以创建一个类。系统注册的类可以在/sysfs/class 目录下面找到。
struct class * __must_check class_create(const char *name);
void class_destroy(const struct class *cls);
3. sysfs文件系统
sysfs 是一个特殊的文件系统,类似于/proc。sysfs 不仅像/proc 一样允许用户空间访问内
核的数据,而且以更结构化的方式向用户提供内核数据信息。sysfs 是一种内存文件系统,它
与 kobject 关系非常密切。系统中的每一个 kobject 对应着 sysfs中的一个目录,而每一个 sysfs 中的目录代表一个 kobject 对象,每个 sysfs 文件代表对应的 kobject 的属性。
sysfs 文件系统非常清晰地展示了设备驱动程序模型中各组件的层次关系。其顶级目录包括 block、device、bus、drivers、class、power、firmware 等。
fz@fz:/sys$ ls
block bus class dev devices firmware fs hypervisor kernel module power
目录对应说明如下;
- block 块设备
- bus 总线
- class 设备类
- devices 设备
- fireware 固件
- kernel 内核
- module 内核模块
- power 电源
sysfs 文件系统最基本的函数包括:
int sysfs_create_file(struct kobject * kobj, const struct attribute * attr) ; //创建文件
int sysfs_chmod_file(struct kobject *kobj, struct attribute *attr, mode_t mode); //修改文件属性
void sysfs_remove_file(struct kobject * kobj, const struct attribute * attr); //删除文件
int sysfs_create_dir_ns(struct kobject *kobj, const void *ns); //创建目录
void sysfs_remove_dir(struct kobject *kobj); //删除目录
int sysfs_create_group(struct kobject *kobj,const struct attribute_group *grp); //创建一组属性
sysfs 文件系统一般是自动加载到/sys 下的,也可以通过下面的命令手工加载:
mount -t sysfs sysfs /sys
device_create 函数用于在/sysfs 文件系统中创建一个指定设备号的设备:
struct device *device_create(const struct class *cls, struct device *parent, dev_t devt,
void *drvdata, const char *fmt, ...);
本例演示如何在驱动中创建/sys 类节点与/dev 设备节点。先要在/sys/class 目录中创建节
点,发出内核事件,才能触发 udev 或者 mdev 等设备事件处理程序来增删设备节点。
本例代码见\samples\2model\2-1module_devicecreate。核心代码如下:
static void simple_register(void)
{
int error, devno = MKDEV(simple_major, simple_minor);
cdev_init(&cdev, &simple_fops);
cdev.owner = THIS_MODULE;
cdev.ops = &simple_fops;
error = cdev_add(&cdev, devno, 1);
if (error)
printk(KERN_NOTICE "cdev_add error %d", error);
// 创建 simple_class
simple_class = class_create(THIS_MODULE, "simple_class");
if (IS_ERR(simple_class))
{
printk("Err: failed in creating class.\n");
return;
}
// 在 simple_class 下创建设备
device_create(simple_class, NULL, devno, NULL, "simple");
}
static int __init simple_init(void)
{
int result;
dev = MKDEV(simple_major, simple_minor);
result = register_chrdev_region(dev, number_of_devices, "simple");
if (result < 0)
{
printk(KERN_WARNING "hello: can't get major number %d\n", simple_major);
return result;
}
simple_register();
printk(KERN_INFO "char device registered\n");
return 0;
}
static void __exit simple_exit(void)
{
dev_t devno = MKDEV(simple_major, simple_minor);
cdev_del(&cdev);
unregister_chrdev_region(devno, number_of_devices);
device_destroy(simple_class, devno);
class_destroy(simple_class);
}
运行结果如下:
[root@urbetter drivers]# insmod smodule.ko
char device registered
[root@urbetter drivers]# ls /dev | grep sim
simple
[root@urbetter drivers]# cd /sys/class
[root@urbetter class]# ls
backlight hwmon mem rtc simple_class vc
bdi i2c-adapter misc scsi_device sound vtconsole
block i2c-dev mmc_host scsi_disk spi_master
dma input mtd scsi_generic tty
graphics lcd net scsi_host ubi
[root@urbetter class]# cd simple_class/
[root@urbetter simple_class]# ls
simple
[root@urbetter simple_class]# cd simple/
[root@urbetter simple]# ls
dev power subsystem uevent
[root@urbetter simple]# rmmod smodule
[root@urbetter simple]# ls /dev | grep sim
[root@urbetter simple]#
可见/sys/class 以及/dev 下面均创建了设备文件。
4. platform概念
平台概念的引入能够更好地描述设备的资源信息,例如总线地址、中断、DMA 信息等。平
台设备模型是对 device 与 driver 模型的扩展,它的总线为 platform_bus_type:
const struct bus_type platform_bus_type = {
.name = "platform",
.dev_groups = platform_dev_groups,
.driver_override = true,
.match = platform_match,
.uevent = platform_uevent,
.probe = platform_probe,
.remove = platform_remove,
.shutdown = platform_shutdown,
.dma_configure = platform_dma_configure,
.dma_cleanup = platform_dma_cleanup,
.pm = &platform_dev_pm_ops,
};
平台设备使用 platform_device 结构描述,该结构定义如下:
struct platform_device {
const char *name;
int id;
bool id_auto;
struct device dev;
u64 platform_dma_mask;
struct device_dma_parameters dma_parms;
u32 num_resources;
struct resource *resource;
const struct platform_device_id *id_entry;
/* MFD cell pointer */
struct mfd_cell *mfd_cell;
/* arch specific additions */
struct pdev_archdata archdata;
};
平台设备注册与注销接口如下;
int platform_device_register(struct platform_device *);
void platform_device_unregister(struct platform_device *);
平台驱动使用 platform_driver 结构描述:
struct platform_driver {
int (*probe)(struct platform_device *);
void (*remove)(struct platform_device *);
void (*shutdown)(struct platform_device *);
int (*suspend)(struct platform_device *, pm_message_t state);
int (*resume)(struct platform_device *);
struct device_driver driver;
const struct platform_device_id *id_table;
bool prevent_deferred_probe;
/*
* For most device drivers, no need to care about this flag as long as
* all DMAs are handled through the kernel DMA API. For some special
* ones, for example VFIO drivers, they know how to manage the DMA
* themselves and set this flag so that the IOMMU layer will allow them
* to setup and manage their own I/O address space.
*/
bool driver_managed_dma;
};
平台设备驱动必须提供 probe 和 remove 方法,并且在 probe 中确保设备资源的可用性。
平台驱动还支持电源管理和设备停止等事件。平台驱动的注册接口如下:
#define platform_driver_register(drv) \
__platform_driver_register(drv, THIS_MODULE, KBUILD_MODNAME)
extern int __platform_driver_register(struct platform_driver *,
struct module *, const char *mod_name);
extern void platform_driver_unregister(struct platform_driver *);
platform_driver_register 和 platform_driver_unregister 本质上就是调用 driver_register 和
driver_unregister。老的 Linux 内核需要在模块入口中调用这两个函数,新的 Linux 内核中只
需要用 module_platform_driver 即可实现这两个函数的功能。
resource 结构是对平台资源的描述,定义如下:
struct resource {
resource_size_t start; // 起始地址
resource_size_t end; // 终止地址
const char *name; // 名称
unsigned long flags; // 资源类别
unsigned long desc;
struct resource *parent, *sibling, *child; // 资源上下级关系
};
在 platform_device 结构中可以设置多种资源信息。资源的 flags 标志包括:
#define IORESOURCE_TYPE_BITS 0x00001f00 /* Resource type */
#define IORESOURCE_IO 0x00000100 /* PCI/ISA I/O ports */
#define IORESOURCE_MEM 0x00000200
#define IORESOURCE_REG 0x00000300 /* Register offsets */
#define IORESOURCE_IRQ 0x00000400
#define IORESOURCE_DMA 0x00000800
#define IORESOURCE_BUS 0x00001000
内核提供了一组函数,用来获取设备的资源信息:
//根据资源类型和序号来获取指定的资源。
struct resource *platform_get_resource(struct platform_device *, unsigned int, unsigned int);
//根据序号获取资源中的中断号。
int platform_get_irq(struct platform_device *, unsigned int);
//根据名称和类别获取指定的资源。
struct resource *platform_get_resource_byname(struct platform_device *, unsigned int, const char *);
//根据名称获取资源中的中断号。
int platform_get_irq_byname(struct platform_device *, const char *);
5. attributes
属性用来描述内核对象的特性。属性定义如下:
struct attribute {
const char *name;
umode_t mode;
#ifdef CONFIG_DEBUG_LOCK_ALLOC
bool ignore_lockdep:1;
struct lock_class_key *key;
struct lock_class_key skey;
#endif
};
一组属性用 attribute_group 表示:
struct attribute_group {
const char *name;
__SYSFS_FUNCTION_ALTERNATIVE(
umode_t (*is_visible)(struct kobject *,
struct attribute *, int);
umode_t (*is_visible_const)(struct kobject *,
const struct attribute *, int);
);
umode_t (*is_bin_visible)(struct kobject *,
const struct bin_attribute *, int);
size_t (*bin_size)(struct kobject *,
const struct bin_attribute *,
int);
union {
struct attribute **attrs;
const struct attribute *const *attrs_const;
};
const struct bin_attribute *const *bin_attrs;
};
设备、驱动、类均有自己的属性,这些属性在 attribute 结构的基础上,增加了显示与存
储接口。具体定义如下:
// 设备属性结构
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);
};
// 设备属性接口
int device_create_file(struct device *device, const struct device_attribute *entry);
void device_remove_file(struct device *dev, const struct device_attribute *attr);
// 驱动属性结构
struct driver_attribute
{
struct attribute attr;
ssize_t (*show)(struct device_driver *driver, char *buf);
ssize_t (*store)(struct device_driver *driver, const char *buf, size_t count);
};
// 驱动属性接口
int driver_create_file(struct device_driver *driver, const struct driver_attribute *attr);
void driver_remove_file(struct device_driver *driver, const struct driver_attribute *attr);
// 类属性结构
struct class_attribute
{
struct attribute attr;
ssize_t (*show)(struct class *class, struct class_attribute *attr, char *buf);
ssize_t (*store)(struct class *class, struct class_attribute *attr, const char *buf, size_t count);
};
// 类属性接口
int class_create_file(struct class *class, const struct class_attribute *attr);
void class_remove_file(struct class *class, const struct class_attribute *attr);
6. 设备事件通知
6.1 kobject uevent
enum kobject_action {
KOBJ_ADD, //添加
KOBJ_REMOVE, //删除
KOBJ_CHANGE, //变更
KOBJ_MOVE, //移动
KOBJ_ONLINE, //在线
KOBJ_OFFLINE, //下线
KOBJ_MAX
};
内核事件通过kobject_uevent 函数发出:
int kobject_uevent(struct kobject *kobj, enum kobject_action action);
Linux 下设备管理通常使用udev 工具。mdev 是busybox 工具包中的设备管理工具,用来在嵌入式系统中替代udev。udev 包含一个一直运行的udevd 后台进程。与udev 不同,mdev 不是一直运行的后台程序,它使用的是内核唤醒方式,在系统启动时必须将mdev 设置成uevent_helper 程序。
6.2 udev
udev 是一种应用层工具,提供一个基于用户空间的动态设备节点管理和命名的解决方案,它能够根据系统中的硬件设备的状态动态更新设备文件,包括设备文件的创建、删除、块设备的加载等。
udev 的主体部分在udevd .c 文件中,它主要监控来自udev 客户端的控制消息、内核的hotplug 事件、配置文件变化的事件。当有设备插入/拔除(hotplug)时,udev 就会收到通知,它会根据事件中所带参数和sysfs 中的信息,调用合适的事件处理程序,创建或删除/dev节点。
udev 规则文件中一个不以"#"开头的行就是一条规则。每条规则包含匹配键与执行键。匹配键以"=="号与值连接;执行键用"="与值连接。常用的键如下:
SUBSYSTEM:匹配子系统
ACTION:匹配动作
KERNEL:匹配内核名
RUN:执行程序
NAME:设备节点命名
SYMLINK:创建软链接
下面截取一段规则文件作为示例:
# Media automounting
当子系统为"block",动作为"add"或者"remove",均执行/etc/udev/scripts/mount.sh
SUBSYSTEM=="block",ACTION=="add" RUN+="/etc/udev/scripts/mount.sh"
SUBSYSTEM=="block",ACTION=="remove"RUN+="/etc/udev/scripts/mount.sh"
# Handle network interface setup
当子系统为"net",动作为"add"或者"remove",均执行/etc/udev/scripts/network.sh
SUBSYSTEM=="net",ACTION=="add" RUN+="/etc/udev/scripts/network.sh"
SUBSYSTEM=="net",ACTION=="remove" RUN+="/etc/udev/scripts/network.sh"
# SCSI devices
当子系统为" scsi ",内核名为sr[0-9]*,均创建scd 节点与sr 软链接
SUBSYSTEMS=="scsi", KERNEL=="sr[0-9]*", NAME="scd%n", SYMLINK+="sr%n"
7.设备树
设备树(Device Tree)用来描述板卡的板级硬件信息。Linux 源码的arch 目录下有大量的设备板级信息描述文件,采用设备树后,内核不用再包含大量的硬件描述文件,当板卡的硬件接口信息变动而驱动逻辑没有变化时,只需要修改设备树,而不需要修改内核代码。设备树文件位于Linux 内核源代码arch/arm/boot/dts 目录下面,其中dts 文件为板级定义,dtsi 文件为
Soc 级定义。编译Linux 设备树的命令为make dtbs,生成*.dtb 文件(Device Tree Blob)。设备树文件编译之后会被放到一个独立的存储区,bootloader 启动时会将设备树信息读入内存,并将该内存地址传递给Linux 内核。内核在启动时会建立设备树节点:
void __init setup_arch(char **cmdline_p)
{
mdesc = setup_machine_fdt(__atags_pointer); // 建立设备树
unflatten_device_tree(); // 扫描设备树,将设备树组织成device_node 结构用于后面解析信息
arm_dt_init_cpu_maps();
psci_dt_init();
}
device 结构的of_node 成员指向设备关联的设备树。device 结构包含在其他设备结构
中,如platform_device 结构。假如已知平台设备platform_device *op 结构,则可通过op->
dev. of_node 访问设备树。反之,通过设备树查找平台设备的函数如下:
struct platform_device *of_find_device_by_node(struct device_node *np);
内核中定义了一系列设备树API,具体代码在/drivers/of 目录下。主要的设备树函数如下:
// 判断节点的compatible 属性是否包含name
int of_device_is_compatible(const struct device_node *device, const char *name);
// 根据设备节点的compatible 属性寻找节点
struct device_node *of_find_compatible_node(struct device_node *from, const char *type, const char *compat);
// 获取属性
void *of_get_property(const struct device_node *node, const char *name, int *lenp);
// 获取字节型属性的值
int of_property_read_u8(const struct device_node *np, const char *propname, u8 *out_value);
// 读字节数组型的属性
int of_property_read_u8_array(const struct device_node *np, const char *propname, u8 *out_values, size_t sz);
// 读字符串型的属性
int of_property_read_string(struct device_node *np, const char *propname, const char **out_string);
// 获取中断
unsigned int irq_of_parse_and_map(struct device_node *dev, int index)
浙公网安备 33010602011771号