【Linux-驱动开发】

Linux-驱动开发

  • ■ Linux-应用程序对驱动程序的调用流程
  • ■ Linux-file_operations 结构体
  • ■ Linux-驱动模块的加载和卸载
    • ■ 1. 驱动编译进 Linux 内核中
    • ■ 2. 驱动编译成模块(Linux 下模块扩展名为.ko)
  • ■ Linux-
  • ■ Linux-
  • ■ Linux-设备号
    • ■ Linux-设备号-分配
      • ■ 静态分配设备号
      • ■ 动态分配设备号
  • ■ Linux-驱动分类
    • ■ 字符设备:
      • ■ 字符设备-注册与注销函数
      • ■ 字符设备-具体操作函数
      • ■ 字符设备-LICENSE 和作者信息
      • ■ 示例一:

■ Linux-应用程序对驱动程序的调用流程

在这里插入图片描述
在这里插入图片描述

■ Linux-file_operations 结构体

在 Linux 内核文件 include/linux/fs.h 中有个叫做 file_operations 的结构体,此结构体就是 Linux 内核驱动操作函数集合,

1588 struct file_operations {
1589 struct module *owner;        // 拥有该结构体的模块的指针,一般设置为 THIS_MODULE
1590 loff_t (*llseek) (struct file *, loff_t, int);  //llseek 函数用于修改文件当前的读写位置。
1591 ssize_t (*read) (struct file *, char __user *, size_t, loff_t*);  // read 函数用于读取设备文件。
1592 ssize_t (*write) (struct file *, const char __user *, size_t,loff_t *); //write 函数用于向设备文件写入(发送)数据。
1593 ssize_t (*read_iter) (struct kiocb *, struct iov_iter *);
1594 ssize_t (*write_iter) (struct kiocb *, struct iov_iter *);
1595 int (*iterate) (struct file *, struct dir_context *);
1596 unsigned int (*poll) (struct file *, struct poll_table_struct*);  //是个轮询函数,用于查询设备是否可以进行非阻塞的读写。
1597 long (*unlocked_ioctl) (struct file *, unsigned int, unsigned long); //unlocked_ioctl 函数提供对于设备的控制功能,与应用程序中的 ioctl 函数对应。
1598 long (*compat_ioctl) (struct file *, unsigned int, unsigned long); //compat_ioctl 函数与 unlocked_ioctl 函数功能一样,区别在于在 64 位系统上,
32 位的应用程序调用将会使用此函数。在 32 位的系统上运行 32 位的应用程序调用的是
unlocked_ioctl。
1599 int (*mmap) (struct file *, struct vm_area_struct *); //mmap 函数用于将设备的内存映射到进程空间中(也就是用户空间),一般帧缓
冲设备会使用此函数,比如 LCD 驱动的显存,将帧缓冲(LCD 显存)映射到用户空间中以后应用
程序就可以直接操作显存了,这样就不用在用户空间和内核空间之间来回复制。
1600 int (*mremap)(struct file *, struct vm_area_struct *);
1601 int (*open) (struct inode *, struct file *); //open 函数用于打开设备文件。
1602 int (*flush) (struct file *, fl_owner_t id);
1603 int (*release) (struct inode *, struct file *); //release 函数用于释放(关闭)设备文件,与应用程序中的 close 函数对应。
1604 int (*fsync) (struct file *, loff_t, loff_t, int datasync); //fasync 函数用于刷新待处理的数据,用于将缓冲区中的数据刷新到磁盘中
1605 int (*aio_fsync) (struct kiocb *, int datasync); //aio_fsync 函数与 fasync 函数的功能类似,只是 aio_fsync 是异步刷新待处理的
数据。
1606 int (*fasync) (int, struct file *, int);
1607 int (*lock) (struct file *, int, struct file_lock *);
1608 ssize_t (*sendpage) (struct file *, struct page *, int, size_t,loff_t *, int);
1609 unsigned long (*get_unmapped_area)(struct file *, unsigned long,unsigned long, unsigned long, unsigned long);
1610 int (*check_flags)(int);
1611 int (*flock) (struct file *, int, struct file_lock *);
1612 ssize_t (*splice_write)(struct pipe_inode_info *, struct file *,loff_t *, size_t, unsigned int);
1613 ssize_t (*splice_read)(struct file *, loff_t *, structpipe_inode_info *, size_t, unsigned int);
1614 int (*setlease)(struct file *, long, struct file_lock **, void**);
1615 long (*fallocate)(struct file *file, int mode, loff_t offset, loff_t len);
1617 void (*show_fdinfo)(struct seq_file *m, struct file *f);
1618 #ifndef CONFIG_MMU
1619 	unsigned (*mmap_capabilities)(struct file *);
1620 #endif
1621 };

