Chinaunix首页 | 论坛 | 博客
  • 博客访问: 72517
  • 博文数量: 25
  • 博客积分: 2035
  • 博客等级: 大尉
  • 技术积分: 300
  • 用 户 组: 普通用户
  • 注册时间: 2008-10-30 21:21
文章分类
文章存档

2008年(25)

我的朋友
最近访客

分类: LINUX

2008-11-04 21:20:41

2.6字符设备分析
 
内核只关心major number,而minor number 是由设备驱动来区别的.
内核内部,类型dev_t存储着设备号,且定义了一组宏来维护它.
MKDEV(int major,int minor);//return dev_t
MAJOR( dev_t dev);
MINOR (dev_t dev);
比如,我们用mknod建立一个新的设备文件
#mknod /dev/newchr c 50 0
建立/dev/newchr设备文件,类型是c(char,字符型),major number 是50,minor number 是0.mknod的用法可以用man来查看.
在内核内部,我们用上面的宏来维护:
dev_t mydev;
mydev=MKDEV(50,0);
我们也可以由mydev得到major 和minor number.
int major,minor;
major=MAJOR(mydev);
minor=MINOR(mydev);
注册设备号
我们定义好major和minor number 后就可以在内核中注册一个设备了.注册一个字符设备需要用到下面几个函数:
int register_chrdev_region(dev_t first,unsigned int count,
char *name);
first是你要注册的设备号范围的开始(其中minor号一般设置为0),count是你所申请的连续设备号的总数.name是设备的名称.它会在/proc/devices中出现.
int alloc_chrdev_region(dev_t *dev,unsigned int firstminor,
unsigned int count,char *name);
这个函数是用来动态分配设备号的.若不知道所要用的major号是多少,便让内核动态分配一个.参数dev是个output-only参数.
 
他们都主要是通过调用函数__register_chrdev_region() 来实现的
要注意,这两个函数仅仅是注册设备号! 如果要和cdev关联起来,还要调用cdev_add()
也就是说register_chrdev_region和alloc_chrdev_region只是对相应的设备号进行注册!!!

void unregister_chrdev_region(dev_t first,unsigned int count);
一般在模块清除函数中调用.
#include 
#include 
#include 
#include 
#include 
#include 
static unsigned int major = 0;
static unsigned int minor = 0;
static unsigned int devno;
static char *filename = "mydevice";
static struct cdev *mycdev = NULL;
static int mycdevflg = 0;
static int devnoflg = 0;
static int adddevflg = 0;
MODULE_LICENSE("Dual BSD/GPL");
static int myopen(struct inode *inodep, struct file *flipl)
{
printk("my open is run\n");
return 0;
}
static ssize_t myread(struct file *flip, char __user *buf, size_t size, loff_t offset)
{
printk("myread is ok!\n");
static int i = 0;
///copy_to_user(buf,from,size);
*buf = i++;
return 0;
}
static int myrelease(struct inode *myindoe, struct file *flip)
{
printk("myrelease is run.\n");
return 0;
}
static struct file_operations myfops =
{
.owner = THIS_MODULE,
.open = myopen,
.read = myread,
.release= myrelease,
};
/* 初始化设备的过程主要是三步:1,生成设备号;2初始化设备;3,添加到内核。
*/
static int __init myinit(void)
{
int result = -1;
if(major)
{
devno = MKDEV(major,minor); //假如我们要用自定义的设备号,那么用MKDEV生成设备号
result = register_chrdev_region(devno,1,filename);//注册设备号devno,1为设备的个数
devnoflg = 1;
}
else
{
result = alloc_chrdev_region(&devno,minor,1,filename);//假如让内核动态生成设备号,那么用此函数,生成设备号并且注册,mior为次设备号,这里注意的是指针devno?
major = MAJOR(devno);
devnoflg = 1;
}
if(result < 0)
{
printk("can't register the major num!\n");
devnoflg = 0;
return -1;
}
//mycdev = kmalloc(sizeof(struct cdev),GFP_KERNEL);
//如果采用cdev_init(struct cdev*,struct file *)方式的话,这个才需要。

mycdev = cdev_alloc();//申请cdev内存并且初始化设备cdev,不能在这之前申请内存,
否则要释放两次!
if(NULL == mycdev)
{
printk("can't request the memory!\n");
}
mycdevflg = 1;
//memset(mycdev,0,sizeof(mycdev));

mycdev->owner = THIS_MODULE;//如果采用cdev_init(struct cdev*,struct file *)方式的话,
这两项可以去掉
mycdev->ops = &myfops;//如果采用cdev_init(struct cdev*,struct file *)方式的话,
这两项可以去掉
result = cdev_add(mycdev,devno,1);//设备和设备号联系起来,
即通常说的添加设备到内核
if(result < 0)
{
printk("can't add cdev.2\n");
adddevflg = 0;
}
else
{
adddevflg = 1;
}
return 0;
}
/* 以和设备注册的次序"卸载",注意有的时候,当出错的时候,要注意是否用到这些操作,
所以要加上判断,否则比如对空指针free会导致系统崩溃!
*/
static void myexit(void)
{
printk("myexit begin.\n");
if(adddevflg)
{
cdev_del(mycdev);
adddevflg = 0;
}
if(mycdev)
{
kfree(mycdev);
mycdev = NULL;
}
if(devnoflg)
{
unregister_chrdev_region(devno,1);
devno = 0;
devnoflg = 0;
}
printk("exit over.\n");
}
module_init(myinit);
module_exit(myexit);
阅读(977) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~