驱动,就是让硬件动起来,来为我们工作.
编写驱动重点在于驱动提供的是机制而不是策略,我们只需要提供驱动能干什么就行,至于怎么用这些功能就是应用开发者的事情了.
而且驱动编写的时候不能使用标准C库,不能动态链接,只能是使用内核提供的库.
因为驱动在内核空间工作,所以我们要非常的小心,申请的资源应该及时的释放,操作也要考虑并发,阻塞,抢占,同步,异步等不稳定因素.
驱动分为字符设备驱动(以字节流的方式进行读写),块设备驱动(以存储块的方式进行读写),网络设备驱动(以数据报的方式进行读写).
编译驱动的一般makefile形式(使用的源码必须经过编译,形成完整的内核树):
ifneq ($(KERNELRELEASE),)
obj-m := hello.o
#module-objs := file_name1.o ..... #多文件编译
else
PWD := $(shell pwd)
#KDIR:= /root/kernel/linux-2.6.32.2 #自定义短语的内核源码
KDIR := /lib/modules/`uname -r`/build #编译电脑端的内核源码
all:
make -C $(KDIR) M=$(PWD)
clean:
rm -rf *.o *.mod.c *.symvers *.c~ *~ *.order
endif
装载驱动模块
insmod,使用公有符号解析不存在的符号
modprobe:查找需要的依赖模块,将依赖模块一起装载
卸载驱动模块
rmmod
-f强制卸载
printk:在内核空间中运行的输出,和printf类似,就是添加了输出优先级
KERN_EMERG"<0>"/*紧急事件消息,系统崩溃之前提示,表示系统不可用*/
KERN_ALERT"<1>"/*报告消息,表示必须立即采取措施*/
KERN_CRIT"<2>"/*临界条件,通常涉及严重的硬件或软件操作失败*/
KERN_ERR"<3>"/*错误条件,驱动程序常用KERN_ERR来报告硬件的错误*/
KERN_WARNING"<4>"/*警告条件,对可能出现问题的情况进行警告*/
KERN_NOTICE"<5>"/*正常但又重要的条件,用于提醒。常用于与安全相关的消息*/
KERN_INFO"<6>"/*提示信息,如驱动程序启动时,打印硬件信息*/
KERN_DEBUG"<7>"/*调试级别的消息*/
dmesg查看所有的输出,-c 查看并清除,-n 查看该优先级的输出
current变量:<linux/sched.h>,显示当前进程的一些信息
一个模块需要导出到另外的模块时,可以添加:
EXPORT_SYMBOL(name);//导出到任意模块
EXPORT_SYMBOL_GPL(name);//只能导出到GPL许可的模块
MODULE_LICENSE("GPL");//许可凭证,如果没有标记许可,内核就会觉得被"变脏"了
其他的凭证还有
"GPL v2" gpl第二版本
"GPL and additional rights" gpl及附加权利
"Dual BSD/GPL" 双重许可
"Dual MPL/GPL" 双重许可
"Proprietary" 专有
MODULE_AUTHOR("表明模块的作者");
MODULE_VERSION("代码修订号");
MODULE_ALIAS("别名");
MODULE_DEVICE_TABLE("所支持的设备");
MODULE_DESCRIPTION("描述模块的作用");
模块的一般初始化函数
static int __init function_name(void){
}
module_init(function_name);//调用该初始化函数
模块的一般清除函数
static void __exit function_name(void){
}
module_exit(fuction_name);
模块随时会出错,出错以后必须将之前申请的资源全部释放.可以使用goto语句方便错误处理.
<linux/moduleparam.h>
module_param(variable,type,perm);
用来创建模块参数的宏,用户可以在装载时,调整这些参数
参数:变量,数据类型,访问控制
访问控制:
S_IRUGO:可读不可写
S_IRUGO | S_IWUSR 超级用户可以修改
0:不会有对应的sysfs入口,否则参数模块会在/sys/module中出现.如果在sys中被修改,也会同样修改相应的值
/sys/module:当前已装载模块的信息的目录