linux字符设备

linux有一个全局的结构体数组,共255个元素,记录系统中的设备节点。主设备号相同,次设备号不同的设备组成链表。参考:http://edsionte.com/techblog/archives/1393

注册一个字符设备调用函数register_chrdev(major, DEV_NAME, &fops)

static inline int register_chrdev(unsigned int major, const char *name,
                  const struct file_operations *fops)
{
    return __register_chrdev(major, 0, 256, name, fops);
}
//申请次设备号最小从0开始,申请256个次设备。
int __register_chrdev(unsigned int major, unsigned int baseminor,  
              unsigned int count, const char *name,
              const struct file_operations *fops)
{
    struct char_device_struct *cd;
    struct cdev *cdev;
    int err = -ENOMEM;
    //从全局的字符设备结构体数组中获取可以使用的结构体char_device_struct
    cd = __register_chrdev_region(major, baseminor, count, name); 
    if (IS_ERR(cd))
        return PTR_ERR(cd);

    cdev = cdev_alloc();
    if (!cdev)
        goto out2;

    cdev->owner = fops->owner;
    cdev->ops = fops;
    kobject_set_name(&cdev->kobj, "%s", name);
    //将字符设备添加到系统中
    err = cdev_add(cdev, MKDEV(cd->major, baseminor), count);
    if (err)
        goto out;

    cd->cdev = cdev;

    return major ? 0 : cd->major;
out:
    kobject_put(&cdev->kobj);
out2:
    kfree(__unregister_chrdev_region(cd->major, baseminor, count));
    return err;
}

static struct char_device_struct *
__register_chrdev_region(unsigned int major, unsigned int baseminor,
               int minorct, const char *name)
{
    struct char_device_struct *cd, **cp;
    int ret = 0;
    int i;

    cd = kzalloc(sizeof(struct char_device_struct), GFP_KERNEL);
    if (cd == NULL)
        return ERR_PTR(-ENOMEM);

    mutex_lock(&chrdevs_lock);
    /* temporary */
    if (major == 0) {
    //如果设备号为0,那么遍历字符设备节点的数组,查找空闲的设备号,分配给设备
        for (i = ARRAY_SIZE(chrdevs)-1; i > 0; i--) {
            if (chrdevs[i] == NULL)
                break;
        }

        if (i == 0) {
            ret = -EBUSY;
            goto out;
        }
        major = i;
        ret = major;
    }

    cd->major = major;
    cd->baseminor = baseminor;
    cd->minorct = minorct;
    strlcpy(cd->name, name, sizeof(cd->name));

    i = major_to_index(major);
    //遍历设备链表,将新的设备根据主设备号和次设备号添加到链表的合适位置
    //偏移主设备号
    for (cp = &chrdevs[i]; *cp; cp = &(*cp)->next)
        if ((*cp)->major > major ||
            ((*cp)->major == major &&
             (((*cp)->baseminor >= baseminor) ||
              ((*cp)->baseminor + (*cp)->minorct > baseminor))))
            break;
   //检查次设备号的范围是否有重叠,但是这个函数一开始就已经指定了此设备0-255(256个),   //所以肯定会重叠,这就要求,主设备号不能与已经存在的字符设备重叠,否则注册设备必然会失败   //其他的函数范围可能更小,就需要下面进行判断    /* Check for overlapping minor ranges.  */
    if (*cp && (*cp)->major == major) {
        int old_min = (*cp)->baseminor;
        int old_max = (*cp)->baseminor + (*cp)->minorct - 1;
        int new_min = baseminor;
        int new_max = baseminor + minorct - 1;

        /* New driver overlaps from the left.  */
        if (new_max >= old_min && new_max <= old_max) {
            ret = -EBUSY;
            goto out;
        }

        /* New driver overlaps from the right.  */
        if (new_min <= old_max && new_min >= old_min) {
            ret = -EBUSY;
            goto out;
        }
    }
    //添加到链表中
    cd->next = *cp;
    *cp = cd;
    mutex_unlock(&chrdevs_lock);
    return cd;
out:
    mutex_unlock(&chrdevs_lock);
    kfree(cd);
    return ERR_PTR(ret);
}

