【linux-IMX6ULL-字符设备驱动简单框架实验】

目录

  • 1. 字符设备驱动简介
    • 1.1 重要函数
    • 1.2 简单框架代码流程
    • 1.3 linux中关于驱动的重要命令
  • 2. 字符设备驱动简单框架编写
    • 2.1 添加LICENSE信息
    • 2.2 驱动模块的入口与出口
    • 2.3 入口和出口函数的编写
    • 2.4 设备操作结构体定义
      • 2.4.1 结构体函数内容填充
  • 3. 应用程序简介:
  • 4. 应用程序的编写思路

1. 字符设备驱动简介

  目前的驱动开发一般是分为三类,第一类就是字符设备驱动、块设备驱动、和网络驱动三类,其中字符设备驱动是最多最杂的,现在对字符设备驱动进行一个简要的介绍:
  字符设备驱动,是指那些以字节流进行数据传输的设备、IIC、SPI、LCD、按键、例如键盘、鼠标、打印机等,其中包含以下几个关键的部分:

  1. 设备注册和注销:通过设备注册使设备能被系统识别;注销则相反;
  2. 数据操作函数:通常包含open,read,write,realse等;
  3. 中断处理:处理设备产生的中断,以响应特定事件;

   通过字符型设备驱动,可以使系统方便统一管理不同的设备,这样就可以给上层应用提供相应的接口函数,方便应用程序与设备之间进行数据交换和通信;



   本次的实验是通过简单的实验建立一个设备驱动开发的基本框架,为后续的学习打下基础,其中会列出重要的函数以及重要的挂载指令,当然其中有一些函数是比较老的,例如要手动分配设备号,但是为了便于学习目前就以简单的为主,因为越是抽象的越简单,但是越抽象就越难以理解;

1.1 重要函数

  这里只是把本实验相关的重要函数给罗列出来了,主要的作用就是对本次实验的一个总结,如果没有做个这个实验,那么看着没啥感觉的;例如我下面罗列函数的顺序就是我们在编写驱动实验时整个流程的顺序,首先就是注册入口和出口函数,其次就是编写入口和出口函数,再次就是编写文件结构体的对应相关的函数内容;

  • MODULE-LICENSE("GPL"): 表示该内核模块遵循的许可协议是通用公共许可(GPL)。指定许可协议非常重要,它明确了该模块在使用、分发等方面的权利和限制。如果不写这个的话就会导致在装载设备驱动时出现警告;
  • module_init(*****_init):模块的入口函数,进行初始化,也就是加载模块时第一个就是运行这个函数注册的函数*******;对模块进行初始化设置;
  • module_exit(*****_exit):模块的出口函数,对模块进行卸载时就会执行这个函数注册的函数*******;
  • static int __init *****_init(void):设备加载函数,进行模块的初始化和加载的设置;这个函数要被入口函数进行注册后起作用
  • register_chrdev(unsigned int major, const char *name, const struct file_operations *fops):对设备进行注册;
  • static int __exit *****_exit(void):设备卸载函数、进行模块的卸载时这个函数内部的程序就会执行,不过要被出口函数进行注册
  • unregister_chrdev(unsigned int major, const char *name):对模块进行卸载;
  • static const struct file_operations **_fops : struct file_operations是一个很重要的结构体,其中定义了很多与文件操作有关的函数指针,例如read,write,realse,open等等,这些函数可以按需进行填充,这样就方便与设备之间进行文件的操作;下面进行实验时会进行一个详细的说明;

1.2 简单框架代码流程

  代码实验编写流程:


在这里插入图片描述


