1.3 字符设备驱动

news2024/11/24 17:51:23

1、字符设备驱动工作原理

2、file_operations结构体

struct file_operations { 
  struct module *owner;                                                                    //拥有该结构的模块的指针,一般为THIS_MODULES  
   loff_t (*llseek) (struct file *, loff_t, int);                                            //用来修改文件当前的读写位置  
   ssize_t (*read) (struct file *, char __user *, size_t, loff_t *);                         //从设备中同步读取数据   
   ssize_t (*write) (struct file *, const char __user *, size_t, loff_t *);                  //向设备发送数据  
   ssize_t (*aio_read) (struct kiocb *, const struct iovec *, unsigned long, loff_t);        //初始化一个异步的读取操作   
   ssize_t (*aio_write) (struct kiocb *, const struct iovec *, unsigned long, loff_t);       //初始化一个异步的写入操作   
  int (*readdir) (struct file *, void *, filldir_t);                                       //仅用于读取目录,对于设备文件,该字段为NULL   
   unsigned int (*poll) (struct file *, struct poll_table_struct *);                         //轮询函数,判断目前是否可以进行非阻塞的读写或写入   
  int (*ioctl) (struct inode *, struct file *, unsigned int, unsigned long);               //执行设备I/O控制命令   
  long (*unlocked_ioctl) (struct file *, unsigned int, unsigned long);                     //不使用BLK文件系统,将使用此种函数指针代替ioctl  
  long (*compat_ioctl) (struct file *, unsigned int, unsigned long);                       //在64位系统上,32位的ioctl调用将使用此函数指针代替   
  int (*mmap) (struct file *, struct vm_area_struct *);                                    //用于请求将设备内存映射到进程地址空间  
  int (*open) (struct inode *, struct file *);                                             //打开   
  int (*flush) (struct file *, fl_owner_t id);   
  int (*release) (struct inode *, struct file *);                                          //关闭   
  int (*fsync) (struct file *, struct dentry *, int datasync);                             //刷新待处理的数据   
  int (*aio_fsync) (struct kiocb *, int datasync);                                         //异步刷新待处理的数据   
  int (*fasync) (int, struct file *, int);                                                 //通知设备FASYNC标志发生变化   
  int (*lock) (struct file *, int, struct file_lock *);   
  ssize_t (*sendpage) (struct file *, struct page *, int, size_t, loff_t *, int);   
  unsigned long (*get_unmapped_area)(struct file *, unsigned long, unsigned long, unsigned long, unsigned long);   
  int (*check_flags)(int);   
  int (*flock) (struct file *, int, struct file_lock *);  
  ssize_t (*splice_write)(struct pipe_inode_info *, struct file *, loff_t *, size_t, unsigned int);  
  ssize_t (*splice_read)(struct file *, loff_t *, struct pipe_inode_info *, size_t, unsigned int);   
  int (*setlease)(struct file *, long, struct file_lock **);   
};  

3、注册字符设备驱动

向内核注册字符设备,也就是在内核中登记,让内核中分配一个设备号,标识该驱动。

(1)在Linux内核2.4版本前注册字符设备驱动的方式:

// 申请注册设备号以第一个参数来辨别动态与静态。
//1、如果第一个参数是0,表示动态的分配给此驱动程序一个主设备号,
//2、非零时候,表示备驱动程序向系统申请主设备号,
static inline int register_chrdev(unsigned int major, const char *name, const struct file_operations *fops)
{
    return __register_chrdev(major, 0, 256, name, fops);
}

//主设备号必须和注册时候的主设备号一致,如果注册时候是动态的分配的主设备号,就需要保存起来
static inline void unregister_chrdev(unsigned int major, const char *name)
{
    __unregister_chrdev(major, 0, 256, name);
}

(2)在Linux内核2.6版本后注册字符设备驱动的方式:

设备号申请

//静态申请
/*************************************
* from:注册的指定起始设备编号
* count:需要连续注册的次设备编号个数
* name:字符设备名称
*************************************/
int register_chrdev_region(dev_t from, unsigned count, const char *name);

//动态申请
/******************************************************
* dev: 存放起始设备编号的指针
* baseminor:次设备号基地址
* count:需要连续注册的次设备编号个数
* name:字符设备名称
*******************************************************/
int alloc_chrdev_region(dev_t *dev, unsigned baseminor, unsigned count,const char *name);