另一种注册字符设备函数,过程与上面的类似。struct cdev cdev;dev_t devno;int __init test_init(void){    int ret;  //静态分配  //指定主设备号和次设备号,并设备好的范围是1  //dev_t devno = MKDEV(globalmem_major, 0);  //ret = register_chrdev_region(&devno, 1, DEV_NAME);    //动态分配    ret = alloc_chrdev_region(&devno, 0, 1, DEV_NAME);    if(ret)    {           printk("alloc_chrdev_region failed!\n");        return -EAGAIN;    }       else        printk("major = %d\n", MAJOR(devno));

    cdev_init(&cdev, &fops);    cdev.owner = THIS_MODULE;

    ret = cdev_add(&cdev, devno, 1);     if(ret)    {           //释放之前分配的设备号        unregister_chrdev_region(devno, 1);         printk("cdev_add failed!\n");    }   

    return ret;}

int register_chrdev_region(dev_t from, unsigned count, const char *name){    struct char_device_struct *cd;    dev_t to = from + count;    dev_t n, next;   //判断申请的设备号的范围是否会溢出到下一个主设备。   //溢出的话,就分成基本分,一部分在当前主设备节点分配    //另一部分到下一主设备节点分配    for (n = from; n < to; n = next) {        next = MKDEV(MAJOR(n)+1, 0);        if (next > to)            next = to;        cd = __register_chrdev_region(MAJOR(n), MINOR(n),                   next - n, name);        if (IS_ERR(cd))            goto fail;    }    return 0;fail:    to = n;    for (n = from; n < to; n = next) {        next = MKDEV(MAJOR(n)+1, 0);        kfree(__unregister_chrdev_region(MAJOR(n), MINOR(n), next - n));    }    return PTR_ERR(cd);}

int alloc_chrdev_region(dev_t *dev, unsigned baseminor, unsigned count,            const char *name){    struct char_device_struct *cd;    cd = __register_chrdev_region(0, baseminor, count, name);    if (IS_ERR(cd))        return PTR_ERR(cd);    *dev = MKDEV(cd->major, cd->baseminor);    return 0;}

结构体关系如下所示

时间: 2024-08-08 09:42:21

linux字符设备的相关文章

linux字符设备驱动

一.字符设备.字符设备驱动与用户空间访问该设备的程序三者之间的关系. 如图,在Linux内核中使用cdev结构体来描述字符设备,通过其成员dev_t来定义设备号(分为主.次设备号)以确定字符设备的唯一性.通过其成员file_operations来定义字符设备驱动提供给VFS的接口函数,如常见的open().read().write()等. 在Linux字符设备驱动中,模块加载函数通过register_chrdev_region( ) 或alloc_chrdev_region( )来静态或者动态获

linux字符设备-自动创建设备号和设备节点

Linux字符设备-自动创建设备号和设备节点 先写一个自动分配字符设备号和设备节点的例子及APP 手动安装步骤: Insmod my_char_dev.ko 不需要再安装设备节点 然后是测试app ./my_char_dev_app 1 1 #include <linux/module.h> 2 #include <linux/init.h> 3 #include <linux/io.h> 4 #include <linux/fs.h> 5 #include

20150216简单的Linux字符设备驱动程序

20150216简单的Linux字符设备驱动程序 2015-02-16 李海沿 关于字符设备驱动程序详细的知识点,本文就不再介绍了,很多同志,看了知识点,还是一头雾水,写不出来,所以,本文从实战出发,带领各位同胞们来实现一个字符设备驱动程序,改程序可作为字符设备的通用模板. 好了废话不多说,先上驱动程序,在驱动程序中加入详细注释: 1 /****************************** 2 linux 字符设备驱动程序 3 *****************************/

Linux字符设备中的两个重要结构体(file、inode)

对于Linux系统中,一般字符设备和驱动之间的函数调用关系如下图所示 上图描述了用户空间应用程序通过系统调用来调用程序的过程.一般而言在驱动程序的设计中,会关系 struct file 和 struct inode 这两个结构体. 用户空间使用open()系统调用函数打开一个字符设备时( int fd = open("dev/demo", O_RDWR) )大致有以下过程: 在虚拟文件系统VFS中的查找对应与字符设备对应 struct inode节点 遍历字符设备列表(chardevs

linux字符设备驱动程序框架

#include <linux/module.h> #include <linux/kernel.h> #include <linux/init.h> #include <linux/fs.h> #include <linux/delay.h> #include <asm/uaccess.h> #include <asm/irq.h> #include <asm/io.h> #include <asm/a

linux字符设备驱动程序框架(老方法)

#include <linux/module.h> #include <linux/kernel.h> #include <linux/init.h> #include <linux/fs.h> #include <linux/delay.h> #include <asm/uaccess.h> #include <asm/irq.h> #include <asm/io.h> #include <asm/a

linux字符设备驱动程序源码分析

本文主要分析linux-2.6.28内核版本的字符设备抽象层源码文件char_dev.c.该文件代码量不大,但其为linux应用程序访问实际字符型硬件设备搭建了桥梁,进一步限定了linux字符设备驱动的设计框架. void __init chrdev_init(void) {  cdev_map = kobj_map_init(base_probe, &chrdevs_lock);  bdi_init(&directly_mappable_cdev_bdi); } 该函数在系统初始化启动时

深入理解Linux字符设备驱动

文章从上层应用访问字符设备驱动开始,一步步地深入分析Linux字符设备的软件层次.组成框架和交互.如何编写驱动.设备文件的创建和mdev原理,对Linux字符设备驱动有全面的讲解. 本文整合之前发表的<Linux字符设备驱动剖析>和<Linux 设备文件的创建和mdev>两篇文章,基于linux字符设备驱动的所有相关知识给读者一个完整的呈现. 一.从最简单的应用程序入手 1.很简单,open设备文件,read.write.ioctl,最后close退出.如下: 二./dev目录与文

(57)Linux驱动开发之三Linux字符设备驱动

1.一般情况下,对每一种设备驱动都会定义一个软件模块,这个工程模块包含.h和.c文件,前者定义该设备驱动的数据结构并声明外部函数,后者进行设备驱动的具体实现. 2.典型的无操作系统下的逻辑开发程序是:这种三层的裸机驱动模型是足够满足低耦合.高内聚的特点的. 3.当有操作系统存在时,设备驱动成为了连接硬件和内核的桥梁,这时候的设备驱动对外表现为操作系统的API,与直接裸机开发不同,裸机开发时的设备驱动是应用工程师的API.如果设备驱动都按照操作系统给出的独立于设备的接口而设计,应用程序将可以使用统

Linux字符设备驱动实现

Linux字符设备驱动实现 要求 编写一个字符设备驱动,并利用对字符设备的同步操作,设计实现一个聊天程序.可以有一个读,一个写进程共享该字符设备,进行聊天:也可以由多个读和多个写进程共享该字符设备,进行聊天 主要过程 实现 字符驱动设备 /* 参考:深入浅出linux设备驱动开发 */ #include <linux/module.h> #include <linux/init.h> #include <linux/fs.h> #include <asm/uacc