字符设备驱动内部实现原理解析

这篇具有很好参考价值的文章主要介绍了字符设备驱动内部实现原理解析。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

一. 字符设备驱动对象内部实现原理解析

字符设备驱动内部实现原理解析
用户层:

​ 当用户打开(open)一个文件时,会生成一个文件描述符表

内核层:

  1. 内核层的VFS虚拟文件系统层,根据open传递的文件路径找到文件的inode 结构体
    • inode号是存在文件系统的唯一标识,同时也是索引当前文件的inode的索引号
  2. 根据inode结构体找到文件对应的驱动对象指针(struct cdev)
  3. 根据驱动对象指针找到驱动对象的操作方法结构体指针(struct file_operations *ops)
  4. 回调操作方法结构体指针中的open

二. 字符设备驱动的注册流程

  1. 为字符设备驱动对象申请空间

    1. struct cdev cdev;//直接分配一个变量空间

    2. struct cdev *cdev_alloc(void);//手动申请字符设备驱动对象空间
      返回值:成功返回申请空间首地址,失败返回NULL
      
  2. 字符设备驱动对象的初始化

    1. 实现字符设备驱动对象的部分初始化

      void cdev_init(struct cdev *cdev, const struct file_operations *fops);
      功能:实现字符设备驱动对象的部分初始化
      参数:
      cdev:字符设备驱动对象指针
      fops:操作方法结构体指针
      返回值:无

    2. 申请设备号

      1. 静态指定设备号

        int register_chrdev_region(dev_t from, unsigned count, const char *name)
        参数:
        from:要申请的设备号
        count:要申请的设备资源的数量
        name:驱动名字
        返回值:成功返回0,失败返回 错误码

      2. 动态申请一定范围的设备号

        int alloc_chrdev_region(dev_t *dev, unsigned baseminor, unsigned count,
        const char *name)
        参数:
        dev:申请的设备号填充在这个变量中
        baseminor:次设备号的起始值
        count:要申请的设备资源的数量
        name:驱动名字
        返回值:成功返回0,失败返回 错误码

    3. 将字符设备驱动对象注册进内核

    int cdev_add(struct cdev *cdev, dev_t dev, unsigned count);
    参数:
    cdev:字符设备驱动对象指针
    dev:申请的设备号的起始值
    count:设备数量
    返回值:成功返回0,失败返回错误码

    1. 注销流程

      1. 字符设备驱动对象的注销

      void cdev_del(struct cdev *);
      功能:字符设备驱动对象的注销
      参数:字符设备驱动对象指针
      返回值:无

      1. 释放申请的设备号

      unregister_chrdev_region(MKDEV(major,minor),3);

      1. 释放对象空间

        kfree(cdev);

三. 代码示例

#include <linux/init.h>
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/io.h>
#include <linux/device.h>
#include <linux/cdev.h>
#include <linux/slab.h>

#include <linux/init.h>
#include <linux/module.h>

struct cdev *cdev;
int major, minor; // 设备号,全局为0
dev_t devno;
struct class *cls;
struct device *dev;

int mycdev_open(struct inode *inode, struct file *file)
{
    printk("%s:%s:%d\n", __FILE__, __func__, __LINE__);
    return 0;
}

long mycdev_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
{
    return 0;
}
int mycdev_close(struct inode *inode, struct file *file)
{
    printk("%s:%s:%d\n", __FILE__, __func__, __LINE__);
    return 0;
}
struct file_operations ops = {
    .open = mycdev_open,
    .unlocked_ioctl = mycdev_ioctl,
    .release = mycdev_close,
};
static int __init mycdev_init(void)
{
    int ret;
    // 分配对象空间
    cdev = cdev_alloc();
    if (NULL == cdev)
    {
        printk("字符设备驱动对象分配失败\n");
        ret = -EFAULT;
        goto OUT1;
    }
    printk("字符设备驱动对象分配成功\n");
    // 初始化对象
    cdev_init(cdev, &ops);
    // 申请设备号,采用动态申请的方式
    ret = alloc_chrdev_region(&devno, minor, 1, "mycdev");
    if (ret)
    {
        printk("申请设备号失败\n");
        goto OUT2;
    }
    major = MAJOR(devno);
    minor = MINOR(devno);
    printk("设备号申请成功\n");
    // 注册字符设备驱动对象
    ret = cdev_add(cdev, MKDEV(major, minor), 1);
    if (ret)
    {
        printk("注册字符设备驱动对象失败\n");
        goto OUT3;
    }
    printk("注册字符设备驱动对象成功\n");
    // 向上提交目录
    cls = class_create(THIS_MODULE, "mycdev");
    if (IS_ERR(cls))
    {
        printk("向上提交目录失败\n");
        ret = -PTR_ERR(cls);
        goto OUT4;
    }
    // 向上提交结点信息
    dev = device_create(cls, NULL, MKDEV(major, 0), NULL, "mycdev");
    if (IS_ERR(dev))
    {
        ret = -PTR_ERR(dev);
        goto OUT5;
    }
OUT5:
    // 释放已经申请的设备节点信息

    device_destroy(cls, MKDEV(major, 0));

    // 释放目录空间
    class_destroy(cls);
OUT4:
    // 注销字符设备驱动对象
    cdev_del(cdev);
OUT3:
    // 释放设备号
    unregister_chrdev_region(MKDEV(major, minor), 1);
OUT2:
    kfree(cdev);
OUT1:
    return ret;
}
static void __exit mycdev_exit(void)
{
    // 销毁设备节点
    device_destroy(cls, MKDEV(major, 0));
    // 释放目录空间
    class_destroy(cls);
    // 1.注销字符设备驱动对象
    cdev_del(cdev);
    // 2.释放设备号
    unregister_chrdev_region(MKDEV(major, minor), 0);
    // 3.释放对象空间
    kfree(cdev);
}
module_init(mycdev_init);
module_exit(mycdev_exit);
MODULE_LICENSE("GPL");

