Linux字符设备驱动开发一

linux字符设备驱动

    • 0 驱动介绍
    • 1 字符设备驱动
      • 1.1 字符设备相关概念和结构体
      • 1.2 实现简单的字符设备模块
      • 1.3 创建字符设备
      • 1.4 总结

应用程序调用文件系统的API(open、close、read、write) -> 文件系统根据访问的设备类型,调用对应设备的驱动API -> 驱动对硬件进行操作。
请添加图片描述

0 驱动介绍

驱动类型:

  1. 字符设备驱动:以一个字节一个字节读写的硬件对应的驱动。比如串口、watchdog、rtc、鼠标、触摸屏等。字符设备必须以串行顺序依次进行访问。
  2. 块设备驱动:以一个扇区一个扇区(512kb、4096kb)读写的硬件对应的驱动。比如硬盘。块设备可以按任意顺序进行访问。
  3. 网络设备驱动:走内核里的协议栈内存。比如网卡驱动、can驱动。网络设备面向数据包的接收和发送而设计。

除网络设备外,字符设备与块设备都被映射到Linux文件系统的文件和目录,通过文件系统的系统调用接口open()、write()、read()、close()等即可访问字符设备和块设备。

驱动的动态加载和静态加载区别:

  1. 编译方式不同:静态加载–obj-y,动态加载–obj-m
  2. 存在位置不同:静态加载,则直接编译到linux内核镜像中uimag;动态加载,则是独立的ko,驱动代码不在内核镜像中,而是独立存在于文件系统中
  3. 加载时机不同:静态加载,内核启动时就加载;动态加载,在系统启动的时候首先uboot的启动,然后linux内核启动,最后文件系统启动。静态加载,随linux内核一起加载启动,动态加载则需要等文件系统启动后手动insmod加载。

动态加载优势:

  1. 实现热插拔机制:硬件要实现热插拔,则驱动必须使用动态加载。设备插入时,加载驱动;设备移除时,卸载驱动。
  2. 驱动调试:调试更方便。单独编译驱动,通过insmod、rmmod加载和卸载进行单独调试。
  3. 开机优化:有的驱动加载非常耗时,影响设备启动速度。动态加载,可以等用户的图形界面起来后,再加载驱动,让用户感觉系统启动速度加快了。

1 字符设备驱动

字符设备必须以串行顺序依次进行访问,常见的字符设备:串口、watchdog、rtc、鼠标、触摸屏等。字符设备的读写以字节为单位。

1.1 字符设备相关概念和结构体

了解字符设备相关概念和结构体,为之后阅读字符设备驱动代码打下坚实基础。

1 设备号dev_t:类似一个人的身份证号。内核中通过dev_t来描述设备号,其实质是unsigned int 32位整数,其中高12位为主设备号,低20位为次设备号。

  • 为每一个外设设置一个独一无二的ID,让内核能够区分每一个设备。
  • 高12区分不同类别的设备
  • 低20位,区分同一类别的不同设备

linux如何为每一个设备生成设备号呢?
可以是驱动自行构造一个设备号,然后调用函数register_chrdev_region向内核注册。如果注册成功,该函数返回0;注册失败,返回一个负数,就不能用这个设备号。

int register_chrdev_region(dev_t from, unsigned count, const char *name);

2 设备描述cdev、file_operations:
一个linux设备,需要通过cdev结构体来描述设备信息,通过file_operations来描述如何操作设备。最后要通过cdev_add函数来注册设备,让linux内核知道这个设备。

/*设备信息的描述*/
struct cdev {struct kobject kobj; /**/struct module *owner;const struct file_operations *ops; /*一组函数集*/struct list_head list; /*链表*/dev_t dev; /*设备号*/unsigned int count; /*已支持的设备*/
}/*设备行为的描述:驱动设备开发,就要实现这些函数*/
struct file_operations {struct module *owner;ssize_t (*read) (struct file *, char __user *, size_t, loff_t *);ssize_t (*write) (struct file *, const char __user *, size_t, loff_t *);int (*open) (struct inode *, struct file *);int (*release) (struct inode *, struct file *);...
};/*相关注册函数*/
int cdev_add(struct cdev *p, dev_t dev, unsigned count);

1.2 实现简单的字符设备模块

