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)
posted @ 2026-09-17 09:30  鲨鱼辣椒fz  阅读(1)  评论(0)    收藏  举报