LDD3 SCULL驱动的测试程序,字符驱动模型的理解

这两天闲暇,突然想再把LDD3书上的例子细过一遍,就着手看了,说实话,这个上面SCULL驱动有点晦涩难懂,真是看了大半天。

开发板被搞得现在还没起来,没办法只能在x86 ubuntu上实验驱动了。

驱动其实本身并不难写尤其是很多常见的芯片,网上有很多可用的驱动程序,即便没有也可以着手芯片手册尝试写出来,而linux驱动难得地方不在于驱动本身,而在于你要知道怎么把一个驱动程序放进linux 驱动的框架中,这个时候你就不仅要了解芯片的工作初始化原理,更重要的是你要知道linux驱动框架的模型。

驱动的本质不变,还是对芯片进行初始化,然后让芯片进行工作。

首先结合书本说一下scull这个驱动的功能(后面会举例子说明), 站在用户角度,该驱动的功能就是用户可以使用open打开设备然后使用write吧自己的数据(该数据可以无限大小,当然也不是无限,会受到实际物理内存的限制)存进ram中,然后close,存储的数据会常驻ram,你可以在任何事或重新打开(该例子规定,必须以只读打开才能读取到之前存储的数据,如果以只写打开所有的数据会被清除,重新写新东西)读取到之前存储的数据。该“设备”的主要功能是往RAM中存储数据数据的大小不限,当然实际的功能没有什么优势,因为该例子主要是为了讲字符设备的驱动模型。

结构体中 quantum为量子的大小等于4000,qset为上图char*指针数组的大小等于1000,这样计算所得一个scull_qset可以存储的字节大小约等于4m,long为数据总大小,为啥定义为long?因为此“设备”可以存储无限制大小的数据,long就是这个数据的总大小字节数,cdev使用来注册核心。

再大概说一下linux字符驱动的模型:

简单的流程就是,在insmod的时候会执行module_init 用来给scull_dev分配空间并注册你的设备号,之后向核心注册cdev设备并把file_operation 和申请设备号的设备文件关联起来,做好使用的准备,之后用户就可以使用open,read,write 去打开并操作设备功能以达到目的。

为了测试加强理解  特别的写了一个测试程序:

我在驱动中读写都加了item,rest,q_pos,quantum的打印。

#include <stdio.h>
#include <unistd.h>
#include <linux/i2c.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>

int main()
{
 int fd;
 char buf[4000] = {0};
 char buf1[100] = {0};
 char buf2[4000] = {0};
 char buf3[4000] = {2};
 char buf4[3333] = {2};

 memset(buf,48,sizeof(buf));
 memset(buf1,49,sizeof(buf1));
 memset(buf2,50,sizeof(buf2));
 memset(buf3,51,sizeof(buf3));
 memset(buf4,52,sizeof(buf4));

 char buf_read[4096];
 int i = 0;
 fd = open("/dev/scull1",O_WRONLY);/* 用只写方式打开 */
     
 write(fd,buf,sizeof(buf)); //把buf中的内容写入memdev设备
 write(fd,buf1,sizeof(buf1)); //把buf中的内容写入memdev设备
 write(fd,buf2,sizeof(buf2)); //把buf中的内容写入memdev设备
 for(i = 0;i < 1000;i++) {
 write(fd,buf3,sizeof(buf3)); //把buf中的内容写入memdev设备
 }
 write(fd,buf4,sizeof(buf4)); //把buf中的内容写入memdev设备

 close(fd);
 return 0;
}

计算一下写入的总大小为 4000+4000+4000*1000+3333=4011333字节

此函数执行写函数我们在scull.h中定义SCULL_DEBUG宏之后,直接读取/proc/scullmen 得到如下结果

可以看到总共写入的大小为4011333字节这与我们计算的值相同,在最后一次写入的时候也就是驱动程序中写入3333字节的时候计算索引值,一个item为4000000字节,此时ram中已经写入了408000字节,所以item肯定=1,一个item=4000000 一个rest=总大小对item求余,所以rest=8000,spos = 2 ,q_pos = 0,所以

item=1,rest=8000,spos=2,qpos=0这确定了要写入3333字节数据的位置,我们用dmesg查看内核驱动的打印结果如下:

结果与我们计算相吻合,这是我们使用写打印的结果,因为数据均一次写入4000个,最后一次写入3333个,如果我们使用4000的buf去读出数据,最后一次输出的item,rest,spos,qpos值将与写入一致,

这次我们以1字节为单位去读取数据并打印item,rest,spos,qpos的结果,我们先预测一下:

如果一次只读一个字节(比较耗时,效率低),现在有4011333个字节要读取,当读取到最后一个字节的时候,进入read函数,此时计算的指针(item,rest,spos,qpos)势必指向4011333-1的位置此时:

item=1,rest=11332,spos=11332/4000=2,qpos=11332%4000=3332

#include <stdio.h>
#include <unistd.h>
#include <linux/i2c.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>

int main()
{
 int fd;

 char buf4[4000] = {2};

 memset(buf4,0,sizeof(buf4));
 int ret = 1;
 int i = 0;
 fd = open("/dev/scull1",O_RDONLY);/* 用只写方式打开 */

 while(ret > 0){
	ret = read(fd,buf4,1);
 }
 close(fd);
 return 0;
}

程序执行后用dmesg查看内核打印为:

结果与我们计算结果一致。

回归主题:这个例子重要的是字符设备驱动模型,可能只有我这样的闲人才看例子的逻辑- -。

原创文章 7 获赞 3 访问量 951

猜你喜欢

转载自blog.csdn.net/w346665682/article/details/100067000