当前位置: 首页 > news >正文

I2C 驱动 --- 控制器

示例

  • 使用说明
# use i2c-bcm2711 
BCM2711 I2C manageri2c-bcm2711 [options]Options:
-p addr         I2C base address(Default: 0)
-i irq          I2C interrupt (Default: 149)
-c clock        Default to 5000(100KHz)
-v              Set verbose(Default: 2)
-s slave addr   Slave address(only 7-bit addr supported. Default: 0)Generic options:
--b bus_speed       Default bus speed. (Default: 100000)
--u unit            Unit number. Number to append to device nameprefix (/dev/i2c). (Default: 0)Example:
i2c-bcm2711  -p0xfe804000 --b100000 --u1
  • 示例1

    # Specify a port of address 0xFE804000 with an IRQ of 149 to the first unit
    i2c-bcm2711 -p0xfe804000 --b100000
    

日志查看

# 查看设备
$ ls /dev/i2c* 
/dev/i2c0  /dev/i2c1# 查看日志
$ slog2info -r -b i2c_bcm2711

定位难点:运行时没有任何打印信息。

源码跟踪

main 入口

$ grep -rn main src/hardware/i2c/
grep: src/hardware/i2c/bcm2711/aarch64/le/i2c-bcm2711: binary file matches

反汇编

尝试通过 反汇编 方式将 i2c-bcm2711 运行流程进制一个大致梳理。

先从当前环境正常编译过程中的最后链接命令入手:

/home/gaoyang3513/Workspaces/qnx800/host/linux/x86_64/usr/bin/qcc -Vgcc_ntoaarch64 -Wl,--no-keep-memory \-o /home/gaoyang3513/Source/05-Raspi/02-Raspi/02-Projects/SDK_QNX_Raspi4B_BSP/src/hardware/i2c/bcm2711/aarch64/le/i2c-bcm2711 \bus_speed.o    fini.o    info.o    init.o    lib.o    options.o    recv.o    send.o    slave_addr.o    version.o    wait.o  \-L. -L/home/gaoyang3513/Source/05-Raspi/02-Raspi/02-Projects/SDK_QNX_Raspi4B_BSP/src/hardware/i2c/../../../install/aarch64le/lib \-L/home/gaoyang3513/Source/05-Raspi/02-Raspi/02-Projects/SDK_QNX_Raspi4B_BSP/src/hardware/i2c/../../../install/aarch64le/usr/lib \-L/home/gaoyang3513/Workspaces/qnx800/target/qnx/aarch64le/lib -L/home/gaoyang3513/Workspaces/qnx800/target/qnx/aarch64le/usr/lib \-Wl,--rpath-link,. -Wl,--rpath-link,/home/gaoyang3513/Source/05-Raspi/02-Raspi/02-Projects/SDK_QNX_Raspi4B_BSP/src/hardware/i2c/../../../install/aarch64le/lib \-Wl,--rpath-link,/home/gaoyang3513/Source/05-Raspi/02-Raspi/02-Projects/SDK_QNX_Raspi4B_BSP/src/hardware/i2c/../../../install/aarch64le/usr/lib \-Wl,--rpath-link,/home/gaoyang3513/Workspaces/qnx800/target/qnx/aarch64le/lib \-Wl,--rpath-link,/home/gaoyang3513/Workspaces/qnx800/target/qnx/aarch64le/usr/lib \-li2c-master    -ldrvr    -lsecpol   -EL
$ find ~/Workspaces/qnx800 -name "*i2c-master*"
/home/gaoyang3513/Workspaces/qnx800/target/qnx/x86_64/lib/libi2c-master.a
/home/gaoyang3513/Workspaces/qnx800/target/qnx/aarch64le/lib/libi2c-master.a

三个库文件 libi2c-master.a、libdrvr.a、 libsecpol.so 都以预编译文件形式存放于 qnx800/target/qnx/<aarch84le> 目录下。另外命令中没有显式的链接脚本(后缀 .lds),所以使用隐性的连接方式。对 i2c-bcm2711 进制反汇编,发现因未启用调试信息导致生成dump文件没有C源码做对比参考,方便阅读,在编译命令中加入 -g 重新生成带调试信息(C源码)的 i2c-bcm2711,命令如下:

  • 编译命令

    # 清理
    make -C src/hardware/i2c/bcm2711/ clean
    # 重新生成,添加调试信息(-g)
    make -C src/hardware/i2c/bcm2711/ CCFLAGS=-g
    
  • 反汇编命令

    $ aarch64-unknown-nto-qnx8.0.0-objdump -xdS src/hardware/i2c/bcm2711/aarch64/le/i2c-bcm2711 > i2c-bcm2711.dump
    
    • -x, --all-headers Display the contents of all headers,显示所有头部信息;
    • -d, --disassemble Display assembler contents of executable sections,显示执行区的汇编代码;
    • -S, --source Intermix source code with disassembly,混合C与反汇编代码;