序号描述
1驱动加载成功以后会在“/dev”目录下生成一个相应的文件
2比如现在有个叫做/dev/led 的驱动文件,此文件是 led 灯的驱动文件。应用程序使用 open 函数来打开文件/dev/led,使用完成以后使用close 函数关闭/dev/led 这 个文件。 open和 close 就是打开和关闭 led 驱动的函数,如果要点亮或关闭 led
3应用程序运行在用户空间,而 Linux 驱动属于内核的一部分,因此驱动运行于内核空间
4
5
6
7
8

■ Linux-驱动模块的加载和卸载

■ 1. 驱动编译进 Linux 内核中

这样当 Linux 内核启动的时候就会自动运行驱动程序。

■ 2. 驱动编译成模块(Linux 下模块扩展名为.ko)

在Linux 内核启动以后使用“insmod”命令加载驱动模块。在调试驱动的时候一般都选择将其编译为模块,这样我们修改驱动以后只需要编译一下驱动代码即可,不需要编译整个 Linux 代码。

module_init(xxx_init); //注册模块加载函数 当使用“insmod”命令加载驱动的时候, xxx_init 这个函数就会被调用
module_exit(xxx_exit); //注册模块卸载函数 当使用“rmmod”命令卸载具体驱动的时候, xxx_exit 函数就会被调用。

示例一:
在这里插入图片描述
第15行,调用函数module_init 来声明 xxx_init 为驱动入口函数,当加载驱动的时候 xxx_init函数就会被调用。
第16行,调用函数module_exit来声明xxx_exit为驱动出口函数,当卸载驱动的时候xxx_exit函数就会被调用。

关键字功能作用说明
加载驱动insmodinsmod 是最简单的模块加载命令 例如 insmod drv.ko ,
insmod 命令不能解决模块的依赖关系,
比如 drv.ko 依赖 first.ko 这个模块,就必须先使用insmod 命令加载 first.ko 这个模块,然后再加载 drv.ko 这个模块。
加载驱动modprobemodprobe 会分析模块的依赖关系,然后会将所有的依赖模块都加载到内核中,
因此modprobe 命令相比 insmod 要智能一些。
推荐使用 modprobe 命令来加载驱动。
modprobe 命令默认会去/lib/modules/目录中查找模块,
卸载驱动modprobe -r例如 modprobe -r drv.ko
使用 modprobe 命令可以卸载掉驱动模块所依赖的其他模块,前提是这些依赖模块已经没有被其他模块所使用,否则就不能使用 modprobe 来卸载驱动模块。
卸载驱动rmmod例如 rmmod drv.ko
推荐使用 rmmod 命令。

■ Linux-

■ Linux-

■ Linux-设备号

Linux 中每个设备都有一个设备号,设备号由主设备号和次设备号两部分组成,
主设备号表示某一个具体的驱动,
次设备号表示使用这个驱动的各个设备。
dev_t 其实就是 unsigned int 类型,是一个 32 位的数据类型。高 12 位为主设备号, 低 20 位为次设备号。

typedef unsigned int __u32;      
typedef __u32 __kernel_dev_t;
typedef __kernel_dev_t dev_t;   dev_t 其实就是 unsigned int 类型,是一个 32 位的数据类型。高 12 位为主设备号, 低 20 位为次设备号。

在include/linux/kdev_t.h 中提供了几个关于设备号的操作函数(本质是宏),如下所示:

#define MINORBITS 20       宏 MINORBITS 表示次设备号位数,一共是 20 位。
#define MINORMASK ((1U << MINORBITS) - 1)         宏 MINORMASK 表示次设备号掩码。
#define MAJOR(dev) ((unsigned int) ((dev) >> MINORBITS))  宏 MAJOR 用于从 dev_t 中获取主设备号,将 dev_t 右移 20 位即可。
#define MINOR(dev) ((unsigned int) ((dev) & MINORMASK))  宏 MINOR 用于从 dev_t 中获取次设备号,取 dev_t 的低 20 位的值即可。
#define MKDEV(ma,mi) (((ma) << MINORBITS) | (mi))   宏 MKDEV 用于将给定的主设备号和次设备号的值组合成 dev_t 类型的设备号。

