创建一个字符设备1.1
在开始之前,我们先了解什么是内核模块。
内核模块是一种没有经过链接,不能独立运行的目标文件,是在内核空间运行的程序。
内核模块可以让操作系统内核需要的时候加载,不需要的时候由操作系统进行卸载。拓展了操作系统的功能,不会让操作系统变得很臃肿。
------------------------------------------------------------------------------------------------------------------------------------------------
内核模块与应用程序区别
应用程序 驱动程序
运行 用户空间 内核空间
入口 main module_init(初始化函数)
出口 无 module_exit(卸载函数)
编译 gcc -c Makefile
链接 ld insmod
运行 ./ insmod
由上面阐述可知,应用程序和驱动程序存在不同空间,操作的方法也不同。而且,又由于驱动是内核的一部分。
所以,我们在编写驱动程序的时候要非常仔细,像不做出错处理、忘记释放内存等动作在我们的驱动程序的编写中都是不允许的!
========================================================================================
下面,我们来说下如何创建一个简单的字符设备驱动。
1. 我们在写驱动时,首先要查阅芯片厂商提供的源代码。看它如何创建,然后我们依葫芦画瓢地进行我们驱动的编写。如本人用的是三星公司的S5PV210 cortex-A8架构的芯片。
2. 创建一个设备初始化函数、设备退出函数 如下:
int __init gec210_led_init(void)
{
printk("hello gec210 led driver\n");
return 0;
}
void __exit gec210_led_exit(void)
{
printk("exit gec210 led driver\n");
}
其中: 我们的加载指令insmod 卸载指令rmmod分别对应如下函数
module_init(gec210_led_init); //insmod
module_exit(gec210_led_exit); //rmmod
最后,我们需要加上设备的相关信息
MODULE_AUTHOR("Mr.Jin_Fa"); 作者
MODULE_DESCRIPTION("S5PV210 LED driver"); 功能描述
MODULE_LICENSE("GPL"); 设置宏定义,遵守GPL证书 如不写或写其他参数,编译可能会失败。
以上代码能实现最基本的加载、卸载驱动过程。 不过这里有几点需要注意的
1)不能使用C语言的库函数
printf memcpy memset .......
2)使用GNU C语言,会跟ANSI C语言有所出入,如在初始化结构体的方法上。
3)内核编程的时候不要使用浮点数,因为浮点数的运算需要库的支持 (本人之前在做ARM裸机开发的时候,就遇到过不支持浮点运算的问题
解决办法是 在Makefile上加上arm-linux-gcc 库的路径,并且在主函数那里加一个空的raise()函数)
-------------------------------------------------------------------------------------------------------------------------------------------------------------
用于管理编译模块的Makefile的编写
obj-m +=led_drv.o 将led_drv.c文件编译为led_drv.o文件(通过查找下面的规则),然后将led_drv.o文件再链接成一个独立的module(led_drv.ko)
KERN_DIR=/home/gec/android-kernel-samsung-dev 定义了一个已经正确编译过的内核源代码的路径,编译过程中,要去内核源代码拿编译工具,使用相应的头文件。
PWD:=$(shell pwd) 指出当前路径
modules: // 目标moudules 的生成规则如下
$(MAKE) -C $(KERN_DIR) M=$(PWD) modules 编译的时候,去内核源代码目录下,找编译工具(Makefile、Kbuild)和头文件;然后回到当前路径下,将驱动文件编译为一个module。
clean: 将过程文件和 .ko模块文件clean掉
$(MAKE) -C $(KERN_DIR) M=$(PWD) modules clean
写完管理编译工具Makefile后 我们就可以编译我们的模块了
=================================================================================================
实现效果
gec@ubuntu:/mnt/hgfs/share/001/demo1$ make
make -C /home/gec/android-kernel-samsung-dev M=/mnt/hgfs/share/001/demo1 modules
make[1]: Entering directory `/home/gec/android-kernel-samsung-dev' 这里可以看到 Makefile进入我们制定的源代码路径找工具...
CC [M] /mnt/hgfs/share/001/demo1/led_drv.o
Building modules, stage 2.
MODPOST 1 modules
CC /mnt/hgfs/share/001/demo1/led_drv.mod.o 生成 .o 文件
LD [M] /mnt/hgfs/share/001/demo1/led_drv.ko 连接成 .ko文件
make[1]: Leaving directory `/home/gec/android-kernel-samsung-dev'
编译完后,在ubuntu当中查看驱动信息
gec@ubuntu:/mnt/hgfs/share/001/demo1$ modinfo led_drv.ko
filename: led_drv.ko
license: GPL
description: S5PV210 LED driver
author: Mr.Jin_Fa
depends:
vermagic: 2.6.35.7-GEC210 preempt mod_unload ARMv7
然后愉快地上传到开发板看看...
在开发板测试与卸载驱动
[root@GEC210 /]# insmod led_drv.ko //加载驱动
[ 58.687003] hello gec210 led driver
[root@GEC210 /]# lsmod //显示当前的led_drv.ko是否加载成功
led_drv 540 0 - Live 0xbf011000
...........
[root@GEC210 /]# rmmod led_drv //卸载驱动
[ 102.104536] exit gec210 led driver
PS: 这里能正常运行是因为 我们开发板的内核版本、本地版本和 编译模块是一致的,这是保证模块和内核的兼容性,是一种安全措施。
版本为:2.6.35.7-GEC210 2.6.35.7 为内核版本 -GEC210 为本地版本
如何写一个硬件相关的字符设备驱动,在《创建一个字符设备1.2》这篇细讲...