前言:linux是gnu系统的内核;ubuntu系统是一个linux内核的桌面系统;再详细我也不会了,就这样吧,先定个义;
1 对于单片机而言,代码可以随便访问单片机的各种外设和资源,效率极高;
但是对于Linux系统而言,如果所有的程序都可以访问和更改寄存器和CPU的各种参数,系统就会不稳定;所以linux给自己分成了两个空间:内核空间和用户空间
1.1 用户空间:用户空间的代码可以通过一些库函数与内核空间进行数据交互;
1.2 内核空间:内核空间的代码可以访问和修改各种外设及CPU;linux驱动的代码运行在内核空间中;
1.3 空间的交互
常用的方式有API函数,系统调用C库的open函数等;(称之为软中断或陷入)
C库的系统调用函数在arch/arm64/include/asm/unistd32.h里对两个空间的函数使用软中断进行了映射;
1.3.1 copy_from_user(kernelBuff, userBuff, sizeof(len)); 将用户空间userbuff的数据拷贝len字节,到内核空间;
1.3.2copy_to_user(userBuff, kernelBuff, sizeof(len)); 将内核kernelbuff地址的数据拷贝len字节,到用户空间;
buff的位置都是当前意思的(*to,*from,len);linux函数大部分成功返回0,失败返回失败的字节数或负数;
2 驱动开发
linux觉得直接操作寄存器太繁琐,效率低下;所以linux下的驱动开发使用驱动框架开发,
只要把驱动的硬件信息告诉框架,然后就可以使用框架提供的函数与硬件通信,省去了操作寄存器;
单片机驱动也具有模块开发的特点,只是单片机系统较小,并没有像linux那样分工明确,模块与模块之间严格分层,逻辑清晰;
2.1 驱动类型
linux下的驱动类型主要分为三类驱动设备进行开发
2.1.1 字符设备:为顺序数据流传递类型的设备,目的是为了通信;比如uart、usb、键盘、显示器等;
2.1.2 块设备:以单位块大小来传递数据的设备,目的是为了存储数据;比如硬盘、u盘、flash等;
2.1.3 网络设备:收发数据帧的设备,比如蓝牙、wifi、ethenet等;
2.2 驱动设备的分层编写
2.2.1 硬件层 是设备的硬件信息device:通过设备树从../mach-XXX下读取;设备的硬件信息可以保存到设备树.dts内;
接口层 是统一的API接口bus:内核启动时总线会自动匹配,总线通过设备树的.compatible适配属性或match函数匹配;
驱动层 是设备的操作函数driver:应该就是module里的各种读写开关文件probe等。
2.2.2 有些外设没有总线的概念,便使用虚拟的platform总线来驱动。
device和driver对应为platform_device和platform_driver;
总线为bus_type结构体,位置:/include/linux/device.h;而虚拟出来的platform位于:/device/base/platform.c中。
2.3 驱动编译的两种方式
2.3.1 同源码一起编译到内核里;
2.3.1.1 在kernel/drivers/char下新建目录chardev_test;chardev_test目录下新建.c源文件,Kconfig,Makefile文件;
2.3.1.2 修改char文件夹下的Makefile和Kconfig,包含新建chardev_test进编译;
#kernel/drivers/char目录 #Makefile文件结尾:表示将chardev_test目录下的文件编译进源码;chardev_test下的编译规则由chardev_test下的Makefile决定; obj-y +=chardev_test/ #Kconfig文件结尾:表示包含递归目录下的Kconfig文件; source "char/chardev_test/Kconfig"
2.3.1.3 chardev_test文件下的Kconfig文件和Makefile文件
#Kconfig文件:作用是配置config菜单显示的内容,配置.config中的变量CONFIG_XXX config CHARDEV_TEST tristate "This shows on config menu for CHARDEV_TEST config" help here help is jie shi for this config #Makefile文件:作用是告诉编译器怎么编译源文件; #此处的CONFIG_CHARDEV_TEST是由Kconfig文件中的config CHARDEV_TEST决定的; obj-$(CONFIG_CHARDEV_TEST) +=kernel_driver.o
配置文件Kconfig会生成配置菜单给用户选择,然后用户选择完之后会生成顶层目录下的.config文件,CONFIG_XXX是在.config中定义的,由Kconfig文件决定;
2.3.2 单独编译为.ko文件,然后通过insmod命令加载到模块中;
具体可见https://www.cnblogs.com/caesura-k/p/12627835.html 3.1小节
3 字符设备
3.1 字符设备的编写主要就是对/include/linux/fs.h文件内file_operation结构体的重新编写;
在module_init(xxxdev_init)中指定模块的初始化函数,在模块初始化函数xxxdev_init()中注册一个设备;然后配置好该设备的操作函数即可;
应用程序如果想操作设备,需要传入设备节点告诉内核想要使用的驱动设备文件;那么设备号dev_t和设备节点node有什么不同呢?
设备号是内核用来区分驱动设备的,设备号是驱动设备的主要属性之一。
设备节点是应用程序用来告诉内核我想操作什么驱动设备的。对于同一个驱动设备来说他们是相等的,设备节点存储在/dev/下。