#include <linux/module.h>
#include <linux/init.h>
#include <linux/cdev.h>
#include <linux/kdev_t.h>
#include <linux/types.h>
#include <linux/slab.h>
#include <linux/fs.h>
#define OK (0)
#define ERROR (-1)
#define DEV_NUM (10)/*创建设备ID:MKDEV注册设备ID:给设备行为结构体的成员函数赋值初始化设备:建立设备行为结构体file_operations和设备信息结构体cdev的关系;添加设备:向内核注册设备,使内核知道设备,建立设备ID和设备信息cdev的关系;加载设备:insmod创建设备文件:建立设备文件与设备ID的关系:mknod /dev/my_chr_dev c 232 0测试设备行为结构体的成员函数功能
*/dev_t g_dev_num; /*设备号*/
struct cdev *g_cdev; /*设备信息*/
struct file_operations *g_fops; /*设备行为信息*/
const int g_major = 232; /*主设备号*/
const int g_minor = 0; /*次设备号*/
unsigned g_dev_count = 3;
const char g_dev_name[] = "my_char_dev"; /*设备名称,内核管理用的,随便写*/ssize_t my_dev_read(struct file *my_file, char __user *my_user, size_t my_size, loff_t *my_loff)
{printk(KERN_EMERG "read my dev success\n");return OK;
}ssize_t my_dev_write(struct file *my_file, const char __user *my_user, size_t my_size, loff_t *my_loff)
{printk(KERN_EMERG "write my dev success\n");return OK;
}int my_dev_open(struct inode *my_inode, struct file *my_file)
{printk(KERN_EMERG "open my dev success\n");return OK;
}int my_dev_release(struct inode *my_inode, struct file *my_file)
{printk(KERN_EMERG "release my dev success\n");return OK;
}static int __init chr_init(void)
{g_dev_num = MKDEV(g_major, g_minor); /*生成设备号*/printk("dev id:%d\n", g_dev_num);/*注册设备号*/if (register_chrdev_region(g_dev_num, g_dev_count, g_dev_name) != 0) {printk(KERN_EMERG "register dev id failed\n");return ERROR;}/*内核函数:自动生成设备函数, 避免自定义设备号冲突,导致注册失败if (alloc_chrdev_region(&g_dev_num, g_major, g_dev_count, g_dev_name) != OK) {printk(KERN_EMERG "register dev id failed\n");return ERROR;}*//*为设备信息结构体cdev和设备行为结构体file_operations申请内存;kzalloc相比kmalloc,会将申请的内存地址置为0,*/g_cdev = kzalloc(sizeof(struct cdev), GFP_KERNEL);g_fops = kzalloc(sizeof(struct file_operations), GFP_KERNEL);/*为设备行为结构体成员函数赋值*/g_fops->read = my_dev_read;g_fops->write = my_dev_write;g_fops->open = my_dev_open;g_fops->release = my_dev_release;g_fops->owner = THIS_MODULE; /*一般就赋值为THIS_MODULE宏*/cdev_init(g_cdev, g_fops); /*初始化设备:建立cdev和file_operations的联系*/cdev_add(g_cdev, g_dev_num, g_dev_count); /*向内核注册设备:建立cdev和设备号的联系*/printk(KERN_EMERG "add my_chr_dev success\n");return OK;
}static void __exit chr_exit(void)
{cdev_del(g_cdev); /*删除设备*/unregister_chrdev_region(g_dev_num, g_dev_count); /*注销设备号*/printk(KERN_EMERG "unregister my_chr_dev success\n");
}module_init(chr_init);
module_exit(chr_exit);
MODULE_LICENSE("GPL");

make编译模块后,insmod my_chr_dev加载字符设备驱动。
makefile文件解读:

  • ifneq,首先判断是否定义了内核宏,第一次执行由于没有定义内核宏,先执行else分支,然后执行all。
  • make -C,切换到内核目录内,-M指定编译的模块。此时进入-M指定的模块内,再次执行makefile文件。
  • 此时,内核宏定义了,执行if分支,编译内核模块。
ifneq ($(KERNELRELEASE),)obj-m := my_chr_dev.o
elseKERDIR ?="/lib/modules/$(shell uname -r)"/buildPWD := $(shell pwd)all:$(MAKE) -C $(KERDIR) M=$(PWD)
clean:rm -rf *.o *.ko *mod.c 9.module.o *.order
endif

1.3 创建字符设备

mknod /dev/my_chr_dev c 232 0232 0为主、次设备号。

  • mknod,实际上就是建立设备文件my_chr_dev与设备号232 0之间的联系。

驱动测试程序:

#include <stdio.h>
#include <fcntl.h> /*O_RDWR*/
#include <unistd.h> /*read open write close usleep*/
#define DATA_NUM (10)int main()
{int fd;int wd, rd;char my_buffer[DATA_NUM] = "12345";fd = open("/dev/my_chr_dev", O_RDWR);printf("fd=%d\n", fd);if (fd == -1){printf("open failed\n");return -1;}wd = write(fd, my_buffer, DATA_NUM);rd = read(fd, my_buffer, DATA_NUM);printf("wd=%d, rd=%d\n", wd, rd);return 0;
}

