字符设备驱动开发

news2024/9/22 3:40:02

1、字符设备驱动简介

字符设备是 Linux 驱动中最基本的一类设备驱动,字符设备就是一个一个字节,按照字节 流进行读写操作的设备,读写数据是分先后顺序的。比如我们最常见的点灯、按键、IIC、SPI, LCD 等等都是字符设备,这些设备的驱动就叫做字符设备驱动。

先来简单的了解一下 Linux 下的应用程序是如 何调用驱动程序的,Linux 应用程序对驱动程序的调用

        在 Linux 中一切皆为文件,驱动加载成功以后会在“/dev”目录下生成一个相应的文件,应 用程序通过对这个名为“/dev/xxx”(xxx 是具体的驱动文件名字)的文件进行相应的操作即可实 现对硬件的操作。比如现在有个叫做/dev/led 的驱动文件,此文件是 led 灯的驱动文件。应用程 序使用 open 函数来打开文件/dev/led,使用完成以后使用 close 函数关闭/dev/led 这个文件。open 和 close 就是打开和关闭 led 驱动的函数,如果要点亮或关闭 led,那么就使用 write 函数来操 作,也就是向此驱动写入数据,这个数据就是要关闭还是要打开 led 的控制参数。如果要获取 led 灯的状态,就用 read 函数从驱动中读取相应的状态。

           应用程序运行在用户空间,而 Linux 驱动属于内核的一部分,因此驱动运行于内核空间。 当我们在用户空间想要实现对内核的操作,比如使用 open 函数打开/dev/led 这个驱动,因为用 户空间不能直接对内核进行操作,因此必须使用一个叫做“系统调用”的方法来实现从用户空 间“陷入”到内核空间,这样才能实现对底层驱动的操作。open、close、write 和 read 等这些函 数是由 C 库提供的,在 Linux 系统中,系统调用作为 C 库的一部分。

 

2、每一 个系统调用,在驱动中都有与之对应的一个驱动函数,在 Linux 内核文件include/linux/fs.h 中 有个叫做 file_operations 的结构体,此结构体就是 Linux 内核驱动操作函数集合

1588 struct file_operations {
1589     struct module *owner;
1590     loff_t (*llseek) (struct file *, loff_t, int);
1591     ssize_t (*read) (struct file *, char __user *, size_t, loff_t 
        *);
1592     ssize_t (*write) (struct file *, const char __user *, size_t,
            loff_t *);
1593     ssize_t (*read_iter) (struct kiocb *, struct iov_iter *);
1594     ssize_t (*write_iter) (struct kiocb *, struct iov_iter *);
1595     int (*iterate) (struct file *, struct dir_context *);
1596     unsigned int (*poll) (struct file *, struct poll_table_struct 
            *);
1597     long (*unlocked_ioctl) (struct file *, unsigned int, unsigned
            long);
1598     long (*compat_ioctl) (struct file *, unsigned int, unsigned
            long);
1599     int (*mmap) (struct file *, struct vm_area_struct *);
1600     int (*mremap)(struct file *, struct vm_area_struct *);
1601     int (*open) (struct inode *, struct file *);
1602     int (*flush) (struct file *, fl_owner_t id);
1603     int (*release) (struct inode *, struct file *);
1604     int (*fsync) (struct file *, loff_t, loff_t, int datasync);
1605     int (*aio_fsync) (struct kiocb *, int datasync);
1606     int (*fasync) (int, struct file *, int);
1607     int (*lock) (struct file *, int, struct file_lock *);
1608     ssize_t (*sendpage) (struct file *, struct page *, int, size_t,
            loff_t *, int);
1609     unsigned long (*get_unmapped_area)(struct file *, unsigned long,
            unsigned long, unsigned long, unsigned long);
1610     int (*check_flags)(int);
1611     int (*flock) (struct file *, int, struct file_lock *);
1612     ssize_t (*splice_write)(struct pipe_inode_info *, struct file *,
            loff_t *, size_t, unsigned int);
1613     ssize_t (*splice_read)(struct file *, loff_t *, struct
            pipe_inode_info *, size_t, unsigned int);
1614     int (*setlease)(struct file *, long, struct file_lock **, void
                **);
1615     long (*fallocate)(struct file *file, int mode, loff_t offset,
1616         loff_t len);
1617     void (*show_fdinfo)(struct seq_file *m, struct file *f);
1618     #ifndef CONFIG_MMU
1619     unsigned (*mmap_capabilities)(struct file *);
1620     #endif
1621 };

简单介绍一下 file_operation 结构体中比较重要的、常用的函数:

第 1589 行,owner 拥有该结构体的模块的指针,一般设置为 THIS_MODULE。

第 1590 行,llseek 函数用于修改文件当前的读写位置。

第 1591 行,read 函数用于读取设备文件。

第 1592 行,write 函数用于向设备文件写入(发送)数据。

第 1596 行,poll 是个轮询函数,用于查询设备是否可以进行非阻塞的读写。

第 1597 行,unlocked_ioctl 函数提供对于设备的控制功能,与应用程序中的 ioctl 函数对应。

第 1598 行,compat_ioctl 函数与 unlocked_ioctl 函数功能一样,区别在于在 64 位系统上, 32 位的应用程序调用将会使用此函数。在 32 位的系统上运行 32 位的应用程序调用的是 unlocked_ioctl。

第 1599 行,mmap 函数用于将设备的内存映射到进程空间中(也就是用户空间),一般帧缓 冲设备会使用此函数,比如 LCD 驱动的显存,将帧缓冲(LCD 显存)映射到用户空间中以后应用 程序就可以直接操作显存了,这样就不用在用户空间和内核空间之间来回复制。

第 1601 行,open 函数用于打开设备文件。

第 1603 行,release 函数用于释放(关闭)设备文件,与应用程序中的 close 函数对应。 第 1604 行,fasync 函数用于刷新待处理的数据,用于将缓冲区中的数据刷新到磁盘中。 第 1605 行,aio_fsync 函数与 fasync 函数的功能类似,只是 aio_fsync 是异步刷新待处理的 数据。

在字符设备驱动开发中最常用的就是上面这些函数,关于其他的函数大家可以查阅相关文 档。我们在字符设备驱动开发中最主要的工作就是实现上面这些函数,不一定全部都要实现, 但是像 open、release、write、read 等都是需要实现的,当然了,具体需要实现哪些函数还是要 看具体的驱动要求。

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

在 Linux 驱 动开发中肯定也是要初始化相应的外设寄存器,这个是毫无疑问的。只是在 Linux 驱动开发中 我们需要按照其规定的框架来编写驱动,所以说学 Linux 驱动开发重点是学习其驱动框架。

4、驱动模块的加载和卸载

Linux 驱动有两种运行方式第一种就是将驱动编译进 Linux 内核中,这样当 Linux 内核启 动的时候就会自动运行驱动程序第二种就是将驱动编译成模块(Linux 下模块扩展名为.ko),在 Linux 内核启动以后使用“insmod”命令加载驱动模块。在调试驱动的时候一般都选择将其编译 为模块,这样我们修改驱动以后只需要编译一下驱动代码即可,不需要编译整个 Linux 代码。 而且在调试的时候只需要加载或者卸载驱动模块即可,不需要重启整个系统。总之,将驱动编 译为模块最大的好处就是方便开发,当驱动开发完成,确定没有问题以后就可以将驱动编译进 Linux 内核中,当然也可以不编译进 Linux 内核中,具体看自己的需求。

模块有加载和卸载两种操作,我们在编写驱动的时候需要注册这两种操作函数,模块的加载和 卸载注册函数如下:

module_init(xxx_init); //注册模块加载函数
module_exit(xxx_exit); //注册模块卸载函数

module_init() 函数用来向 Linux 内核注册一个模块加载函数,参数 xxx_init 就是需要注册的 具体函数,当使用“insmod”命令加载驱动的时候,xxx_init 这个函数就会被调用。module_exit() 函数用来向 Linux 内核注册一个模块卸载函数,参数 xxx_exit 就是需要注册的具体函数,当使 用rmmod”命令卸载具体驱动的时候 xxx_exit 函数就会被调用。

字符设备驱动模块加载和卸 载模板如下所示:

/* 驱动入口函数 */
static int __init xxx_init(void)
 {
 /* 入口函数具体内容 */
  return 0;
 }
 
 /* 驱动出口函数 */
static void __exit xxx_exit(void)
 {
 /* 出口函数具体内容 */
 }

 /* 将上面两个函数指定为驱动的入口和出口函数 */
module_init(xxx_init);
module_exit(xxx_exit);

第 2 行,定义了个名为 xxx_init 的驱动入口函数,并且使用了“__init”来修饰。

第 9 行,定义了个名为 xxx_exit 的驱动出口函数,并且使用了“__exit”来修饰。

第 15 行,调用函数 module_init 来声明 xxx_init 为驱动入口函数,当加载驱动的时候 xxx_init 函数就会被调用。

第16行,调用函数module_exit来声明xxx_exit为驱动出口函数,当卸载驱动的时候xxx_exit 函数就会被调用

