驱动编写的三种方式:传统/总线/设备树
字符设备驱动程序框架:
如何编写驱动代码
① 确定主设备号,也可以让内核分配
② 定义自己的 file_operations 结构体
③ 实现对应的 drv_open/drv_read/drv_write 等函数,填入 file_operations 结构体
④ 把 file_operations 结构体告诉内核:register_chrdev
⑤ 谁来注册驱动程序啊?得有一个入口函数:安装驱动程序时,就会去调用这个入口函数
⑥ 有入口函数就应该有出口函数:卸载驱动程序时,出口函数调用 unregister_chrdev
⑦ 其他完善:提供设备信息,自动创建设备节点:class_create, device_create
我们需要知道什么资料或整体操作步骤是什么?
如何告诉驱动程序引用的是什么那个引脚(传统/总线/设备树):
三者的区别在于如何指定硬件资源。
传统方法:
即在代码中写死,即直接将寄存器地址配置给设备。
LED: 0X......
总线模式:
包含两个文件:
led_drv.c :注册file_operations,初始化结构体。
led_dev.c: 指定引脚
设备树模式:
led_drv.c :注册file_operations,初始化结构体。
jz3440.dts: 指定引脚
三者的区别:
以点亮两个LED的案例来解释:
传统:
首先,写led_drv.c:
分配:allc file_operations.
设置:配置相应的LED操作函数:read/write/open等
注册:
入口函数/出口函数:
但如果是配置两个LED驱动,方法是将前面注册调用的操作复制一遍,将其pin1->pin2。所以,相对编程简单,但是不容易维护,因为新的板子过来需要重新修改led.c的代码
总线模式:
总线将引脚抽出来,单独去配置。
led_drv.c / led_dev.c 将文件挂载在platform总线,led.drv与前面操作相同,不同在于led_dev.c用来指定资源
led_dev.c 会分配/注册一个platform_devices,通过“.resource”来指定引脚。对于不同的board,公用同一led_drc.c。
led-drv.c 分配/设置/注册 platform_driver struct。内包含“probe”, "driver_struct"
probe:分配注册file_operations
设置/注册.....与前面相同
而引脚在platform中被指定,我们在操作GPIO即platform的硬件资源,当borad被修改后,直接修改led_dev.c 的platform中的硬件资源即可。
但是这种方式对于不同的驱动程序,需要注册不同的“resource”,造成代码的冗余,且因为dev为.c文件需要重新编译
设备树:
组成:led_drv.c(与总线相同),设备树文件(*.dts)执行硬件资源
kernel根据*.dts构造platform_dev,在运行时去解析dts文件,来分配/注册一个platform_dev,所以在配置时不需要重新编译,只需要拷贝dts文件即可