1.3 linux中关于驱动的重要命令

  • lsmod:显示有哪些模块被加载了,也就是显示所有的加载模块
  • lsmod:显示有哪些模块被加载了,也就是显示所有的加载模块
  • depmod:更新模块的依赖关系,也就是新加载一个模块时,要先运行一下这个命令,不然有错误
  • modprob ***.ko:这个命令的作用就是加载模块***.ko
  • mknod /dev/*** c 200 0:mknod:手动创建节点的命令。/dev/*** :创建设备的名称。c:字符型设备。200:主设备号,自己指定。0:次设备号,自己指定。
  • cat /proc/devices:这个命令的作用是查看所有加载的模块,显示所有设备号等;
  • rmmod ***.ko:卸载模块***.ko;
  • cat /sys/devices/system/cpu/cpu0/cpufreq/cpuinfo_cur_freq:这个命令是显示当成CPU的频率;

2. 字符设备驱动简单框架编写

  上面已经介绍了驱动编写的则整体流程,以及介绍了重要的加载驱动的一些linux的命令,因此下面的代码就不做过多的介绍:

2.1 添加LICENSE信息

  我们需要在代码中添加LICENSE信息,否则编译会出错,不过我们还可以添加一些其他的信息,例如作者,邮箱等等;

MODULE_LICENSE("GPL");

2.2 驱动模块的入口与出口

  linux的驱动有两种加载形式,一就是编译进linux内核中,当内核启动,驱动也启动,另一种方法就是把启动编译成模块也就***.ko文件,通过insmode或者modprob的命令进行加载模块,这样做的好处就是便于调试,而且不用重启linux内核;代码如下,下面两个函数的作用就是对模块进行加载和卸载,并在加载和卸载函数中对字符设备进行注册,如我们裸机编程中系统中断函数对中断服务函数的注册一样;

/* 
 * 模块入口和出口函数注册
 */ 
module_init(chrdevbase_init);/*入口,加载模块*/
module_exit(chrdevbase_exit);/*出口,卸载模块*/

2.3 入口和出口函数的编写

  注意,入口函数是通过__init来修饰(注意是两个杠),而对于出口函数通过__exit进行修饰,在出入口函数中对设备进行注册,CHRDEVBASE_MAJOR是设备号,CHREEVBAScE_NAME是设备名,chrdevbase_fops是文件操作结构体;出口函数是类似的;

#define CHRDEVBASE_MAJOR 200  
#define CHREEVBASE_NAME "chrdevbase"

static int __init chrdevbase_init(void)
{
	printk("chrdevbase_init3\r\n");
	/*注册字符设备*/
	register_chrdev(CHRDEVBASE_MAJOR, CHREEVBASE_NAME,&chrdevbase_fops);
	return 0;
}
static void __exit chrdevbase_exit(void)
{
	printk("chrdevbase_exit\r\n");
	unregister_chrdev(CHRDEVBASE_MAJOR,CHREEVBASE_NAME);
}

2.4 设备操作结构体定义

  对于设备的操作file_operations结构体,也称为文件操作结构体,这也是为什么linux下一切皆文件,我们的操作大部分都是通过文件来进行管理,我们可能用不到那么多的功能,因此我们要用到什么功能就进行对应的添加和书写就行,代码如下,注意这里使用的是=:

static const struct file_operations chrdevbase_fops={
	.owner	= THIS_MODULE,
	.open	= chrdevbase_open,
	.release	= chrdevbase_release,
	.read	= chrdevbase_read,
	.write	=	chrdevbase_write,
};

2.4.1 结构体函数内容填充

  可以看到,我们对上面的文件操作结构体中定义了四个函数指针,分别是:chrdevbase_open、chrdevbase_release、chrdevbase_read、chrdevbase_write,接下来就是对这四个函数进行内容填充:

static char readbuf[100];/*读缓冲*/
static char writebuf[100];
static char kerneldata[]={"kernel data!"};

/************/
static ssize_t chrdevbase_read(struct file * file, char * buf, size_t count, loff_t *off)
{
	int ret=0;
	memcpy(readbuf,kerneldata,sizeof(kerneldata));
	ret = copy_to_user(buf,readbuf,count);
	if(ret<0){
		printk("Error!");
	}else{
	}
	return 0;
}
/************/
static ssize_t chrdevbase_write(struct file * file, const char * buf, size_t count,loff_t *off)
{
	int ret =0;
	ret = copy_from_user(writebuf,buf,count);
	printk("Kernel recevdata:%s\r\n",writebuf);
	if(ret = 0){
		printk("Kernel recevdata:%s\r\n",writebuf);
	}
	return 0;
}
static int chrdevbase_release(struct inode * inode, struct file * file)
{
  printk("chrdevbase_ralease\r\n");
  return 0;
}
static int chrdevbase_open(struct inode * inode, struct file * file)
{
  printk("chrdevbase_open\n");
  return 0;
}

