linux 字符设备和misc设备

字符设备

2.6版本前使用的结构体和函数

<span style="font-family: Arial, Helvetica, sans-serif; background-color: rgb(255, 255, 255);"></span><pre name="code" class="cpp">typedef __kernel_dev_t      dev_t;//字符设备的设备号  
static inline int register_chrdev(unsigned int major, const char *name,const struct file_operations *fops)//注册字符设备到内核中,疑问1  
static inline void unregister_chrdev(unsigned int major, const char *name)//注销字符设备  
MAJOR(dev_t dev)//计算主设备号  
MINOR(dev_t dev)//计算次设备号  
MKDEV(int major,int minor)//根据major和minor算得dev_t  
struct file_operations{  
.....  
} 
在使用以上函数注册字符设备的时候比较简单,拿misc的实现函数举例,已知了dev_t的主设备号,所以可以直接调用register_chrdev将该设备注册到内核中。  

 
static int __init misc_init(void)
{
  int err;
  
  #ifdef CONFIG_PROC_FS
    proc_create("misc", 0, NULL, &misc_proc_fops);
  #endif
  misc_class = class_create(THIS_MODULE, "misc");//dev下创建misc类,后面在具体设备进行misc_register是再执行device_creat注册具体的misc设备
  err = PTR_ERR(misc_class);
  if (IS_ERR(misc_class))
    goto fail_remove;
  err = -EIO;
  if (register_chrdev(MISC_MAJOR,"misc",&misc_fops))
    goto fail_printk;
  misc_class->devnode = misc_devnode;return 0;
  fail_printk:
    printk("unable to get major %d for misc devices\n", MISC_MAJOR);
  class_destroy(misc_class);
  fail_remove:
    remove_proc_entry("misc", NULL);
    
  return err;
}

2.6版本后使用的结构体和函数

