一、驱动程序的特点
二、设备驱动分类和内核模块
三、字符设备
四、块设备
五、网络设备驱动和网络接口
六、设备文件和设备驱动
很明显,操作系统内部不可能用设备文件名来与物理设备及其驱动进行绑定。其实,操作系统内部是用设备号来与物理设备及其驱动进行绑定的。习惯上,用主设备号与驱动进行关联,用次设备号与具有相同驱动的不同物理设备关联(例如:2个硬盘)。
dennis@dennis-desktop:~$ ls -l /dev/sd[a-c]
brw-rw—- 1 root disk 8, 0 2010-04-13 13:38 /dev/sda
brw-rw—- 1 root disk 8, 16 2010-04-13 13:38 /dev/sdb
brw-rw—- 1 root disk 8, 32 2010-04-13 13:38 /dev/sdc
当用户程序运行open("/dev/ttyS0",…)时,由于设备文件/dev/ttyS0有一个设备号与其关联,因此操作系统可以获知应用程序想操控的设备的设备号,而操作系统内部又将设备号与物理设备及其驱动进行了绑定,因此操作系统就可以知道应该调用哪一个驱动去控制哪一个设备。当然这一切的前提是,操作系统内部要将设备号与物理设备及其驱动进行绑定,那么操作系统内部是用什么手段完成这种绑定关系的呢?实际上,在操作系统内部存在一个结构体链表(就是上图中的Char device list,以后称它为设备链表),链表的每个节点代表一个绑定关系(也就是说:节点至少含有2个字段,1个用于记录设备号,另1个用于记录寻找驱动的信息,通常是一个指向驱动函数结构体的指针)。那么是谁生成节点并将它链入链表的呢?当然是驱动程序!
七、构造和运行模块
1、Kernel Module的特点
2、模块与内核的接口函数(除掉read、write等功能函数)
生成节点并将它链入设备链表这个操作由驱动中的函数实现,这些函数什么时机运行呢?当然最合适的时机是内核加载模块(insmod 模块)的时候。
3、操作模块相关的命令
# insmod /lib/modules/hello.ko
Hello, world
# rmmod hello
Goodbye, cruel world
#cat /lib/modules/2.6.22.6/modules.dep
/lib/modules/s3c24xx_buttons.ko: /lib/modules/leds.ko
/lib/modules/leds.ko:
# lsmod
Module Size Used by Not tainted
# modprobe s3c24xx_buttons
leds initialized
buttons initialized
# lsmod
Module Size Used by Not tainted
s3c24xx_buttons 5944 0
leds 3592 1 s3c24xx_buttons
# rmmod leds
rmmod: leds: Resource temporarily unavailable
# rmmod s3c24xx_buttons
buttons driver unloaded
# lsmod
Module Size Used by Not tainted
leds 3592 0
# rmmod leds
leds driver unloaded
# lsmod
Module Size Used by Not tainted
# insmod s3c24xx_buttons
s3c24xx_buttons: Unknown symbol ledoff
s3c24xx_buttons: Unknown symbol ledon
insmod: cannot insert ‘/lib/modules/s3c24xx_buttons.ko’: Unknown symbol in module (-1): No such file or directory
dennis@dennis-desktop:/work/studydriver/buttons$ modinfo s3c24xx_buttons.ko
filename: s3c24xx_buttons.ko
license: GPL
description: S3C2410/S3C2440 BUTTON Driver
author: YangZhu E-mail: [email protected]
depends:
vermagic: 2.6.22.6 mod_unload ARMv4
4、内核模块的编译方法
内核源码树:指的是内核源代码tar包解压缩后形成的目录(包含其下级所有目录和文件)
已编译内核源码树:指的是已经成功生成过内核的内核源码树(即:已经成功执行过make uImage的内核源码树)
驱动大多都编译为模块,2.6内核中要想编译模块,必须先存在已经成功编译了的内核源码树(即:已编译内核源码树),且该源码树编译出来的内核就是该模块即将运行在其上的内核。
编译方法1:
dennis@dennis-desktop:/work/studydriver/examples/misc-modules$ make -C /work/system/linux-2.6.22.6/ M=`pwd` modules
对该make命令的解释:
要想编译内核模块,只需要在内核源码树的顶层目录下输入make modules来编译Makefile中的modules目标即可,剩下的事情,由内核构造系统全权替我们处理。但由于目前不处于内核源码树的顶层目录,并且当前目录下的Makefile也没有modules目标,因此使用-C参数来告知make程序需要在执行之前切换到/work/system/linux-2.6.22.6/目录。此外,由于模块的源代码在当前目录中,不在内核源码树中,因此需要使用M变量(该变量是内核构造系统的变量)告知内核构造系统,编译模块所需的源代码以及Makefile在当前目录(/work/studydriver/examples/misc-modules)中来找,而且最终生成的模块ko文件也要放在当前目录中。
编译方法2:
ifeq ($(KERNELRELEASE),)
KERNELDIR ?= /work/system/linux-2.6.22.6
PWD := $(shell pwd)
modules:
$(MAKE) -C $(KERNELDIR) M=$(PWD) modules
modules_install:
$(MAKE) -C $(KERNELDIR) M=$(PWD) modules_install
clean:
rm -rf *.o *~ core .depend .*.cmd *.ko *.mod.c .tmp_versions
.PHONY: modules modules_install clean
else
obj-m := hello.o
endif
对该Makefile的解释:
当make时,由于变量KERNELRELEASE尚未赋值,因此ifeq ($(KERNELRELEASE),)为真,于是变量KERNELDIR被赋值为内核源码树目录/work/system/linux-2.6.22.6,变量PWD被赋值为当前目录/work/studydriver/examples/misc-modules,然后执行找到的第1个目标modules,从而执行命令
make -C /work/system/linux-2.6.22.6 M=/work/studydriver/examples/misc-modules modules
,而当该命令执行以调用内核构造系统的时候,内核构造系统会为变量KERNELRELEASE赋值,从而它不再为空,从而当前目录下的Makefile就变成了只有一行:obj-m := hello.o。此时情况与编译方法1的情况完全相同,因此2种编译方法得到了相同的结果。
最后将得到编译好的模块hello.ko
5、简单的内核模块例子
1 #include <linux/init.h>
2 #include <linux/module.h>
3 MODULE_LICENSE("Dual BSD/GPL");
4
5 static int hello_init(void)
6 {
7 printk(KERN_ALERT "Hello, world\n");
8 return 0;
9 }
10
11 static void hello_exit(void)
12 {
13 printk(KERN_ALERT "Goodbye, cruel world\n");
14 }
15
16 module_init(hello_init);
17 module_exit(hello_exit);
模块执行结果:
# insmod hello.ko
Hello, world
# rmmod hello
Goodbye, cruel world
6、带参数的内核模块例子
1 #include <linux/init.h>
2 #include <linux/module.h>
3 #include <linux/moduleparam.h>
4 static char *whom = "world";
5 static int howmany = 1;
6 module_param(howmany, int, S_IRUGO | S_IWUSR);
7 module_param(whom, charp, S_IRUGO);
8
9 static int hello_init(void)
10 {
11 int i;
12 for (i = 0; i < howmany; i++)
13 printk(KERN_ALERT "(%d) Hello, %s\n", i, whom);
14 return 0;
15 }
16
17 static void hello_exit(void)
18 {
19 printk(KERN_ALERT "howmany is %d, whom is %s\n", howmany, whom);
20 printk(KERN_ALERT "Goodbye, cruel world\n");
21 }
22
23 module_init(hello_init);
24 module_exit(hello_exit);
模块执行结果:
# insmod hellop.ko
hellop: module license ‘unspecified’ taints kernel.
(0) Hello, world
# insmod hellop.ko howmany=3 whom="YangZhu"
(0) Hello, YangZhu
(1) Hello, YangZhu
(2) Hello, YangZhu
# rmmod hellop
howmany is 3, whom is YangZhu
Goodbye, cruel world
# insmod hellop.ko howmany=3 whom="YangZhu"
(0) Hello, YangZhu
(1) Hello, YangZhu
(2) Hello, YangZhu
# ls /sys/module/hellop/parameters/ -l
-rw-r–r– 1 root root 4096 May 3 22:09 howmany
-r–r–r– 1 root root 4096 May 3 22:09 whom
# cat /sys/module/hellop/parameters/howmany
3
# cat /sys/module/hellop/parameters/whom
YangZhu
# echo 10 >/sys/module/hellop/parameters/howmany
# echo YangYong >/sys/module/hellop/parameters/whom
-sh: cannot create /sys/module/hellop/parameters/whom: Permission denied
# rmmod hellop
howmany is 10, whom is YangZhu
Goodbye, cruel world
7、编程注意事项
8、集成模块到内核步骤
config HELLO
tristate ‘New Hello’
obj-$(CONFIG_HELLO) += hello.o
八、查看系统支持的设备
Character devices:
1 mem
10 misc
29 fb
400 leds
232 buttons
Block devices:
1 ramdisk
31 mtdblock
254 sbull
leds initialized
snull: snull initialized
buttons initialized
snull: enter snull_open
19000300-19000310 : cs8900
19000300-19000310 : cs8900
30000000-33ffffff : System RAM
30024000-30293fff : Kernel text
30294000-302f1f97 : Kernel data
49000000-490fffff : s3c2410-ohci
49000000-490fffff : ohci_hcd
4d000000-4d0fffff : s3c2410-lcd
4e000000-4e0fffff : s3c2440-nand
4e000000-4e0fffff : s3c2440-nand
50000000-50003fff : s3c2440-uart.0
50000000-500000ff : s3c2440-uart
50004000-50007fff : s3c2440-uart.1
50004000-500040ff : s3c2440-uart
50008000-5000bfff : s3c2440-uart.2
50008000-500080ff : s3c2440-uart
52000000-520fffff : s3c2440-usbgadget
53000000-530fffff : s3c2410-wdt
53000000-530fffff : s3c2410-wdt
54000000-540fffff : s3c2440-i2c
54000000-540fffff : s3c2440-i2c
55000000-550fffff : s3c2410-iis
55000000-550fffff : s3c2410-iis
56000010-5600001b : qq2440_leds
56000054-56000057 : qq2440_button34
56000064-56000067 : qq2440_button12
57000000-570000ff : s3c2410-rtc
57000000-570000ff : s3c2410-rtc
f0300000-f03fffff : s3c2410-lcd
# cat /proc/interrupts
CPU0
16: 4 s3c-ext0 KEY4
18: 0 s3c-ext0 KEY3
30: 461932 s3c S3C2410 Timer Tick
32: 0 s3c s3c2410-lcd
34: 0 s3c I2SSDI
35: 0 s3c I2SSDO
42: 0 s3c ohci_hcd:usb1
43: 0 s3c s3c2440-i2c
53: 11257 s3c-ext eth0
55: 0 s3c-ext KEY2
63: 0 s3c-ext KEY1
70: 990 s3c-uart0 s3c2440-uart
71: 2206 s3c-uart0 s3c2440-uart
83: 0 - s3c2410-wdt
九、Linux驱动相关代码放在drivers/目录下,常见驱动目录介绍