前两篇博文说了一下写驱动之前的一些基础知识,并不全面,这篇博文以及后面的博客会以一个具体的例子来具体说明驱动的一些知识,在这里将编写一个字符设备驱动例
程。这个字符设备与任何具体的硬件设备无关,它的功能是将应用程序写入的数据保存在一小段内存中,当应用程序读设备时也从这块内存中获取数据。
我们将这个例程命名为Kpad,源码中与设备相关的标识符的命名都加上了kpad作为前缀,这样做的好处是避免与内核定义的标识符重名。
前面的博文已经提到写驱动时 固定模式(40%) 内核相关知识(30%) 硬件知识(30%),下面为内核模块编程必须依照的一个固定模式:
下面来一行一行的解释:
1~4行为头文件,注意:编译内核模块时头文件不是在系统头文件目录中搜索的,而是在内核源码的include目录中。第1行为模块编程需要的头文件,第2行为设备号、文件操纵接口所需的,第3行为字符设备编程接口所需的,第4行为访问用户态内存接口。
7~11行为对模块的描述,
第7行必须有,8~11为模块可选参数,即可以不写。 第7行将代码的版权声明为GPL,因为GUN/Linux 内核本身是GPL版权,内核模块被认为是在内核的基础上进行的二次开发,因此必须遵循GPL版权,如果模块中没有版权声明,则加载时可能会被内核拒绝。版权声明放在内核源码中的任何位置都可以。8~11分别定义模块的作者,对模块的描述,模块版本,模块别名。
13~15为模块的初始化函数(此处函数体没写,后面补齐),与应用程序不同,内核模块没有入口函数main,而有一个初始化函数。初始化函数只在模块加载时被调用一次。其中的__init是可选的(最好写上),它告诉编译器将此函数放在一个特殊的代码段上,当模块加载成功后,这个代码段就被释放掉以节约内存空间。
初始化函数必须有一个int型的返回值,返回0代表成功,否则表示初始化失败,按惯例内核的函数失败时返回一个负数,代表失败的原因,称为错误码。 内核的错误码都以宏的形式定义在头文件中,这些宏定义都是正数,所以返回时要加一个负号(后文会再次提到)。
21行是一个宏,它的作用是将所给出的函数指定的为模块的初始化函数,必须加上这一行,这样13行的函数才成功设置成初始化函数。
17~19为模块的退出函数。退出函数只在模块被卸载时调用一次。其中的__exit是可选的(最好写上),它告诉编译器,如果模块被编译进内核,则这个函数根本不需要编译成目标代码,因为在这种情况下不存在不存在卸载操作。
22行是一个宏,它的作用是将所给出的函数指定的为模块的退出函数,必须加上这一行,这样17行的函数才成功设置成退出函数。
注意:内核编程时,因为编译器的语法比较严格,最好加上void参数,否则可能会提示错误
至此内核模块的模式也就写完了,就剩完善了,驱动编写时的40%的模式,这里只表现了一部分,剩下的后面会慢慢到来,嘿嘿!
用一个结构体存储kpad设备的所有信息,具体如下:
接着要表明这个设备所支持的文件操作,和定义这个设备:
注意,file_operations 结构体中 赋值时是逗号,不是分号;
好了,这篇博客就说这么多,欲知后事如何,且看下回分解,嘿嘿!
阅读(1162) | 评论(0) | 转发(0) |