Linux 字符设备驱动基本框架(一)

一、Linux 驱动开发原理

1、Linux 驱动分类
在这里插入图片描述
2、Linux 下的内核态和用户态?

Linux 操作系统分为用户态和内核态,内核态完成与硬件的交互,用户态可以理解为上层的应用程序。

用户态和内核态可以互相转换。当应用程序执行系统调用或被硬件中断挂起时,Linux 操作系统都会从用户态切换到内核态;当系统调用完成或者中断处理完成后,操作系统会从内核态返回到用户态,继续执行应用程序。

3、Linux 下的应用程序应和驱动的交互原理

(1)用户程序通过 API 来使用系统调用,API 是用户可以直接使用的函数接口,系统调用的实现是在内核中完成的。

(2)有些 API 所涉及到的功能涉及到与内核空间进行交互,那么这类 API 会封装系统调用;不涉及与内核进行交互的 API 不会封装系统调用。如:open 函数封装了系统调用,它可以调用内核空间中 file_operations 结构体的 open 成员变量来对驱动设备进行操作!
在这里插入图片描述
4、字符设备驱动开发流程

驱动加载成功我们需要在 “/dev” 目录下创建设备节点文件,应用程序通过对这个名为 “/dev/xxx” 的文件进行相应操作即可实现对硬件的操作。编写字符设备驱动框架最主要实现的就是 file_operations 结构体中的成员变量。

(1)应用程序调用 open 函数打开设备,调用 close 函数关闭设备。

(2)通过 write 函数向 /dev/led 写数据,如:写 1 表示打开,写 0 表示关闭。
在这里插入图片描述
5、Linux 驱动的两种运行方式

(1)将驱动编译进 Linux 内核中(即 zImage 中),这样当 Linux 内核启动的时候就会自动运行驱动程序。

(2)将驱动编译成模块(.ko 文件),在Linux 内核启动以后使用 “modprobe” 加载驱动模块,“rmmod” 卸载驱动模块。

module_init(xxx_init)      // 注册模块加载函数,modprobe 命令加载驱动时,xxx_init 这个函数就会被调用
module_exit(xxx_exit)      // 注册模块卸载函数,rmmod 命令模块卸载时,xxx_exit 这个函数就会被调用

二、字符设备驱动开发步骤

1、驱动模块的加载和卸载
在这里插入图片描述
这里的 __init 和 __exit 有什么用呢?

【答】:编译器将标 __init 、__exit 的所有代码存在特殊的内存段中。提示该函数仅在初始化阶段使用,并在初始化阶段之后释放所使用的内存单元。

2、字符设备注册与注销

当驱动模块加载成功以后需要注册字符设备,同样,卸载驱动模块的时候也需要注销掉字符设备。一般字符设备的注册在驱动模块入口函数 xxx_init 中进行,字符设备的注销在驱动模块出口函数 xxx_exit 中进行。

static inline int register_chrdev(unsigned int major, const char *name,const struct file_operations *fops)
static inline void unregister_chrdev(unsigned int major, const char *name)

先通过 “cat /proc/devices” 查看当前已经被使用掉的设备号,然后再自定义未使用过的设备号

3、实现设备的具体操作函数

file_operations 结构体就是设备的具体操作函数,编写字符设备驱动框架最主要实现的就是 file_operations 结构体中的成员变量。
在这里插入图片描述
结构体中 “.xxx = ” 写法很少见,是什么意思呢?

【答】:定义结构体成员并赋值,等同于:

chrdevbase_fops.owner = THIS_MODULE
chrdevbase_fops.open= chrdevbase_open
chrdevbase_fops.release = chrdevbase_release

THIS_MODULE 是什么意思?

【答】:是一个 struct module 变量,代表当前模块,可通过 THIS_MODULE来引用 struct module 结构体成员,比如使用 THIS_MODULE->state 可以获得当前模块的状态。

#define THIS_MODULE (&__this_module)
extern struct module __this_module        // THIS_MODULE 指向 struct module

(1)chrdevbase_read 函数

应用程序调用 read 函数从设备中读取数据的时候此函数会执行。

函数原型:static inline long copy_to_user(void __user *to, const void *from, unsigned long n)
函数功能:内核空间不能直接操作用户空间的内存,借助 copy_to_user 函数来完成内核空间的数据到用户空间的复制
返回值:复制成功,返回值为 0,如果复制失败则返回负数

为什么应用程序的 read 函数是 3 个参数,而 chrdevbase_read 是 4 个参数呢?

【答】:从应用程序到驱动会经过 IO 层处理,不是直接调用关系。这里具体多出来的那个参数表示当前文件读写位置。
在这里插入图片描述
(2)chrdevbase_write 函数

应用程序调用 write 函数向设备写数据的时候此函数就会执行。

函数原型:unsigned long copy_from_user(void * to, const void __user * from, unsigned long n)
函数功能:用于将用户空间的数据传送到内核空间
返回值:复制成功,返回值为 0,如果复制失败则返回负数

三、编写测试应用程序

编写应用程序,一般需要在 Ubuntu 中通过 man 命令查看应用编程手册,手册中讲了标准命令、系统调用、库函数等。

1、Standard commands(标准命令)         // 如:man 1 ls
2、System calls(系统调用)			  // 如:man 2 open、man 2 read
3、Library functions(库函数)			  // 如:man 3 printf

注意:open 函数返回值是文件描述符,后续对文件进行读写操作时,通过文件描述符即可,不需要通过文件名读写。

为什么要使用 atoi 函数?

【答】:atoi(const char *nptr),atoi 函数将字符串类型 1 转化为 int 类型 1,直接使用 if(argv[2] == 1) 会出错。

四、运行测试

1、加载驱动模块:modprobe xxx.ko

(1)加载驱动命令 insmod 和 modprobe 区别在哪?

【答】:insmod 不能解决模块依赖关系,modprobe 会分析模块依赖关系,然后会将所有的依赖模块都加载到内核中。

(2)为什么要创建 /lib/modules/4.1.15?

【答】:modprobe 默认到 /lib/modules/<Linux 内核版本号> 目录中查找相应驱动模块,一般需要自己创建该目录。

(3)文件夹创建好后,使用 modprobe 加载驱动,出现下列问题如何解决?
在这里插入图片描述
【答】:直接输入 depmod 命令即可自动生成 modules.dep,这个命令是由 busybox 编译出来的。

(4)再次加载又会遇到 module license 问题如何解决?
在这里插入图片描述
【答】:在 C 代码中需要加入 MODULE_LICENSE(“GPL”),模块许可证声明描述内核模块的许可权限,如果不声明LICENSE,模块被加载时,将收到内核被污染(kernel tainted)的警告。

(5)注册字符设备函数中,参数 name 是设备名,我们在创建设备节点时,如:mknod /dev/led c 200 0,它与创建设备节点的 name 有什么不同?

static inline int register_chrdev(unsigned int major, const char *name,const struct file_operations *fops)

【答】:无论创建设备节点时的 name 是怎样,在创建好的设备节点中用 “cat /proc/devices” 查看创建的设备节点名都是注册字符设备函数中的参数 name,但应用程序是通过创建设备节点的名字 “/dev/xxx” 实现对硬件的操作。

2、创建设备节点文件

驱动加载成功需要在 /dev 目录下创建一个与之对应的设备节点文件,应用程序就是通过操作这个设备节点文件来完成对具体设备的操作。

mknod /dev/chrdevbase c 200 0    //mknod - 创建节点命令,c - 字符设备,200 - 主设备号,0 - 次设备号

3、设备操作测试

./chrdevbaseApp /dev/chrdevbase 1
  • 2
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值