//注销设备号
void unregister_chrdev_region(dev_t from, unsigned count);

注册设备驱动

//MKDEV:将主设备号和次设备号转换成dev_t类型
struct cdev
{
    struct kobject    kobj;
    struct module   *owner;                   //所属模块
    const struct file_operations  *ops;
    struct list_head  list;              //与 cdev 对应的字符设备文件的 inode->i_devices 的链表头
    dev_t dev;                    //起始设备编号,可以通过MAJOR(),MINOR()来提取主次设备号
    unsigned int count;                     //连续注册次设备号的个数
}
//通过宏定义来获取主、次设备号
MAJOR(dev_t dev)
MINOR(dev_t dev)

//通过主、次设备号生成dev_t
MKDEV(int major,int minor) 

//初始化,建立cdev和file_operation 之间的连接
void cdev_init(struct cdev *, const struct file_operations *); 

//动态申请一个cdev内存
struct cdev *cdev_alloc(void);  

//释放
void cdev_put(struct cdev *p);   

//注册设备
int cdev_add(struct cdev *, dev_t, unsigned);    

//注销设备
void cdev_del(struct cdev *);

4、应用和内核之间的数据交换

/*************************************************
*to:目标地址(用户空间)
*from:源地址(内核空间)
*n:将要拷贝数据的字节数
*返回:成功返回0,失败返回没有拷贝成功的数据字节数
**************************************************/
unsigned long copy_to_user(void *to, const void *from, unsigned long n);

/*************************************************
*to:目标地址(内核空间)
*from:源地址(用户空间)
*n:将要拷贝数据的字节数
*返回:成功返回0,失败返回没有拷贝成功的数据字节数
**************************************************/
unsigned long copy_from_user(void *to, const void *from, unsigned long n);

5、简单的字符设备驱动示例

#include <linux/module.h>    //module_init() module_exit()
#include <linux/init.h>      //__init __exit

static int module_test_open(struct inode *inode, struct file *file)
{
    return 0;
}

static int module_test_release(struct inode *inode, struct file *file)
{
    return 0;
}

//应用层和驱动之间的数据交换,copy_from_user:从用户空间复制到内核空间,copy_to_user:从内核空间复制到用户空间
static ssize_t module_test_read(struct file *file, char __user *buf, size_t count, loff_t *ppos)
{
    char kBuf[1024] = "module_test_read";
    int ret = copy_to_user(ubuf, kBuf, count);
    if (ret == 0)
        printk(KERN_DEBUG, "copy_to_user success");
    return count;
}
static ssize_t module_test_write(struct file *file, const char __user *ubuf, size_t count, loff_t *ppos)
{
    char kBuf[1024];
    int ret = copy_from_user(kBuf, ubuf, count);
    if (ret == 0)
        printk(KERN_DEBUG, "copy_from_user success");
    return count;
}

struct file_operations module_test_fops =
{
    .owner = THIS_MODULE,
    .read = module_test_read,
    .write = module_test_write,
    .open = module_test_open,
    .release = module_test_realse,
};

//__init是一个宏定义,#define __init xxx,作用是编译时,将__init修饰的
//函数放入.init.text中,内核启动时会统一加载.init.text段,加载后释放
static int __init chrdev_module_init(void)
{
    //printk是内核源码中用来打印信息的函数,KERN_DEBUG是打印级别
    printk(KERN_DEBUG "chrdev_module_init");
    
    //注册字符设备驱动
    register_chrdev(200, "module_test", &module_test_fops);
    
    return 0;
}

static void __exit chrdev_module_exit(void)
{
    printk(KERN_DEBUG "chrdev_module_exit");
    
    //注销字符设备驱动
    unregister_chrdev(200, "module_test");
}

module_init(chrdev_module_init);
module_exit(chrdev_module_exit);

//宏定义
MODULE_LICENSE("GPL")                 //模块的许可证
MODULE_AUTHOR("xy_L")                 //模块的作者
MODULE_DESCIPTION("chrdev module")    //模块的描述
MODULE_ALIAS("module_test")           //模块的别名

