首页 > 其他分享 >zephyr中的驱动机制

zephyr中的驱动机制

时间:2024-10-06 17:43:53浏览次数:1  
标签:调用 struct read zephyr api 驱动 机制 eeprom

本文以eeprom驱动为例。

这是一个典型的驱动包含的文件:

zephyr_project/
├── drivers/
│   └── foo/
│       └── foo_vendor.c
├── include/
│   └── zephyr/
│       └── drivers/
│           └── foo.c
├── CMakeLists.txt
└── Kconfig

include/zephyr/drivers/foo.c声明了驱动的API、内联函数、系统调用入口等
drivers/foo/foo_vendor.c是具体厂商的驱动代码,这其中要定义对应具体厂商的驱动API

一. 驱动API

zephyr中每个设备都以struct device的形式呈现,这个结构体包含设备的基本信息和操作接口。
而每个设备驱动都会定义一个API结构体,包含所有驱动操作的指针(有时也包括一些状态数据)。
例如一个eeprom设备的dev是一个这样的结构体:

zephyr/include/zephyr/device.h
struct device {
    const char *name;
    const struct device_api *api;
    void *driver_data;
    // ...
};

而其中的api则这样定义:

zephyr/include/zephyr/drivers/eeprom.h
__subsystem struct eeprom_driver_api {
	eeprom_api_read read;
	eeprom_api_write write;
	eeprom_api_size size;
};

这就是设备驱动API

具体的函数在哪里?

一般来说具体的函数在类似zephyr/drivers/eeprom/eeprom_stm32.c的位置
该位置下还有eeprom_stm32_api其中指定了具体的API函数

static const struct eeprom_driver_api eeprom_stm32_api = {
	.read = eeprom_stm32_read,
	.write = eeprom_stm32_write,
	.size = eeprom_stm32_size,
};

如:

static size_t eeprom_stm32_size(const struct device *dev)
{
	const struct eeprom_stm32_config *config = dev->config;

	return config->size;
}

在设备初始化时DEVICE_DT_INST_DEFINE宏会将设备实例和设备驱动关联,并指定初始化函数:
在系统启动时,Zephyr 会自动调用设备的初始化函数,将设备实例注册到设备模型中

zephyr/drivers/eeprom/eeprom_stm32.c:121
DEVICE_DT_INST_DEFINE(0, NULL, NULL, NULL, &eeprom_config, POST_KERNEL,
		      CONFIG_EEPROM_INIT_PRIORITY, &eeprom_stm32_api);

该宏指定具体的设备驱动APIeeprom_stm32_api, 这样我们的gen_syscalls.py能找到实际的驱动函数

二.系统调用

Zephyr 支持用户空间和内核空间的分离,用户空间代码不能直接调用内核空间的函数。

为了实现这一点,Zephyr 使用系统调用机制,允许用户空间代码通过特定的接口调用内核空间的函数。这样也提升了稳定性和可维护性。一开始接触这个确实一脸懵b....

1. 什么是__syscall

__syscall 关键字用于标记一个函数为系统调用函数。它告诉编译器和 gen_syscalls.py 脚本,这个函数需要生成系统调用接口
gen_syscalls.py 脚本会生成一个内联函数,用于在用户空间调用该系统调用函数。
这个内联函数会检查是否在用户空间环境下运行,如果是,则通过系统调用机制转发到内核空间的z_impl_eeprom_read函数。

也就是说当用户进程调用eeprom_read时,实际调用的并不是z_impl_eeprom_read或者在eeprom.h中定义的eeprom_read,而是通过系统调用机制间接调用的。

zephyr/build/zephyr/include/generated/zephyr/syscalls/eeprom.h

//这段代码由脚本自动生成
__pinned_func //固定该函数在内存中位置不变
static inline int eeprom_read(const struct device * dev, off_t offset, void * data, size_t len)
{
#ifdef CONFIG_USERSPACE
	if (z_syscall_trap()) {
		union { uintptr_t x; const struct device * val; } parm0 = { .val = dev };
		union { uintptr_t x; off_t val; } parm1 = { .val = offset };
		union { uintptr_t x; void * val; } parm2 = { .val = data };
		union { uintptr_t x; size_t val; } parm3 = { .val = len };
		return (int) arch_syscall_invoke4(parm0.x, parm1.x, parm2.x, parm3.x, K_SYSCALL_EEPROM_READ);
	}
#endif
	compiler_barrier(); //用于防止编译器对代码进行重排序优化。它确保在调用 compiler_barrier() 之前的所有指令在调用之后的指令之前执行
	return z_impl_eeprom_read(dev, offset, data, len);
}

调用过程

  1. 检查用户空间环境:生成的内联函数检查是否在用户空间环境下运行。
  2. 系统调用转发:如果在用户空间环境下运行,系统调用会被转发到内核空间,通过 arch_syscall_invoke4 函数进行实际调用。
  3. 内核空间调用 [z_impl_eeprom_read]:如果不在用户空间环境下运行,内联函数会直接调用 [z_impl_eeprom_read]函数。

2. 定义系统调用

首先,在驱动代码中定义系统调用函数
eeprom.h 文件这样定义 [z_impl_eeprom_read] 函数及其系统调用:

__syscall int eeprom_read(const struct device *dev, off_t offset, void *data,
			  size_t len);

static inline int z_impl_eeprom_read(const struct device *dev, off_t offset,
				     void *data, size_t len)
{
	const struct eeprom_driver_api *api =
		(const struct eeprom_driver_api *)dev->api;

	return api->read(dev, offset, data, len);
}

在构建过程中,CMake 会调用 gen_syscalls.py 脚本。这个脚本会扫描所有标记了 __syscall 宏的函数,并生成相应的系统调用接口文件。

gen_syscalls.py 脚本会生成两个主要文件:

  • syscalls_list.h:包含所有系统调用的列表。
  • syscalls.c:包含系统调用的实现代码。

此外,还会生成每个系统调用的具体接口文件,例如 [eeprom.h]中的内容。
大体就是这样,之后再写具体如何编写一个伺服电机驱动

标签:调用,struct,read,zephyr,api,驱动,机制,eeprom
From: https://www.cnblogs.com/ttwards/p/18449137

相关文章

  • Day10-包机制
    Day10-包机制包机制Java为更好地组织类而提供的机制,用于区别类名的命名空间。包相当于文件夹包语句的语法格式为:(定义包)packagepkg1[.pkg2[.pkg3...]];一般利用公司域名倒置作为包名。为了能够使用某一个包的成员,需要在Java程序中明确导入该包,使用“import”语句可完......
  • 弧形导轨驱动器高效使用技巧!
    弧形导轨驱动器是一种用于驱动滑座沿着导轨做弧线运动的设备,其用方法因具体型号和应用场景的不同而有所差异,通常可以归纳为以下几个步骤:1、安装前要明确弧形导轨的使用需求,根据需求选择合适的弧形导轨驱动器,准备好螺丝刀、扳手、量具等安装所需的工具和螺栓、垫片等材料。2、安装弧......
  • 3.资源《Arduino UNO R3 proteus 电机测速仿真工程文件(含驱动代码)》说明。
     资源链接: ArduinoUNOR3proteus电机测速仿真工程文件(含驱动代码)1.文件明细:2.文件内容说明包含:proteus工程、原理图、仿真程序。3.内容展示4.简述该文件为Proteus工程。用于电机测速验证。5.演示视频proteus仿真电机测速......
  • 如何使用 Vuex 的插件机制来增强状态管理的功能?
    关注我,持续分享逻辑思维&管理思维&面试题;可提供大厂面试辅导、及定制化求职/在职/管理/架构辅导;推荐专栏《10天学会使用asp.net编程AI大模型》,目前已完成所有内容。一顿烧烤不到的费用,让人能紧跟时代的浪潮。从普通网站,到公众号、小程序,再到AI大模型网站。干货满满。学成后可......
  • 卸载时报错:‘’系统找不到指定的驱动器‘’问题处理
    操作系统:win11问题描述:wegame,英雄联盟我早就卸载过了,今天在设置/应用/安装的应用这里又看见了,在此处点击卸载,报如下错误:解决办法:查了一下网上的做法,大多数是删除注册表,我也试了几个,结果还是没有用。最后灵机一动,记得控制面板那边也有卸载应用的位置。控制面板/程序/卸载......
  • 深入 MUX 的三态机制
    电路中一个输出连接多个输入,需要提高输出门的驱动能力;若多个输出连接一个输入,则需要引入高阻态保证逻辑的正确性。一般CMOS的逻辑门单元理想状态下同一时刻总有一个连通,是无法输出高阻态的。输入高阻态需要特殊的器件,在PDK中一般以传输门或者三态buf方式实现。MUX:逻辑实现......
  • 虚拟机类加载机制
    1.类加载时机一个类型(接口/类)从被加载到虚拟机内存中开始,到卸载出内存为止,它的整个生命周期将历加载、验证、准备、解析、初始化、使用和卸载七个阶段,其中验证、准备、解析三个部分统称为连接(Linking)。加载、验证、准备、初始化和卸载这五个阶段的顺序是确定的,类型的加载过程......
  • C++数组衰变机制
    inta[10]={};//下面两个式子等价int*p=a;int*p=&a[0];我们在讨论数组的时候经常看到这么一种说法,也就是说,数组名就是指向数组首元素的指针。但是上面这个过程产生了隐式转换,也就是数组衰变过程数组名!=指针数组就是数组,指针就是指针,不能将数组变量名认为是......
  • 手把手教你学AUTOSAR(四)--AUTOSAR通信机制
    目录AUTOSAR通信机制1.通信层(CommunicationLayer)1.1网络管理(NetworkManagement)1.2协议栈实现1.3消息传输1.4数据交换2.应用层(ApplicationLayer)2.1运行时环境(RuntimeEnvironment,RTE)2.2应用层通信接口3.典型通信场景3.1CAN通信示例3.2LIN通信示例......
  • (六)WPF数据驱动模式
     WPF开发方式; MVVM(ModelViewViewModel)1.绑定XAML数据方式  在 XAML中添加绑定数据和绑定的操作属性        Content="{BindingMyVar}" 在XAML对应了的窗体类的构造函数添加数据绑定        this.DataContext=mainViewModel;//让此页面的数据取......