还没有学驱动时,大部分对驱动的言论,包括老师的介绍都说做驱动是嵌入式里最难的部分,涉及到底层硬件和内核的知识,心里因此增加了不少畏惧。自己开始学习的时候便开始看些驱动方面的书,希望能有个比较好的认识,可慢慢的发现几乎看不懂,看了韦东山老师的部分视频后果然豁然开朗,发现做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等驱动视频!!!
|