linux:驱动开发的预备知识

前言: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/下。

原文地址:https://www.cnblogs.com/caesura-k/p/12704239.html