6、查看Linux系统已经注册的驱动设备号

cat /proc/devices

7、驱动设备文件的创建

设备文件的关键信息:设备号 = 主设备号 + 次设备号,使用mknod创建设备文件:

mknod /dev/xxx c 主设备号 次设备号

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1591612.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【日常记录】【CSS】生成动态气泡小球

文章目录 1、分析2、实现 1、分析 核心有两点&#xff0c;通过这两个不一样就可以实现每个小球的颜色、动画时间不一致 给每个元素都设置一个css 变量 bgc 用于控制每一个小球的颜色给每个元素都设置一个css 变量 duration 用于控制每一个小球的时间 2、实现 <!DOCTYPE ht…

java中的线程通讯和线程池,Callable任务

线程通讯&#xff1a; 在多线程中&#xff0c;某个线程进入“等待状态”时&#xff0c;需要某个线程来唤醒 等待方法&#xff1a; wait()//无线等待 wait(long 毫秒)//计时等待 注意&#xff0c;调用wait方法&#xff0c;会自动释放掉锁资源 处于wait状态只能由其他线程唤醒 唤…

信创产品适配的前因后果

“信创”的本意是指“信息技术应用创新”。这个概念最早来源于“信创工委会”&#xff08;信息技术应用创新工作委员会&#xff09;&#xff0c;一个由24家专业从事软硬件关键技术研究及应用的国内单位&#xff0c;在2016年共同发起成立的非营利性社会组织。近些年来&#xff0…

GitHub repository - commits - branches - releases - contributors

GitHub repository - commits - branches - releases - contributors 1. commits2. branches3. releases4. contributorsReferences 1. commits 在这里可以查看当前分支的提交历史。左侧的数字表示提交数。 2. branches 可以查看仓库的分支列表。左侧的数字表示当前拥有的分…

android studio 网络请求okhttp3、okgo

一、在build.gradle文件里添加 implementation com.squareup.okhttp3:okhttp:4.9.0 implementation com.squareup.okhttp3:okhttp:3.12.0 implementation com.squareup.okio:okio:1.17.4 implementation com.lzy.net:okgo:3.0.4 implementation com.alibaba:fastjson:1.2.57 i…

25、链表-环形链表

思路&#xff1a; 这道题就是判断链表中是否有环&#xff0c;首先使用集合肯定可以快速地解决&#xff0c;比如通过一个set集合遍历&#xff0c;如果遍历过程中有节点在set中已经存在那么说明存在环。 第二种方式就是通过快慢指针方式寻找环。具体思路就是一个慢指针每次直走一…

Android适配平板屏幕尺寸

一、划分手机和平板 人为判断方法: 大于6英寸的就是平板。小于6英寸的都是手机 平板尺寸&#xff1a; 6英寸、7英寸、10英寸、14英寸… Android系统支持多配置资源文件&#xff0c;我们可以追加新的资源目录到你的Android项目中。命名规范&#xff1a; 资源名字-限制符 l…

2024年MathorCup数学建模B题甲骨文智能识别中原始拓片单字自动分割与识别研究解题文档与程序

2024年第十四届MathorCup高校数学建模挑战赛 B题 甲骨文智能识别中原始拓片单字自动分割与识别研究 原题再现&#xff1a; 甲骨文是我国目前已知的最早成熟的文字系统&#xff0c;它是一种刻在龟甲或兽骨上的古老文字。甲骨文具有极其重要的研究价值&#xff0c;不仅对中国文…

Go程序设计语言 学习笔记 第十一章 测试

1949年&#xff0c;EDSAC&#xff08;第一台存储程序计算机&#xff09;的开发者莫里斯威尔克斯在他的实验室楼梯上攀登时突然领悟到一件令人震惊的事情。在《一位计算机先驱的回忆录》中&#xff0c;他回忆道&#xff1a;“我突然完全意识到&#xff0c;我余生中的很大一部分时…

2024妈妈杯mathorcup数学建模C题 物流网络分拣中心货量预测及人员排班

一、数据预处理 数据清洗是指对数据进行清洗和整理&#xff0c;包括删除无效数据、缺失值填充、异常值检测和处理等。数据转换是指对数据进行转换和变换&#xff0c;包括数据缩放、数据归一化、数据标准化等。数据整理是指对数据进行整理和归纳&#xff0c;包括数据分组、数据聚…

