首先选一个典型的例子,一个外部驱动模块,需要传入两个参数,然后通过cat /proc/result 查看他们的和
/*
test_inline_driver.c
这是一个例子,把模块参数val1+ val2的和通过 cat /proc/result 显示出来
*/
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/errno.h>
#include <linux/types.h>
#include <linux/fcntl.h>
#include <asm/uaccess.h>
#include <asm/io.h>
#include <linux/stat.h>
#include <linux/proc_fs.h>
struct proc_dir_entry *sumproc_result_fp=NULL;
int val1=0,val2=0;
module_param(val1,int,S_IRUGO);
module_param(val2,int,S_IRUGO);
MODULE_DESCRIPTION("My kernel module");
MODULE_AUTHOR("deep_pro");
MODULE_LICENSE("GPL");
int read_result(char *page,char **start,off_t off,int count,int *eof,void *da
{
char *buf;
buf=page;
buf+=sprintf(buf,"result %d+%d = %d \n",val1,val2,val1+val2);
*eof=1;
return buf-page;
}
static int test_inline_driver_init_module(void)
{
printk( KERN_DEBUG "Module test_inline_driver init\n" );
sumproc_result_fp=create_proc_entry("result",S_IFREG|S_IRUSR,NULL);
if(sumproc_result_fp!=NULL)
{
sumproc_result_fp->read_proc=read_result;
}
return 0;
}
static void test_inline_driver_exit_module(void)
{
printk( KERN_DEBUG "Module test_inline_driver exit\n" );
remove_proc_entry("result",0);
}
#ifndef MODULE
static int __init get_val(char *str)
{
int ints[10];
str=get_options(str,ARRAY_SIZE(ints),ints);
if(ints[0]==2)
{
val1=ints[1];
val2=ints[2];
}
return 1;
}
__setup("getval=",get_val);
__initcall(test_inline_driver_init_module);
#else
module_init(test_inline_driver_init_module);
#endif
module_exit(test_inline_driver_exit_module);
可以看到,在代码上有两个要求
首先,编译进内核的模块就不能靠module_init来启动了,需要__initcall
其次,编译进内核的模块不能靠insmod命令这样的方式传入参数,只能通过__setup读取linux启动参数
所以需要靠#ifndef MODULE 来判断当前模块是编译成外部模块还是直接编译进内核
然后就需要修改对应驱动目录下的Kconfig和Makefile文件,这个例子放在/linux-2.6.29/drivers/char 目录下
对于Kconfig,打开后在靠前的位置进行如下修改后的前20行
#
# Character device configuration
#
menu "Character devices"
config test_inline_driver
tristate "test_inline_driver is my test"
---help---
this is a sample driver,to test building a driver into kernel
config VT
bool "Virtual terminal" if EMBEDDED
depends on !S390
select INPUT
default y
---help---
If you say Y here, you will get support for terminal devices with
display and keyboard devices. These are called "virtual" because you
can run several virtual terminals (also called virtual consoles) on
其中加粗的是自己添加的内容, config选项定义了符号值
tristate命令可以选择是Y还是M
这样的修改,make menuconfig可以看到
接下来修改Makefile,修改后前20行
#
# Makefile for the kernel character device drivers.
#
#
# This file contains the font map for the default (hardware) font
#
FONTMAPFILE = cp437.uni
obj-y += mem.o random.o tty_io.o n_tty.o tty_ioctl.o tty_ldisc.o tty_buffer.o tty_port.o
obj-$(CONFIG_test_inline_driver) += test_inline_driver.o
obj-$(CONFIG_LEGACY_PTYS) += pty.o
obj-$(CONFIG_UNIX98_PTYS) += pty.o
obj-y += misc.o
obj-$(CONFIG_VT) += vt_ioctl.o vc_screen.o selection.o keyboard.o
obj-$(CONFIG_CONSOLE_TRANSLATIONS) += consolemap.o consolemap_deftbl.o
obj-$(CONFIG_HW_CONSOLE) += vt.o defkeymap.o
obj-$(CONFIG_AUDIT) += tty_audit.o
其中的加粗的一行是自己加的
很容易理解,obj-y是无条件一定会编译进内核的,obj-$(CONFIG_xxxx)是编译成xxxx.o但会不会链接进内核得看make menuconfig的选择的,obj-m就一定会编译成外部模块
好了,准备工作完成,make之后修改启动linux参数,在最后加上 getval=1,2
如mini2440是noinitrd root=/dev/mtdblock2 init=/linuxrc console=ttySAC0 getval=1,2
测试结果
下面以uClinux为例,介绍在一个以模块方式出现的驱动程序test.c基础之上,将其编译进内核的一系列步骤:
(1) 改动test.c源带代码
第一步,将原来的:
#include |
改动为:
#ifdef MODULE #include #include char kernel_version[]=UTS_RELEASE; #else #define MOD_INC_USE_COUNT #define MOD_DEC_USE_COUNT #endif |
第二步,新建函数int init_test(void)
将设备注册写在此处:
result=register_chrdev(254,"test",&test_fops); |
(2) 将test.c复制到/uclinux/linux/drivers/char目录下,并且在/uclinux/linux/drivers/char目录下mem.c中,int chr_dev_init( )函数中增加如下代码:
#ifdef CONFIG_TESTDRIVE |
(3) 在/uclinux/cinux/drivers/char目录下Makefile中增加如下代码:
ifeq($(CONFIG_TESTDRIVE),y) L_OBJS+=test.o Endif |
(4) 在/uclinux/linux/arch/m68knommu目录下config.in中字符设备段里增加如下代码:
bool 'support for testdrive' CONFIG_TESTDRIVE y |
(5) 运行make menuconfig(在menuconfig的字符设备选项里你可以看见我们刚刚添加的'support for testdrive'选项,并且已经被选中);make dep;make linux;make linux.text;make linux.data;cat linux.text linux.data > linux.bin.
(6) 在 /uClinux/romdisk/romdisk/dev/目录下创建设备:
mknod test c 254 0 |
并且在/uclinux/appsrc/下运行make,生成新的Romdisk.s19文件。
到这里,在uClinux中增加设备驱动程序的工作可以说是完成了,只要将新的linux.bin与Romdisk.s19烧入目标板中,你就可以使用自己的新设备test了。
驱动模块可以内核编译好后动态加载进去,也可以在编译内核的时候就直接添加。下面是将驱动程序静态编译进内核的方法:
以一个字符设备为例:
1.修改/drivers/char下的Kconfig文件
在Kconfig中增加如下代码:
config MY_HELLO
bool "this is test"
a. 保存后回到内核根目录进行make menuconfig 你会在字符驱动选项中得到如下图,选择保存退出
b. 在内核根目录的.config 文件中你会发现
CONFIG_MY_HELLO=y
2.修改/drivers/char下的Makefile文件,增加如下
obj-$(CONFIG_MY_HELLO) += my_hello.o
当然前提是你的my_hello.c必须放在当前的目录了
END