还没有学驱动时,大部分对驱动的言论,包括老师的介绍都说做驱动是嵌入式里最难的部分,涉及到底层硬件和内核的知识,心里因此增加了不少畏惧。自己开始学习的时候便开始看些驱动方面的书,希望能有个比较好的认识,可慢慢的发现几乎看不懂,看了韦东山老师的部分视频后果然豁然开朗,发现做LED驱动以及其它驱动时,最主要的是要明白应用程序、库、内核、驱动程序之间的关系,在视频里韦东山老师首先为我们一步一步构造出它们之间的关系,驱动属于底层硬件和上层应用程序之间的中间层,看完这部分视频后你会很清楚的知道驱动、应用程序的那些函数是如何工作的。
接下来就是编写驱动程序了,首先构造了open、read、write等函数,而这些只是一些简单的函数而已,如何发挥作用是通过一个结构体来定义,视频里给出的结构体如下:
static struct file_operations first_drv_fops = {
.owner = THIS_MODULE, /* 这是一个宏,推向编译模块时自动创建的__this_module变量 */
.open = first_drv_open,
.read = first_drv _read,
.write = first_drv _write,
};
然后再定义first_drv_init函数用来初始化设备等,实现方法如下:
static int first_drv_init(void)
{
printk("MY DRIVER MODULE INIT\n");
first_drv_major = register_chrdev(0,DEVICE_NAME, &first_drv_fops); /*注册一设备*/
if (first_drv_major < 0)
{
printk(DEVICE_NAME " can't register major number\n");
return first_drv_major;
}
printk("register first_drv OK! Major = %d\n", first_drv_major);
firstdrv_class = class_create(THIS_MODULE, DEVICE_NAME); /*创建一个类*/
if(IS_ERR(firstdrv_class))
{
printk("Err: failed in firstdrv_class. \n");
return -1 ;
}
/*通过创建的类来创建一设备节点*/
firstdrv_class_dev=device_create(firstdrv_class, NULL, MKDEV(first_drv_major, 0), NULL, DEVICE_NAME);
if(unlikely(IS_ERR(firstdrv_class_dev)))
return PTR_ERR(firstdrv_class_dev);
printk(DEVICE_NAME " initialized\n");
gpfcon = (volatile unsigned long *)ioremap(0x56000050, 16); /*把寄存器的地址映射成虚拟地址*/
gpfdat = gpfcon + 1;
return 0;
}
创建了设备,在退出时就必须注销相关的设备和节点,实现函数如下:
static void first_drv_exit(void)
{
printk("FIRST_DRV MODULE EXIT\n");
unregister_chrdev(first_drv_major, DEVICE_NAME); /*注销该设备*/
device_destroy(firstdrv_class, MKDEV(first_drv_major, 0)); /*注销类创建的设备节点*/
class_destroy(firstdrv_class); /*注销类*/
iounmap(gpfcon); //取消地址映射
iounmap(gpfdat);
}
编写完代码后再编写测试程序和makefile文件,拷贝相关文件到虚拟机下,执行make指令生成.ko文件,再编译测试程序,生成可执行程序,最后把.ko文件和测试程序的可执行文件拷到开发板的根文件系统下通过命令:insmod first_drv.ko动态加载驱动,再执行:./first_drv_test 0可以看到开发板上的灯全亮了,执行./first_drv_test 1时,灯全灭,说明驱动程序编写成功了。
在看到自己跟着视频里写好的代码烧到开发板上能正常运行时,心里是说不出的高兴,由于视频里的代码都是一条一条当场写出来的,讲解也非常清晰、易懂,让自己对驱动的编写有了更深入的了解,同时非常期望韦大神能尽早推出自己的后期3G无线上网,wifi等驱动视频!!! |