5、Linux驱动开发:设备-设备注册

目录

🍅点击这里查看所有博文

  随着自己工作的进行,接触到的技术栈也越来越多。给我一个很直观的感受就是,某一项技术/经验在刚开始接触的时候都记得很清楚。往往过了几个月都会忘记的差不多了,只有经常会用到的东西才有可能真正记下来。存在很多在特殊情况下有一点用处的技巧,用的不多的技巧可能一个星期就忘了。

  想了很久想通过一些手段把这些事情记录下来。也尝试过在书上记笔记,这也只是一时的,书不在手边的时候那些笔记就和没记一样,不是很方便。

  很多时候我们遇到了问题,一般情况下都是选择在搜索引擎检索相关内容,这样来的也更快一点,除非真的找不到才会去选择翻书。后来就想到了写博客,博客作为自己的一个笔记平台倒是挺合适的。随时可以查阅,不用随身携带。

  同时由于写博客是对外的,既然是对外的就不能随便写,任何人都可以看到。经验对于我来说那就只是经验而已,公布出来说不一定我的一些经验可以帮助到其他的人。遇到和我相同问题时可以少走一些弯路。

  既然决定了要写博客,那就只能认真去写。不管写的好不好,尽力就行。千里之行始于足下,一步一个脚印,慢慢来 ,写的多了慢慢也会变好的。权当是记录自己的成长的一个过程,等到以后再往回看时,就会发现自己以前原来这么菜😂。

  本系列博客所述资料均来自互联网资料,并不是本人原创(只有博客是自己写的)。出于热心,本人将自己的所学笔记整理并推出相对应的使用教程,方面其他人学习。为国内的物联网事业发展尽自己的一份绵薄之力,没有为自己谋取私利的想法。若出现侵权现象,请告知本人,本人会立即停止更新,并删除相应的文章和代码。

字符设备架构

  字符设备的驱动架构,我们可将其分为四个部分,分别是用户层、VFS层、驱动层、物理层。

输入图片说明

用户层

  在Linux的设计理念中,一切皆是文件,所有的硬件设备操作到应用层都会被抽象成文件的操作。

  字符设备也不例外,在Linux操作系统中,每个驱动程序在应用层的/dev目录下都会有一个设备文件和它对应。

root@ubuntu:/home/peng/Desktop/driver/Learning/5_cdev# ll /dev/ttyS*
crw-rw---- 1 root dialout 4, 64 Jul 25 06:30 /dev/ttyUSB0
crw-rw---- 1 root dialout 4, 65 Jul 25 06:30 /dev/ttyUSB1

  以ttyUSB设备举例,我们可以通过标准文件操作的的方式,也就是open、read、write等方式直接访问该设备。

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
void main(void)
{int fd;fd = open("/dev/ttyUSB0",O_RDWR);if(fd<0){perror("open fail \n");return;}printf("open ok \n ");
}

  除文件接口之外,还可以通过shell的方式,也就是echo、cat等命令对其进行读写操作。这里不做演示。

VFS层

  在Linux文件系统中,每个文件都用一个struct inode结构体来描述,这个结构体里面记录了这个文件的所有信息。这里操作的是字符设备驱动,需要注意的信息有设备号、设备类型、设备的结构体。

struct inode {// 记录设备的类型umode_t			i_mode;xxxxxxxxxxxxx// 记录文件所对应的设备号dev_t			i_rdev;xxxxxxxxxxxxxunion {xxxxxxxxxxxxx// 记录描述字符设备的结构体struct cdev		*i_cdev;};
};

  当用户层打开设备文件时,可以根据设备文件对应的struct inode结构体描述的信息,知道接下来要操作的设备类型(字符设备还是块设备)。

  在Linux操作系统中,每个驱动程序都要分配一个主设备号。在设备号注册章节中提到了怎么注册设备到/proc/devices中,其中就可以通过自动或者手动的方式去申请设备号。

  根据struct inode结构体里面记录的设备号,就可以找到对应的驱动程序。

  前面的操作主要是在打开文件时能找到驱动设备,在文件被打开后。VFS层会给应用层返回一个文件描述符(fd)。

  这个fd是和struct file结构体对应的。上层的应用程序就可以通过fd来找到strut file,然后在由struct file找到操作字符设备的函数接口(file_operations)了。struct file是在打开文件找到驱动设备后,才被赋值的,驱动层部分会讲到。