该程序用于测试驱动程序的open、write、read、release。编译执行该驱动程序后,执行dmesg,可以查看内核调用驱动程序对应的open、write、read、release函数时日志信息。

1.4 总结

开发字符设备驱动主要有7步:

  1. 创建设备号MKDEV、注册设备号register_chrdev_region;
  2. 编写设备行为结构体的成员函数:成员函数实现硬件的open/close/read/write等操作。
  3. 建立设备信息结构体cdev与设备行为结构体file_operations之间的联系:cdev_init
  4. 向内核注册设备,建立设备信息结构体与设备号之间的联系:cdev_add
  5. make编译、insmod加载设备驱动。
  6. 创建设备文件,建立设备文件与设备号之间的联系:mknod /dev/chrDev c 主设备号 次设备号
  7. 编写测试脚本,测试驱动程序的open/close/read/write功能。

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

面试经典150题——随机链表的复制

​前两天断更了两天有点事情&#x1f917; 1. 题目描述 2. 题目分析与解析 2.1 思路一 开始还是没什么思路&#xff0c;没思路那就先把题目解决不管方法的好坏。如果不考虑复杂度&#xff0c;该怎么解决&#xff1f; 可以有这样的一种思路&#xff1a; 首先复制链表的所有节…

记OnlyOffice的两个大坑

开发版&#xff0c;容器部署&#xff0c;试用许可已安装。 word&#xff0c;ppt&#xff0c;excel均能正常浏览。 自带的下载菜单按钮能用。 但config里自定义的downloadAs方法却不一而足。 word能正常下载&#xff0c;excel和ppt都不行。 仔细比对调试了代码。发现app.js…

fetch,前端 面试题

Fetch Fetch API 是近年来被提及将要取代XHR的技术新标准&#xff0c;是一个 HTML5 的 API。 基于promise的设计&#xff0c;返回的是Promise对象 fetch()采用模块化设计&#xff0c;API 分散在多个对象上&#xff08;Response 对象、Request 对象、Headers 对象&#xff09;…

Java双非大二找实习记录

先说结论&#xff1a;2.22→3.6线上线下面了七家&#xff0c;最后oc两家小公司&#xff0c;接了其中一个。 本人bg&#xff1a; 真名不经传双非一本&#xff0c;无绩点无竞赛无奖项无实习&#xff0c;23年12月开始学java。若非要说一点相关的经历&#xff0c;就是有java基础&…

新手向-从VNCTF2024的一道题学习QEMU Escape

[F] 说在前面 本文的草稿是边打边学边写出来的&#xff0c;文章思路会与一个“刚打完用户态 pwn 题就去打 QEMU Escape ”的人的思路相似&#xff0c;在分析结束以后我又在部分比较模糊的地方加入了一些补充&#xff0c;因此阅读起来可能会相对轻松&#xff08;当然也不排除这是…

Hadoop大数据应用:NFS网关 连接 HDFS集群

目录 一、实验 1.环境 2.NFS网关 连接 HDFS集群 3. NFS客户端挂载HDFS文件系统 二、问题 1.关闭服务报错 2.rsync 同步报错 3. mount挂载有哪些参数 一、实验 1.环境 &#xff08;1&#xff09;主机 表1 主机 主机架构软件版本IP备注hadoop NameNode &#xff08;…

Ubuntu 20.04 系统如何优雅地安装NCL?

一、什么是NCL&#xff1f; NCAR Command Language&#xff08;NCL&#xff09;是由美国大气研究中心&#xff08;NCAR&#xff09;推出的一款用于科学数据计算和可视化的免费软件。 它有着非常强大的文件输入和输出功能&#xff0c;可读写netCDF-3、netCDF-4 classic、HDF4、b…

【遍历方法】浅析Java中字符串、数组、集合的遍历

目录 前言 字符串篇 1.1 使用 for 循环和 charAt 方法 1.2 使用增强 for 循环&#xff08;forEach 循环&#xff09; 1.3 使用 Java 8 的 Stream API 最终效果 数组篇 2.1 使用普通 for 循环 2.2 使用增强型 for 循环( forEach 循环) 2.3 使用 Arrays.asList 和 forE…

C#调用Halcon出现尝试读取或写入受保护的内存,这通常指示其他内存已损坏。System.AccessViolationException

一、现象 在C#中调用Halcon&#xff0c;出现异常提示&#xff1a;尝试读取或写入受保护的内存,这通常指示其他内存已损坏。System.AccessViolationException 二、原因 多个线程同时访问Halcon中的某个公共变量&#xff0c;导致程序报错 三、测试 3.1 Halcon代码 其中tsp_width…

用户视角的比特币和以太坊外围技术整理