以下摘取反汇编文件 i2c-bcm2711.dump 关键信息,梳理启动流程:

#--> i2c-bcm2711.dump
... # 6
start address 0x00000000000018a0                                # 入口地址 18a0
... # 1419
Disassembly of section .text:00000000000014f0 <main>:                                        # main ... # 14431544:	940003df 	bl	24c0 <_i2c_options>                # 调用 options 检查入参... # 1449155c:	94000599 	bl	2bc0 <_i2c_initlib>                # 初始化库文件... # 14961618:	97ffff86 	bl	1430 <dispatch_create@plt>         # 分发器创建... # 1513165c:	97ffff85 	bl	1470 <iofunc_func_init@plt>        # iofunc 初始化... # 155316fc:	97fffed1 	bl	1240 <resmgr_context_alloc@plt>    # 资源管理器 上下文申请... # 161017e0:	d0000023 	adrp	x3, 7000 <__FRAME_END__+0xd74>17e4:	f947e863 	ldr	x3, [x3, #4048]17e8:	d2800021 	mov	x1, #0x1                   	17ec:	f0000000 	adrp	x0, 4000 <_i2c_master_sendrecv+0x2d0>    17f0:	d2800342 	mov	x2, #0x1a                17f4:	91312000 	add	x0, x0, #0xc4817f8:	97ffff16 	bl	1450 <fwrite@plt>    ... # 1659
00000000000018a0 <_start>:                                       # C入口,                      (0... # 8911920:	97fffeb8 	bl	1400 <_init_libc@plt>... # 8961934:	97fffe4f 	bl	1270 <_preinit_array@plt>    ... # 9011948:	97fffebe 	bl	1440 <_fini_array@plt>... # 906195c:	97fffe35 	bl	1230 <_init_array@plt>1960:	97fffe94 	bl	13b0 <__get_errno_ptr@plt>   1964:	b900001f 	str	wzr, [x0]1968:	2a1303e0 	mov	w0, w19196c:	aa1403e1 	mov	x1, x201970:	aa1503e2 	mov	x2, x211974:	aa1f03fd 	mov	x29, xzr1978:	97fffef2 	bl	1540 <main>                         # main 入口                    (1    197c:	97fffed1 	bl	14c0 <exit@plt>1980:	14000000 	b	1980 <_start+0x90>... # 1378
0000000000001df0 <i2c_master_getfuncs>:... # 1404I2C_ADD_FUNC(i2c_master_funcs_t, funcs, init, bcm2711_init, tabsize);              # .init 函数指针,指向 bcm2711_init1e14:	d0000022 	adrp	x2, 7000 <__FRAME_END__+0xa5c>1e18:	f947e442 	ldr	x2, [x2, #4040]1e1c:	f9000802 	str	x2, [x0, #16]... # 1414I2C_ADD_FUNC(i2c_master_funcs_t, funcs, send, bcm2711_send, tabsize);              # .sedn 函数指针,赋值 bcm2711_send1e34:	71009c3f 	cmp	w1, #0x271e38:	54fffe49 	b.ls	1e00 <i2c_master_getfuncs+0x10>  // b.plast1e3c:	d0000022 	adrp	x2, 7000 <__FRAME_END__+0xa5c>1e40:	f947c042 	ldr	x2, [x2, #3968]1e44:	f9001002 	str	x2, [x0, #32]I2C_ADD_FUNC(i2c_master_funcs_t, funcs, recv, bcm2711_recv, tabsize);1e48:	7100bc3f 	cmp	w1, #0x2f1e4c:	54fffda9 	b.ls	1e00 <i2c_master_getfuncs+0x10>  // b.plast1e50:	d0000022 	adrp	x2, 7000 <__FRAME_END__+0xa5c>1e54:	f947bc42 	ldr	x2, [x2, #3960]1e58:	f9001402 	str	x2, [x0, #40]    ... # 2721
0000000000002ce0 <_i2c_initlib>:... # 27532d5c:	f9004e61 	str	x1, [x19, #152]2d60:	a900a80b 	stp	x11, x10, [x0, #8]2d64:	a9018809 	stp	x9, x2, [x0, #24]2d68:	a902a002 	stp	x2, x8, [x0, #40]2d6c:	a9039807 	stp	x7, x6, [x0, #56]2d70:	a9049005 	stp	x5, x4, [x0, #72]2d74:	f9002c03 	str	x3, [x0, #88]2d78:	97fffc1e 	bl	1df0 <i2c_master_getfuncs>         # 调用 i2c_master_getfuncs      (2...