3. 应用程序简介:

  当驱动程序编写完毕后,要通过应用程序进行调用驱动函数的一些接口函数,这些功能的实现是在应用程序中实现的,这样就实现了应用程序与驱动程序的分离可以这样类比,我们写应用程序就相当于我们在windows系统上写C程序,我们写C程序时也没有关注下层,这就是一种分离,不过这两者是可以相互进行数据交换的,不过要用特定的方法;注意驱动程序和应用程序的编译是不一样的:对于应用程序的编译是下面的指令:

  • arm-linux-gnueabihf-gcc ***APP.c -o ***APP:应用程序的编译指令
  • $(MAKE) -C $(KERNELDIR) M=$(CURRENT_PATH) modules:驱动程序的编译:Make部分核心指令

   从上面的编译指令中就可以看出区别,对于驱动的编译要用到一系列的库,这些库包含板子的信息,以及一些arm中的一些库等等,最终生成一个驱动模块,属于下层的驱动文件;
  而对于应用程序的编译则是用到了一个.c文件以及基础库,就像我们编译C语言一样,只不过我们写一个hello.c文件用的是gcc编译器,生成的是x86架构的可执行文件,同理,我们使用arm-linux-gnueabihf-gcc交叉编译器生成的是arm架构的可执行文件,这明显是上层的应用程序,不过我们写上层应用程序时通过传参命令的形式就可以与下层的驱动进行数据文件交换;

4. 应用程序的编写思路

  编写测试APP就是编写Linux应用程序,需要用到C库和文件操作相关的一些函数,open、read、write 和 close 这四个函数;这些函数可以根据下面的命令进行找详细帮助:“man 1”通常是用户命令的手册页;“man 2”一般是系统调用的手册页;“man 3”可能是 C 库函数的手册页;

wyj@BK:~$ man 2 read
wyj@BK:~$ man 2 write
wyj@BK:~$ man 2 close
wyj@BK:~$ man 2 open

  因此在应用程序中编写程序就是如何对驱动程序进行调用和使用,不过要注意的是对于驱动的编写属于内核态,而对于应用程序的编写属于应用态,应用态不能直接操作内核态,要通过一定的程序从而间接操作内核态

int mian(int argc,char *argv[])
{
    int ret = 0;
    int fd = 0;
    char *filename;
    char readbuf[100];
    char writebuf[100];
    static char usrdata[]={"Usr data!Usr data!Usr data!"};
    filename=argv[1];
    fd = open(filename, O_RDWR);
    if(fd<0)
    {
        printf("Can't open file %s\r\n",filename);
    }
    /*read*/
    if(atoi(argv[2])==1)
    {
        ret=read(fd, readbuf, 50); 
        printf("\r\nAPP read data:%s\r\n",readbuf);
    }
     /*write*/
    if(atoi(argv[2])==2)
    {
        memcpy(writebuf,usrdata,sizeof(usrdata));
        ret = write(fd, writebuf,50);
    }
    /*close*/
   ret = close(fd);
   if(ret<0)
   {
       printf("Can't close %s\r\n",filename);
   }
    return 0;
}

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Python正则表达式与Excel文件名批量匹配技术文章

目录 引言 正则表达式基础 Python中的re模块 Excel文件名批量匹配案例 常见问题与解决方案 结论 引言 在现代办公环境中&#xff0c;Excel文件几乎成为了数据分析和处理的标配工具。由于Excel文件可能包含大量的数据和信息&#xff0c;因此&#xff0c;对Excel文件的命名…

16 _ WebAPI:setTimeout是如何实现的?