struct file {xxxxxxxxxxxxx//记录字符设备的操作函数const struct file_operations	*f_op;xxxxxxxxxxxxx//文件指针偏移值loff_t			f_pos;xxxxxxxxxxxxx
};

驱动层

  以字符设备为例,在Linux操作系统中每个字符设备都对应一个char_device_struct 结构体,该结构体和主设备号相关联。该结构体中struct cdev成员描述了字符设备所有的信息。

/*fs/char_dev.c*/
static struct char_device_struct {struct char_device_struct *next;unsigned int major;unsigned int baseminor;int minorct;char name[64];struct cdev *cdev;      /* will die */
} *chrdevs[CHRDEV_MAJOR_HASH_SIZE];/* fs.h */
#define CHRDEV_MAJOR_HASH_SIZE 255

  struct cdev结构体所记录的就是设备驱动相关的数据。其中file_operations就是用户层中用户调用接口的具体实现。其他的数据设备号(完整的设备号),以及次设备的个数了解即可。

struct cdev {struct kobject kobj;struct module *owner;const struct file_operations *ops; //接口函数集合struct list_head list;//内核链表dev_t dev;//设备号unsigned int count;//次设备号个数
};

  找到struct cdev结构体后,Linux内核就会将struct cdev结构体所在的内存空间首地记录在struct inode结构体的i_cdev成员中。将struct cdev结构体的中记录的函数操作接口file_operations 地址记录在struct file结构体的f_op成员中。

物理层

  skip

驱动实现

模块挂载

  在注册设备号之后(register_chrdev_region)。我们需要将devno保存到cdev结构体中(cdev_add)。在保存之前还需要对cdev结构体进行初始化(cdev_init)。

static dev_t devno;
static struct cdev cdev;
static int hello_init(void)
{int result;int error;	printk("hello_init \n");devno = MKDEV(237,0);	result = register_chrdev_region(devno, 1, "hello");if(result<0){printk("register_chrdev_region fail \n");return result;}cdev_init(&cdev,&hello_ops);error = cdev_add(&cdev,devno,1);if(error < 0){printk("cdev_add fail \n");unregister_chrdev_region(devno,1);return error;}return 0;
}
module_init(hello_init);

驱动操作函数

  cdev初始化时,传入的就是设备文件的操作函数。这里就简单实现了一个openclose函数。

  在file_operations结构体的成员中,release 函数指针对应的就是文件的close函数。

  当用户层app调用该设备文件的open函数时,经过内核处理,理应调用到hello_open函数往dmesg中打印"hello_open()\n"。同理,app中调用到close函数时,内核驱动中会调用到hello_release 函数往dmesg中打印"hello_release()\n"

static int hello_open(struct inode *inode, struct file *filep)
{printk("hello_open()\n");return 0;
}
int hello_release (struct inode *inode, struct file *file)
{printk("hello_release()\n");return 0;
}
static struct file_operations hello_ops = 
{.open = hello_open,.release = hello_release,
};

模块注销

  模块注销时,需要按照挂载的相反顺序去执行。先删除cdev,后对chrdev进行解注册。

static void hello_exit(void)
{printk("hello_exit \n");cdev_del(&cdev);unregister_chrdev_region(devno,1);return;
}
module_exit(hello_exit);

代码验证

  文中编写的驱动模块,由于没有配套的自动关联程序,并不会将设备号自动关联到/dev/目录中的具体设备中。

  我们只需要手动运行下面的命令,即可创建设备驱动文件绑定到主设备号为237,从设备号为0的字符设备中(命令中c代表字符设备)。

root@ubuntu:# mknod /dev/hello_test0 c 237 0

  在用户层编写app(test.c),仅调用open函数打开我们注册的**/dev/hello_test0**文件即可。其他的函数暂时未实现,直接使用则会报错。

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
void main(void)
{int fd;fd = open("/dev/hello_test0",O_RDWR);if(fd<0){perror("open fail \n");return;}printf("open ok \n");close(fd);printf("close ok \n");
}

  调用gcc编译app,从app的日志中可以看到打开设备文件是正确的。查看dmesg中驱动的日志,也能看到驱动中的hello_open函数被正常调用。

root@ubuntu:/# gcc ./test.c -o test
root@ubuntu:# ./test 
open ok 
close ok 
root@ubuntu:# dmesg
[ 5964.438242] hello_open()
[ 5964.438331] hello_release()