字符设备驱动内部实现原理解析文章来源地址https://www.toymoban.com/news/detail-498989.html

到了这里,关于字符设备驱动内部实现原理解析的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 字符设备驱动实例(ADC驱动)

            ADC是将模拟信号转换为数字信号的转换器,在 Exynos4412 上有一个ADC,其主要的特性如下。 (1)量程为0~1.8V。 (2)精度有 10bit 和 12bit 可选。 (3)采样时钟最高为5MHz,转换速率最高为1MSPS (4)具有四路模拟输入,同一时刻只有一路进行转换 (5) 转换完成后可以产生中断。

    2024年02月11日
    浏览(38)
  • 嵌入式Linux(8):字符设备驱动--注册字符类设备

    杂项设备 注册杂项设备: 注销杂项设备: 字符类设备 文件:include/linux/cdev.h 步骤流程: 定义一个cdev结构体。 使用cdev_init函数初始化cdev结构体成员变量。 参数: 第一个:要初始化的cdev结构体 第二个:文件操作集: cdev-ops = fops;//实际就是把文件操作集写ops 使用cdev_add函数

    2023年04月22日
    浏览(47)
  • Linux 驱动学习笔记 ——(1)字符设备驱动

    《【正点原子】I.MX6U嵌入式Linux驱动开发指南》学习笔记 字符设备是 Linux 驱动中最基本的一类设备驱动,字节设备就是按照字节流来读写的设备,常见的字符设备包括:LED、蜂鸣器、按键、I2C 以及 SPI 等。 Linux 中一切皆文件,字符设备驱动加载成功后会在 /dev 目录下生成相

    2024年02月08日
    浏览(55)
  • Linux设备驱动——第三章字符驱动

    当对幸福的憧憬过于急切,那痛苦就在人的心灵深处升起。——加缪 本章的目的是编写一个完整的字符设备驱动。我们开发一个字符驱动是因为这一类适合大部分简单的硬件设备。字符驱动也比块驱动易于理解。本章的最终目的是编写一个模块化的字符驱动,但是我们不会在

    2024年02月08日
    浏览(80)
  • 字符设备驱动

    前面内容: 1 Linux驱动—内核模块基本使用 2 Linux驱动—内核模块参数,依赖(进一步讨论) linux根据驱动程序实现的模型框架将设备的驱动分为了三类: 字符设备驱动:以字节流为单位顺序读写,不能随机访问。如,帧缓冲 ( framebuffer)驱动,声卡,串口等。 块设备驱动:以固定

    2023年04月08日
    浏览(32)
  • 字符设备驱动开发

    1、字符设备驱动简介 字符设备是 Linux 驱动中最基本的一类设备驱动, 字符设备就是一个一个字节,按照字节 流进行读写操作的设备,读写数据是分先后顺序的。 比如我们最常见的点灯、按键、IIC、SPI, LCD 等等都是字符设备,这些设备的驱动就叫做字符设备驱动。 先来简

    2024年02月10日
    浏览(36)
  • 字符设备驱动开发(最初方式)

    字符设备是Linux中最基本的一类设备驱动,我们常见的点灯、按键、IIC、SPI、LCD等等都是通过字符设备驱动框架来进行开发的。字符设备驱动是通过一个一个字节流的方式来进行读写操作设备,读写数据是分先后顺序的。 通过空间划分的方式来说,Linux系统中分为用户空间和

    2024年02月17日
    浏览(38)
  • Linux 驱动之字符设备

    什么是设备号 Linux 规定每一个字符设备或者块设备都必须有一个专属的设备号。一个设备号由主设备号和次设备号组成。主设备号用来表示某一类驱动,如鼠标,键盘都可以归类到 USB 驱动中。而次设备号是用来表示这个驱动下的各个设备。比如第几个鼠标,第几个键盘等。

    2024年02月16日
    浏览(38)
  • Linux -- 字符设备驱动--LED的驱动开发(初级框架)

    看原理图确定引脚,确定引脚输出什么电平才能点亮 / 熄灭 LED 看主芯片手册,确定寄存器操作方法:哪些寄存器?哪些位?地址是? 编写驱动:先写框架,再写硬件操作的代码 注意 :在芯片手册中确定的寄存器地址被称为 物理地址 ,在 Linux 内核中无法直接使用。 需要使

    2024年04月28日
    浏览(31)
  • Linux 驱动之高级字符设备

    什么是IO呢? IO 的英文全称是 input 和output,翻译过来就是输入和输出。 在冯.诺依曼结构中,将计算机分成分为5个部分: 运算器、控制器、存储器、输入设备、输出设备 。其中输入设备指的是向计算机输入数据或者信息,如鼠标,键盘都是输入设备。输出设备指的是用于接收

    2023年04月14日
    浏览(41)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包