34_实验三十三_LED驱动两个版本 实验三十三 LED 驱动两个版本——寄存器直控与自动创建设备文件对应课件《第8章 GPIO端口》8.3 节后半Slide 27-34系列说明本系列基于华清远见 FS-MP1ASTM32MP157A开发板对应课件《第8章 GPIO端口》。实验三十二把寄存器地图立好本篇写第一个控制真硬件的驱动LED1PZ5。例 1led.c走寄存器直控路线ioremap readl/writel BSRR设备文件手动 mknod——与 toychar 同款流程例 2mdevled.c升级为自动创建设备文件alloc_chrdev_region → cdev → class_create → device_create 五函数/dev/mdevled由内核自己生成——从这版起告别 mknod设备名跟模块同名由源码LED_NAME决定。两版的测试程序同为ledApp。前置实验三十二寄存器地图、实验三十一外部模块编译流程。一、两版差异一览例 1 led.c例 2 mdevled.c主设备号写死 201alloc_chrdev_region 内核自动分配设备文件手动mknod /dev/led c 201 0自动生成class_create device_createmdev 接报落地注册/注销register/unregister_chrdevcdev 三件套 class/device 两件套注销逆序控灯代码完全相同ioremap writel BSRR完全相同控制约定ledApp 与驱动共用向 /dev/led写 1 开灯LEDON、写 0 关灯LEDOFF一个字节搞定。二、实验环境实际项目实际值操作位置Ubuntu 编译两个驱动 ledApp板上加载、测试根文件系统buildroot 版rfs-buildrootNFS素材drivers-dev.zip的02-led/led.c 247 行 ledApp.c 65 行 Makefile、03-mdevled/mdevled.c 290 行 同款 ledApp Makefile——zip 已在第 7 章目录05_字符设备驱动/复制解压过本篇不重复复制同一包用三章、各取所需子目录板上硬件三颗绿色 LEDPZ5/PZ6/PZ7本篇操作 LED1开工自检10 秒toychar 的*.ko已从板上卸干净lsmod无残留02-led/Makefile的 KERNELDIR 已指本机内核树第 7 章同款改动板上三颗灯当前状态看一眼出厂 TF-A 使能时钟后引脚默认态不定驱动加载会显式开灯。三、课件 ↔ 步骤对应表课件 Slide内容对应步骤27例 1led.c ledApp.c需手动建设备文件步骤 1~328~33自动创建设备文件的五个函数原型与注销逆序步骤 434例 2mdevled.c测试程序仍是 ledApp步骤 4~5本篇动作 → 后面谁用 → 现在含糊的后果本篇动作后面哪一篇要用现在含糊的后果自动创建五函数alloc/cdev/class/device第 8 章后所有驱动模板第 9 章 dtsled 同款每写一个驱动都回到手动 mknod原始社会注销逆序5→4→3→2→1一切驱动的 exit 函数写法注销顺序错内核残留资源或崩溃BSRR 直控灯第 9 章 dtsled同一颗灯换成设备树取参第 10 章 GUI 背光——ls /sys/class/mdevled/的 class 概念以后排查设备文件没生成先看 classdevice_create 失败了还在 mknod 上打转四、实验步骤步骤 1读 led.c 的初始化六步Slide 27源码 247 行02-led/led.c的骨架与 toychar3 相同fops register_chrdev新增的全在 init 里——实验三十二寄存器地图的落地版六步逐行读#defineLED_MAJOR201/* 主设备号 */#defineLED_NAMEled/* 设备名字 */#defineLEDOFF0/* 关灯 */#defineLEDON1/* 开灯 *//* 寄存器物理地址 */#defineRCC_BASE(0x50000000)#defineRCC_MP_AHB5ENSETR(RCC_BASE0x210)#defineGPIOZ_BASE(0x54004000)#defineGPIOZ_MODER(GPIOZ_BASE0x0000)#defineGPIOZ_OTYPER(GPIOZ_BASE0x0004)#defineGPIOZ_OSPEEDR(GPIOZ_BASE0x0008)#defineGPIOZ_PUPDR(GPIOZ_BASE0x000C)#defineGPIOZ_BSRR(GPIOZ_BASE0x0018)映射六个寄存器各ioremap(物理地址, 4)——基址 0x54004000 偏移与实验三十二手册表逐项对应时钟使能#if 0注释段写 RCC_MP_AHB5ENSETR 的 GPIOZEN——安全世界寄存器写不动、TF-A 已使能代码留作教材实验三十二第三节MODERPZ5 设为输出——val ~(0x3 10); val | (0x1 10);PZ5 占 bit11:10清 2 位再写 01 输出模式OTYPER推挽——val ~(0x1 5);bit5 写 0 push-pullOSPEEDR高速、PUPDR上拉——同款清 2 位写新值BSRR 默认开灯val (0x1 5); writel(val, GPIOZ_BSRR_PI);——只写寄存器不读源码注释“此寄存器写 1 有效写 0 无任何作用”。led_switchwrite 函数里被调用就两行核心if(staLEDOFF){val(121);// bit21 BR5高 16 位的复位位写 1 复位 PZ5 → 灯灭writel(val,GPIOZ_BSRR_PI);}elseif(staLEDON){val(15);// bit5 BS5低 16 位的置位位写 1 置位 PZ5 → 灯亮writel(val,GPIOZ_BSRR_PI);}15与121正是 BSRR 的 BS5/BR5——高 16 位复位、低 16 位置位源码里不需要 read-modify-write的注释就是实验三十二 BSRR 那节的结论。步骤 2编译 led.ko 与 ledApp部署# Ubuntu02-led/ 里先 nano Makefile 改一处**CtrlW** 搜 KERNELDIR把第 1 行改成你的内核源码树路径**CtrlO** 回车保存、**CtrlX** 退出——键位卡见实验二十步骤 1obj-m : led.o 素材包已预填不用动make# 收尾 MODPOST 1 modules → LD [M] led.kols*.ko# 就地验证led.ko 存在arm-none-linux-gnueabihf-gcc ledApp.c-oledAppfileledApp# 就地验证ELF 32-bit ... ARM, EABI5sudocpled.ko /home/cnu/nfsboot/rfs-buildroot/lib/modules/5.4.31/sudocpledApp /home/cnu/nfsboot/rfs-buildroot/bin/led.ko 由make产出——只 nano 改 Makefile 不 makels *.ko就是没有那个文件或目录gcc编的ledApp是用户态测试程序与内核模块两码事别混。为什么带 sudo与实验三十一同款——实验二十九 chown 归 root 后凡往 rfs-buildroot 拷文件一律 sudo普通 cp 报权限不够不是坏了。Makefile 的obj-m素材包里已是led.o与 toychar 包同款加强版若你复制了 toychar 的 Makefile记得把三行 obj-m 换成一行obj-m : led.o。图实测——02-led 一屏收全程make收尾MODPOST 1 modules→ 绿框LD [M] led.kofile ledApp见ARM, EABI5sudo cp 两件落位led.ko 进 modules 目录、ledApp 进 bin。步骤 3加载、手动建设备文件、点灯Slide 27# 板上buildroot 根depmod modprobe led lsmod# 就地验证列表里有 ledled.c 平时不打印——printk 只在出错路径dmesg 安静≠没加载mknod/dev/led c2010# 主 201 LED_MAJOR与驱动一致ls-l/dev/led# crw-r--r-- 1 root root 201, 0ledApp /dev/led1# 写 1 LEDON → LED1 亮ledApp /dev/led0# 写 0 LEDOFF → LED1 灭LED1 亮灭随命令切换——第一颗被你亲手点亮的真实硬件盯板上三颗绿灯里丝印LED1那颗别盯错灯。rmmod 后灯灭exit 函数里led_switch(LEDOFF)rmmod leddmesg|tail-2# 无新报错即干净卸载照旧安静属正常rm/dev/led# 手动建的文件手动删——报 No such file见下rm 报 “No such file” 不是闹鬼mdev实验二十六 rcS 常驻的热插拔代理对模块的卸载事件也响应——rmmod 时内核给名为led的模块发 remove 事件mdev 按名字把同名的/dev/led一并 unlink 了。设备文件没了mknod十秒重建无碍。步骤 4mdevled——五个函数设备文件自动出现Slide 28~34例 2 的控灯代码与例 1 完全相同改的是注册/注销这一对。课件给的两个五步清单——注册按 1→5注销按 5→1严格逆序#注册init注销exit1alloc_chrdev_region(devid, 0, LED_CNT, LED_NAME)申请设备号unregister_chrdev_region(devid, LED_CNT)注销设备号2cdev_init(led_cdev, led_fops)初始化 cdev无对应3cdev_add(led_cdev, devid, LED_CNT)添加 cdevcdev_del(led_cdev)删除 cdev4class_create(THIS_MODULE, LED_NAME)创建 classclass_destroy(led_class)删除 class5device_create(led_class, NULL, devid, NULL, LED_NAME)创建设备文件device_destroy(led_class, devid)删除设备文件各函数参数速查课件 Slide 30~33alloc_chrdev_region的 dev 是内核分配的设备号主次打包在一个 u32 里——实验三十的 dev_tbaseminor 起始次设备号、count 数量device_create的 fmt 是设备文件名。cdev 是内核管理驱动的数据结构课件类比进程的 PCB。API 文档在 https://docs.kernel.org。mdevled.c 的 init 关键段290 行六步寄存器初始化与 led.c 相同只列注册段retalloc_chrdev_region(led.devid,0,LED_CNT,LED_NAME);/* 申请设备号 */cdev_init(led.led_cdev,led_fops);retcdev_add(led.led_cdev,led.devid,LED_CNT);led.led_classclass_create(THIS_MODULE,LED_NAME);led.led_devicedevice_create(led.led_class,NULL,led.devid,NULL,LED_NAME);编译部署同步骤 203-mdevled/里 make 同一个 ledApp# Ubuntu03-mdevled/ 里同样先 nano Makefile**CtrlW** 搜 KERNELDIR、第 1 行改成你的内核树——**每个素材子目录各一份 Makefile、各改各的**zip 里预填的都是课件作者路径02-led 改过不会带过来make# MODPOST 1 modules → LD [M] mdevled.kosudocpmdevled.ko /home/cnu/nfsboot/rfs-buildroot/lib/modules/5.4.31/ls/home/cnu/nfsboot/rfs-buildroot/lib/modules/5.4.31/*.ko# 就地验证led.ko mdevled.ko 两件在册图实测——03-mdevled 同款流程nano 改 KERNELDIR每个子目录各改各的→make→ 绿框LD [M] mdevled.kosudo cp 后ls见 modules 目录里五件 .ko——toychar 三件 led mdevledrmmod 只是内存层面卸载.ko 文件留档属正常。步骤 5加载验证——/dev/mdevled 自己出现# 板上depmodmodprobe mdevled lsmod# mdevled 在列 加载成功平时同样不打印ls/sys/class/mdevled/# mdevled —— class_create 的产物/sys 的目录视角ls-l/dev/mdevled# 设备文件已自动生成主设备号是内核分配的不再是 201cat/proc/devices|grepmdevled# 看这次分到的主设备号ledApp /dev/mdevled1# 亮ledApp /dev/mdevled0# 灭名字跟上例不一样mdevled.c 第 21 行#define LED_NAME mdevled——class 与设备文件都叫mdevled别按例 1 的led去找实测按/sys/class/led找扑了个空才对出来的。/dev/mdevled没有经过任何 mknod——device_create发出设备事件实验二十六 rcS 里常驻的 mdev 收到后自动生成文件第 6 章echo /sbin/mdev /proc/sys/kernel/hotplug那一行的兑现时刻。查主设备号的新姿势ls -l /dev/mdevled直接看每次加载可能不同——这就是自动分配。实测全中lsmod见mdevled 16384 0表头照旧Tainted: Gls -l /dev/mdevled见crw-rw---- 240, 0——主设备号 240 是内核现场分配的自动分配的铁证每次加载可能变权限位 660 与手动 mknod 的 644 不同——mdev 建节点有自己的默认权限不影响 root 使用/proc/devices出240 mdevledledApp开关灯后rmmod mdevled收尾。卸载rmmod mdevled——/dev/mdevled 与 /sys/class/mdevled 同步消失注销五步逆序的现场。五、注意事项mknod 的主设备号必须与LED_MAJOR201一致——只对例 1 成立例 2 主设备号是内核给的禁止 mknod自己建文件反而与 device_create 的对不上号。BSRR 是只写寄存器不要先 readl 再改位——读到全 0 是正常现象不是坏了led.c 注释原话。注销五步严格逆序5→4→3→2→1——顺序错了会在引用未释放资源时崩溃mdevled.c 的 exit 与 goto fail 链都按这个序写。class_create/device_create失败要有清理路径源码里goto fail_class_destroy/goto fail_cdev_del的回滚链——照抄即可但要知道那是申请一半失败要退已申请的。切版本前先 rmmod 旧的——两驱动的模块名led/mdevled、设备名/dev/led、/dev/mdevled、设备号201 / 内核分配互不冲突同时加载也不会报 busy但两套驱动控的是同一颗 PZ5 灯同时在场只会让灯是谁点的说不清一次只留一个。灯不亮排查顺序dmesg有无加载报错 →ls /dev/led在不在例 1 忘 mknod 最常见→ 极性我们的板高电平点亮→ 换 LED2/LED3PZ6/PZ7交叉验证硬件。六、验证点一览验证点命令通过的样子在哪一步敲led.ko 编出ls *.ko02-led/led.ko 存在步骤 2加载无报错modprobe ledlsmodled 在列dmesg 无 failed平时不打印、安静属正常LED1 默认亮步骤 3设备文件例1ls -l /dev/ledmknod 后201, 0字符设备步骤 3开关灯ledApp /dev/led 1/0LED1 亮 / 灭步骤 3设备文件例2modprobe mdevled后ls -l /dev/mdevled自动出现实测crw-rw---- 240, 0mdev 默认 660 权限与 mknod 的 644 不同步骤 5class 视角ls /sys/class/mdevled/mdevled目录步骤 5卸载同步rmmod mdevled后ls /dev/mdevled、ls /sys/class/mdevled/两者都消失步骤 5不达标时的排查现象先查什么ledApp 报Cant open file例 1 忘了 mknod例 2 敲成/dev/led了设备文件叫/dev/mdevled真没生成再查 device_createdmesg 报错、ls /sys/class/mdevled/灯不亮但命令成功极性认知高电平亮换ledApp /dev/led连续 1/0 对比看板子上 LED1 丝印位置别盯错灯modprobe led报 busytoychar/led 旧模块没卸lsmod清场device_create 后 /dev 无文件mdev 在岗吗实验二十六 rcS 的 hotplug 行 实验二十七 uevent helper 勾选——两道前提先ls /sys/class/mdevled/确认 class 在不在rmmod 后又加载主设备号变了正常——alloc_chrdev_region 每次分配这就是与写死 201 的区别七、实验完成标志led.ko 与 ledApp 编译部署完成mknod /dev/led c 201 0后ledApp开关灯实测无报错步骤 1~3 实测LED1 物理亮灭已确认同颗 PZ5 在实验35 dtsled 复测时用户实认确实亮了跟灭了——同一套 BSRR 控灯路径rmmod led 干净卸载步骤 3 实测/dev/led 的清理由 mdev 随模块 remove 事件自动完成——手动 rm 报 “No such file” 属正常步骤 3 实测mdevled.ko 加载后/dev/mdevled 自动生成实测crw-rw---- 240, 0——mdev 默认 660 权限、/sys/class/mdevled/mdevled在列、/proc/devices出240 mdevled步骤 4~5 实测ledApp 对 /dev/mdevled 开关灯同样可控步骤 5 实测rmmod 后设备文件与 class 同步消失同款机制已在实验35 dtsled 上实测确认注销五步逆序与失败回滚链在源码里能指出步骤 4八、下一步第 9 章 设备树版 LED 驱动寄存器直控版虽然通了但地址写死在驱动里——换块板就报废。第 9 章《设备树》把 LED 迁到设备树路线drivers-dev.zip 的04-dtsled在设备树里描述 LED 节点、驱动从设备树取寄存器地址of_iomap/of_platformPZ5 还是那颗灯代码却换上了硬件描述与驱动分离的正装。