时间差不多该开始学驱动编程了, 本节开始记录学习驱动的过程。Linux设备驱动会以内核模块的形式出现,因此,在进行学习驱动编程之前,首先要有关于模块和内核编程的概念.。开发这样的专门技术对任何类型的模块化的驱动都是重要的基础.。但是,处于学习驱动编写的目的, 一个标准内核是最好的。不管你的内核来源,,建立 2.6.x 的模块需要你有一个配置好并建立好的内核树在你的系统中(如果是交叉测试,应该和你的板子跑的是一样的内核)。因为我们稍后会见到的原因,生活通常是最容易的如果当你建立模块时真正运行目标内核,尽管这不是需要的。
一、Linux内核模块简介
Linux 内核的整体结构非常庞大,其包含的组件也非常多。我们怎样把需要的部分都包含在内核中呢?
一种方法是把所有需要的功能都编译到Linux内核。这会导致两个问题,一是生成的内核会很大,二是如果我们要在现有的内核中新增或删除功能,将不得不重新编译内核。有没有一种机制使得编译出的内核本身并不需要包含所有功能,而在这些功能需要被使用的时候,其对应的代码可被动态加载到内核中呢?Linux提供了这样的一种机制,这种机制被称为模块(Module),可以实现以上效果。模块具有以下特点:
(1)模块本身不被编译入内核映像,从而控制了内核的大小;
(2)模块一旦被加载,它就和内核中的其它部分完全一样。
二、 配置编译好一个可执行的内核,并跑到你的板子上
在这里我使用的是Linux2.6.22.6的内核,通过打补丁的方式编译生成uImage。
(1)解压,并打补丁
$tar xjf linux-2.6.22.6.tar.bz2
$cd Linux2.6.22.6
$patch -p1 < ../linux-2.6.22.6_jz2440.patch
(2)配置内核
2.1使用厂家提供的配置文件(在这里我使用的)
cp config_ok .config
make menuconfig(记得把dm9000去掉,选上cs89x0)
2.2使用内核里面默认的配置文件, 在它的基础上配置
find -name "*defconfig" | grep "2410"
make s3c2410_defconfig
make menuconfig
2.3从头开始,自己配置
make menuconfig
注意:如果make menuconfig出错,说是找不到ncurse.h,要安装ncurse开发包:(apt-get install ncurses-dev)
(3)修改Makefile
186 ARCH ?= arm
187 CROSS_COMPILE ?= arm-linux-gnu-
(4) 编译内核:make, make uImage
$make uImage
如果最后说 mkimage not found,从u-boot的tools目录下把mkimage复制到/bin目录去,再执行 make uImage
到这里就已经配置编译了一个可以使用的内核了,把uImage拷贝到你的/tftpboot目录下让你的板子尝尝。这个编译过的内核我是放在/home/kernel/linux2.6.22.6下,后面写驱动都会用到这个。
三、字符设备驱动Hello World
首先在主机端操作,在NFS文件系统中,我的在/rootfs/filesystem中创建一个/work/hello目录,在里面创建hello.c驱动程序,test.c测试程序,Makefile文件。然后启动开发板挂载网络文件系统就可以直接测试我们的Hello World啦!
字符设备程序:hello.c
#include <linux/fs.h>
ssize_t hello_read(struct file *file, char __user *buf, size_t size, loff_t *offset) ssize_t hello_write(struct file *file, const char __user *buf, size_t size, loff_t *offset) static const struct file_operations hello_fops = {
printk(KERN_INFO "hello_init!\n");(KERN_INFO为printk的优先级) void hello_exit(void) printk(KERN_INFO "hello_exit!\n"); /*宏定义, 声明一个模块的初始化和清理函数*/ MODULE_LICENSE("Dual BSD/GPL"); |
测试程序:test.c
#include <stdio.h> int main(int argc, char **argv) fd = open("/dev/hello", O_RDWR); ret = read(fd, buf, 10); |
Makefile文件
obj-m += hello.o KDIR := /home/kernel/linux-2.6.22.6(刚才编译好的内核,你换成你主机上存放的路径,其中uImage跑在板子上) build: |
现在开始编译我们的第一个字符设备驱动程序-hello.c
$make
$ls
hello.c hello.mod.c hello.o Makefile
hello.ko hello.mod.o test.c Module.symvers
再来交叉编译我们的测试程序
$arm-linux-gnu-gcc test.c -o test
$ls
hello.c hello.mod.c hello.o Makefile test
hello.ko hello.mod.o test.c Module.symvers
打开putty终端,把上面编译好的hello程序包
#cd /work/hello
#ls
hello.c hello.mod.c hello.o Makefile test
hello.ko hello.mod.o test.c Module.symvers
#insmod hello.ko
#hello_init!
# lsmod
Module Size Used by Not tainted
hello 1920 0
#cat /proc/devices
99 ppdev
100 hello
116 alsa
128 ptm
136 pts
180 usb
189 usb_device
204 s3c2410_serial
253 usb_endpoint
254 rtc
测试我们的hello程序,首先得手动创建设备节点
当我们没创建时
# ./test
can't open /dev/hello
(/dev/hello为设备节点,c 代表字符设备,100 为主设备号,0 次设备号)
# mknod /dev/hello c 100 0
# ls /dev/hello
/dev/hello
# ./test
hello_open ***
hello_read ***
ret = 0
#
卸载设备驱动程序模块
# rmmod hello
#hello_exit!