shanl@ubuntu:~/Android/android-2.3.5_r1$ cd kernel/goldfish/drivers/
shanl@ubuntu:~/Android/android-2.3.5_r1/kernel/goldfish/drivers$ mkdir hello
二、hello目录里建立一个hello.h头文件,加入一个虚拟设备驱动,内容如下:
#ifndef _HELLO_H
#define _HELLO_H
#include <linux/cdev.h> //字符设备需要的头文件
struct hello_dev{
int reg;
struct cdev dev;
};
#endif
#ifndef#define#endif的用法
头件的中的#ifndef,这是一个很关键的东西。比如你有两个C文件,这两个C文件都include了同一个头文件。而编译时,这两个C文件要一同编译成一个可运行文件,于是问题来了,大量的声明冲突。还是把头文件的内容都放在#ifndef和#endif中吧。不管你的头文件会不会被多个文件引用,你都要加上这个。一般格式是这样的:
#ifndef <标识>
#define <标识>
......
......
#endif
<标识>在理论上来说可以是自由命名的,但每个头文件的这个“标识”都应该是唯一的。标识的命名规则一般是头文件名全大写,前后加下划线,并把文件名中的“.”也变成下划线,如:stdio.h
#ifndef _STDIO_H_
#define _STDIO_H_
......
#endif
回到之前的头文件,该文件中定义了一个字符设备结构体hello_dev,这个就是我们虚拟的硬件设备了,reg成员变量就代表设备里面的寄存器,它的类型为int,dev成员变量是一个内嵌的字符设备,这个Linux驱动程序自定义的字符设备结构体。
三、 在hello目录中增加hello.c文件,这是驱动程序的实现部分。 首先写上一个字符设备的框架:
#include <linux/fs.h>
#include <linux/module.h>
#include "hello.h"
#define CHARDEV "hello_char"
static int hello_major = 0;
static int hello_minor = 0;
static struct hello_dev* hellodev = NULL;
struct file_operations hello_fops = {//需要头文件linux/fs.h
};
static int hellodev_init(struct hello_dev *dev) //字符设备的初始化封装函数
{
struct cdev cdev= dev->dev;
int err;
dev_t devno = MKDEV(hello_major,hello_minor);
cdev_init(&cdev, &hello_fops);
cdev.owner = THIS_MODULE;//需要头文件linux/modules.h
cdev.ops = &hello_fops;
err = cdev_add(&(dev->dev),devno, 1);
if(err) {
return err;
}
return 0;
}
static int __init hello_init(void)
{
dev_t dev = 0;
int err;
err = alloc_chrdev_region(&dev, 0, 1,CHARDEV);//dev是个输出参数,这个函数动态分配主设备和次设备号
if (err < 0) {
printk(KERN_ERR "Fail to alloc chardev num\n");
goto fail;
}
//获取主设备和次设备号
hello_major = MAJOR(dev);
hello_minor = MINOR(dev);
hellodev = kmalloc(sizeof(struct hello_dev), GFP_KERNEL);//分配虚拟设备空间
if (!hellodev)
return -ENOMEM;
//初始化字符设备
hellodev_init(hellodev);
fail:
return err;
}
static void __exit hello_exit(void)
{
dev_t dev = MKDEV(hello_major,hello_minor);
cdev_del(&(hellodev->dev));
kfree(hellodev);
unregister_chrdev_region(dev, 1);
}
module_init(hello_init);
module_exit(hello_exit);
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("First Android Driver");
四、修改配置文件将hello驱动编译到内核中:
1.加入Kconfig文件
shanl@ubuntu:~/Android/android-2.3.5_r1/kernel/goldfish/drivers/hello$ vi Kconfig
内容:
config HELLO
tristate "First Android Driver"
default n
help
This is the first android driver.
加入这个Kconfig后,会在make menuconfig中找到相应的选项
2.加入Makefile文件:
shanl@ubuntu:~/Android/android-2.3.5_r1/kernel/goldfish/drivers/hello$ vi Makefile
内容:
obj-$(CONFIG_HELLO) += hello.o
3.进入上一层目录:
shanl@ubuntu:~/Android/android-2.3.5_r1/kernel/goldfish/drivers$ vi Kconfig
和arch/arm/Kconfig文件
shanl@ubuntu:~/Android/android-2.3.5_r1/kernel/goldfish/arch/arm$ vi Kconfig
在menu "Device Drivers"和endmenu之间加入:
source "drivers/hello/Kconfig"
4.shanl@ubuntu:~/Android/android-2.3.5_r1/kernel/goldfish/drivers$ vi Makefile
加入:obj-$(CONFIG_HELLO) += hello/
5.make menuconfig 选中 First Android Driver
6.重新编译内核。
启动模拟器,adb shell
shanl@ubuntu:~$ adb shell
# cd /proc
# cat devices
Character devices:
1 mem
4 /dev/vc/0
4 tty
5 /dev/tty
5 /dev/console
5 /dev/ptmx
7 vcs
10 misc
13 input
29 fb
90 mtd
128 ptm
136 pts
252 hello_char
253 ttyS
254 rtc
Block devices:
1 ramdisk
259 blkext
7 loop
31 mtdblock
43 nbd
179 mmc
254 device-mapper
#
发现我们注册的字符设备已经注册成功了。
总结一下如何注册字符设备:
1.alloc_chrdev_region :动态分配主设备号和次设备号,输出到dev_t参数中。由MAJOR(dev_t),MINOR(dev_t)获取。
2.cdev_init(&cdev, &hello_fops);//初始化cdev,将字符设备的相关操作函数指针付给cdev的成员ops
3.cdev_add:将字符设备加入到系统中。