注意:若前面没有执行mknod函数去创建设备文件。或者mknod时填入的主设备号,次设备号错误。那么这里的open函数调用也是会报错的。

root@ubuntu:# rm -rf /dev/hello 
root@ubuntu:# ./test 
open fail 
: No such file or directory
root@ubuntu:# mknod /dev/hello c 238 0
root@ubuntu:# ./test 
open fail 
: No such device or address

  那么本篇博客就到此结束了,这里只是记录了一些我个人的学习笔记,其中存在大量我自己的理解。文中所述不一定是完全正确的,可能有的地方我自己也理解错了。如果有些错的地方,欢迎大家批评指正。如有问题直接在对应的博客评论区指出即可,不需要私聊我。我们交流的内容留下来也有助于其他人查看,说不一定也有其他人遇到了同样的问题呢😂。

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

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

相关文章

Kubernetes 上的数据已跨越鸿沟:在 GKE 上运行有状态应用程序的案例

Kubernetes 是当今云原生开发的事实上的标准。长期以来&#xff0c;Kubernetes 主要与无状态应用程序相关&#xff0c;例如 Web 和批处理应用程序。然而&#xff0c;与大多数事物一样&#xff0c;Kubernetes 也在不断发展。如今&#xff0c;我们看到 Kubernetes 上有状态应用程…

26967-2011 一般用喷油单螺杆空气压缩机

声明 本文是学习GB-T 26967-2011 一般用喷油单螺杆空气压缩机. 而整理的学习笔记,分享出来希望更多人受益,如果存在侵权请及时联系我们 1 范围 本标准规定了一般用喷油单螺杆空气压缩机(以下简称"单螺杆空压机")的术语和定义、型号、基本 参数、要求、试验方法、…

华为OD七日集训第6期 十一特辑 - 按算法分类,由易到难,循序渐进,玩转OD

目录 专栏导读华为OD机试算法题太多了&#xff0c;知识点繁杂&#xff0c;如何刷题更有效率呢&#xff1f; 一、逻辑分析二、数据结构1、线性表① 数组② 双指针 2、map与list3、优先队列4、滑动窗口5、二叉树6、并查集7、栈 三、算法1、基础算法① 贪心算法② 二分查找③ 分治…

【Linux】完美解决ubuntu18.04下vi不能使用方向键和退格键

今天在刚安装完ubuntu18.04&#xff0c;发现在使用vi命令配置文件时使用方向键并不能移动光标&#xff0c;而是出现一堆奇怪的英文字母&#xff0c;使用退格键也不能正常地删除内容&#xff0c;用惯了CentOS的我已经感觉到ubuntu没有centos用着丝滑&#xff0c;但是没办法&…

2.4g无线收发芯片:Ci24R1(DFN8)

Ci24R1 采用GFSK/FSK数字调制与解调技术。数据传输速率与PA输出功率都可以调节&#xff0c;支持2Mbps, 1Mbps, 250Kbps三种数据速率。高的数据速率可以在更短的时间完成同样的数据收发&#xff0c;因此可以具有更低的功耗。 Ci24R1 是一颗工作在2.4GHz ISM频段&#xff0c;专为…

2023 “华为杯” 中国研究生数学建模竞赛(F题)深度剖析|数学建模完整代码+建模过程全解全析

F题代码思路 当大家面临着复杂的数学建模问题时&#xff0c;你是否曾经感到茫然无措&#xff1f;作为2021年美国大学生数学建模比赛的O奖得主&#xff0c;我为大家提供了一套优秀的解题思路&#xff0c;让你轻松应对各种难题。 让我们一起看看研赛的F题呀&#xff01;全文都已…

Machine Learning(study notes)

There is no studying without going crazy Studying alwats drives us crazy 文章目录 DefineMachine LearningSupervised Learning&#xff08;监督学习&#xff09;Regression problemClassidication Unspervised LearningClustering StudyModel representation&#xff08…

025 - STM32学习笔记 - 液晶屏控制(二) - 代码实现

025- STM32学习笔记 - 液晶屏控制&#xff08;二&#xff09; - 代码实现 好久没更新学习笔记了&#xff0c;最近工作上的事情太多了&#xff0c;趁着国庆中秋&#xff0c;多更新一点看看。 上节学习了关于LTDC与DMA2D以及显示屏的相关知识点&#xff0c;这节开始实操&#xf…

域环境介绍