在上一篇文章中我们介绍了页面中的事件和消息队列&#xff0c;知道了浏览器页面是由消息队列和事件循环系统来驱动的。 那在接下来的两篇文章中&#xff0c;我会通过setTimeout和XMLHttpRequest这两个WebAPI来介绍事件循环的应用。这两个WebAPI是两种不同类型的应用&#xff0…

嵌入式岗位,你有能力,你同样可以拿到高薪资

在开始前刚好我有一些资料&#xff0c;是我根据网友给的问题精心整理了一份「嵌入式的资料从专业入门到高级教程」&#xff0c; 点个关注在评论区回复“888”之后私信回复“888”&#xff0c;全部无偿共享给大家&#xff01;&#xff01;&#xff01; 就算你进去了&#xff0…

分布式限流总结

1、计数器 java内部可以使用原子计数器AtomicInteger\Semaphore信号量来做简单的限流 // 限流的个数private int maxCount 10;// 指定的时间内private long interval 60;// 原子类计数器private AtomicInteger atomicInteger new AtomicInteger(0);// 起始时间private lon…

【译】组复制和 Percona XtraDB 集群: 常见操作概述

原文地址&#xff1a;Group Replication and Percona XtraDB Cluster: Overview of Common Operations 在这篇博文中&#xff0c;我将概述使用 MySQL Group Replication 8.0.19&#xff08;又称 GR&#xff09;和 Percona XtraDB Cluster 8 (PXC)&#xff08;基于 Galera&…

Codesys软件做EtherNET/IP主站与HT3S-EIS-MDN网关通讯步骤

Codesys软件做EtherNET/IP主站与HT3S-EIS-MDN网关通讯步骤 打开codesys软件&#xff0c;新建项目&#xff0c;选择标准项目进入界面后选择CODESYS Control Win v3 x64 &#xff0c;点击确定。 3.在桌面找到显示隐藏图标&#xff0c;找到如下图所示图标&#xff0c;右击在弹出…

Linux编译器-gcc/g++使用

&#x1fa90;&#x1fa90;&#x1fa90;欢迎来到程序员餐厅&#x1f4ab;&#x1f4ab;&#x1f4ab; 主厨&#xff1a;邪王真眼 主厨的主页&#xff1a;Chef‘s blog 所属专栏&#xff1a;青果大战linux 总有光环在陨落&#xff0c;总有新星在闪烁 gcc概述 GCC是GNU C…

C++ | Leetcode C++题解之第101题对称二叉树

题目&#xff1a; 题解&#xff1a; class Solution { public:bool check(TreeNode *u, TreeNode *v) {queue <TreeNode*> q;q.push(u); q.push(v);while (!q.empty()) {u q.front(); q.pop();v q.front(); q.pop();if (!u && !v) continue;if ((!u || !v) ||…

Excel插入多行VBA实现

我们还可以利用 VBA&#xff08;Visual Basic for Applications&#xff09;宏语言&#xff0c;在 Excel 中写一个 VBA 宏来自动插入多行数据。这种方法可以方便我们自定义需要插入的行数和插入位置。下面是编写 VBA 宏的步骤&#xff1a; 1、按下Alt F11快捷键&#xff0c;打…

亚马逊云科技介绍

&#x1f525;博客主页&#xff1a; 小羊失眠啦. &#x1f3a5;系列专栏&#xff1a;《C语言》 《数据结构》 《C》 《Linux》 ❤️感谢大家点赞&#x1f44d;收藏⭐评论✍️ 文章目录 一、亚马逊云科技云计算1.1 云计算的优势 二、领先的云平台三、亚马逊云科技区域的全球网络…

从零开始实现自己的串口调试助手(1) - ui界面搭建

UI 界面搭建 ui界面整体演示 ui对象拆分 更多的细节就不方便展开了&#xff0c;下面有提示完成ui设计的提示 在创建工程前 记得把编码改为utf-8 ui设计技巧: ctrl 鼠标左键实现拖动实现复制粘贴 groupBox &#xff1a; 带标题的文本框 栅格布局 -- 只有一个控件的时候会铺满…