■ Linux-设备号-分配

■ 静态分配设备号

具体分配的内容可以查看文档 Documentation/devices.txt。
看硬件平台运行过程中有没有使用这个主设备号,使用“cat /proc/devices” 命令即可查看当前系统中所有已经使用了的设备号。

■ 动态分配设备号

静态分配设备号很容易带来冲突问题, Linux 社区推荐使用动态分配设备号,在注册字符设备之前先申请一个设备号,系统会自动给你一个没有被使用的设备号,这样就避免了冲突。

int alloc_chrdev_region(dev_t *dev, unsigned baseminor, unsigned count, const char *name)  //用于申请设备号,
	dev:保存申请到的设备号。
	baseminor: 次设备号起始地址, alloc_chrdev_region 可以申请一段连续的多个设备号,这些设备号的主设备号一样,但是次设备号不同,次设备号以 baseminor 为起始地址地址开始递增。一般 baseminor 为 0,也就是说次设备号从 0 开始。
	count: 要申请的设备号数量。
	name:设备名字。

void unregister_chrdev_region(dev_t from, unsigned count)     //设备号释放函数
	from:要释放的设备号。
	count: 表示从 from 开始,要释放的设备号数量。

■ Linux-驱动分类

■ 字符设备:

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

■ 字符设备-注册与注销函数

static inline int register_chrdev(unsigned int major, const char *name,const struct file_operations *fops)
static inline void unregister_chrdev(unsigned int major, const char *name)

major: 主设备号,
name:设备名字,指向一串字符串。
fops: 结构体 file_operations 类型指针,指向设备的操作函数集合变量。

■ 字符设备-具体操作函数

打开
关闭

写 操作

■ 字符设备-LICENSE 和作者信息

MODULE_LICENSE() //添加模块 LICENSE 信息
MODULE_AUTHOR() //添加模块作者信息

■ 示例一:

#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/fs.h>
#include <linux/slab.h>
#include <linux/uaccess.h>
#include <linux/io.h>

#define CHRDEVBASE_MAJOR      200           //主设备号
#define CHRDEVBASE_NAME     "chrdevbase"    //名字

static char readbuf[100]; /*读缓冲 */
static char writebuf[100];  /* 写缓冲 */
static char kerneldata[] = {"kernel data!"};


static int chrdevbase_open(struct inode *inode, struct file *filp)
{
   // printk("chrdevbase_open\r\n");
    return 0;
}

static int chrdevbase_release(struct inode *inode, struct file *filp)
{
   // printk("chrdevbase_release\r\n");
    return 0;   
}

static ssize_t chrdevbase_read(struct file *filp, __user char *buf, size_t count,
			loff_t *ppos)
{ 
    int ret  = 0;
    //printk("chrdevbase_read\r\n");
    memcpy(readbuf, kerneldata, sizeof(kerneldata));
    ret = copy_to_user(buf, readbuf, count);
    if(ret == 0) {

    } else {

    }
    return 0;  
}

static ssize_t chrdevbase_write(struct file *filp, const char __user *buf, size_t count, loff_t *ppos)
{
    int ret = 0;
    //printk("chrdevbase_write\r\n");
    ret = copy_from_user(writebuf, buf, count);
    if(ret == 0) {
        printk("kernel recevdata:%s\r\n", writebuf);
    } else {
 
    }
    return 0; 
}

/*
 * 字符设备 操作集合
 */
static struct file_operations chrdevbase_fops={
    .owner = THIS_MODULE,
    .open = chrdevbase_open,
    .release = chrdevbase_release,
    .read = chrdevbase_read,
    .write = chrdevbase_write,
};

static int __init chrdevbase_init(void)
{
    int ret = 0;
    printk("chrdevbase_init\r\n");
    /* 注册字符设备 */
    ret = register_chrdev(CHRDEVBASE_MAJOR, CHRDEVBASE_NAME, &chrdevbase_fops);
    if(ret < 0) {
        printk("chrdevbase init failed!\r\n");
    }
	return 0;
}

static void __exit chrdevbase_exit(void)
{
    printk("chrdevbase_exit\r\n");	
    /* 注销字符设备 */
    unregister_chrdev(CHRDEVBASE_MAJOR, CHRDEVBASE_NAME);
}

