天天看點

字元裝置之register_chrdev與register_chrdev_region

    之前寫字元裝置驅動,都是使用register_chrdev向核心注冊驅動程式中建構的file_operations結構體,之後建立的裝置檔案,隻要是主裝置号相同(次裝置号不同),則綁定的都是同一個file_operations結構體,應用程式使用的也都是這一個結構體中注冊的函數。這就會出現這樣的一個弊端:同一類字元裝置(即主裝置号相同),會在核心中連續注冊了256(分析核心代碼中可知),也就是是以的此裝置号都會被占用,而在大多數情況下都不會用到這麼多次裝置号,是以會造成極大的資源浪費。是以register_chrdev在某個角度上是有弊端的,這也是老版本核心中使用。

   register_chrdev的注冊,還分為靜态注冊和動态注冊。而register_chrdev_region和alloc_chrdev_region正相當于将register_chrdev拆分來,它們分别是靜态和動态注冊的個體,但同時也解決了register_chrdev資源浪費的缺點。

register_chrdev_region(dev_t from, unsigned count, const char * name)      

從參數中分析該函數的作用:

from:要配置設定的裝置号範圍的起始值。

count:所要求的連續裝置編号個數。

name:和該編号範圍相關的裝置名稱。

register_chrdev_region允許注冊一個規定的裝置号的範圍,也就不一定把0~255個此裝置号都注冊占用。

在2.6之後的核心,利用的是一個struct cdev結構體來描述一個字元裝置。

struct cdev {
struct kobject kobj;
struct module *owner;
const struct file_operations *ops;
struct list_head list;
dev_t dev;
unsigned int count;
};      

再介紹幾個用到的函數:

void cdev_init(struct cdev *, const struct file_operations *);//清空cdev,并填充file_operations 結構體
int cdev_add(struct cdev *, dev_t, unsigned);//注冊字元裝置到核心      

例子:寫一個簡單的字元裝置驅動,主裝置号為major,隻注冊0~1兩個此裝置号,并建立主裝置号為major,次裝置号建立0,1,2三個裝置檔案。利用應用程式打開這三個檔案,看有什麼現象(是否都能打開)

#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/init.h>
#include <linux/delay.h>
#include <linux/irq.h>
#include <asm/uaccess.h>
#include <asm/irq.h>
#include <asm/io.h>
#include <asm/arch/regs-gpio.h>
#include <asm/hardware.h>
#include <linux/poll.h>
#include <linux/cdev.h>
static int hello_open(struct inode *inode, struct file *filp)
{
printk("hello_open\n");
return 0;
}
//建構file_operations結構體
static struct file_operations hello_fops={
.owner=THIS_MODULE,
.open   =   hello_open,
};
static int major;
static struct cdev hello_cdev;
static struct class* hello_class;
static struct class_device* hello_class_dev[3];
static int hello_init(void)
{
dev_t devid;
if(major==0)
{   
alloc_chrdev_region(&devid,0,2,"hello");//主裝置号為major,次裝置号為0,1則對應該file_operations
major=MAJOR(devid);
//int alloc_chrdev_region(dev_t *dev, unsigned baseminor, unsigned count,const char *name)
}
else
{
devid=MKDEV(major,0);
register_chrdev_region(devid,2,"hello");
//int register_chrdev_region(dev_t from, unsigned count, const char *name)
}
cdev_init(&hello_cdev,&hello_fops);
//注冊
cdev_add(&hello_cdev,devid,2);
//建立類
hello_class=class_create(THIS_MODULE,"hello");
int i;
for(i=0;i<3;i++)
{   //自動建立裝置
hello_class_dev[i]=class_device_create(hello_class,NULL,MKDEV(major,i),NULL,"hello%d",i);
}
return 0;
}
static void hello_exit(void)
{
cdev_del(&hello_cdev);
unregister_chrdev_region(MKDEV(major,0),2);
int i;
for(i=0;i<3;i++)
{
class_device_destroy(hello_class, MKDEV(major, i));
}
class_destroy(hello_class);
}
module_init(hello_init);
module_exit(hello_exit);
MODULE_LICENSE("GPL");      

應用程式很簡單:

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <fcntl.h>
int main(int argc, char const *argv[])
{
int fd=open(argv[1],O_RDWR);
if(-1==fd)
{
printf("Can't open!\n");
return ;
}
printf("Open OK!\n");
return 0;
}      

繼續閱讀