MyBatis中Where标签:揭秘高效SQL构建的秘密

哈喽&#xff0c;大家好&#xff0c;我是木头左&#xff01; 理解Where标签的基础概念 在MyBatis中&#xff0c;<where>标签是用于构建SQL查询语句中的一个非常重要的元素。它允许你在一个动态的SQL语句中添加WHERE子句&#xff0c;而不需要担心SQL语法错误或额外的逗号…

记一次重定向问题(浏览器安全)解决

近期做单点登陆功能&#xff0c;本身应该是一个很简单的功能&#xff0c;却发生了意向不到的问题…让我们看下&#xff1a; 首先第三方给出的地址需要通过JWT框架获取token拼接后跳转&#xff0c;我这边为了方便首选肯定是考虑用response.sendRedirect(url)&#xff0c;但是做好…

网络中断时接口status显示canceled,无法捕捉到状态进行相关操作

遇到的问题&#xff1a;如图所示&#xff0c;loading状态栏无法关闭&#xff0c;因为.then和.catch还有.finally 中都无法捕捉到接口canceled的状态&#xff0c;导致一直在loading状态&#xff0c;无法进行操作关闭弹框这一步。 产生原因&#xff1a;客户端 在服务器响应前关闭…

kafka Kerberos集群环境部署验证

背景 公司需要对kafka环境进行安全验证,目前考虑到的方案有Kerberos和SSL和SASL_SSL,最终考虑到安全和功能的丰富度,我们最终选择了SASL_SSL方案。处于知识积累的角度,记录一下kafka keberos安装部署的步骤。 机器规划 目前测试环境公搭建了三台kafka主机服务,现在将详细…

多家知名媒体到访“光子1号金融算力中心“ 交流AI与算力未来观

5月23日&#xff0c;企商在线 “光子1号金融算力中心媒体参观日”活动成功举办&#xff0c;十多家主流媒体、IT行业媒体及自媒体代表走进光子1号金融算力中心&#xff0c;深入了解企商业务发展、战略规划及“光子1号金融算力中心”等企商打造的新型数字基础设施&#xff0c;共同…

基于STC12C5A60S2系列1T 8051单片机的TM1637键盘数码管模块的数码管显示与单片机连接的按键的按键值的功能

基于STC12C5A60S2系列1T 8051单片机的TM1638键盘数码管模块的数码管显示与单片机连接的按键的按键值应用 STC12C5A60S2系列1T 8051单片机管脚图STC12C5A60S2系列1T 8051单片机I/O口各种不同工作模式及配置STC12C5A60S2系列1T 8051单片机I/O口各种不同工作模式介绍TM1637键盘数码…

云计算期末复习(1)

云计算基础 作业&#xff08;问答题&#xff09; &#xff08;1&#xff09;总结云计算的特点。 透明的云端计算服务 “无限”多的计算资源&#xff0c;提供强大的计算能力 按需分配&#xff0c;弹性伸缩&#xff0c;取用方便&#xff0c;成本低廉资源共享&#xff0c;降低企…

坚守互联网底层逻辑,搜狐走向长期主义的next level

2024年以来&#xff0c;随着我国经济回升向好态势进一步巩固增强&#xff0c;网络内容供给不断丰富&#xff0c;新型消费持续活跃&#xff0c;互联网板块整体估值向预期进行修正。因此&#xff0c;中概互联网指数ETF&#xff08;KWEB&#xff09;一转颓势&#xff0c;截至5月21…

Aiseesoft Video Converter Ultimate视频转换大师,免安装中文旗舰版 v10.8.32

软件介绍 视频转换大师是一款集转码、编辑以及压缩功能于一身的专业软件&#xff0c;支持处理超过1,000种视频和音频格式。该工具不仅提供基础的格式转换服务&#xff0c;还拓展至高级功能&#xff0c;包括视频效果调整、数据压缩以及ID3信息编辑等&#xff0c;同时&#xff0…