一、向内核中添加驱动步骤
在向内核中添加驱动的时候要完成3 项工作,包括:
1、在Kconfig 中添加新代码对应项目的编译条件
2、将驱动源码添加到对应的目录中
3、在目录Makefile 中文件中增加针对新代码的编译条目
二、编译器路径的设置
1、打开家目录下的.bashrc文件,在其最下面添加如下内容:
export PATH=$PATH:/home/mint/itop/arm-2009q3/bin
使其生效
. .bashrc
2、该目录与交叉编译器的解压放置位置相对应
/home/mint/itop/arm-2009q3/bin

3、打开内核源码的顶层目录的Makefile文件
在195行的位置有如下内容
export KBUILD_BUILDHOST := $(SUBARCH)
ARCH ?= arm
CROSS_COMPILE ?= /home/mint/itop/arm-2009q3/bin/arm-none-linux-gnueabi-
#CROSS_COMPILE ?= /usr/local/arm/4.5.1/bin/arm-none-linux-gnueabi-
CROSS_COMPILE ?= $(CONFIG_CROSS_COMPILE:"%"=%)
该交叉编译工具链的位置与上面是一一对应的。
三、使用make zImage编译内核执行过程
在这里以BUZZER的驱动为例。
1、在make zImage命令执行之后,它会首先找到源码顶层目录下的Makefile文件,运行Makefile 文件之后,它会在Makefile 文件中找到编译器的路径,然后系统根据环境变量找到“export PATH=$PATH/home/mint/itop/arm-2009q3/bin”编译器的路径。
2、在driver/char/ 目录下,暂时关注以下三个文件
itop4412_buzzer.c //led 驱动的源码
itop4412_buzzer.o //生成最终zImage 二进制的中间文件
Makefile //make执行时所需要的文件
3、打开该目录下的Makefile文件
obj-$(CONFIG_BUZZER_CTL) += itop4412_buzzer.o

如果想添加类似的字符驱动,就可以在这个目录下添加。
四、Makefile 脚本语法简介
1、强制编译进内核
obj-y += misc.o

2、条件编译
obj-$(CONFIG_BUZZER_CTL) += itop4412_buzzer.o
需要在Kconfig 中定义,在menuconfig中配置之后,编译器运行的时候找到对应的宏变量CONFIG_BUZZER_CTL之后才会编译。
3、Makefile的层层调用
打开driver/下的Makefile文件,在第33行中
obj-y += char/
“加等号”右边有文件变为了文件夹。这里表示强制编译当前目录“/drivers”的下一级目录“/char”。在执行编译命令执行到这一句的时候,就会先跳转到“/char”目录下的“Makefile”文件。

五、Makefile 的测试
目标:通过配置menuconfig 中的SKYFALL,来将内核编译进内核或者不编译进内核。
实现步骤:
(1) 在源码目录下的driver/char目录下编写itop4412_skyfall.c源文件,文件内容如下。最后在/dev下显示的设备节点的名字为skyfall_007。
#include <linux/init.h>
#include <linux/module.h>
/*驱动注册的头文件,包含驱动的结构体和注册和卸载的函数*/
#include <linux/platform_device.h>
/*注册杂项设备头文件*/
#include <linux/miscdevice.h>
/*注册设备节点的文件结构体*/
#include <linux/fs.h>
#define DRIVER_NAME "skyfall"
#define DEVICE_NAME "skyfall_007"
MODULE_LICENSE("Dual BSD/GPL");
MODULE_AUTHOR("SKYFALL");
//long (*unlocked_ioctl) (struct file *, unsigned int, unsigned long);
//驱动实际操作的实现
static long skyfall_ioctl( struct file *file, unsigned int cmd, unsigned long arg){
printk("%s,%d\n",__func__,__LINE__);
printk("cmd is %d,arg is %d\n",cmd,arg);
return 0;
}
//int (*release) (struct inode *, struct file *);
//关闭该驱动的实现
static int skyfall_release(struct inode *inode, struct file *file){
printk("%s,%d\n",__func__,__LINE__);
printk(KERN_EMERG "skyfall release\n");
return 0;
}
//int (*open) (struct inode *, struct file *);
//打开该驱动的实现
static int skyfall_open(struct inode *inode, struct file *file){
printk("%s,%d\n",__func__,__LINE__);
printk(KERN_EMERG "skyfall open\n");
return 0;
}
//定义一个字符设备操作集合
static struct file_operations skyfall_ops = {
.owner = THIS_MODULE,
.open = skyfall_open,
.release = skyfall_release,
.unlocked_ioctl = skyfall_ioctl,
};
//定义一个杂项设备结构体
static struct miscdevice skyfall_dev = {
.minor = MISC_DYNAMIC_MINOR,
.name = DEVICE_NAME,
//字符设备操作集合,需要提前定义一个,然后取地址
.fops = &skyfall_ops,
};
static int skyfall_probe(struct platform_device *pdv){
printk(KERN_EMERG "initialized\n");
printk("%s,%d\n",__func__,__LINE__);
//注册杂项设备,需要一个传一个杂项设备的结构体,所以需要提前定义一个,然后取地址
misc_register(&skyfall_dev);
return 0;
}
static int skyfall_remove(struct platform_device *pdv){
printk(KERN_EMERG "\tremove\n");
//注销杂项设备
misc_deregister(&skyfall_dev);
return 0;
}
static void skyfall_shutdown(struct platform_device *pdv){
;
}
static int skyfall_suspend(struct platform_device *pdv,pm_message_t pmt){
return 0;
}
static int skyfall_resume(struct platform_device *pdv){
return 0;
}
struct platform_driver skyfall_driver = {
.probe = skyfall_probe,
.remove = skyfall_remove,
.shutdown = skyfall_shutdown,
.suspend = skyfall_suspend,
.resume = skyfall_resume,
.driver = {
.name = DRIVER_NAME,
.owner = THIS_MODULE,
}
};
static int __init skyfall_init(void)
{
int DriverState;//定义驱动状态,判断驱动是否注册成功
printk(KERN_EMERG "skyfall driver enter!\n");
printk("%s,%d\n",__func__,__LINE__);
DriverState = platform_driver_register(&skyfall_driver);
/*
驱动一旦注册成功,会与设备进行匹配,匹配成功是由
platform_match函数进行匹配,驱动调用probe初始化函数
*/
printk(KERN_EMERG "DriverState = %d\n",DriverState);
return 0;
}
static void __exit skyfall_exit(void)
{
printk(KERN_EMERG "skyfall driver exit!\n");
platform_driver_unregister(&skyfall_driver);
}
module_init(skyfall_init);
module_exit(skyfall_exit);
以上这些需要配合后面的注册设备与注册驱动相结合。
(2)打开源码目录下的driver/char目录下Makefile文件,添加编译选项
obj-$(CONFIG_SKYFALL_CTL) += itop4412_skyfall.o

(3)打开源码目录下的driver/char目录下Kconfig文件,添加菜单选项
config SKYFALL_CTL
bool "Enable SKYFALL config"
default y
help
Enable SKYFALL config!

(4)打开menuconfig界面,将Device Drivers —> Character devices—> [] Enable SKYFALL config (NEW) 添加 。

添加完毕后,保存,生成新的.config文件。
(5)查看.config文件
CONFIG_SKYFALL_CTL=y

(6)编译内核 sudo make zImage,查看有没有生成itop4412_skyfall.o中间文件

编译内核


(7)将新生成的内核烧写至开发板中
cp arch/arm/boot/zImage /mnt/share/
(8)在开发板的/dev目录中产生itop4412_skyfall.c 驱动的设备节点skyfall_007。在源码的driver/char目录下,生成了对应的中间文件。

(9)以上是生成skyfall设备节点的步骤。
(10)打开menuconfig界面,将Device Drivers —> Character devices—> [ ] Enable SKYFALL config 去掉 。

保存并退出,生成新的.config文件。
(11)打开.config文件,发现配置成功。
# CONFIG_SKYFALL_CTL is not set

(12)再次编译内核,发现没有中间文件itop4412_skyfall.o生成。
sudo make zImage

(13)将新生成的内核烧写至开发板中
cp arch/arm/boot/zImage /mnt/share/
(14)在开发板的/dev目录中没有产生itop4412_skyfall.c 驱动的设备节点skyfall_007。


(15)至此,整个过程全部完成。
六、内核编译的全部过程

1、红色的线条表示配置文件Kconfig 这一部分,在Kconfig 中要定义针对具体驱动文件的宏变量。然后使用menuconfig 工具生成新的.config文件。
2、黑色的线条表示编译文件Makefile 这一部分,在Makefile 中针对宏变量编译驱动文件。
3、执行make 命令之后,调用.config文件,配合各级目录中的Makefile 文件编译具体的驱动源代码,将源代码编译成.o中间文件。
4、当中间文件全部编译完成之后,编译工具会生成一个非常精炼的zImage二进制文件。