Linux驱动程序,首先应该知道它是linux的内核模块。
Linux内核模块是使得复杂而庞大的linux内核条理清晰、可裁剪、高兼容性的重要特性。
Linux内核模块的特点:
1, 模块本身不被编译进内核镜像,能够控制内核的大小。
2, 模块可以在需要的时候中被动态加载,一旦加载完成就和内核其它部分完全一样。
下面便是linux内核模块的helloworld程序,结构十分固定。
(1) 模块加载函数
当通过insmod或者modprobe命令加载内核模块时,模块的加载函数会自动执行,完成本模块的相关初始化工作
(2) 模块卸载函数
当通过rmmod命令卸载内核模块时,模块的卸载函数会自动执行,完成本模块的卸载功能
(3) 模块许可证声明
如果不声明LICENSE,模块被加载时,将收到内核被污染(Kernel Tainted)的警告。
(4) 模块参数(可选)
模块参数是模块被加载的时候可以传递给它的值,它本身对应模块内部的全部变量
(5) 模块导出符号(可选)
内核模块可以导出的符号(symbol,对应函数或变量),若导出,则其他模块可以使用本模块中的变量或函数
(6) 模块作者等信息声明(可选)
这个驱动并不具有任何控制硬件的行为,只是为了展示linux驱动的通用结构。这几乎是所有驱动程序的通用模版,如led的驱动程序,只需要在hello_ioctl函数中根据不同的传入参数操作gpio寄存器即可。(应用层没有操作硬件的权限,而内核中具有所有权限。驱动程序的作用就是高效的、封装的、有限的向应用层提供服务)
代码:
1 /* 2 hello.c - The simplest kernel module. 3 */ 4 #include <linux/kernel.h> 5 #include <linux/init.h> 6 #include <linux/module.h> 7 #include <linux/slab.h> 8 #include <linux/io.h> 9 #include <linux/interrupt.h> 10 11 #include <linux/of_address.h> 12 #include <linux/of_device.h> 13 #include <linux/of_platform.h> 14 15 /* Standard module information */ 16 MODULE_LICENSE("GPL"); 17 MODULE_AUTHOR("pp."); 18 MODULE_DESCRIPTION("hello module template "); 19 20 #define DRIVER_NAME "hello" 21 22 unsigned myint = 0xdeadbeef; 23 char *mystr = "default"; 24 25 module_param(myint, int, S_IRUGO); 26 module_param(mystr, charp, S_IRUGO); 27 28 static int __init hello_init(void) 29 { 30 printk(KERN_INFO "Hello module world. "); 31 printk(KERN_INFO "Module parameters were (0x%08x) and "%s" ", myint,mystr); 32 33 return 0; 34 } 35 36 37 static void __exit hello_exit(void) 38 { 39 printk(KERN_ALERT "Goodbye module world. "); 40 } 41 42 module_init(hello_init); 43 module_exit(hello_exit);
编译后生成.ko文件,移植到开发板linux下测试
默认情况下
root@plnx_arm:/mnt# insmod hello.ko Hello module world. Module parameters were (0xdeadbeef) and "default" root@plnx_arm:/mnt# lsmod Tainted: G hello 817 0 - Live 0xbf004000 (O) root@plnx_arm:/mnt# rmmod hello Goodbye module world.
传入参数时:
root@plnx_arm:/mnt# insmod hello.ko myint=123 mystr="pp" Hello module world. Module parameters were (0x0000007b) and "pp" root@plnx_arm:/mnt# rmmod hello Goodbye module world.
通过其他的查询命令可以看到内核的输出:
root@plnx_arm:/mnt# ls /sys/module/hello/parameters/ myint mystr root@plnx_arm:/mnt# tail -n 2 /var/log/messages Jun 4 09:56:33 plnx_arm user.info kernel: Hello module world. Jun 4 09:56:33 plnx_arm user.info kernel: Module parameters were (0x0000007b) and "pp"
在Linux下可以通过两种方式加载驱动程序:静态加载和动态加载。
静态加载就是把驱动程序直接编译进内核,系统启动后可以直接调用。静态加载的缺点是调试起来比较麻烦,每次修改一个地方都要重新编译和下载内核,效率较低。若采用静态加载的驱动较多,会导致内核容量很大,浪费存储空间。
动态加载利用了Linux的module特性,可以在系统启动后用insmod命令添加模块(.ko),在不需要的时候用rmmod命令卸载模块,采用这种动态加载的方式便于驱动程序的调试,同时可以针对产品的功能需求,进行内核的裁剪,将不需要的驱动去除,大大减小了内核的存储容量。
在台式机上,一般采用动态加载的方式;在嵌入式产品里,可以先采用动态加载的方式进行调试,调试成功后再编译进内核。