1. 引言 要点&#xff1a; 比特币L2基本强调交易内容的隐蔽性&#xff0c;P2P交易&#xff08;尤其是支付&#xff09;成为主流&#xff0c;给用户带来一定负担&#xff08;闪电网络&#xff09;在以太坊 L2 中&#xff0c;一定程度上减少了交易的隐蔽性&#xff0c;主流是实…

C语言 数据在内存中的存储

目录 前言 一、整数在内存中的存储 二、大小端字节序和字节序判断 2.1.练习一 2.2 练习二 2.3 练习三 2.4 练习四 2.5 练习五 2.6 练习六 三、浮点数在内存中的存储 3.1 浮点数存的过程 3.2 浮点数取的过程 总结 前言 数据在内存中根据数据类型有不同的存储方式&#xff0c;今…

基于YOLOv8/YOLOv7/YOLOv6/YOLOv5的火焰与烟雾检测系统详解(深度学习模型+UI界面升级版+训练数据集)

摘要&#xff1a;本研究详细介绍了一种集成了最新YOLOv8算法的火焰与烟雾检测系统&#xff0c;并与YOLOv7、YOLOv6、YOLOv5等早期算法进行性能评估对比。该系统能够在包括图像、视频文件、实时视频流及批量文件中准确识别火焰与烟雾。文章深入探讨了YOLOv8算法的原理&#xff0…

Parade Series - Web Streamer Low Latency

Parade Series - FFMPEG (Stable X64) 延时测试秒表计时器 ini/config.ini [system] homeserver storestore\nvr.db versionV20240312001 verbosefalse [monitor] listrtsp00,rtsp01,rtsp02 timeout30000 [rtsp00] typelocal deviceSurface Camera Front schemartsp ip127…

软件杯 深度学习 python opencv 动物识别与检测

文章目录 0 前言1 深度学习实现动物识别与检测2 卷积神经网络2.1卷积层2.2 池化层2.3 激活函数2.4 全连接层2.5 使用tensorflow中keras模块实现卷积神经网络 3 YOLOV53.1 网络架构图3.2 输入端3.3 基准网络3.4 Neck网络3.5 Head输出层 4 数据集准备4.1 数据标注简介4.2 数据保存…

前端框架vue的样式操作,以及vue提供的属性功能应用实战

✨✨ 欢迎大家来到景天科技苑✨✨ &#x1f388;&#x1f388; 养成好习惯&#xff0c;先赞后看哦~&#x1f388;&#x1f388; &#x1f3c6; 作者简介&#xff1a;景天科技苑 &#x1f3c6;《头衔》&#xff1a;大厂架构师&#xff0c;华为云开发者社区专家博主&#xff0c;…

基于Springboot+Vue+Sercurity实现的大学生健康管理平台

1.项目介绍 大学生健康档案管理系统&#xff0c;通过电子健康档案管理系统这个平台&#xff0c;可以实现人员健康情况的信息化、网络化、系统化、规范化管理&#xff0c;从繁杂的数据查询和统计中解脱出来&#xff0c;更好的掌握人员健康状况。系统的主要功能包括&#xff1a;…

2024年5家香港服务器推荐,性价比top5

​​香港服务器是中小企业建站、外贸建站、个人博客建站等领域非常受欢迎的服务器&#xff0c;2024年有哪些云厂商的香港服务器是比较有性价比的&#xff1f;这里根据小编在IT领域多年服务器使用经验&#xff0c;给大家罗列5家心目中最具性价比的香港服务器厂商。 这五家香港服…

StarRocks面试题及答案整理,最新面试题

StarRocks 的 MV&#xff08;物化视图&#xff09;机制是如何工作的&#xff1f; StarRocks 的物化视图&#xff08;MV&#xff09;机制通过预先计算和存储数据的聚合结果或者转换结果来提高查询性能。其工作原理如下&#xff1a; 1、数据预处理&#xff1a; 在创建物化视图时…

【静夜思】为什么我们会如此喜欢夜晚呢

作为一名大学生&#xff0c;熬夜对我来说已是常态。每天都是近乎一点钟才有困意&#xff0c;觉得应该上床睡觉了&#xff0c;即使明天早八&#xff0c;即使明天有很多课。我也观察过身边的朋友&#xff0c;他们中大多数也和我一样&#xff0c;基本都是在12点过后才入睡。当今的…

HTML静态网页成品作业(HTML+CSS)——家乡广州介绍设计制作(5个页面)

&#x1f389;不定期分享源码&#xff0c;关注不丢失哦 文章目录 一、作品介绍二、作品演示三、代码目录四、网站代码HTML部分代码 五、源码获取 一、作品介绍 &#x1f3f7;️本套采用HTMLCSS&#xff0c;未使用Javacsript代码&#xff0c;共有5个页面。 二、作品演示 三、代…