dev_t;//字符设备的设备号  
MAJOR(dev_t dev)//计算主设备号  
MINOR(dev_t dev)//计算次设备号  
MKDEV(int major,int minor)//根据major和minor算得dev_t  
struct cdev {      //新增了cdev结构体,可以看到cdev除了包含了dev_t成员,还有kobj相关成员,2.6正是引入了kobj模型  
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,主要是赋值cdev的ops成员  
struct cdev *cdev_alloc(void);//动态分配一个cdev结构体,此时是程序中只定义了一个cdev指针的时候使用,如果定义了一个cdev结构体,使用cdev_init  
void cdev_put(struct cdev *p);//释放alloc的cdev  
int cdev_add(struct cdev *, dev_t, unsigned);//向系统注册字符设备  
void cdev_del(struct cdev *);  
extern int alloc_chrdev_region(dev_t *, unsigned, unsigned, const charchar *);//动态向系统申请设备号<span style="font-family: Arial, Helvetica, sans-serif;">dev_t</span>  
extern int register_chrdev_region(dev_t, unsigned, const charchar *);//已知dev_t时静态申请  
struct file_operations{  
.....  
}  
可以看出2.6以后的版本在注册字符设备时稍复杂些,还是举个例子,可以看到在2.6以后的版本中注册字符设备的过程是:申请设备号dev_t->cdev初始化->注册cdev到系统中,对比2.6以前的版本多了针对cdev的操作过程。
static int __init pc8736x_gpio_init(void)  
{  
    int rc;  
    dev_t devid;  
  
......  
    if (major) {  
        devid = MKDEV(major, 0);  
        rc = register_chrdev_region(devid, PC8736X_GPIO_CT, DEVNAME);  
    } else {  
        rc = alloc_chrdev_region(&devid, 0, PC8736X_GPIO_CT, DEVNAME);  
        major = MAJOR(devid);  
    }  
  
    if (rc < 0) {  
        dev_err(&pdev->dev, "register-chrdev failed: %d\n", rc);  
        goto undo_request_region;  
    }  
    if (!major) {  
        major = rc;  
        dev_dbg(&pdev->dev, "got dynamic major %d\n", major);  
    }  
  
    pc8736x_init_shadow();  
  
    /* ignore minor errs, and succeed */  
    cdev_init(&pc8736x_gpio_cdev, &pc8736x_gpio_fileops);  
    cdev_add(&pc8736x_gpio_cdev, devid, PC8736X_GPIO_CT);  
  
    return 0;  
  
undo_request_region:  
    release_region(pc8736x_gpio_base, PC8736X_GPIO_RANGE);  
undo_platform_dev_add:  
    platform_device_del(pdev);  
undo_platform_dev_alloc:  
    platform_device_put(pdev);  
  
    return rc;  
}

misc设备

misc使用的结构体和函数

misc设备其实也是字符设备,主不过misc设备驱动在字符设备的基础上又进行了一次封装,使用户可以更方便的使用。

struct miscdevice  {  
    int minor;  
    const charchar *name;  
    const struct file_operations *fops;//还是字符设备中的文件操作结构体,只不过misc结构体对其又封装了一次  
    struct list_head list;  
    struct device *parent;  
    struct device *this_device;  
    const charchar *nodename;  
    mode_t mode;  
};  
int misc_register(struct miscdevice * misc)  
还是举个例子来看下,用户在注册misc设备的时候只需要:初始化file_operations结构体->初始化miscdevice结构体->调用misc_register将miscdevice注册到系统中就ok了。  
static const struct file_operations mmtimer_fops = {  
.owner = THIS_MODULE,  
.mmap =mmtimer_mmap,  
.unlocked_ioctl = mmtimer_ioctl,  
.llseek = noop_llseek,  
};  
static struct miscdevice mmtimer_miscdev = {  
SGI_MMTIMER,  
MMTIMER_NAME,  
&mmtimer_fops  
};  
static int __init mmtimer_init(void)  
{  
    cnodeid_t node, maxn = -1;  
  
......  
    if (request_irq(SGI_MMTIMER_VECTOR, mmtimer_interrupt, IRQF_PERCPU, MMTIMER_NAME, NULL)) {  
        printk(KERN_WARNING "%s: unable to allocate interrupt.",  
            MMTIMER_NAME);  
        goto out1;  
    }  
  
    if (misc_register(&mmtimer_miscdev)){  
        printk(KERN_ERR "%s: failed to register device\n",  
               MMTIMER_NAME);  
        goto out2;  
    }  
......  
  
    return 0;  
  
out3:  
    kfree(timers);  
    misc_deregister(&mmtimer_miscdev);  
out2:  
    free_irq(SGI_MMTIMER_VECTOR, NULL);  
out1:  
    return -1;  
}  
那么到了这里就会有疑问,为什么linux还费劲的又造了一个misc设备呢?为什么不直接都使用字符设备驱动呢?
为什么要有misc设备
首先的好处就是方便,快捷,用户可以只初始化2个结构体,然后调用一个函数就可以创建一个misc设备了,本质上来说他也是个字符设备。
还有应该就是注册misc设备节约了主设备号的占用,linux中只提供了256个主设备号,本身内核中一些驱动已经占用了部分,试想如果有一大波设备都想以字符设备注册进内核,必然导致主设备号不够用的情况,而misc设备在初始化时占用主设备号10,当设备驱动以misc设备注册进内核的时候,只为其分配次设备号,主设备号不变。
为什么要有misc设备的观点从知乎上看来的,自己思考了下,总结下来,后面如果再想到会继续添加。


疑问1:该函数在fs.h中声明为static inline,静态的内联函数,既然是静态的,不是应该不能被其他文件内的函数调用?
其实该函数声明在.h文件中,那么,只要c文件包含了该头文件就可以调用了,也就是说在编译时,调用该函数的地方会被编译器将实际代码直接展开在该c文件中,那么在该c文件中该函数是static的,其他c文件没包含该头文件的是不可以使用该函数的。


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值