分类: LINUX
2014-05-27 09:17:45
原文地址:字符设备驱动编写与设备节点创建方法 作者:Liuqz2009
设计驱动程序框架
字符设备指那些必须以串行顺序依次访问的设备,并且不需要缓冲,通常用于不需要大量数据请求传送的设备类型,所以对于该按键设备适合用字符设备类型来实现。对于设备驱动,通常根据设备类型的不同所以实现框架会有所不同,比如字符设备、块设备和网络设备分别都有自己的实现框架和相应的内核API函数。通常字符设备驱动实现的内容有:模块的加载(字符设备的注册,硬件的初始化,中断的注册等);字符设备的操作函数实现(open,close,read,write和ioctl等);中断程序的实现;模块的卸载(注销字符设备,注销其他申请的资源)。下面来分析一下该按键设备驱动的具体实现。
3.按键模块的加载
1 static int __init buttons_init(void)
2 {
3 int ret,devno;
4 dev_t dev;
5 ret = alloc_chrdev_region(&dev,0,1,DEVICE_NAME); //在系统中申请一个字符设备区域,主设备号由系统动态分配。
6 buttons_major_number = MAJOR(dev); //摘取出主设备号
7 printk(KERN_INFO "Initial QT2410E Board Buttons driver!\n");
8 if (ret<0) {
9 printk(KERN_WARNING "button:can't get major number %d\n",buttons_major_number);
10 return ret;
11 }
12 ret = request_irqs(); //注册中断,下文会具体分析该函数的实现
13 if (ret) { //如果注册中断失败,则注销上面申请的字符设备区域。
14 unregister_chrdev_region(dev,1);
15 printk(KERN_WARNING "button:can't request irqs\n");
16 return ret;
17 }
18 devno = MKDEV(buttons_major_number,0);
19 cdev_init(&buttons_dev,&buttons_fops); //初始化buttons_dev字符设备结构
20 buttons_dev.owner = THIS_MODULE;
21
22
23 ret = cdev_add(&buttons_dev,devno,1);// 将字符设备加入到内核中
24 if (ret) { //如果添加失败,则做上述注册的释放操作。
25 free_irqs();
26 unregister_chrdev_region(dev,1);
27 printk(KERN_NOTICE "Error %d adding buttons device\n",ret);
28 return ret;
29 }
30
31 #ifdef CONFIG_DEVFS_FS //如果定义devfs,系统会自动创建/dev目录下的字符设备节点,比如这里会自动创建/dev/buttons字符设备节点
32 devfs_mk_cdev(MKDEV(buttons_major_number,0), S_IFCHR | S_IRUSR | S_IWUSR,DEVICE_NAME);
33 printk(KERN_INFO"/dev/%s has been added to your system.\n",DEVICE_NAME);
34 #else //否则需要执行 mknod命令手动创建字符设备节点。
35 printk(DEVICE_NAME "Initialized\n");
36 printk(KERN_INFO "You must create the dev file manually.\n");
37 printk(KERN_INFO "Todo: mknod c /dev/%s %d 0\n",DEVICE_NAME,buttons_major_number);
38 #endif
39 return 0;
40 }
分析上述代码,buttons_init是该按键模块的驱动入口函数,也是该内核模块的加载函数,主要用于注册资源、申请资源和初始化设备等工作。第5行,alloc_chrdev_region()是内核提供的申请字符设备号函数,它会动态的为设备申请一个主设备号,并且根据输入参数申请多个次设备。第19行,cdev_init()是用于初始化一个cdev结构,这里初始化的是buttons_dev全局变量。第32行,devfs_mk_cdev()函数会在/dev目录下自动创建一个设备节点,早期的内核版本是需要手动通过mknod工具创建设备节点。
(三)用udev配合class创建设备节点示例
http://hi.baidu.com/zengzhaonong/blog/item/dd55b26efcad8ade81cb4a8a.html
使用udev在/dev/下动态生成设备文件
create_chrdev.c
---------------------------------------------
#include
#include
#include
#include
dev_t devid;
static struct cdev *led_cdev;
static int led_Major =
0;
static int led_Minor = 0;
static struct class
*led_class;
static struct file_operations led_fops = {
.owner = THIS_MODULE,
};
static int __init
hello_init(void)
{
int err;
//初始化cdev
led_cdev = cdev_alloc();
cdev_init(led_cdev,
&led_fops);
led_cdev->owner =
THIS_MODULE;
//动态获取主设备号(dev_t devid中包含"主设备号"和"次设备号"信息)
alloc_chrdev_region(&devid,
66,
1, "led");
led_Major = MAJOR(devid);
led_Minor = MINOR(devid);
printk(KERN_INFO "I was assigned major number %d.\n", led_Major);
printk(KERN_INFO "I was assigned minor number %d.\n", led_Minor);
//注册字符设备 (1)
err = cdev_add(led_cdev, devid,
1);
if (err) {
printk(KERN_NOTICE "Error %d adding
device\n", err);
return -1;
}
led_class = class_create(THIS_MODULE,
"led_class1");
if (IS_ERR(led_class)) {
printk(KERN_INFO "create class error\n");
return -1;
}
class_device_create(led_class,
NULL, devid, NULL, "led"
"%d", MINOR(devid));
return 0;
}
static void __exit
hello_exit(void)
{
unregister_chrdev_region(devid, 1);
cdev_del(led_cdev);
class_device_destroy(led_class,
devid);
class_destroy(led_class);
}
module_init(hello_init);
module_exit(hello_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("zengxiaolong
");
MODULE_DESCRIPTION("A sample
driver");
MODULE_SUPPORTED_DEVICE("testdevice");
class_create,class_simple_create在/sysy/class创建类目录。
class_device_create,class_simple_
device_add两者功能类似,如果dev指向不为NULL,它将创建一个从类入口到/sys/devices下相应入口的符号链接。
Makefile
---------------------------------------------
all:
default
obj-m += create_chrdev.o
default:
make -C
/home/zxl/soft/kernel/linux-2.6.22 M=`pwd` modules
clean:
make -C /home/zxl/soft/kernel/linux-2.6.22 M=`pwd` clean
字符设备的生成 --
这个版本简洁
-------------------------------------------------------
static
struct class *node_class;
static int __init
init_mtdchar(void)
{
if (register_chrdev(CHAR_MAJOR, "dev_node",
&node_fops))
{
printk(KERN_NOTICE "Can't allocate major
number %d.\n", CHAR_MAJOR);
return -EAGAIN;
}
node_class = class_create(THIS_MODULE, "dev_node_class");
if
(IS_ERR(node_class)) {
printk(KERN_ERR "Error creating
node_class.\n");
unregister_chrdev(CHAR_MAJOR,
"dev_node");
return PTR_ERR(node_class);
}
class_device_create(node_class, NULL, MKDEV(CHAR_MAJOR, 0), NULL,
"node_name" "%d", CHAR_MINOR);
return
0;
}
static void __exit cleanup_mtdchar(void)
{
class_device_destroy(node_class, MKDEV(CHAR_MAJOR, 0));
class_destroy(node_class);
unregister_chrdev(CHAR_MAJOR,
"dev_node");
}
linux-2.6.21.7
内核中的使用实例
-------------------------------------------------------
static
void adbdev_init(void)
{
if (register_chrdev(ADB_MAJOR, "adb",
&adb_fops)) {
printk(KERN_ERR "adb: unable to get major %d\n",
ADB_MAJOR);
return;
}
adb_dev_class =
class_create(THIS_MODULE, "adb");
if (IS_ERR(adb_dev_class))
return;
class_device_create(adb_dev_class, NULL, MKDEV(ADB_MAJOR, 0),
NULL, "adb");
}
linux-2.6.28
内核中的使用实例
-------------------------------------------------------
static
void __init adbdev_init(void)
{
if (register_chrdev(ADB_MAJOR, "adb",
&adb_fops)) {
printk(KERN_ERR "adb: unable to get major %d\n",
ADB_MAJOR);
return;
}
adb_dev_class =
class_create(THIS_MODULE, "adb");
if (IS_ERR(adb_dev_class))
return;
device_create(adb_dev_class, NULL, MKDEV(ADB_MAJOR, 0), NULL,
"adb");
platform_device_register(&adb_pfdev);
platform_driver_probe(&adb_pfdrv, adb_dummy_probe);
}