一文读懂Java中的WebEndpointProperties类(附Demo)

目录 前言1. 基本知识2. Demo3. 彩蛋 前言 对于Java的相关知识&#xff0c;推荐阅读&#xff1a;java框架 零基础从入门到精通的学习路线 附开源项目面经等&#xff08;超全&#xff09; 1. 基本知识 Spring Boot 的配置类 WebEndpointProperties&#xff0c;用于配置 Web 端…

【python】基于pyttsx3库的字符串转音频文件

一、源码 import pyttsx3 engine pyttsx3.init() engine.setProperty(volume, 0.8) engine.setProperty(rate, 150) engine.save_to_file("Hello, World!", "output.mp3") engine.runAndWait()二、介绍 使用pyttsx3库&#xff0c;设置声音与速率&#x…

RTR3学习笔记

目录 引言第二章、图形渲染管线2.1 图形渲染管线架构概述&#xff08;1&#xff09;渲染管线的主要功能&#xff08;2&#xff09;渲染结果是由输入对象相互作用产生的&#xff08;3&#xff09;图像渲染管线的三个阶段&#xff08;4&#xff09;其他讨论 2.2 应用程序阶段&…

SpringBoo利用 MDC 机制过滤出单次请求相关的日志

&#x1f3f7;️个人主页&#xff1a;牵着猫散步的鼠鼠 &#x1f3f7;️系列专栏&#xff1a;Java全栈-专栏 &#x1f3f7;️个人学习笔记&#xff0c;若有缺误&#xff0c;欢迎评论区指正 目录 1.前言 2.MDC 是什么 3.代码实战 4.总结 1.前言 在服务出现故障时&#xff…

Composer 安装与配置

Composer 是 PHP 领域中非常重要的一个工具&#xff0c;它作为 PHP 的依赖管理工具&#xff0c;帮助开发者定义、管理、安装项目所依赖的外部库。Composer 的出现极大地简化了 PHP 项目的构建和管理过程&#xff0c;使得开发者可以更加专注于代码的编写和功能的实现。 Compose…

matlab使用教程(42)—常见的二维图像绘制方法

这个博客用于演示如何在 MATLAB 中创建曲线图、条形图、阶梯图、误差条形图、极坐标图、针状图、散点图。 1.曲线图 plot 函数用来创建 x 和 y 值的简单线图。 x 0:0.05:5; y sin(x.^2); figure plot(x,y) 运行结果&#xff1a; 线图可显示多组 x 和 y 数据。 x 0:0.05:…

气膜建筑的优势与应用—轻空间为您解答

气膜建筑作为一种利用气膜材料构建主体结构的建筑形式&#xff0c;在现代建筑领域日益受到关注。轻空间将介绍气膜建筑的优势以及其在不同领域的应用。 1. 轻便灵活&#xff1a; 气膜建筑采用轻质材料&#xff0c;相比传统建筑更为轻便&#xff0c;从而减轻了基础负荷和运输成本…

day05-Elasticsearch01

1.初识elasticsearch 1.1.了解ES 1.1.1.elasticsearch的作用 elasticsearch 是一款非常强大的开源搜索引擎&#xff0c;具备非常多强大功能&#xff0c;可以帮助我们从海量数据中快速找到需要的内容 例如&#xff1a; 在 GitHub 搜索代码在电商网站搜索商品在百度搜索答案在打…

Vue第三方组件使用

文章目录 一、组件传值二、elementui组件使用三、fontawesome图标 一、组件传值 1、父组件与孩子组件传值 在孩子组件中定义props属性&#xff0c;里面定义好用于接收父亲数据的变量。 孩子组件是Movie Movie.vue。注意看在Movie组件里面有props对象中的title和rating属性用…

如何利用python机器学习解决空间模拟与时间预测问题

徐老师&#xff1a;副教授&#xff1a;长期从事定量遥感、遥感数据同化、地表水热通量转化等相关领域的研究&#xff0c;发表多篇相关领域核心期刊论文&#xff0c;具有丰富的实践技术经验。 专题一、机器学习原理与概述 了解机器学习的发展历史、计算原理、基本定义&#xf…