首先说明:写这个第一个linux设备驱动程序的目的是熟悉Linux驱动的框架以及编程流程,所以是通过打印的信息来观察程序运行的情况,并不是真正的实现了某一个具体设备的驱动,可以类比于C语言编程中的“Hello World”。
Linux下的设备驱动架构如下图所示:
在本篇文章中以first_device_driver为例进行介绍
一般来说,写出完整的设备驱动程序需要如下几个步骤:
下面进行详细讲解
步骤一:在新创建的.c文件中编写如图例中的代码
步骤二:编写Makefile脚本
值得提出的一点是,编写Makefile是Linux驱动工程师必备的基础,但是要明白我们并不需要完全的掌握Makefile的语法及编程,我们只需要能模仿着其他工程的Makefile文件写出我们自己想要的Makefile文件即可。
步骤三:在对应目录中执行make命令,生成.ko模块文件
这一步骤较为简单,只是在相应目录输入make命令即可。
步骤四:通过U盘或者nfs网络文件系统将该.ko文件加载到内核中
我们在加载驱动模块之前可以先通过命令:cat /proc/devices来查看字符主设备号是否已经被占用。proc文件系统是Linux在运行时存在于内存中的文件系统,它记录着系统运行的实时信息,当关闭系统时,proc文件系统也随之释放。
然后可通过命令:insmod first_drv.ko将模块挂载到内核, 通过命令:cat /proc/devices可以观察first_drv设备是否已经挂载成功;另外也可以通过modprobe来加载驱动模块,这两者的区别在于modprobe可以解决加载模块时的依赖关系,它是通过/lib/modules/#uname -r/modules.dep(.bb)文件来查找依赖关系的,而insmod不能解决模块间的依赖问题。
步骤五:创建dev/first_driver设备节点
通过命令:mknod /dev/first_driver 100 0 来创建设备节点。
步骤六:编写应用程序进行测试
测试的应用程序如下:
在这里我们可以发现测试程序里的open()函数实际就是调用了驱动中的first_drv_open()函数,而write()函数实际调用了驱动中的first_drv_write()函数。本质上是这样的一个执行过程:用户空间的open()函数->文件系统的sys_open()函数->驱动的first_drv_open()函数。
另外很重要的一点,上文中图例中的程序是需要手动创建设备节点,从而提供给用户程序访问的,如此一来当驱动模块较多的时候就很麻烦,所以Linux也提供自动创建设备节点的接口,建议使用自动创建设备节点的机制。如下是自动创建设备节点的方法:
1、首先创建一个class设备类,然后在class类下,创建一个class_device,即在类下面创建类的设备;
2、在驱动入口函数中添加步骤:firstdrv_class= class_create(THIS_MODULE,"first_drv");irstdrv_class_devs=class_device_create(firstdrv_class,NULL,MKDEV(major,0),NULL,"first_drv");
3、在驱动出口函数中添加:class_device_unregister(firstdrv_class_devs);class_destroy(firstdrv_class);
这个自动创建设备节点的功能是基于Linux支持的热拔插功能,Linux内核中每当设备出现变动时,都会处理对应的信息,使用户程序对/dev目录下的设备进行操作。
最后,此驱动程序运行的实际效果就是打印信息,这个Linux设备驱动例程可以类比于C语言中的“Hello World”例程,希望可以帮助大家初步认识Linux的设备驱动程序。