驱动开发——字符设备

字符设备

Linux 将系统设备分为:字符设备、块设备、网络设备。

Linux系统框架

工作原理

	字符设备是 Linux 驱动中最基本的一类设备驱动,字符设备就是一个一个字节,
按照字节流进行读写操作的设备,读写数据是分先后顺序的。在Linux的世界里面一切皆文件,所有的硬件设备操作到应用层都会被抽象成文件
的操作。我们知道如果应用层要访问硬件设备,它必定要调用到硬件对应的驱动程序。1.在Linux文件系统中,每个文件都用一个struct inode结构体来描述,这个结构体里面记录了	这个文件的所有信息,例如:文件类型,访问权限等。

struct inode信息

2.在Linux操作系统中,每个驱动程序在应用层的/dev目录下都会有一个设备文件和它对应,并且该文件会有对应的主设备号和次设备号。
3.在Linux操作系统中,每个驱动程序都要分配一个主设备号,字符设备的设备号保存在struct cdev结构体中。struct cdev {struct kobject kobj;struct module *owner;const struct file_operations *ops;//接口函数集合struct list_head list;//内核链表dev_t dev;    //设备号unsigned int count;//次设备号个数};
4.在Linux操作系统中,每打开一次文件,Linux操作系统在VFS层都会分配一个struct file结构体来描述打开的这个文件。该结构体用于维护文件打开权限、文件指针偏移值、私有内存地址等信息。(strcut file有两个非常重要的字段:文件描述符和缓冲区)

在这里插入图片描述

字符驱动相关函数

注册

	内核共提供了三个函数来注册一组字符设备编号,这三个函数分别是 	
register_chrdev_region()、alloc_chrdev_region()和 register_chrdev()。

注意事项

1.使用register_chrdev注册字符设备,其内部申请struct cdev 结构,并调用cdev_add函数添加设备。
2.使用register_chrdev_region/alloc_chrdev_region注册字符设备,需要在外部事先定义struct cdev 结构,然后使用函数cdev_init初始化它,最后还需在外部调用cdev_add函数添加设备。
//比较老的内核注册的形式 早期的驱动
static inline int register_chrdev(unsigned int major, const char *name,const struct file_operations *fops)
功能:注册或者分配设备号,并注册fops到cdev结构体,如果major>0,功能为注册该主设备号,如果major=0,功能为动态分配主设备号。
参数:@major : 主设备号@name : 设备名称,执行 cat /proc/devices显示的名称@fops  : 文件系统的接口指针
返回值如果major>0   成功返回0,失败返回负的错误码如果major=0  成功返回主设备号,失败返回负的错误码
int register_chrdev_region(dev_t from, unsigned count, const char *name)
功能:注册一个范围()的设备号
参数:@from 设备号@count 注册的设备个数@name 设备的名字
返回值:成功返回0,失败返回错误码(负数)
int alloc_chrdev_region(dev_t *dev, unsigned baseminor, unsigned count,const char *name)
功能:注册一个主设备号由内核动态分配,次设备号为baseminor~baseminor+count的设备驱动
参数:@dev: 用来获取设备号@baseminor:次设备号起始值@count: 次设备号个数@name: 设备名称     
返回值:成功返回0,失败返回错误码(负数)     
//注销
static inline void unregister_chrdev(unsigned int major, const char *name)
void cdev_init(struct cdev *cdev, const struct file_operations *fops)
功能:初始化cdev结构体
参数:@cdev cdev结构体地址@fops 操作字符设备的函数接口地址
返回值:无
int cdev_add(struct cdev *p, dev_t dev, unsigned count)
功能:添加一个字符设备到操作系统
参数:@p cdev结构体地址@dev 设备号@count 次设备号个数
返回值:成功返回0,失败返回错误码(负数)
void cdev_del(struct cdev *p)
功能:从系统中删除一个字符设备
参数:@p cdev结构体地址
返回值:无
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 **); };

实例