/*
 模块入口与出口
*/
module_init(chrdevbase_init);  /* 入口 */
module_exit(chrdevbase_exit);  /* 出口 */

MODULE_LICENSE("GPL");      
MODULE_AUTHOR("zuozhongkai");

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

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

相关文章

【设计模式深度剖析】【2】【结构型】【装饰器模式】| 以去咖啡馆买咖啡为例 | 以穿衣服出门类比

&#x1f448;️上一篇:代理模式 目 录 装饰器模式定义英文原话直译如何理解呢&#xff1f;4个角色类图1. 抽象构件&#xff08;Component&#xff09;角色2. 具体构件&#xff08;Concrete Component&#xff09;角色3. 装饰&#xff08;Decorator&#xff09;角色4. 具体装饰…

5分钟在 VSCode 中使用 PlantUML 绘图

去年&#xff0c;写过一篇在 VSCode 中使用 PlantUML 的博客&#xff0c;那时候我嫌弃本地安装麻烦&#xff0c;所以采用的是在本地运行 docker 容器的方法部署的 PlantUML 服务端。不过&#xff0c;现在来看这样还必须依赖在本地手动启动 docker 容器&#xff08;如果有一个不…

7.类和对象

类和对象 当我们没有去了解过java的知识点中 不免产生一些问题&#xff1a; 什么是类&#xff1f;什么是对象&#xff1f; 记住一句话&#xff1a;在java当中 一切皆对象 类&#xff1a;是用来描述一个对象的 而对象是一个真正存在的实体 在Java这门纯面向对象的语言中 我们…

Nginx企业级负载均衡:技术详解系列(10)—— Nginx核心配置详解(HTTP配置块)

你好&#xff0c;我是赵兴晨&#xff0c;97年文科程序员。 今天咱们聊聊Nginx核心配置中的HTTP配置块&#xff0c;这个配置块在我们的日常使用中极为常见&#xff0c;它的重要性不言而喻。 HTTP配置块在Nginx的配置架构中占据着核心地位&#xff0c;它直接关系到服务器如何处…

panic: concurrent write to websocket connection【golang、websocket】

文章目录 异常信息原由代码错误点 解决办法 异常信息 panic: concurrent write to websocket connection原由 golang 编写 websocket go版本&#xff1a;1.19 使用了第三方框架&#xff1a; https://github.com/gorilla/websocket/tree/main 代码 server.go // Copyright …

蓝桥楼赛第30期-Python-第三天赛题 从参数中提取信息题解

楼赛 第30期 Python 模块大比拼 提取用户输入信息 介绍 正则表达式&#xff08;英文为 Regular Expression&#xff0c;常简写为regex、regexp 或 RE&#xff09;&#xff0c;也叫规则表达式、正规表达式&#xff0c;是计算机科学的一个概念。 所谓“正则”&#xff0c;可以…

nssctf——web

[SWPUCTF 2021 新生赛]gift_F12 1.打开环境后&#xff0c;这里说要900多天会有flag&#xff0c;这是不可能的 2.f12查看源码&#xff0c;然后在html中查找flag &#xff08;在最上方的栏目中&#xff0c;或者按ctrlf&#xff09; [SWPUCTF 2021 新生赛]jicao 1.打开环境是一段…

数据结构(树)

1.树的概念和结构 树&#xff0c;顾名思义&#xff0c;它看起来像一棵树&#xff0c;是由n个结点组成的非线性的数据结构。 下面就是一颗树&#xff1a; 树的一些基本概念&#xff1a; 结点的度&#xff1a;一个结点含有的子树的个数称为该结点的度&#xff1b; 如上图&#…

Qt | QCalendarWidget 类(日历)

