1 前言今天遇到一个问题要修改某个PMIC的寄存器。求爷爷告奶奶到处问。最后才说是挂载debugfs然后修改里面的regmap即可。这次还真是翻车不光没用过连听都没听说过这个debugfs。所以今天晚上总结一下。之前看驱动的时候不少驱动提供了在sysfs的接口而且也看了怎么创建。但是debugfs还真的是第一次遇到。所以先看看这个和sysfs还有procfs的区别。比如说gpio我记得是直接在sysfs下面就有。今天在debugfs里面也看到了。查了一下结论如下1. sysfs 下的 gpio位于 /sys/class/gpio/。提供基本的 GPIO 控制接口允许用户导出 GPIO 引脚、设置输入/输出方向、读取/写入引脚电平。常用于生产环境中的 GPIO 控制比较稳定。2. debugfs 下的 gpio位于 /sys/kernel/debug/gpio。主要用于调试显示所有 GPIO 引脚的状态信息包括引脚方向、值、驱动程序分配情况等。提供详细的调试信息不用于直接控制 GPIO。看来基本概念就是debugfs下面的节点调试信息更多。在第四节增加了更详尽的解释20250419更新2 树莓派3B的debugfs2.1 整体说明挂载的命令sudo mount -t debugfs none /sys/kernel/debug sudo unmount /sys/kernel/debug这个是基于树莓派3B的输出tomraspberrypi:~/alsa $ ls /sys/kernel/debug/ asoc bdi cec debug_enabled dma_buf dri fault_around_bytes i2c kprobes lru_gen_full mmc0 phy pwm regmap slab swiotlb vchiq bcm2835_thermal binder clear_warn_once device_component dmaengine extfrag gpio ieee80211 kvm media mmc1 pinctrl ramdisk_pages regulator sleep_time tracing vc-mem bcm7271-uart block clk devices_deferred dma_pools f2fs hid irq lru_gen memblock opp pm_genpd ras sched stackdepot usb vcsm-cma系统调试 / 通用调试目录功能说明tracing/ftrace系统级函数跟踪器可以跟踪内核函数调用、时间、频率等。适用于调试驱动函数是否被调用比如max98357a_daiops_trigger。kprobes/内核探针接口设置动态断点高级调试工具用于监控任意函数。regmap/显示所有注册的 regmap 信息通常用于 I2C/SPI Codec 寄存器映射。slab/SLAB 分配器的信息调试内存分配和释放。stackdepot/记录所有 stack trace 信息通常在内存泄漏时分析调用路径。sched/调度器的调试信息如负载、调度延迟、实时线程等。fault_around_bytes用于调试 page fault 时周围内存区域的行为。音频 / 多媒体相关目录功能说明asoc/ALSA SoC 的核心调试目录可以看到 widget 状态、route、DAPM 状态、声卡状态等。对调试max98357a极有帮助。vchiq/树莓派专用的 VCHIQ 组件视频解码、摄像头、VPU 通信层。media/所有 V4L2/video 相关驱动的信息。cec/HDMI CECConsumer Electronics Control调试接口。usb/所有 USB 设备和控制器状态信息。vc-mem、vcsm-cma视频核心的共享内存信息VPU、GPU 共享内存树莓派专属。总线 / 外设调试目录功能说明gpio/显示所有 GPIO 引脚的方向、电平、所属驱动非常适合观察sdmode-gpio。pinctrl/查看所有引脚的复用状态I2S、PWM、SPI 是否已复用pwm/显示所有 PWM 控制器的当前配置dmaengine/DMA 设备状态、传输任务信息i2c/所有 I2C 总线设备的信息可查看是否识别到了max98357a如果你用 I2C 控制clk/所有内核时钟节点如 I2S 时钟、CPU 时钟支持 enable/disable 和频率查看phy/USB PHY 状态信息比如高速/全速模式mmc0//mmc1/SD 卡控制器的调试信息boot 卡通常是 mmc0树莓派 / 特有目录功能说明bcm2835_thermalBCM SoC 的温度传感器信息vcsm-cma/vc-memGPU 共享内存接口用于 video core 和 CPU/GPU 通信bcm7271-uart如果启用了该 UART 控制器这里是调试信息接口其他系统调试目录功能说明binder/Android 系统下用于 binder 进程通信调试Raspbian 基本不用dma_buf/内核 DMA buffer 管理信息通常用于视频、音频等零拷贝 buffer 管理extfrag/外部内存碎片化信息lru_gen/和lru_gen_full/新版 Linux LRU 页面回收机制调试点高级内存管理opp/OPPOperating Performance Points用于 DVFS动态电压频率调整regulator/显示所有电压调节器状态如供电电压等sleep_time/内核空闲时长调试省电情况swiotlb/用于 DMA 的 bounce buffer 状态2.2 一个例子regmap播放前后3f203000.i2s的变化如下其中0x3f203000 是 BCM2835 I2S控制器的寄存器基地址.i2s 表示这个 regmap 是挂载在 I2S 控制器的寄存器上的。关于寄存器之前写过一篇可以参考硬件寄存器的简单理解-CSDN博客在设备树中I2S控制器配置如下i2s: i2s7e203000 { compatible brcm,bcm2835-i2s; reg 0x7e203000 0x24; ... };在linux虚拟地址中0x7e203000 被remap成0x3f203000。这个具体的含义可以参考BCM2835的硬件手册。经过比对变化的就是00: 020802a0 - 00: 030802a5也就是上图的CS_A和FIFO_A位0 EN 从0变1I2S 控制器启用位4 TXON 从0变1发送通道启用位2 TXCLR 从0变1清除 FIFO可能刚开始播放位28 SYNC 从0变1同步触发开始在这里其实可以手动修改寄存器去调试。尤其是某些值需要修改的时候。# 写入格式echo offset value registers # 举例将 0x00 偏移的 CS_A 改成 0x00000000关闭 I2S echo 0x00 0x00000000 | sudo tee /sys/kernel/debug/regmap/3f203000.i2s/registers2.3 第二个例子gpio可以在debugfs下查看gpiotomraspberrypi:~/alsa $ sudo cat /sys/kernel/debug/gpio gpiochip0: GPIOs 512-565, parent: platform/3f200000.gpio, pinctrl-bcm2835: gpio-512 (ID_SDA ) gpio-513 (ID_SCL ) gpio-514 (GPIO2 ) gpio-515 (GPIO3 ) gpio-516 (GPIO4 |sdmode ) out lo gpio-517 (GPIO5 ) gpio-518 (GPIO6 ) gpio-519 (GPIO7 ) gpio-520 (GPIO8 ) gpio-521 (GPIO9 ) gpio-522 (GPIO10 ) gpio-523 (GPIO11 ) gpio-524 (GPIO12 ) gpio-525 (GPIO13 ) gpio-526 (GPIO14 ) gpio-527 (GPIO15 ) gpio-528 (GPIO16 ) gpio-529 (GPIO17 ) gpio-530 (GPIO18 ) gpio-531 (GPIO19 ) gpio-532 (GPIO20 ) gpio-533 (GPIO21 ) gpio-534 (GPIO22 ) gpio-535 (GPIO23 ) gpio-536 (GPIO24 ) gpio-537 (GPIO25 ) gpio-538 (GPIO26 ) gpio-539 (GPIO27 ) gpio-540 (NC ) gpio-541 (LAN_RUN_BOOT ) gpio-542 (CTS0 ) gpio-543 (RTS0 ) gpio-544 (TXD0 ) gpio-545 (RXD0 ) gpio-546 (SD1_CLK ) gpio-547 (SD1_CMD ) gpio-548 (SD1_DATA0 ) gpio-549 (SD1_DATA1 ) gpio-550 (SD1_DATA2 ) gpio-551 (SD1_DATA3 ) gpio-552 (PWM0_OUT ) gpio-553 (PWM1_OUT ) gpio-554 (ETH_CLK ) gpio-555 (WIFI_CLK ) gpio-556 (SDA0 ) gpio-557 (SCL0 ) gpio-558 (SMPS_SCL ) gpio-559 (SMPS_SDA ) gpio-560 (SD_CLK_R ) gpio-561 (SD_CMD_R ) gpio-562 (SD_DATA0_R ) gpio-563 (SD_DATA1_R ) gpio-564 (SD_DATA2_R ) gpio-565 (SD_DATA3_R ) gpiochip1: GPIOs 566-567, parent: platform/soc:firmware:virtgpio, brcmvirt-gpio, can sleep: gpio-566 ( |ACT ) out lo gpiochip2: GPIOs 568-575, parent: platform/soc:firmware:expgpio, raspberrypi-exp-gpio, can sleep: gpio-568 (BT_ON ) gpio-569 (WL_ON ) gpio-570 (STATUS_LED ) gpio-571 (LAN_RUN ) gpio-572 (HDMI_HPD_N |hpd ) in lo ACTIVE LOW gpio-573 (CAM_GPIO0 |cam1_regulator ) out lo gpio-574 (CAM_GPIO1 ) gpio-575 (PWR_LOW_N |PWR ) in hi据说这个比较直观而且可以看到被什么占用了。比如gpio-516 (GPIO4 |sdmode ) out lo这里可以看出就是被用去做I2S的sdmode了。。。3 debugfs的编程接口3.1 一个例子例子代码#include linux/module.h #include linux/debugfs.h #include linux/uaccess.h #define DEBUGFS_DIR_NAME mydebug #define DEBUGFS_FILE_NAME mydebugfile #define BUF_SIZE 128 static struct dentry *debug_dir; static struct dentry *debug_file; static char debug_data[BUF_SIZE] Hello debugfs!\n; static ssize_t my_read(struct file *file, char __user *buf, size_t count, loff_t *ppos) { return simple_read_from_buffer(buf, count, ppos, debug_data, strlen(debug_data)); } static ssize_t my_write(struct file *file, const char __user *buf, size_t count, loff_t *ppos) { size_t size min(count, (size_t)(BUF_SIZE - 1)); if (copy_from_user(debug_data, buf, size)) return -EFAULT; debug_data[size] \0; return count; } static const struct file_operations my_fops { .owner THIS_MODULE, .read my_read, .write my_write, }; static int __init my_debugfs_init(void) { debug_dir debugfs_create_dir(DEBUGFS_DIR_NAME, NULL); if (!debug_dir) return -ENOMEM; debug_file debugfs_create_file(DEBUGFS_FILE_NAME, 0666, debug_dir, NULL, my_fops); if (!debug_file) return -ENOMEM; pr_info(debugfs module loaded\n); return 0; } static void __exit my_debugfs_exit(void) { debugfs_remove_recursive(debug_dir); pr_info(debugfs module unloaded\n); } module_init(my_debugfs_init); module_exit(my_debugfs_exit); MODULE_LICENSE(GPL);Makefileobj-m my_debugfs.o all: make -C /lib/modules/$(shell uname -r)/build M$(PWD) modules clean: make -C /lib/modules/$(shell uname -r)/build M$(PWD) clean运行3.2 debugfs API主要就是在#include linux/debugfs.h常用debugfs函数汇总函数名说明debugfs_create_dir()创建一个目录返回dentry*debugfs_create_file()创建一个自定义读写的文件传入file_operationsdebugfs_create_u8/u16/u32/u64()创建一个自动管理的整数变量文件debugfs_create_bool()创建一个布尔变量文件如开关debugfs_create_blob()创建一个二进制 blob 文件debugfs_remove()移除单个 debugfs 项文件或目录debugfs_remove_recursive()递归移除目录及其所有子项debugfs_initialized()判断 debugfs 是否已经挂载官方文档可以查看DebugFS — The Linux Kernel documentation4 最开始的问题也就是sysfsprocfsdebugfs的区别。总体区别一览特性sysfsprocfsdebugfs目标用途内核对象建模 配置接口进程/内核状态展示调试、临时数据用户对象用户可控的系统配置例如设备读取内核状态/信息内核开发者、调试/测试文件权限强制标准单个属性文件通常是只读也有些可写无强制标准只要你愿意生命周期管理自动与设备绑定持久存在较独立开发调试阶段运行期动态创建销毁是否默认挂载是/sys是/proc需挂载/sys/kernel/debug写入作用通常用于配置系统或驱动行为通常不能写或写行为有限可以自由实现读/写、控制测试行为推荐使用场景对外公开配置/控制驱动的属性展示统计信息如内核状态/驱动信息驱动调试、测试、内部状态临时导出简单总结就是运行时需要提供的数据属性状态等。用sysfs。调试时提高效率简化开发临时用的使用debugfs。至于procfs现在先别用了。。。