#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 <asm/uaccess.h>
#include <asm/irq.h>
#include <asm/io.h>
#include <asm/arch/regs-gpio.h>
#include <asm/hardware.h>
#include <linux/poll.h>
#include <linux/cdev.h>/* 1. 确定主设备号 */
static int major;static int hello_open(struct inode *inode, struct file *file)
{printk("hello_open\n");return 0;
}static int hello2_open(struct inode *inode, struct file *file)
{printk("hello2_open\n");return 0;
}/* 2. 构造file_operations */
static struct file_operations hello_fops = {.owner = THIS_MODULE,.open  = hello_open,
};static struct file_operations hello2_fops = {.owner = THIS_MODULE,.open  = hello2_open,
};#define HELLO_CNT   2static struct cdev hello_cdev;
static struct cdev hello2_cdev;
static struct class *cls;static int hello_init(void)
{dev_t devid;/* 3. 告诉内核 */
#if 0major = register_chrdev(0, "hello", &hello_fops); /* (major,  0), (major, 1), ..., (major, 255)都对应hello_fops */
#elseif (major) {// 事先知道可用的主设备号,和起始次设备号devid = MKDEV(major, 0);register_chrdev_region(devid, HELLO_CNT, "hello");  /* (major,0~1) 对应 hello_fops, (major, 2~255)都不对应hello_fops */} else {// 事先不知道可用的主设备号,由内核动态分配alloc_chrdev_region(&devid, 0, HELLO_CNT, "hello"); /* (major,0~1) 对应 hello_fops, (major, 2~255)都不对应hello_fops */major = MAJOR(devid);                     }cdev_init(&hello_cdev, &hello_fops);cdev_add(&hello_cdev, devid, HELLO_CNT);devid = MKDEV(major, 2);register_chrdev_region(devid, 1, "hello2");cdev_init(&hello2_cdev, &hello2_fops);cdev_add(&hello2_cdev, devid, 1);#endifcls = class_create(THIS_MODULE, "hello");// 使用register_chrdev注册,下面的四个设备节点都将对应该设备驱动,都能调用hello_open// 使用 register_chrdev_region/alloc_chrdev_region (60/63)注册,设备节点/dev/hello0、/dev/hello1对应hello_fops设备驱动,调用hello_open打开// 使用 register_chrdev_region/alloc_chrdev_region (71)注册,设备节点/dev/hello2对应hello2_fops设备驱动,调用hello2_open打开// /dev/hello3节点未注册到设备驱动,无法打开设备。class_device_create(cls, NULL, MKDEV(major, 0), NULL, "hello0"); /* /dev/hello0 */class_device_create(cls, NULL, MKDEV(major, 1), NULL, "hello1"); /* /dev/hello1 */class_device_create(cls, NULL, MKDEV(major, 2), NULL, "hello2"); /* /dev/hello2 */class_device_create(cls, NULL, MKDEV(major, 3), NULL, "hello3"); /* /dev/hello3 */return 0;
}static void hello_exit(void)
{class_device_destroy(cls, MKDEV(major, 0));class_device_destroy(cls, MKDEV(major, 1));class_device_destroy(cls, MKDEV(major, 2));class_device_destroy(cls, MKDEV(major, 3));class_destroy(cls);cdev_del(&hello_cdev);unregister_chrdev_region(MKDEV(major, 0), HELLO_CNT);cdev_del(&hello2_cdev);unregister_chrdev_region(MKDEV(major, 2), 1);
}module_init(hello_init);
module_exit(hello_exit);MODULE_LICENSE("GPL");

在这里插入图片描述


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

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

相关文章

如何修复损坏的DOC和DOCX格式Word文件?

我们日常办公中&#xff0c;经常用到Word文档。但是有时会遇到word文件损坏、无法打开的情况。这时该怎么办&#xff1f;接着往下看&#xff0c;小编在这里就给大家带来最简单的Word文件修复方法&#xff01; 很多时候DOC和DOCX Word文件会无缘无故的损坏无法打开&#xff0c;一…

【福建事业单位-综合基础知识】03行政法

【福建事业单位-综合基础知识】03行政法 1.行政法概述&#xff08;原则重点&#xff09;行政主体范围 行政行为总结 二.行政处罚2.1行政处罚的种类总结 行政法框架 1.行政法概述&#xff08;原则重点&#xff09; 行政法的首要原则是合法&#xff1b;自由裁量——合理行政&…

SSM——用户、角色、权限操作