使用AI工具,逆向一部分main函数的实现

#include "proto.h"// 全局变量 'done',用于控制主循环退出
int done = 0; // 位于 .bss 段,偏移 0x800c// 信号处理函数,用于设置 'done' 标志以退出程序
void exit_signal(int signo) {atomic_set(&done, 1); // 原子地设置 done 为 1
}int main(int argc, char *argv[]) {// 局部变量声明 (根据栈帧布局和使用推断)bcm2711_dev_t local_dev;          // 用于传递给 bcm2711_options 和 bcm2711_init 的设备句柄i2c_master_funcs_t master_funcs;  // I2C 主机功能函数表resmgr_attr_t resmgr_attr;        // 资源管理器属性char path[256];                   // 资源管理器路径字符串 "/dev/i2c-bcm2711"struct sigevent event;            // 信号事件结构体sigset_t mask;                    // 信号集struct sigaction act, oact;       // 信号动作结构体dispatch_t *dpp = NULL;           // 调度器句柄resmgr_context_t *ctp = NULL;     // 资源管理器上下文int resmgr_id = -1;               // 资源管理器 IDvoid *hdl_from_init = NULL;       // bcm2711_init 返回的设备句柄int ret = 0;                      // 函数返回值,最终作为 main 的返回值// 初始化 master_funcs 结构体为零memset(&master_funcs, 0, sizeof(master_funcs));// 调用 _i2c_options 处理命令行参数// local_dev 用于存储解析后的选项ret = bcm2711_options(&local_dev, argc, argv);if (ret == -1) {goto cleanup;}// 调用 _i2c_initlib 初始化 I2C 库函数表// master_funcs 结构体将被填充 I2C 驱动的实际函数指针ret = _i2c_initlib(&master_funcs);if (ret == -1) {goto cleanup;}// 调用 I2C 驱动的初始化函数// bcm2711_init 返回一个新分配的 bcm2711_dev_t 句柄hdl_from_init = master_funcs.init(&local_dev, argc, argv); // 传入 local_dev 作为 hdl_unusedif (hdl_from_init == NULL) {goto cleanup;}// 设置从设备地址 (根据命令行选项)// 假设 master_funcs.set_slave_addr 的第二个参数是地址,第三个参数是格式// 这里使用 local_dev.slave_addr 和 I2C_ADDRFMT_7BITif (master_funcs.set_slave_addr(hdl_from_init, local_dev.slave_addr, I2C_ADDRFMT_7BIT) == -1) {fprintf(_Stderr, "main: Error setting slave address: %s\n", strerror(errno));ret = -1;goto cleanup_with_fini;}// 设置总线速度 (根据命令行选项)unsigned int actual_speed = 0;if (master_funcs.set_bus_speed(hdl_from_init, local_dev.speed, &actual_speed) == -1) {fprintf(_Stderr, "main: Error setting bus speed: %s\n", strerror(errno));ret = -1;goto cleanup_with_fini;}// 创建调度器dpp = dispatch_create();if (dpp == NULL) {perror("dispatch_create");ret = -1;goto cleanup_with_fini;}// 初始化 I/O 函数和资源管理器属性iofunc_func_init(dpp, &resmgr_attr.io_funcs, &resmgr_attr.attr, 0x21, 0x9); // 0x21, 0x9 是 QNX 特定的常量iofunc_attr_init_sized(&master_funcs, sizeof(bcm2711_dev_t), &resmgr_attr.attr); // master_funcs 作为 attr 的 base// 格式化资源管理器路径snprintf(path, sizeof(path), "/dev/i2c-bcm2711");// 打开安全策略secpol_open(0, 1); // 0, 1 是 QNX 特定的常量// 附加资源管理器resmgr_id = secpol_resmgr_attach(dpp, NULL, path, 0, 0, &master_funcs, &resmgr_attr.attr, NULL);if (resmgr_id == -1) {perror("resmgr_attach");ret = -1;goto cleanup_with_fini;}// 转换安全策略类型secpol_transition_type(0, 0, 0);// 分配资源管理器上下文ctp = resmgr_context_alloc(dpp);if (ctp == NULL) {perror("resmgr_context_alloc");ret = -1;goto cleanup_with_fini;}// 设置 SIGTERM 信号处理函数sigemptyset(&mask);sigaddset(&mask, SIGTERM);act.sa_handler = exit_signal;act.sa_flags = 0;sigaction(SIGTERM, &act, &oact);// 进程守护化procmgr_daemon(0, 0);// 资源管理器主循环while (!done) {resmgr_block(ctp); // 阻塞等待消息resmgr_handler(ctp); // 处理消息}cleanup_with_fini:// 调用 I2C 驱动的结束函数if (hdl_from_init != NULL) {master_funcs.fini(hdl_from_init);}cleanup:// 恢复寄存器和栈帧,执行栈保护检查,并返回return ret;
}