一、概述 内网也指局域网&#xff0c;指的是某个区域由多台计算机互连而成的计算机组&#xff0c;范围通常在数千米以内&#xff0c;在局域网中&#xff0c;可以实现文件管理&#xff0c;应用软件共享&#xff0c;打印机共享、工作组内的日程安排、电子邮件和传真通信服务等&a…

excel筛选后求和

需要对excel先筛选&#xff0c;后对“完成数量”进行求和。初始表格如下&#xff1a; 一、选中表内任意单元格&#xff0c;按ctrlshiftL&#xff0c;开启筛选 二、根据“部门”筛选&#xff0c;比如选择“一班” 筛选完毕后&#xff0c;选中上图单元格&#xff0c;然后按alt后&…

力扣 -- 115. 不同的子序列

解题步骤&#xff1a; 参考代码&#xff1a; class Solution { public:int numDistinct(string s, string t) {int ns.size();int mt.size();//多开一行&#xff0c;多开一列vector<vector<double>> dp(m1,vector<double>(n1));for(size_t j0;j<n;j){dp[…

国庆周《Linux学习第二课》

Linux开篇指南针环境安装(第一课)-CSDN博客 Linux详细的环境安装介绍在上面 第一 环境准备过程 安装过程

k8s+kubeedge+sedna安装的全套流程

一&#xff0c;环境准备 把两台虚拟机的ip地址设置成静态的IP地址&#xff0c;否则ip地址会变 虚拟机配置静态IP&#xff08;NAT模式&#xff09;_nat子网的准入_阿祖&#xff0c;收手吧的博客-CSDN博客​​​​​​ 节点IP软件 云节点192.168.133.139kubernetescloudcore边…

【独家专访】“数网”同防筑牢屏障——新型电力系统网络安全保障体系需加快调整

随着全球数字化进程不断加快&#xff0c;在国际竞争和冲突中&#xff0c;网络战和数据战已然屡见不鲜。电力作为关系国计民生的关键行业&#xff0c;更成为网络攻击的重要对象。加强电力等关键信息基础设施的网络安全保障&#xff0c;是国家今后一段时期的重点工作。7月15日召开…

结构型设计模式——外观模式

摘要 本文主要分析设计模式 - 结构型 - 外观(Facade)&#xff0c;它提供了一个统一的接口&#xff0c;用来访问子系统中的一群接口&#xff0c;从而让子系统更容易使用。 一、外观模式的意图 提供了一个统一的接口&#xff0c;用来访问子系统中的一群接口&#xff0c;从而让…

26962-2011 高频电磁场综合水处理器技术条件

声明 本文是学习GB-T 26962-2011 高频电磁场综合水处理器技术条件. 而整理的学习笔记,分享出来希望更多人受益,如果存在侵权请及时联系我们 1 范围 本标准规定了高频电磁场综合水处理器(以下简称处理器)的术语和定义、分类和型号、结构型式、 要求及检验、标志、包装和贮运…

PyQt/PySide ImportError: DLL load failed while importing Shiboken,PyQt库和python

最近在测试PySide项目&#xff0c;在新环境下报错了&#xff1a;ImportError: DLL load failed while importing Shiboken: 找不到指定的程序。 Traceback (most recent call last):File "D:/xxx.py", line 10, in <module>from PySide6.QtWidgets import QAp…

怒刷LeetCode的第20天(Java版)

目录 第一题 题目来源 题目内容 解决方法 方法一&#xff1a;回溯算法 方法二&#xff1a;permute方法 方法三&#xff1a;交换法 第二题 题目来源 题目内容 解决方法 方法一&#xff1a;回溯算法 方法二&#xff1a;递归和交换 方法三&#xff1a;二维列表 第三…

Vue城市选择器示例(省市区三级)

Vue城市选择器&#xff08;省市区&#xff09; 读者可以参考下面的省市区三级联动代码思路&#xff0c;切记要仔细研究透彻&#xff0c;学习交流才是我们的本意&#xff0c;而非一成不变。切记切记&#xff01; 最近又重读苏子的词&#xff0c;颇为感慨&#xff0c;愿与诸君共…

数学建模Matlab之评价类方法

大部分方法来自于http://t.csdnimg.cn/P5zOD 层次分析法 层次分析法&#xff08;Analytic Hierarchy Process, AHP&#xff09;是一种结构决策的定量方法&#xff0c;主要用于处理复杂问题的决策分析。它将问题分解为目标、准则和方案等不同层次&#xff0c;通过成对比较和计算…