Linux驱动
-
HelloDriverMode
-
导读:Linux设备驱动会以模块形式出现,学会编写、编译、加载、卸载Linux内核模块
-
学习模块化编译驱动的方法,学习如何加载驱动、查看驱动、卸载驱动
-
Linux内核针对驱动的处理有两方式:
- 把所有的功能全部编译到内核中,需要重新添加或者删除功能的时候,需要重新编译内核
- 动态的添加模块
#inlcude <linux/init.h> //包含模块加载和卸载函数 #include <linux/module.h> //包含初始化加载模块的头文件
//声明模块信息
MODULE_LICENSE(“Dual BSD/GPL”);//声明是开源的没有内核版本限制
MODULE_AUTHOR(“iTOPEET”); //声明作者//功能函数
static int hello_init(void)
{
printk(KERN_ENERG “Hello World enter!\n”); //KERN_ENERG表示紧急信息
return 0;
}static void hello_exit(void)
{
printk(KERN_ENERG “Hello World exit!\n”); //KERN_ENERG表示紧急信息
}//模块入口加载
module_init(hello_init);
//模块的出口卸载
module_exit(hello_init);
-
-
Menuconfig_Kconfig
-
Linux内核配置系统由三部分组成
- Makefile文件:分布在Linux内核源码中的Makefile文件,定义了内核编译规则
- Kconfig:给用户提供选择的功能
- 配置工具 Menuconfig
-
掌握Menuconfig用法,
理解Kconfig文件并掌握修改Kconfig的方法,
理解配置文件.config,
Linux内核配置裁剪,通过Menuconfig工具实现的
Menuconfig是基于菜单的配置页面,现在大部分都是通过这个工具裁剪配置内核的
-
Menuconfig的操作方法
- 操作完成后,.config文件就会被修改,再次编译内核的时候,系统会根据新的config文件来编译整个内核。
-
.config和Menuconfig的关系
- 举例,LED驱动,在Menuconfig中不选中保存退出,在.config中看到led宏定义被注释掉了。 Menuconfig配置会自动修改.config文件
-
Kconfig和Menuconfig之间的关系
-
打开源码下 drivers/char/Konfig 文件 搜索LEDS_CTL 和Menuconfig图形界面中"Enable LEDS config" 对应
config LEDS_CTL bool "Enable LEDS config" default y help Enable LEDS config
-
-
-
-
Makefile编译
-
掌握linux内核编译命令
- make 编译生成内核的二进制镜像
-
掌握编译器路径设置的方法
-
根目录下 vim .bashrc 打开环境变量文件 “.bashrc”,进入到最低行,将环境变量文件中编译器路径设置为
export PATH=$PATH:/usr/local/arm/arm-2009q3/bin
-
使用命令 cd /usr/local/arm ,进入解压编译器“arm-2009q3.tar.bz2”的文件目录
-
可以明显的看到,环境变量中设置的路径和解压的路径是对应的
-
源码目录下,vim Makefile 打开Makefile文件,查找"CROSS_COMPILE" 可以看到
/usr/local/arm/arm-2009q3/bin/arm-none-linux-gnueabi-
-
-
理解环境变了路径、编译器、源码Makefile文件中编译器路径三者之间的关系
- CROSS_COMPILE”以及环境变量还有编译器实际解压路径三者对应
-
理解Makefile文件结构
- 文件开头版本号
- CROSS_COMPILE变量 编译器路径
-
掌握向Makefile文件中添加脚本命令的方法,能够看懂Makefile脚本
-
make 编译内核执行过程
总线设备驱动注册流程讲解
-
了解Linux总线的概念
- 总线设备
- 虚拟总线/平台总线/platform总线,针对不是字面上理解的总线设备,这个总线有对应的设备 platform_device,对应的驱动叫 platform_driver。
- 防止Linux驱动的碎片化,降低Linux的使用难度
- ls /sys/bus 查看总线
-
理解平台总线platform
-
理解Linux设备的概念
- 三大类:字符设备、块设备、网络设备
- cat /proc/devices 可以看到不同设备对应的编号
-
掌握Linux驱动注册流程
- Linux内核要求没出现一个设备就要想总线汇报,或者说注册,出现一个驱动也要向总线汇报或叫注册
- 系统初始化,每个设备都建立一个struct_device的变量,插入到devices链表中
- 系统初始化,每个驱动要建立一个struct_driver的变量,插入到drivers链表中
- 注册设备的结构体为platform_device,注册驱动的结构体为platform_driver,设备和驱动结构体的成员name,相同则匹配。
- 系统在注册驱动的时候通过platform_match函数匹配设备和驱动
-
设备节点简介
- Linux一切皆文件,上层应用使用设备节点访问对应的设备 目录 /dev
-
设备注册
-
掌握在平台文件中注册设备的方法
-
在虚拟总线上注册设备
查看注册设备的结构体platform_device
vim include/linux/platform_device.h //打开platform_device 所在文件
struct platform_device{ const char *char; int id; struct resource *resource; }
“name”:设备名称,在sys/devices中显示,注册驱动时需要和驱动"name"字段比较
“id” 子设备编号,一个设备如果有多个子设备,则需要写入子设备号的数量,如果只要一个则用-1表示;
“*resource” k设备使用的资源数组
-
添加设备到平台总线
-
打开平台文件
vim arch/arm/mach-exynos/mach-itop4412.c
-
查找led的驱动内容,模仿写 查找宏定义"LED_CTL"
#ifdef CONFIG_LEDS_CTL struct platform_device s3c_device_leds_ctl = { .name = "leds", .id = -1, }; #endif
以上是led注册设置的结构体,只调用了两个参数,模仿,添加一个设备"hello_ctl"
#ifdef CONFIG_HELLO_CTL struct platform_device s3c_device_hello_ctl = { .name = "hello", .id = -1, }; #endif
如果确认有宏定义HELLO_CTL,在生成内核的时候才会将其编译到内核
-
添加"HELLO_CTL"宏
vim drivers/char/Kconfig //打开配置宏文件
定义宏
config HELLO_CTL tristate "Enable HELLO config" default y help Enable HELLO config
使用界面配置宏 make menuconfig — Character ---- Enable HELLO config 陪之后保存退出
再次打开平台文件,再搜索"LEDS_CTL"
vim arch/arm/mach-exynos/mach-itop4412.c
#ifdef CONFIG_LEDS_CTL &s3c_device_leds_ctl, #endif
保存退出,重新编译内核,烧写到开发板。
启动开发板,ls /sys/devices/platform/ 可以查看到新注册的hello设备
-
-
驱动注册
-
掌握将驱动注册到平台总线的方法
-
platform_driver_register函数和platform_driver_unregister函数
-
这两个函数用于注册和卸载驱动
vim include/linux/platform_device.h
-
查找 platform_driver_register
extern int platform_driver_register(struct platform_driver *); extern int platform_driver_unregister(struct platform_driver *);
只需要会调用即可,这两个函数都调用一个platform_driver类型的结构体
-
-
platform_driver结构体
-
查看platform_driver结构体
vim include/linux/platform_device.h
-
-
-
struct platform_driver{ int (*probe)(struct platform_device *); //设备的探测和初始化 int (*remove)(struct platform_device *); //移除驱动 void (*shutdown)(struct platform_device *);// 。。。 struct device_driver driver; }
这个结构体包含了一组函数和一个struct device_driver的对象。在驱动中首先要做的就是定义platform_driver中的函数,并创建按这个结构的一个对象实例,然后在init()函数中调用platform_driver_register()向系统注册驱动。
-
prob_linux_module.c
#include <linux/init.h> #include <linux/module.h> #include <linux/platform_device.h> #define DRIVER_NAME "hello_ctl" MODULE_LICENSE("Dual BSD/GPL"); MODULE_AUTHOR("AIOT"); static int hello_init(void) { int DriverState; printk(KERN_EMERG "HELLO WORLD enter!\n"); DriverState = platform_driver_register(&hello_driver); printk(KERN_EMERG "\tDriverState is %d\n",DriverState); return } static void hello_exit(void) { printk(KERN_EMERG "HELLO WORLD exit!\n") platform_driver_unregister(&hello_driver); } module_init(hello_init); module_exit(hello_init);
-
生成设备节点
-
上层应用通过一套标准的接口函数调用设备节点就可以控制底层以及和底层通信。
-
杂项设备节点如何生成
-
-
cat /proc/misc //查看杂项设备
-
linux驱动分为字符设备,块设备,网络设备,无法归类的统称为杂项设备
-
-
-