您的位置:首页 > 运维架构 > Linux

Linux驱动编程 step-by-step (二)

2011-10-30 16:00 537 查看
简单字符设备驱动
1、主次设备号

主设备号标识设备连接的的驱动,此设备号由内核使用,标识在相应驱动下得对应的设备

在linux中设备号是一个32位的dev_t类型

typedef __u32 __kernel_dev_t;

typedef __kernel_dev_t dev_t;

crw------- 1 root root 10, 1 Apr 11
2011 psaux

crw------- 1 root root 4, 1 Oct 2803:04
tty1

crw-rw-rw- 1 root tty 4, 64 Apr 11
2011 ttys0

crw-rw---- 1 root uucp 4, 65 Apr 11 2011
ttyS
上图是再/dev目录下用$ls -l 命令显示的部分结果可以看到tty driver的主设备号都为4(各个系统版本有差别),次设备号不同

前12位标识主设备号MAJOR(dev_t dev)获得主设备号
后20位标识此设备号MINOR(dev_t dev)获得此设备号
由主次设备号生成设备号

可以使用宏MKDEV

dev_t dev_num = MKDEV(dev_t major, dev_t minor);

2、分配与释放设备号

在linux2.6的字符设备中(kernel3.0也是)首先做的事就是申请一个或者多个设备号

/* 静态分配设备号
* parameter:
* first : 分配的第一个设备号
* count: 分配的设备个数
* name : 设备名
* return value:
* 0: success
* 负值:出现错误,错误码
*/
int register_chrdev_region(dev_t first, unsigned int count, char *name);

/* 动态分配设备号
* parameter:
* dev : 用来存储分配的设备号值
* firstminor: 次设备号(一般填0)

* count: 分配的设备个数
* name : 设备名
* return value:
* 0: success
* 负值:出现错误,错误码
*/
int alloc_chrdev_region(dev_t *dev, unsigned int firstminor, unsigned int count, char *name);

/* 释放设备号
* parameter:
* first: 设备号
* count: 分配的设备个数
*/
void unregister_chrdev_region(dev_t first, unsigned int count);

静态分配设备号,是在已经知道一个可用设备号的时候使用,而程序员在编写程序之前大多并知道设备号是否可用,或者现在可用,不能确保在系统升级时候次设备还是可用的

所以linux社区极力推荐使用动态分配,它会去寻找可用的设备号,而不会产生冲突。在次设备卸载的时候需要释放次设备号。

3、一个没有作用的字符设备驱动

#include <linux/init.h>
#include <linux/module.h>
#include <linux/types.h>

#include <linux/fs.h>

#define SIMPLE_DEBUG 1
#define DEV_COUNT 2

#define SIMPLE_NAME "simple_char"

static int simple_major = 108;
static int simple_minor = 0;

static __init int simple_init(void)
{
dev_t dev;
int err;
#if SIMPLE_DEBUG
printk(KERN_INFO "In %s\n", __func__);
#endif
dev = MKDEV(simple_major, simple_minor); //求取设备号
if(dev > 0)//设备号有效
{
#if SIMPLE_DEBUG
printk(KERN_INFO "try to register static char dev %d \n", dev);
#endif
err = register_chrdev_region(dev,DEV_COUNT, SIMPLE_NAME); //静态分配设备号
if(err < 0) //静态分配出错 尝试使用动态分配
{
printk(KERN_WARNING "register static char dev error\n");
err = alloc_chrdev_region(&dev, 0, DEV_COUNT, SIMPLE_NAME); //动态分配设备号
if(err < 0)
{
printk(KERN_ERR "register char dev error in line %d\n",__LINE__);
goto error;
}
else
{
simple_major = MAJOR(dev);//重新计算主设备号
simple_minor = MINOR(dev);//重新计算此设备号
}

}
else{

}
}
else //设备号无效使用动态分配
{
#if SIMPLE_DEBUG
printk(KERN_INFO "try to register alloc char dev \n");
#endif
err = alloc_chrdev_region(&dev, 0, DEV_COUNT, SIMPLE_NAME);
if(err < 0)
{
printk(KERN_ERR "register char dev error in line %d\n\n",__LINE__);
goto error;
}
else
{
simple_major = MAJOR(dev);
simple_minor = MINOR(dev);
}

}

#if SIMPLE_DEBUG
printk(KERN_INFO "register char dev success major = %d minor = %d \n", simple_major, simple_minor);
#endif

error:
return err;
}

static __exit void simple_exit(void)
{
dev_t dev;
#if SIMPLE_DEBUG
printk(KERN_INFO "In %s\n", __func__);
#endif
dev = MKDEV(simple_major, simple_minor);
unregister_chrdev_region(dev, DEV_COUNT); //释放设备号
}

module_init(simple_init);
module_exit(simple_exit);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("kai_zhang(jsha.zk@163.com)");
MODULE_DESCRIPTION("simple char driver!");

这里只在模块初始化的时候去分配设备号,在模块注销的时候去释放次驱动拥有的设备号

在函数里边我们看到用到了在应用编程里边声名狼藉的goto函数,在linux驱动编程时 goto 函数可以让我们的编程更加有条理性,在出现错误时候能更快的去处理。

如果在调用函数检查返回者都去做错误处理则模块函数就显得臃肿,庞大。所以还是建议合理使用goto函数的。

加载次模块后

运行 $cat /proc/devices可以看到 simple_char 的设备以及主设备号。



这里我们看到原来假设的主设备号是不可用的,所以使用的动态分配设备号,由此我们申请到主设备号为249,我们可以在上边添加我们的设备,具体操作下一节会讲到。呵呵留点悬念先。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: