## 概述
一个驱动模块主要由以下部分组成:
- 头文件(必选)
- 模块参数(可选)
- 模块功能函数(可选)
- 其他(可选)
- 模块加载函数(必须)
- 模块卸载函数(必须)
- 模块许可声明(必须)
一个规范的驱动模块应该包含上图所示的结构,这些结构在图中的顺序也是在源文件中的顺序。
## 头文件
驱动模块会使用到内核中的许多函数,其中有两个头文件是必须包含的:
```c
#include /* 包含了加载模块时需要使用的大量符号和函数定义 */
#include /* 包含了模块加载函数和模块卸载函数的宏定义 */
```
## 模块参数
模块参数是驱动模块加载时,传递给它的参数。如果一个模块需要实现两种类似的功能,可以通过传递一个参数到驱动模块,以决定其使用哪一种功能。
如前文图片所示,模块参数是可选的。
### 模块参数的指定
参数需要在加载模块时指定,如`insmod module.ko param_1=value_1 param_2=value_2`。
### 模块参数的定义
可以用`module_param(name, type, mode)`来为模块定义参数,例如下列代码定义了一个整型参数:
```c
static int a = 1;
module_param(a, int, S_IRUGO);
```
参数的数据类型可以是:
|可用数据类型|含义|
|--|--|
|byte|字节类型|
|short|短整型|
|ushort|无符号短整型|
|int|整型|
|uint|无符号整型|
|long|长整型|
|ulong|无符号长整型|
|bool|布尔类型
|charp|字符指针|
由于内核不能完美地支持浮点数操作,因此模块参数的类型中没有浮点类型。
## 模块加载函数
模块加载函数是模块加载时,由操作系统执行的函数。通过使用宏`module_init(your_init_func)`来指定。模块的初始化函数必须符合下面的形式:
int my_init(void);
因为初始化函数通常不会被外部函数调用,所以不必导出该函数,可以将它标记为`static`。初始化函数会返回一个整型值,如果初始化顺利完成,那么它返回零;否则返回非零值。
## 模块卸载函数
模块退出函数是模块卸载时,由操作系统执行的函数。通过使用宏`module_exit(your_exit_func)`来指定。模块的退出函数必须符合如下形式:
void my_exit(void);
与初始化函数一样,可以将它标记为`static`。
## 模块许可声明
模块许可声明表示模块受内核支持的程度。有许可权的模块会更受到开发人员的重视。如果一个模块没有包含任何许可权,那么就会认为是不符合规范的。这时,内核加载这种模块时,会收到内核加载了一个非标准模块的警告。开发人员不喜欢维护这种没有遵循许可权标准的内核模块。
使用宏`MODULE_LICENSE`指定模块的许可权,内核可以识别的许可权限有:
![](https://leanote.com/api/file/getImage?fileId=5889792aab6441508e000f49)
## 其他
另外,`MODULE_AUTHOR()`宏和`MODULE_DESCRIPTION()`宏指定了代码作者和模块的简要描述,它们完全是用作信息记录目的的。