1. 数据库与表结构 1.1 用户表 1.1.1 用户表信息描述 users 1.1.2 sql语句 CREATE TABLE users( id varchar2(32) default SYS_GUID() PRIMARY KEY, email VARCHAR2(50) UNIQUE NOT NULL, username VARCHAR2(50), PASSWORD VARCHAR2(50), phoneNum VARCHAR2(20), STATUS INT…

i18n 配置vue项目中英文语言包(中英文转化)

一、实现效果 二、下载插件创建文件夹 2.1 下载cookie来存储 npm install --save js-cookienpm i vue-i18n -S 2.2 封装组件多页面应用 2.3 创建配置语言包字段 三、示例代码 3.1 main.js 引用 i18n.js import i18n from ./lang// 实现语言切换:i18n处理element&#xff0c…

Unity ARFoundation 配置工程 (Android)

注意&#xff1a; 1、AR Core是Google的产品&#xff0c;因为谷歌制裁华为&#xff0c;所以 有些 华为机可能不支持AR Core的软件&#xff1b; 2、手机在设置里搜索Google Play&#xff0c;看看是否已经安装上了&#xff0c;如果没有装此服务&#xff0c;去商城里搜索Google Pl…

互联网发展历程:跨越远方,路由器的启示

互联网的蓬勃发展&#xff0c;一直在追求更广阔的连接&#xff0c;更遥远的距离。然而&#xff0c;在早期的网络中&#xff0c;人们面临着连接距离有限的问题。一项重要的技术应运而生&#xff0c;那就是“路由器”。 连接受限的问题&#xff1a;距离有限 早期的网络受限于直接…

微服务-Nacos(注册中心)

Nacos是SpringCloud的一个功能非常强大的组件&#xff0c;想比eureka的功能更加丰富 官方的nacos简介 Nacos&#xff08;全称&#xff1a;Naming and Configuration Service&#xff09;是一个开源的动态服务发现、配置管理和服务管理平台。它由阿里巴巴集团开发并贡献给开源…

【设计模式——学习笔记】23种设计模式——策略模式Strategy(原理讲解+应用场景介绍+案例介绍+Java代码实现)

文章目录 案例引入传统方案实现实现分析 介绍基本介绍登场角色 案例实现案例一类图实现 案例二类图实现问答 策略模式在JDK源码中的使用总结文章说明 案例引入 有各种鸭子&#xff0c;比如野鸭、北京鸭、水鸭等。 鸭子有各种行为&#xff0c;比如走路、叫、飞行等。不同鸭子的…

安防监控视频云存储EasyCVR平台H.265转码功能更新:新增分辨率配置

安防视频集中存储EasyCVR视频监控综合管理平台可以根据不同的场景需求&#xff0c;让平台在内网、专网、VPN、广域网、互联网等各种环境下进行音视频的采集、接入与多端分发。在视频能力上&#xff0c;视频云存储平台EasyCVR可实现视频实时直播、云端录像、视频云存储、视频存储…

C++ string类详解

⭐️ string string 是表示字符串的字符串类&#xff0c;该类的接口与常规容器的接口基本一致&#xff0c;还有一些额外的操作 string 的常规操作&#xff0c;在使用 string 类时&#xff0c;需要使用 #include <string> 以及 using namespace std;。 ✨ 帮助文档&…

【HarmonyOS】服务卡片 API6 JSUI跳转不同页面

【引言】 “JS卡片支持为组件设置action&#xff0c;包括router事件和message事件&#xff0c;其中router事件用于应用跳。若设置router事件&#xff0c;则action属性值为"router"&#xff1b;abilityName为卡片提供方应用的跳转目标Ability名&#xff1b;params中的…

Spring Security6 最新版配置该怎么写,该如何实现动态权限管理

Spring Security 在最近几个版本中配置的写法都有一些变化&#xff0c;很多常见的方法都废弃了&#xff0c;并且将在未来的 Spring Security7 中移除&#xff0c;因此又补充了一些新的内容&#xff0c;重新发一下&#xff0c;供各位使用 Spring Security 的小伙伴们参考。 接下…

LeetCode--HOT100题(33)

目录 题目描述&#xff1a;148. 排序链表&#xff08;中等&#xff09;题目接口解题思路代码 PS: 题目描述&#xff1a;148. 排序链表&#xff08;中等&#xff09; 给你链表的头结点 head &#xff0c;请将其按 升序 排列并返回 排序后的链表 。 LeetCode做题链接&#xff1…

VR/AR眼镜方案,MTK联发科平台智能眼镜安卓主板设计方案

随着人工智能在不同领域的逐渐深入&#xff0c;人们对一款产品的需求不再局限于某种单一的功能或单一场景&#xff0c;尤其是在工业医疗等专业领域&#xff0c;加快数字化转型才能实现产业的升级。 AR智能眼镜&#xff0c;是一个可以让现场作业更智能的综合管控设备。采用移动…

Hlang--用Python写个编程语言-函数与基本数据结构实现

文章目录 前言语法表述解析器修改词法解析函数节点函数节点解析List的解析实现解释器节点函数操作String和List处理总结前言 okey,经过一段时间的努力,接下来要实现的是函数。当然还有对应的基本数据结构,那么之后的话,我们的工作就开始进一步转换了。 那么在这块我们要实…

vscode搭建java开发环境

一、配置extensions环境变量VSCODE_EXTENSIONS 该环境变量路径下的存放安装组件&#xff1a; 二、setting配置文件 {"java.jdt.ls.java.home": "e:\\software\\jdk\\jdk17",// java运行环境"java.configuration.runtimes": [{"name":…

vscode远程连接Linux失败,提示过程试图写入的管道不存在(三种解决办法)

vscode报错如下&#xff1a; 一、第一种情况 原因是本地的known_hosts文件记录服务器信息与现服务器的信息冲突了&#xff0c;导致连接失败。 解决方案就是把本地的known_hosts的原服务器信息全部删掉&#xff0c;然后重新连接。 二、第二种情况 在编写配置文件config时&…

批量提取文件名到excel,详细的提取步骤

如何批量提取文件名到excel&#xff1f;我们的电脑中可能存储着数量非常多的电子文件&#xff0c;现在需要快速将这些文件的名称全部提取到Excel中。虽然少量数据可以通过复制粘贴的方式轻松完成&#xff0c;但是对于上万个数据而言&#xff0c;复制粘贴都是行不通的&#xff0…

XQuery创建BaseX数据库实例

XQuery创建BaseX数据库实例 文章目录 XQuery创建BaseX数据库实例1、准备工作2、demo目录结构3、IDEA配置BaseX4、工具类BaseXClient5、Example 1、准备工作 开发工具&#xff1a; IDEAOxygen 技术&#xff1a; JavaBaseXXpathXquery BaseX需要阅读的文档&#xff1a; htt…