驱动编译完成以后扩展名为.ko,有两种命令可以加载驱动模块insmod和modprobeinsmod 是最简单的模块加载命令,此命令用于加载指定的.ko 模块,比如加载 drv.ko 这个驱动模块,命 令如下:

insmod drv.ko

insmod 命令不能解决模块的依赖关系,比如 drv.ko 依赖 first.ko 这个模块,就必须先使用 insmod 命令加载 first.ko 这个模块,然后再加载 drv.ko 这个模块。但是 modprobe 就不会存在这 个问题,modprobe 会分析模块的依赖关系,然后会将所有的依赖模块都加载到内核中,因此 modprobe 命令相比 insmod 要智能一些。modprobe 命令主要智能在提供了模块的依赖性分析、 错误检查、错误报告等功能,推荐使用 modprobe 命令来加载驱动。modprobe 命令默认会去 /lib/modules/目录中查找模块,比如使用的 Linux kernel 的版本号为 4.1.15, 因此 modprobe 命令默认会到/lib/modules/4.1.15 这个目录中查找相应的驱动模块,一般自己制 作的根文件系统中是不会有这个目录的,所以需要自己手动创建。

驱动模块的卸载使用命令“rmmod”即可,比如要卸载 drv.ko,使用如下命令即可:

rmmod drv.ko

也可以使用“modprobe -r”命令卸载驱动,比如要卸载 drv.ko,命令如下:

modprobe -r drv.ko

使用 modprobe 命令可以卸载掉驱动模块所依赖的其他模块,前提是这些依赖模块已经没 有被其他模块所使用,否则就不能使用 modprobe 来卸载驱动模块。所以对于模块的卸载,还是 推荐使用 rmmod 命令。

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/494495.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

不断积累,步步高升:记录我的Python学习历程-python中f-string介绍

最近在看python基础的时候,又发现一个之前没怎么掌握的字符串格式化的小技巧,f-string f-string 格式化 f-string是Python3.6引入的一种字符串格式化方式,它提供了一种简洁、直观的方式来将变量值嵌入到字符串中。在 f-string 中,…

2022 Hubei Provincial Collegiate Programming Contest B. Potion(easy version)

题目链接 Output For each testcase, if Twilight Sparkle couldn’t make the specific mixture, print a single integer: −1. Otherwise, print the minimum number of operation 1 to do that. Example input 3 3 5 1 1 2 6 1 1 5 7 1 1 output 4 3 -1 题目大意 题目保证…

顺序表---(数据结构的开始)

目录 前言: 1.线性表的性质 2.静态数组or动态数组 2.1静态数组 2.2动态数组 3.结构体的创建 4*接口函数的详细讲解 4.1初始化结构体 4.2尾插 4.3打印数据 4.4用完后销毁创建的堆空间 4.5 尾删 4.6头插 4.7头删 4.8查找 4.9任意位置插入 4.10任意位…

正则表达式规则

元字符: 正则表达式主要依赖于元字符。 元字符不代表他们本身的字面意思,他们都有特殊的含义。一些元字符写在方括号中的时候有一些特殊的意思。以下是一些元字符的介绍: 运算符优先级: 正则表达式从左到右进行计算,…

idea一些不常见但是非常有用的sql

1.这个功能适合生产环境遇到故障,然后本地,但是前提是jvm配置了如下参数,还有一个就是要保证线上代码和本地的一致,这样就可以本地调式远程代码 在linux 运行 java -agentlib:jdwptransportdt_socket,servery,suspendn,address500…

牛客 BM19 寻找峰值

描述 给定一个长度为n的数组nums,请你找到峰值并返回其索引。数组可能包含多个峰值,在这种情况下,返回任何一个所在位置即可。 1.峰值元素是指其值严格大于左右相邻值的元素。严格大于即不能有等于 2.假设 nums[-1] nums[n] -∞ 3.对于所有…

中台产品经理01:中台落地工具MSS模型

众所周知,每家企业的内部经营管理都是大相径庭的,就算相同行业的两家企业其内部也会有显著的特殊性,因此企业对于自身中台的建设需求也一定是不同的,可以说中台建设必须是为企业量身定制的。 而在每家企业的中台建设中&#xff0…

Visual Studio 2022 搭建GLFW OpenGL开发环境

最近工作需要 需要写一个全景的视频播放器 网上搜了下大概解决方案是 ffmpegopengl b站有很多视频 按照视频 搭建了OpenGL的开发环境 先去GLFW的网站下载 windows平台的库文件 为什么使用GLFW 因为GLFW是跨平台的 我下的是64位版本解压后有目录如下 包含了动态库和静态…