01、QCalendarWidget 类 1、QCalendarWidget 类是 QWidget 的直接子类,该类用于日历,见下图 02、QCalendarWidget 属性 ①、dateEditAcceptDelay:int 访问函数:int dateEditAcceptDelay()const; void setDateEditAcceptDelay(int) 获取和设置日期编辑器的延迟时间(以毫秒…

go routing 之 gorilla/mux

1. 背景 继续学习 go 2. 关于 routing 的学习 上一篇 go 用的库是&#xff1a;net/http &#xff0c;这次我们使用官方的库 github.com/gorilla/mux 来实现 routing。 3. demo示例 package mainimport ("fmt""net/http""github.com/gorilla/mux&…

设计模式11——代理模式

写文章的初心主要是用来帮助自己快速的回忆这个模式该怎么用&#xff0c;主要是下面的UML图可以起到大作用&#xff0c;在你学习过一遍以后可能会遗忘&#xff0c;忘记了不要紧&#xff0c;只要看一眼UML图就能想起来了。同时也请大家多多指教。 代理模式&#xff08;Proxy&am…

ATA-7020高压放大器原理介绍

高压放大器是一种电子设备&#xff0c;用于增加输入信号的幅度&#xff0c;使其输出具有更大的电压。它在各种领域中发挥着关键作用&#xff0c;尤其是在需要高电压信号的应用中&#xff0c;如声学、医学成像、科学研究等领域。 高压放大器工作原理介绍&#xff1a; 信号输入&a…

图像上下文学习|多模态基础模型中的多镜头情境学习

【原文】众所周知&#xff0c;大型语言模型在小样本上下文学习&#xff08;ICL&#xff09;方面非常有效。多模态基础模型的最新进展实现了前所未有的长上下文窗口&#xff0c;为探索其执行 ICL 的能力提供了机会&#xff0c;并提供了更多演示示例。在这项工作中&#xff0c;我…

go mod模式下,import gitlab中的项目

背景 为了go项目能够尽可能复用代码&#xff0c;把一些公用的工具类&#xff0c;公用的方法等放到共用包里统一管理。把共用包放到gitlab的私有仓库中。 遇到的问题 通过https方式&#xff0c;执行go get报了错误。 通过ssh方式&#xff0c;执行go get报了错误。 修改配置&am…

Android:使用Kotlin搭建MVC架构模式

一、简介Android MVC架构模式 M 层 model &#xff0c;负责处理数据&#xff0c;例如网络请求、数据变化 V 层 对应的是布局 C 层 Controller&#xff0c; 对应的是Activity&#xff0c;处理业务逻辑&#xff0c;包含V层的事情&#xff0c;还会做其他的事情&#xff0c;导致 ac…

WebRTC-SFU服务器-Janus部署【保姆级部署教程】

一、SFU WebRTC SFU(Selective Forwarding Unit)构架是一种通过服务器来路由和转发WebRTC客户端音视频数据流的方法。这种构架的核心特点是将服务器模拟成一个WebRTC的Peer客户端,从而实现了音视频流的直接转发。 在SFU构架中,服务器作为中心节点,但并不负责音视频流的混…

TG5032CGN TCXO 超高稳定10pin端子型适用于汽车动力转向控制器

TG5032CGN TCXO / VC-TCXO是一款应用广泛的晶振&#xff0c;具有超高稳定性&#xff0c;CMOS输出和使用晶体基振的削波正弦波输出形式。且有低相位噪声优势&#xff0c;是温补晶体振荡器(TCXO)和压控晶体振荡器(VCXO)结合的产物&#xff0c;具有TCXO和VCXO的共同优点&#xff0…

海山数据库(He3DB)代理ProxySQL使用详解:(一)架构说明与安装

一、ProxySQL介绍 1.1 简介 业界比较知名的MySQL代理&#xff0c;由ProxySQL LLC公司开发并提供专业的服务支持&#xff0c;基于GPLv3开源协议进行发布,大部分配置项可动态变更。后端的MySQL实例可根据用途配置到不同的hostgroup中&#xff0c;由ProxySQL基于7层网络协议,将来…

Python 实现Word (DOC或DOCX)与TXT文本格式互转

目录 引言 安装Python库 使用Python将Word转换为TXT文本格式 使用Python将TXT文本格式转换为Word 引言 Word文档和TXT文本文件是日常工作和生活中两种常见的文件格式&#xff0c;各有其特点和优势。Word文档能够保留丰富的格式设置&#xff0c;如字体、段落、表格、图片等…

格雷母线与卸料小车的非接触式定位技术

在现代化的工业生产中&#xff0c;自动化与智能化已成为提高生产效率、降低成本的关键手段。特别是在钢铁、矿山等重工业领域&#xff0c;卸料小车作为物料搬运的重要设备&#xff0c;其定位精度和工作效率直接影响了整个生产线的运行状况。格雷母线高精度位移测量系统的引入&a…