驱动开发——字符设备

字符设备

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   2

static struct cdev hello_cdev;
static struct cdev hello2_cdev;
static struct class *cls;

static int hello_init(void)
{
	dev_t devid;
	
	/* 3. 告诉内核 */
#if 0
	major = register_chrdev(0, "hello", &hello_fops); /* (major,  0), (major, 1), ..., (major, 255)都对应hello_fops */
#else
	if (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);
	
#endif

	cls = 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");



在这里插入图片描述


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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

黑客自学路线

谈起黑客&#xff0c;可能各位都会想到&#xff1a;盗号&#xff0c;其实不尽然&#xff1b;黑客是一群喜爱研究技术的群体&#xff0c;在黑客圈中&#xff0c;一般分为三大圈&#xff1a;娱乐圈 技术圈 职业圈。 娱乐圈&#xff1a;主要是初中生和高中生较多&#xff0c;玩网恋…

简单着色器编写(下)

函数部分介绍完了&#xff0c;最后来介绍一下main函数中的部分。 std::string vertexShader "#version 330 core\n" "\n" "layout(location0)in vec4 position;" "\n" "void main()\n" "{\n&…

淘宝商品优惠券详情item_get_app-获得淘宝app商品详情原数据

item_get_app-获得淘宝app商品详情原数据 taobao.item_get_app 公共参数 名称类型必须描述keyString是调用key&#xff08;必须以GET方式拼接在URL中&#xff09;调用API接口入口secretString是调用密钥api_nameString是API接口名称&#xff08;包括在请求地址中&#xff09…

微信小程序拉起支付报: 调用支付JSAPI缺少参数: total_fee

1. 调用支付JSAPI缺少参数: total_fee 2. 检查返回给前端调起支付的参数是否正确 一开始是params.put("package", prepay_id); 回来改回params.put("package", "prepay_id"prepay_id);

【测试】pywinauto的简单使用(安装、常用对象、元素控件、鼠标操作、键盘操作)

1.说明 pywinauto是一个用于自动化Python 模块&#xff0c;适合Windows系统的软件&#xff08;GUI&#xff09;&#xff0c;可以通过Pywinauto遍历窗口&#xff08;对话框&#xff09;和窗口里的控件&#xff0c;也可以控制鼠标和键盘输入&#xff0c;所以它能做的事情比之前介…

36k字从Attention解读Transformer及其在Vision中的应用(pytorch版)

文章目录 0.卷积操作1.注意力1.1 注意力概述(Attention)1.1.1 Encoder-Decoder1.1.2 查询、键和值1.1.3 注意力汇聚: Nadaraya-Watson 核回归1.2 注意力评分函数1.2.1 加性注意力1.2.2 缩放点积注意力1.3 自注意力(Self-Attention)1.3.1 自注意力的定义和计算1.3.2 自注意…

数据结构初阶--排序

目录 一.排序的基本概念 1.1.什么是排序 1.2.排序算法的评价指标 1.3.排序的分类 二.插入排序 2.1.直接插入排序 2.2.希尔排序 三.选择排序 3.1.直接选择排序 3.2.堆排序 重建堆 建堆 排序 四.交换排序 4.1.冒泡排序 4.2.快速排序 快速排序的递归实现 法一&a…

『SEQ日志』在 .NET中快速集成轻量级的分布式日志平台

&#x1f4e3;读完这篇文章里你能收获到 如何在Docker中部署 SEQ&#xff1a;介绍了如何创建和运行 SEQ 容器&#xff0c;给出了详细的执行操作如何使用 NLog 接入 .NET Core 应用程序的日志&#xff1a;详细介绍了 NLog 和 NLog.Seq 来配置和记录日志的步骤日志记录示例&…

CSS background 背景

background属性为元素添加背景效果。 它是以下属性的简写&#xff0c;按顺序为&#xff1a; background-colorbackground-imagebackground-repeatbackground-attachmentbackground-position 以下所有示例中的花花.jpg图片的大小是4848。 1 background-color background-col…

【rust/egui】(四)看看template的app.rs:update以及组件TopBottomPanelButton

说在前面 rust新手&#xff0c;egui没啥找到啥教程&#xff0c;这里自己记录下学习过程环境&#xff1a;windows11 22H2rust版本&#xff1a;rustc 1.71.1egui版本&#xff1a;0.22.0eframe版本&#xff1a;0.22.0上一篇&#xff1a;这里 update update实际上还是eframe::App的…

BaiqiSoft MstHtmlEditor for .NET Crack

BaiqiSoft MstHtmlEditor for .NET Crack BaiqiSoft MstHtmlEditor获取.NET for win表单被认为是一个可以被用户轻松灵活地集成到C#、VB.NET甚至WPF软件中的元素。负责编辑的控制器&#xff0c;用于.NET Win Forms的MstHtmlEditor&#xff0c;允许用户和开发人员&#xff0c;甚…

stm32之11.USART串口通信

可以添加上拉电阻&#xff0c;但会增加功耗&#xff0c;传输距离变长 要添加库函数USART 官方参考文档说明书位置 ALT&#xff0b;左键可实现整体删除&#xff08;如下图&#xff09; 输出模式第三种模式AF ---------------------- 源码 远程控制pc端 #include <stm32f4x…

UE4/5Niagara粒子特效之Niagara_Particles官方案例:2.4->3.2

之前的案例 UE4/5Niagara粒子特效之Niagara_Particles官方案例&#xff1a;1.1-&#xff1e;1.4_多方通行8的博客-CSDN博客 UE4/5Niagara粒子特效之Niagara_Particles官方案例&#xff1a;1.5-&#xff1e;2.3_多方通行8的博客-CSDN博客 2.4 Location Events 这次的项目和之…

江西抚州新能源汽车3d扫描零部件逆向抄数测量改装-CASAIM中科广电

汽车改装除了在外观方面越来越受到消费者的青睐&#xff0c;在性能和实用性提升上面的需求也是日趋增多&#xff0c;能快速有效地对客户指定汽车零部件进行一个改装&#xff0c;是每一个汽车改装企业和工程师的追求&#xff0c;也是未来消费者个性化差异化的要求。下面CASAIM中…

【Docker】存储卷Volume

Docker Volume概念 什么是存储卷 存储卷就是将宿主机的本地文件系统中存在的某个目录直接与容器内部的文件系统上的某一目录建立绑定关系。这就意味着&#xff0c;当我们在容器中的这个目录下写入数据时&#xff0c;容器会将其内容直接写入到宿主机上与此容器建立了绑定关系的…

裂缝检测,只依赖OPENCV,基于YOLO8S

裂缝检测&#xff0c;只依赖OPENCV&#xff0c;YOLOV8S 现在YOLOV8S训练目标非常方便&#xff0c;可以直接转换成ONNX让OPENCV调用&#xff0c;支持C/PYTHON&#xff0c;原理很简单&#xff0c;自己找博客&#xff0c;有兴趣相互交流

数字化技术无限延伸,VR全景点亮智慧生活

随着互联网的发展&#xff0c;我们无时无刻不再享受着互联网给我们带来的便利&#xff0c;数字化生活正在无限延伸&#xff0c;各行各业也开始积极布局智能生活。要说智慧生活哪个方面应用的比较多&#xff0c;那应该就是VR全景了&#xff0c;目前VR全景已经被各个行业广泛应用…

Mesa 23.2 开源图形栈现已可供下载

作为 Mesa 23 系列的第二个重要版本&#xff0c;Mesa 23.2 开源图形栈现已可供下载&#xff0c;它为 AMD GPU 的 RADV Vulkan 驱动程序带来了新功能&#xff0c;改进了 Linux 游戏&#xff0c;并新增了 Asahi 功能。 Mesa 23.2 的亮点包括 Asahi 上的 OpenGL 3.1 和 OpenGL ES …

前端需要理解的HTML知识

HTML&#xff08;超文本标记语言&#xff0c;HyperText Markup Language&#xff09;不是编程语言&#xff0c;而是定义了网页内容的含义和结构的标记语言。。“超文本”&#xff08;hypertext&#xff09;是指连接单个网站内或多个网站间的网页的链接。HTML 使用“标记”&…

怎么检测UI卡顿?(线上及线下)

什么是UI卡顿&#xff1f; 在Android系统中&#xff0c;我们知道UI线程负责我们所有视图的布局&#xff0c;渲染工作&#xff0c;UI在更新期间&#xff0c;如果UI线程的执行时间超过16ms&#xff0c;则会产生丢帧的现象&#xff0c;而大量的丢帧就会造成卡顿&#xff0c;影响用…