3. linux 字符设备驱动框架

TOC

1.主设备号码和次设备号

dev_t

dev_t 类型是定义在中定义用来保存设备编号(前12位用来表示主设备号,后20位表示次设备号)

注意:我们获取主设备号和次设备号应该使用内核提供的宏
MAJOR(dev_t):获取主设备号
MINOR(dev_t):获取次设备号
MKDEV(int major, int minor):将主设备号和次设备号转换为dev_t类型

  • 分配和释放设备编号

在建立一个字符设备之前,首先需要分别一个或多个字符设备。

静态分配设备号

/*
first: 要分配的设备编号范围的初始值,次设备号为0
count: 连续编号范围
name:编号相关联的设备名称(/proc/devices)


*/
int register_chrdev_region(dev_t first, unsigned int count, char *name);

动态分配设备号

/*
dev:分配到的设备号
firstminor:被请求的第一个次设备号,通常为0
count:请求的连续设备号数
name:设备名
*/
int alloc_chrdev_region(dev_t *dev,unsigned int firstminor,unsigned int count,char *name);

释放

void unregist_chrdev_region(dev_t first,unsigned int count);

2.字符设备的注册

static void __init chrdev_init(void)
{
    //构造cdev设备对象
    struct cdev *my_cdev = cdev_alloc();
    my_cdev->ops = &my_fops;


    //初始化cdev设备对象
    cdev_init(my_cdev, &my_fops);
    my_cdev->owner = THIS_MODULE;


    //申请设备号,动态or静态
    dev_t dev;
    int ret = 0;
    if(major)
    {
        //为字符设备静态申请第一个设备号
        dev = MKDEV(major, minor);
        ret = register_chrdev_region(dev, 1, "mydev");
    }
    else
    {
        //为字符设备动态申请一个设备号
        ret = alloc_chrdev_region(&dev, minor, 1, "mydev");
        major = MAJOR(dev);
    }


    //将字符设备对象注册到内核
    ret = cdev_add(my_cdev, dev, 1);


}




static void __exit chrdev_exit(void)
{
    //从内核注销cdev设备对象
    cdev_del(my_dev);


    //回收设备号
    unregister_chrdev_region(dev, 1);
}



3.字符设备驱动程序样例

#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 <linux/poll.h>  
#include <linux/cdev.h>  
#include <linux/device.h>


#define HELLO_CNT   2  


//主设备号为0,表示动态分配设备号 
dev_t dev = 0;
static int major = 0;   
static int minor = 0;

static struct cdev *hello_cdev[HELLO_CNT];
static struct class *hello_class = NULL;
static struct class_device * hello_class_dev[HELLO_CNT];




//文件操作结构体
static const struct file_operations fops = 
{
    .owner = THIS_MODULE,
    //.open = hello_open,
    //.release = hello_release,
    //.read = hello_read,
    //.write = hello_write,
};


static void setup_cdev(int index)
{
    int err, devno = MKDEV(major, index);

    cdev_init(hello_cdev[index], &fops);
    hello_cdev[index]->owner = THIS_MODULE;
    hello_cdev[index]->ops = &fops;
    err = cdev_add(hello_cdev[index], devno, 1);
    if(err)
    {
        printk(KERN_NOTICE "Error %d adding hello%d", err, index);
    }
}

static void __init hello_init(void)
{


    //申请设备号,动态or静态
    int ret = 0;
    if(major)
    {
        //为字符设备静态申请第一个设备号
        dev = MKDEV(major, minor);
        ret = register_chrdev_region(dev, HELLO_CNT, "hello");
    }
    else
    {
        //为字符设备动态申请一个设备号
        ret = alloc_chrdev_region(&dev, minor, HELLO_CNT, "hello");
        major = MAJOR(dev);
    }


    //构造cdev设备对象
    int i = 0;
    for(i = 0; i < HELLO_CNT; ++i)
    {
        hello_cdev[i] = cdev_alloc();


    }


    //初始化设备对象    
    for(minor = 0; minor < HELLO_CNT; ++minor)
    {
        setup_cdev(minor);
    }

    hello_class = class_create(THIS_MODULE, "hello");
    for(minor = 0; minor < HELLO_CNT; ++minor)
    {
        hello_class_dev[minor] = device_create(hello_class, NULL, MKDEV(major, minor), NULL, "hello%d",minor);
    }


}




static void __exit hello_exit(void)
{
    for(minor = 0; minor < HELLO_CNT; ++minor)
    {
        device_destroy(hello_class, MKDEV(major, minor));
    }
    class_destroy(hello_class);


    //从内核注销cdev设备对象
    cdev_del(hello_cdev);

    //回收设备号
    unregister_chrdev_region(dev, HELLO_CNT);
}


module_init(hello_init);  
module_exit(hello_exit);  


MODULE_LICENSE("GPL");  

posted @ 2020-03-23 11:17  standardzero  阅读(227)  评论(0编辑  收藏  举报