1

xiaoxiao2021-02-28  74

一. 概念介绍 从上到下,一个软件系统可以分为:应用程序、库、内核、驱动程序。 (1)应用程序使用库提供的open函数打开代表LED的设备文件。 (2)库根据open函数传入的参数执行“swi”指令,这条指令会引发CPU异常,进入内核。 (3)内核的异常处理函数根据这些参数找到相应的驱动程序,返回一个文件句柄给库,进而返回给应用程序。 (4)应用程序得到文件句柄后,使用库提供的write或ioclt函数发出控制命令。 (5)库根据write或ioclt函数传入的参数执行“swi”指令,这条指令会引起CPU异常,进入内核。 (6)内核的异常处理函数根据这些参数调用驱动程序的相关函数,点亮LED。

open ,write 这些函数不是我们实现的,是C库实现的, C库也属于应用层。 当我们的应用程序调用open,write这些“系统调用”的时候,C库会进入到内核,内核通过VFS(virtual Filesystem)来实现调用不同的驱动函数。 C库怎么进入内核呢? 这些open ,write的实现,实际上是执行一条swi汇编指令,然后后面加入某个值,这条汇编指令就会引发一个异常,相当于中断一样,当发生异常的时候,就会进入内核的异常处理函数里面。 内核这一层“syatem lan interface 系统调用接口”:在异常处理函数里面根据发生中断的原因,调用不同的处理函数。比如我们用open的时候,传进来的值是val1,用write的时候,传进来的值是val2,内核里面的系统调用接口就根据传进来不同的值,去调用我们的sys_open,sys_write。 例如:我们有一个函数, int main() { int fd1, fd2; int val=1; fd1 = open(“/dev/led”, O_RDWR); // fd1是打开led的设备文件,O_RDWR可读可写 write(fd1, &val, 4); //将&val的值写入设备文件”/dev/led”,应用程序里的write函数将参数传给驱动程序里的first_drv_write函数 fd2 = open(“hello.txt”, O_RDWR); //fd2是打开一个文本文件 write(fd2, &val, 4); //将&val所指向内存地址的值写入文本文件 } 函数里两个open函数(或两个write函数),引发了不一样的行为,第一个是操控硬件,第二个是读写文件。 谁来实现不一样的行为呢?内核通过VFS(virtual Filesystem)来实现调用不同的驱动函数。sys_open,sys_write最终会根据打开不同的文件找到不同的底层驱动程序,调用不同驱动程序里的open函数、write函数来实现这些功能。

二、字符设备驱动程序的框架 实现步骤: 1. 在Source Insight新建一个文件,这是我们的第一个驱动程序,命名first_drv.c,然后写出first_drv_open, first_drv_write函数 2. 定义file_operations结构体, 把驱动函数first_drv_open, first_drv_write填充到里面 3. 把这个结构告诉内核, 通过这个注册函数 register_chrdev(major, “first_drv”, &first_drv_fops) 来实现 4. 谁来调用注册函数 ? 驱动的入口函数first_drv_init 来调用这个注册函数 5. 修饰一下这个入口函数module_init(first_drv_init) 第一个驱动的入口函数是first_drv_init,那第二个呢?有不同的入口函数,函数名称各有不同, 内核怎么知道去调用你这个函数?怎么知道你的入口函数是这一个?那得修饰一下入口函数。 怎么修饰? 是用这么一个宏来定义的 module_init(first_drv_init); module_init定义一个结构体,结构体里面有一个函数指针,指向first_drv_init 这个入口函数。 当我们去加载或安装一个驱动程序的时候,内核就会自动去找到这么一个结构体module_init,然后调用里面的 函数指针,它就指向我们的入口函数first_drv_init。 我们的入口函数first_drv_init就把这个结构file_operations告诉内核。

first_drv.c

//第一步:驱动功能实现 static int first_drv_open(struct inode *inode, struct file *file) //应用程序打开设备文件的时候就会进入到内核里面, { printk("first_drv_open\n"); //调用驱动程序里面的first_drv_open,然后打印"first_drv_open" return 0; } static ssize_t first_drv_write(struct file *file, const char __user *buf, size_t count, loff_t * ppos) { printk("first_drv_write\n"); return 0; } //第二步:定义结构体,并把驱动函数填充进去 static struct file_operations first_drv_fops = { .owner = THIS_MODULE, /* 这是一个宏,推向编译模块时自动创建的__this_module变量 */ .open = first_drv_open, .write = first_drv_write, }; //第四步:实现驱动入口函数 int first_drv_init(void) //入口函数调用注册函数 { //第三步:把结构体告诉内核 register_chrdev(111, "first_drv", &first_drv_fops); //注册,告诉内核,"first_drv"名字可随便写 return 0; } void first_drv_exit(void) { unregister_chrdev(111, "first_drv"); //卸载 return 0; } //第五步:修饰入口函数及出口函数 module_init(first_drv_init); module_exit(first_drv_exit);

firstdrvtest.c

#include <sys/types.h> /*应用程序调用open, write函数,这两个函数是C库实现的*/ #include <sys/stat.h> #include <fcntl.h> #include <stdio.h> int main(int argc, char **argv) { int fd; int val = 1; fd = open("/dev/xxx", O_RDWR); //open一个设备, xxx名字不重要 if(fd<0) //如果不能打开文件,就打印"can't open!" printf("can't open!\n"); write(fd, &val, 4); return 0; }

三. 设置主设备号及创建设备节点 1、设置主设备号 我们设备驱动程序里面可以自动分配主设备号, 也可以由我们自己手工指定。

// 设置为 0 时是系统自动分配主设备号,通过cat /proc/device看一下系统为我们的first_drv分配的主设备号是多少。 major = register_chrdev(0, "first_drv", &first_drv_fops); // 手动分配主设备号111给first_drv register_chrdev(111, "first_drv", &first_drv_fops);

2、创建设备节点 当应用程序执行 open(“/dev/xyz”) 操作时,这个/dev/xyz是怎么来的 ? 2.1 手工建立

// 创建设备节点(在串口执行命令) mknod /dev/xyz c(表示是字符设备) 主设备号 次设备号 //查看设备信息 ls -l /dev/xyz

2.2 自动创建 mdev – 根据系统信息创建设备节点

static struct class *firstdrv_class; //一个类 static struct class_device *firstdrv_class_dev; //类下面再建立一个设备 int major; static int first_drv_init(void) { major = register_chrdev(0, "first_drv", &first_drv_fops); //主设备号可写为0,让系统自动给我们分配 //创建设备信息,执行后会出现 /sys/class/firstdrv firstdrv_class = class_create(THIS_MODULE, "firstdrv"); //创建一个类,类下面再创建一个设备 //创建设备节点,就是根据上面的设备信息来的 firstdrv_class_dev = class_device_create(firstdrv_class, NULL, MKDEV(major, 0), NULL, "xyz"); /* /dev/xyz */ return 0; } static void first_drv_exit(void) { unregister_chrdev(major, "first_drv"); //卸载 //删除节点及信息 class_device_unregister(firstdrv_class_dev); class_destroy(firstdrv_class); }

对应的测试程序firstdrvtest.c也要修改:

#include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <stdio.h> int main(int argc, char **argv) { int fd; int val = 1; fd = open("/dev/xyz", O_RDWR); //无论主设备号怎么变,/dev/xyz都是系统帮我们创建的 if(fd<0) printf("can't open!\n"); write(fd, &val, 4); return 0; }
转载请注明原文地址: https://www.6miu.com/read-62168.html

最新回复(0)