Linux设备驱动开发入门之

您所在的位置:网站首页 怎么在电脑上编写程序 Linux设备驱动开发入门之

Linux设备驱动开发入门之

2024-07-07 16:53:34| 来源: 网络整理| 查看: 265

1. Linux驱动程序的分类

Linux 中主要分为三大类驱动:字符设备驱动、块设备驱动和网络设备驱动。

1、字符设备驱动:因为软件操作设备是是以字节为单位进行的,是按照字节流进行读写操作的一种设备。典型的如LCD、蜂鸣器、SPI、触摸屏等驱动,都属于字符设备驱动的范畴。大部分的驱动程序都是属于字符设备驱动。

2、块设备驱动:块设备驱动是相对于字符设备驱动而定义的,因为块设备被软件操作时,是以块为单位进行操作的(块指的是多个字节组成一个块)。块设备大多指的都是各种存储类类设备,比如EMMC、SD卡、NANDFlash、U盘等等。

3、网络设备驱动:专门针对网络设备而设计的一种驱动,不管是有线还是无线网络,都属于网络设备驱动。

另外,一个设备可以属于多种设备驱动类型,比如 USB WIFI设备,其使用 USB 接口,所以属于字符设备,但是其又能上网,所以也属于网络设备驱动。

2. 与Linux驱动开发相关的介绍

1、Linux下的应用程序是如何调用驱动程序的

应用程序在使用C库函数所提供的 open/read/write 等等函数时,最终会进入到内核里面,调用内核所提供的 sys_open/sys_read/sys_write 等等函数。而此时如果内核发现应用程序需要访问的是驱动的话,那么就会调用该驱动程序所提供的 drv_open/drv_read/drv_write 等函数;如果发现应用程序访问的不过是普通文件的话,那么内核就会调用访问普通文件的那套函数。下图形象的给出了调用关系: 在这里插入图片描述 驱动程序实际上起到承上启下的作用,上承应用程序,对下则实现了具体的硬件操作。

2、Linux驱动程序的两种运行方式

可以把驱动程序编译进内核里面,这样内核启动后就会自动运行驱动程序了;将驱动程序编译成以.ko为后缀模块文件,然后在Linux启动后,我们自己手动安装驱动程序。一般来说,这种方式在开发驱动阶段常用。

3、Linux驱动开发中常用的几个命令

insmod(install module):用于安装以Linux的驱动模块rmmod(remove module):卸载驱动模块lsmod(list module):打印出当前内核中已经安装的模块modinfo(module information):打印出某个 xxx.ko 文件的模块信息。用法:modinfo xxx.ko 3. Linux驱动开发需要准备的工作

1、已经安装好的交叉编译工具链

​ 我们开发的驱动程序是要运行在ARM架构上的,所以需要准备好ARM架构的编译工具链。一般来说我们使用开发板厂商,或者SoC原厂提供交叉编译工具链即可。具体如何安装交叉编译工具链这里不多啰嗦了。

2、准备已经配置和编译好你对应板子的内核源码

​ 驱动程序是运行在内核空间的,不同于应用程序运行在用户空间。驱动程序已经是属于内核的一部分了,而编译驱动程序需要借助于内核源码来编译。

​ 另外,内核源码的版本一定要和你板子上实际运行的版本相一致,否则编译出来的驱动程序会因为版本不同而无法在你的板子上运行。

3、你的开发板接线正常,网络正常(要保证开发板和ubuntu之间可以相互ping通,因为我们通过nfs方式把ubuntu编译好的 xxx.ko 等文件传输到开发板)。

4. show出你的代码 4.1 hello驱动的编写

我们在编写应用程序的时候,首先也是先学会如何再电脑屏幕上输出 “helllo world”。同样的,我们编写的第一个驱动程序,也是先学会hello驱动,该驱动不涉及任何的硬件操作,而且也是属于字符设备驱动的范畴。主要实现的功能是:

1、应用程序调用 open、read、write 等函数时,对应的驱动函数都打印出内核信息;

2、应用程序调用 write 函数时,传入的数据保存在驱动中;

3、应用程序调用 read 函数时,把驱动中保存的数据再返回给应用程序,并打印出来

驱动程序的编写其实也是有迹可循的,主要的编写步骤如下:

确定主设备号,也可以让内核自动分配定义自己的 file_operations 结构体实现对应的 drv_open/drv_read/drv_write 等函数,填入 file_operations 结构体把 file_operations 结构体告诉内核:register_chrdev谁来注册驱动程序啊?得有一个入口函数:安装驱动程序时,就会去调用这个入口函数有入口函数就应该有出口函数:卸载驱动程序时,出口函数调用 unregister_chrdev其他完善:提供设备信息,自动创建设备节点:class_create, device_create

其中,驱动程序核心中的核心就是 file_operations 这个结构体了。在这个结构体里面,就是要实现这个驱动程序自己的 open、read、write等函数,并通过Linux内核提供的接口注册到内核里面去。而其他的一些步骤都是为了遵循LInux驱动程序的编写规范,用于完善这个驱动程序的。

驱动代码的编写也不用完全都自己写,我们可以参考Linux内核提供的一些已有的驱动程序,下面我们就参考内核的一份 misc 驱动,编写我们自己的hello驱动程序,hello驱动代码如下:

#include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* 1. 确定主设备号 */ static int major = 0; static char kernel_buf[1024]; static struct class *hello_class; #define MIN(a, b) (a int err; printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__); err = copy_from_user(kernel_buf, buf, MIN(1024, size)); return MIN(1024, size); } /* * @description : 打开设备 * @param - node : 设备节点 * @param - file : 文件描述符 * @return : 打开成功返回0,失败返回-1 */ static int hello_drv_open (struct inode *node, struct file *file) { printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__); return 0; } /* * @description : 关闭设备 * @param - node : 设备节点 * @param - file : 文件描述符 * @return : 关闭成功返回0,失败返回-1 */ static int hello_drv_close (struct inode *node, struct file *file) { printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__); return 0; } /* 2. 定义自己的file_operations结构体 */ static struct file_operations hello_drv = { .owner = THIS_MODULE, .open = hello_drv_open, .read = hello_drv_read, .write = hello_drv_write, .release = hello_drv_close, }; /* 4. 把file_operations结构体告诉内核:注册驱动程序 */ /* 5. 谁来注册驱动程序啊?得有一个入口函数:安装驱动程序时,就会去调用这个入口函数 */ static int __init hello_init(void) { int err; printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__); major = register_chrdev(0, "hello", &hello_drv); /* /dev/hello */ /* 7. 其他完善:提供设备信息,自动创建设备节点 */ hello_class = class_create(THIS_MODULE, "hello_class"); err = PTR_ERR(hello_class); if (IS_ERR(hello_class)) { printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__); unregister_chrdev(major, "hello"); return -1; } device_create(hello_class, NULL, MKDEV(major, 0), NULL, "hello"); /* /dev/hello */ return 0; } /* 6. 有入口函数就应该有出口函数:卸载驱动程序时,就会去调用这个出口函数 */ static void __exit hello_exit(void) { printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__); device_destroy(hello_class, MKDEV(major, 0)); class_destroy(hello_class); unregister_chrdev(major, "hello"); } /* 指定驱动的入口和出口,以及声明自己的驱动遵循GPL协议(不声明的话无法把驱动加载进内核) */ module_init(hello_init); module_exit(hello_exit); MODULE_LICENSE("GPL");

hello驱动程序的几点说明:

驱动程序要打印信息的时候,调用的函数是 printk ,而应用程序调用的是 printf

应用程序和驱动程序之间传递数据,不能使用简单的赋值或者memcpy等,要使用内核提供的 copy_from_user/copy_to_user 函数。当然如果需要传递大量数据的时候,还可以使用内存映射的方式

阅读一个驱动程序,首先要找到驱动程序的入口函数。上面的驱动入口函数就是hello_init函数,该函数做的事情就是向内核注册了一个 file_oprations 结构体,并且完成自动创建设备节点相关的代码

file_oprations 结构体是驱动程序的核心,里面提供了本驱动 open/read/write/release 等成员,当应用程序调用了open/read/write/release 等函数时,就会导致对应驱动的这些成员函数被调用

4.2 编写hello应用程序测试

下面我们编写一个hello应用程序来测试我们编写好的驱动程序。该应用程序要实现的功能是:

向驱动程序写入一串字符串把驱动程序保存起来的字符串读出来

hello_drv_test 应用程序代码如下:

#include #include #include #include #include #include /* 该应用程序用法: * ./hello_drv_test -w abc 向hello驱动写入字符串 abc * ./hello_drv_test -r 读取驱动程序中保存的数据 */ int main(int argc, char **argv) { int fd; char buf[1024]; int len; /* 1. 判断参数 */ if (argc printf("can not open file /dev/hello\n"); return -1; } /* 3. 写文件或读文件 */ if ((0 == strcmp(argv[1], "-w")) && (argc == 3)) { len = strlen(argv[2]) + 1; len = len


【本文地址】

公司简介

联系我们

今日新闻


点击排行

实验室常用的仪器、试剂和
说到实验室常用到的东西,主要就分为仪器、试剂和耗
不用再找了,全球10大实验
01、赛默飞世尔科技(热电)Thermo Fisher Scientif
三代水柜的量产巅峰T-72坦
作者:寞寒最近,西边闹腾挺大,本来小寞以为忙完这
通风柜跟实验室通风系统有
说到通风柜跟实验室通风,不少人都纠结二者到底是不
集消毒杀菌、烘干收纳为一
厨房是家里细菌较多的地方,潮湿的环境、没有完全密
实验室设备之全钢实验台如
全钢实验台是实验室家具中较为重要的家具之一,很多

推荐新闻


图片新闻

实验室药品柜的特性有哪些
实验室药品柜是实验室家具的重要组成部分之一,主要
小学科学实验中有哪些教学
计算机 计算器 一般 打孔器 打气筒 仪器车 显微镜
实验室各种仪器原理动图讲
1.紫外分光光谱UV分析原理:吸收紫外光能量,引起分
高中化学常见仪器及实验装
1、可加热仪器:2、计量仪器:(1)仪器A的名称:量
微生物操作主要设备和器具
今天盘点一下微生物操作主要设备和器具,别嫌我啰嗦
浅谈通风柜使用基本常识
 众所周知,通风柜功能中最主要的就是排气功能。在

专题文章

    CopyRight 2018-2019 实验室设备网 版权所有 win10的实时保护怎么永久关闭