医院不良事件管理系统源码,PHP语言开发,填写上报、流转审批、发生原因分析定位

医疗安全不良事件管理系统源码提供不良事件的上报、事件审核处理、时间按分析、事件跟踪与持续改进,事件提醒、权限控制、外部上报等功能。从报告内容填写上报、流转审批、发生原因分析定位、处置对策的制定、统计汇总等方面,提供了不良事件处理的全过程…

[Leetcode] 0705. 设计哈希集合

705. 设计哈希集合 English Version 题目描述 不使用任何内建的哈希表库设计一个哈希集合(HashSet)。 实现 MyHashSet 类: void add(key) 向哈希集合中插入值 key 。bool contains(key) 返回哈希集合中是否存在这个值 key 。void remove(key) 将给定值 key 从哈希集…

这些神仙问题,整垮了chatGpt

垮掉,全部垮掉 今天刚上了一个chat聊天功能,网友们就不甘落后,纷纷展示了自己的技能。分分钟给我整垮了。 神仙问题 玉皇大帝住在平流层还是对流层?空腹的时候可以吃饭吗?自来水不能直接喝,那么用自来水…

CAD二次开发--根据内部点识别最近外部边界范围即boundary命令代码实现(含源码)

在AutoCAD中我们知道使用Boundary(BO)命令可以快速找到内部点附近的最近边界范围(通常要求闭合)。然而网上很多文章却停留在如何用BO命令,并没有讲如何用代码去实现!本文将实现代码贴出来并进行简单介绍。 先看下实现效果图吧! 该功能在某些时候还是具备一定的实…

yolov8 浅记

目录 Pre: 1. YOLOv8 概述 2. 模型结构设计 3. Loss 计算 4.训练数据增强 5. 训练策略 6、部署推理 End Pre: yolo系列发布时间: 先贴一下yolo各系列的发布时间(说出来很丢人,我以为 yolox是 最新的): yoloX 2…

【leetcode刷题】2659. 将数组清空(树状数组初体验)

目录 题目截图题目分析ac code树状数组模版树状数组总结 题目截图 题目分析 直接模拟的话就是平方复杂度,会超时间复杂度我们删掉一个最小的,然后就要删次小的,如何找到其中间隔的距离呢?间隔中可能有一些又被删掉了?…

循环队列、双端队列

循环队列、双端队列 1. 循环队列1.1 循环队列1.2 代码实现1.3 力扣622. 设计循环队列2. 双端队列 1. 循环队列 1.1 循环队列 特殊的队列,首尾相连,空间可重复利用;环形队列常使用数组实现,且为了方便队列的判空、判满处理&#x…

二十七、BGP技术总结

文章目录 BGP:边界网关路由协议(Border Gateway Protocol)概述1、路由协议分类1.1、IGP:Interior Gateway Protocol(内部网关协议)1.2、EGP:Exterior Gateway Protocol(外部网关协议…

人机智能中几个困难问题浅析

1、人机之间与人人之间信任的区别人机之间的信任与人人之间的信任存在以下异同:①信任对象。人机之间的信任的对象是计算机系统、算法、机器人等,而人人之间的信任的对象是其他人。②信任方式。人机之间的信任是基于技术、安全协议等建立的,例…

Baumer工业相机堡盟工业相机如何联合BGAPISDK和Halcon实现图像的线性灰度变换Emphasize 算法增强(C#)

Baumer工业相机堡盟工业相机如何联合BGAPISDK和Halcon实现图像的线性灰度变换算法增强(C#) Baumer工业相机Baumer工业相机使用图像算法增加图像的技术背景Baumer工业相机通过BGAPI SDK联合Halcon使用Emphasize 线性灰度变换增强算法1.引用合适的类文件2.…

基于标签的协同过滤算法实现与个人兴趣相关的文章推荐

一、前言 在当前信息爆炸的时代,每天都会涌现出大量的文章,人们有时候会感到信息的获取难度比筛选更大。而作为信息的提供者,我们应当为用户提供依据个人兴趣的文章推荐。 本项目中的文章标签相似度推荐功能使用了一种基于标签的协同过滤算…

【设计模式】抽象工厂模式

【设计模式】抽象工厂模式 参考资料: Java设计模式 - 抽象工厂模式 重学 Java 设计模式:实战抽象工厂模式 文章目录 【设计模式】抽象工厂模式一、抽象工厂模式介绍1.1、什么是工厂方法模式1.2、角色概述 二、案例场景模拟2.1、背景一:Redis…