可知:从main分析看来,进制应该阻塞等待资源管理器回调的,但实际执行 i2c-bcm2711 直接返回,这个表示不解。对资源做一个简单梳理
在这里插入图片描述

可知当前驱动主要是 I2C 控制器的驱动,而非某一 I2C 设备的驱动。但从 i2c-bcm2711 的代码分析:其应长驻后台运行,这样才能处理 i2c 设备的收发需求,从日志发现其长驻后台:

...
Welcome to QNX 8.0.0 on RaspberryPi4B !Starting wdtkick ...
Starting I2C driver ...                   # I2C 控制器驱动加载
...

跟读源码:

$ grep -rn "Starting I2C driver" ./
./images/rpi4.build:46:    display_msg "Starting I2C driver ..."
grep: ./images/ifs-rpi4.bin: binary file matches#--> images/rpi4.build
... # 15
[+script] startup-script = {... # 46display_msg "Starting I2C driver ..."i2c-bcm2711 -p0xfe804000 --b100000 --u1

使用 pidin 命令可看到其在后台的状态

# pidin -p i2c-bcm2711 pid tid name                         prio STATE          Blocked                     20488   1 sbin/i2c-bcm2711              10r RECEIVE        1  

其状态为:阻塞等待信息

设备驱动

向 I2C 控制器进制发送发送数据,才能最终向从物理层面。需要查找设备端可用的API接口,尝试在下篇编写一个 EEPROM 的驱动。

备注

  1. BSP 包自带的’i2c-bcm2711.sym’文件进制反汇编,'.sym’就是带符号的可执行文件。

    $ aarch64-unknown-nto-qnx8.0.0-objdump -xDS ./binary_files_with_symbols/aarch64le/sbin/i2c-bcm2711.sym > i2c-bcm2711.dump2
    

参考

  • __FRAME_END__ 是编译器和链接器自动生成的一个符号,常见于 ELF 格式的可执行文件或目标文件中。它的作用如下:

    1. 标记栈回溯信息的结束位置
      __FRAME_END__ 通常用于标记 .eh_frame 段(异常处理帧信息段)的结束。该段保存了函数调用栈的回溯信息,供异常处理、栈展开(unwind)、调试等使用。
    2. 调试和异常处理支持
      在程序发生异常(如C++异常、断点、崩溃)时,调试器或运行时库会利用 .eh_frame 段中的信息进行栈回溯。FRAME_END 作为该段的终止符号,帮助工具正确解析和遍历帧信息。
    3. 链接器定位
      链接器通过 FRAME_END 确定 .eh_frame 段的边界,便于生成和合并调试信息。

    简要总结:__FRAME_END__ 主要用于标记异常处理帧信息的结束位置,支持调试、异常处理和栈回溯等功能。对于普通开发者来说,它一般是自动生成和使用的,无需手动干预。

http://www.dtcms.com/a/532653.html

相关文章:

  • 创意网站设计团队郑州百度推广托管
  • 网盘做网站空间杭州简单网技术有限公司
  • TensorFlow学习入门
  • 强电控制器-非正常工作实验
  • 网站修改域名服务器企业静态网站
  • GitHub等平台形成的开源文化正在重塑结帖人
  • 考古并发历史(1)
  • 班级网站设计外国大气网站
  • 深拷贝浅拷贝
  • 样本与样本值
  • 无极网站网站首屏高度
  • ansible自动化运维入门篇-ansible部署
  • 如何在搜索中找到自己做的网站o2o电商交易类平台有哪些
  • Rust中错误处理机制
  • Ubuntu 24.04上安装MySQL 8.0
  • Java基于SpringBoot的高校报修与互助平台小程序【附源码、文档说明】
  • 工信部icp备案流程关键词在线优化
  • 做视频的模板下载网站ppt汇报模板免费下载
  • 10.16-10.25力扣计数刷题
  • 在K8s中部署多个ASP.NET Core实例
  • 14.如何利用ArcGIS将矢量线、面的坐标数据保存为txt
  • 网站开发者模式怎么打开做策划网站推广怎么写简历
  • zynq ttc pwm例子
  • 【底层机制】linux IO 为什么要有进程表项、文件表项、v节点表项、i节点表项
  • 怎么用wordpress修改网站源码镇江网站网站建设
  • 设计方案表网站名称汉台网站制作
  • git误合并两分支如何回退
  • 【Linux系统编程】编译器gcc/g++
  • LeetCode 面试经典 150_链表_K 个一组翻转链表(61_25_C++_困难)(四指针法;头插法)
  • 做一个简单网站多少钱建设银行网站买手机