标准驱动开发(Linux2.6(cdev) 的开发)

Linux2.6(cdev) 的开发

目录

Linux2.6(cdev) 的开发

回顾

Linux2.6(cdev) 的开发

了解一下 Linux2.6 开发框架

学习 Linux2.6 的相关接口

1、申请设备号(alloc_chrdev_region)

2、初始化 cdev 结构体(cdev_init)

3、注册设备(cdev_add)

编写一个 Linux2.6 的代码->蜂鸣器驱动

手动生成设备文件的指令和接口

结合 Linux2.6 写一个自动生成设备文件的驱动

1、生成设备文件所需要的类结构体(class_create)

2、生成设备文件(device_create)

补充:那个卸载函数采用倒序

小结:

编写一个 Linux2.6 的代码->蜂鸣器驱动(整体代码)


回顾

-- 1:何为杂项

miscdevice 不想分类的设备!
这种方法注册驱动设备目前是最简单!
注册完毕后自动生成了一个设备文件!
实际上其他的注册方法则不会!

-- 2:什么是驱动、Linux 驱动有什么特点

我认为驱动是:驱使硬件正常工作的代码或者程序
通用、统一接口、设备抽象成文件!

-- 设备文件有什么特点

属于系统的特殊文件之一
需要用非缓冲区操作
一般在/dev/xxxxx
有设备号:uint32_t 数字
分为 主设备号(高 12bit)
次设备号(低 20bit)

-- 什么是特殊文件?(普通人见不到的文件,不是开发人员见不到)

-- 特殊文件需要非缓冲区操作?

-- 普通文件都是需要缓冲区,因为容易造成浪费?

-- 为什么杂项注册时候,只提供次设备号
因为主设备号固定为 10

-- 主设备号和次设备号都是0-255(但这是32位的芯片,64位的不一定)


-- 何为 GPIO 子系统?
中间层:系统内核自己编写的统一接口

-- GPIO 子系统的 gpio 编号是如何计算的!

瑞芯微: 32大组号 + 8 小组号 + 本身编号


-- !!!A系列的芯片要取代单片机(比如人脸识别,单片机就不可以)

-- 目前海思,瑞星微,全智这几个厂商比较可以


Linux2.6(cdev) 的开发

-- 这是最标准的驱动开发

-- 对比于杂项而言,Linux2.6 的开发:

  • 1:开发较为复杂一些
  • 2:设备号需要用户自己申请
  • 3:设备注册完毕后不会生成,需要用户自己生成

其他特点完全等于与杂项设备,同样设备号不再限制了!

了解一下 Linux2.6 开发框架

  • 1:先去申请设备号-> alloc_chrdev_region->连续申请多个设备号
  • 2:再去拿申请的设备号注册设备->连续注册多个设备 cdev_init cdev_add
  • 3:Linux2.6 注册完毕后 不会生成设备文件的,我们需要手动生成 class_create device_create

alt text

alt text

学习 Linux2.6 的相关接口

1、申请设备号(alloc_chrdev_region)

-- 函数的功能:在内核中 申请一个/多个可用的设备号

-- 函数的头文件:<linux/fs.h>

-- 函数的原型:int alloc_chrdev_region(dev_t *dev, unsigned int baseminor, unsigned int count, const char *name);

-- 函数的参数:

  • dev_t *dev:申请的设备号,用户自己定义一个变量,函数会自动赋值!(dev_t == unsigned int)

就是你申请得到设备号存放的位置! 如果你申请多个设备号,那么它只存放你申请得到的第一个设备号!

  • baseminor:次设备号,你打算从哪个次设备号开始申请,随便填写 ,可以填写99

  • count:你申请得到的数量

举个例子:如果 你 baseminor 填的是 99 count 填写的是 3
如果此时系统返回的设备号主设备号 为 88
那么 就代表你申请了三个设备号返回了:

主设备号      次设备号        
88            99 ->第一个设备号存放于 第一个参数空间
88            100
88            101
  • label:标签是无所谓的一个名字

-- 函数返回值:

  • 如果申请成功返回0
  • 申请失败返回非 0
2、初始化 cdev 结构体(cdev_init)

-- 函数的功能:初始化 cdev 结构体

-- 函数的头文件:<linux/cdev.h>

-- 函数的原型:void cdev_init(struct cdev *cdev, const struct file_operations *fops);

-- 函数的参数:

  • cdev:用户自己定义一个变量,函数会自动赋值!

类似昨天的 杂项结构体
不同的是 这个结构体不需要像杂项一样,需要我们自己初始化
这个结构体你只需要申请这样的变量、或者开辟这样的空间
传入该函数里面即可!
靠函数做初始化工作!
就是 cdev 核心结构体->Linux2.6 核心结构体

  • fops:
    大家都在杂项见过,他就是内核层的文件接口

-- 函数返回值:无

3、注册设备(cdev_add)

-- 函数的功能:注册设备

-- 函数的头文件:<linux/cdev.h>

-- 函数的原型:int cdev_add(struct cdev *cdev, dev_t dev, unsigned count);

-- 函数的参数:

  • cdev:毫无疑问,他就是你已经完成初始化工作的 cdev 核心结构体

  • dev:你要告诉我你接下来注册的设备的!!!!->首设备号

  • count: 你要连续注册几个设备!

举个例子:
如果你提供的 dev : 主设备号为
88 次设备号 88
你的 count 提供的是 3
那么 你将向内核连续注册三个设备:
分别为:

主设备号    次设备号
88          88
88          89
88          90

-- 函数返回值:

  • 成功返回 0
  • 失败返回 非 0

编写一个 Linux2.6 的代码->蜂鸣器驱动

#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/miscdevice.h>
#include <linux/gpio.h>
#include <linux/fs.h>
#include <linux/cdev.h>dev_t devnumber;
struct cdev mydev;struct file_operations ops;int mybeep_open(struct inode *i, struct file *f)
{gpio_set_value(36,1);return 0;
}int mybeep_close(struct inode *i, struct file *f)
{gpio_set_value(36,0);return 0;
}static int __init mybeep_init(void)
{//1:申请一个可以用的设备号int ret = alloc_chrdev_region(&devnumber,88,1,"beep");if(ret < 0 ){printk("申请失败!\r\n");return -EINVAL;//如果你返回的是这个东西则代表加载失败  insmod加载失效}printk("申请成功,mydevnum = %d\r\n",devnumber);printk("主设备号 === %d\r\n",devnumber >> 20);//主设备号是设备号的高12位,现在将设备号向右移20位,将高12位放在低位,得到主设备号printk("次设备号 === %d\r\n",devnumber & 0xFFFFF);//次设备号是设备号的低20位,这里是将低20位与出来,然后将高12位清0.(16进制一个F是4位,2*5 = 20位)//2:初始化  cdev ops.owner = THIS_MODULE;ops.open = mybeep_open;ops.release = mybeep_close;cdev_init(&mydev,&ops);//3:往内核里面添加cdev 结构体ret = cdev_add(&mydev,devnumber,1);if(ret <0){printk("注册设备失败!\r\n");return  -EINVAL;}printk("设备注册成功!\r\n");//4:申请GPIO 设置GPIOgpio_request(36,"beep");gpio_direction_output(36,0);return 0;
}static void __exit mybeep_exit(void)
{}module_init(mybeep_init);
module_exit(mybeep_exit);MODULE_LICENSE("GPL");

-- 编译成功后,加载驱动程序,查看设备号!

alt text

alt text

-- 连接串口

-- 可以看出正常生成了设备号

alt text

-- 但是查看设备文件,发现没有生成设备文件!

alt text

ls /dev/myb*

-- 可以看出虽然注册到了设备号,但是没有生成设备文件!,所以我们需要手动生成设备文件!

手动生成设备文件的指令和接口

 mknod /dev/mybeep c 234 88

-- 然后在main.c中写入打开这个设备文件

alt text


-- 但是手动生成设备文件没有什么意义,我们还是需要写一个驱动程序,自动生成设备文件!

结合 Linux2.6 写一个自动生成设备文件的驱动

1、生成设备文件所需要的类结构体(class_create)

-- 函数的功能:生成一个类结构体

-- 函数的头文件:<linux/device.h>

-- 函数的原型:struct class *class_create(struct module *owner, const char *name);

-- 函数的参数:

  • owner:只要你在内核见到这个东西,填写 THIS_MODULE

  • name:无所谓,生成类的标识名字

-- 函数返回值:返回的就是你创建的类结构体, 这个类结构体用于生成设备文件的函数的传参!

2、生成设备文件(device_create)

-- 函数的功能:在内核里面创建一个设备文件

-- 函数的头文件:<linux/device.h>

-- 函数的原型:struct device *device_create(struct class *class, struct device *parent, dev_t devt, void *drvdata, const char *fmt, ...);

-- 函数的参数:

  • class:他就是刚才我们所讲到 类结构体,class_create 创建的结构体‘

  • parent:父设备,一般填写 NULL

  • devt:你要创建的设备文件的设备号

  • drvdata:你要创建的设备文件的设备号

  • fmt:printf();这就是你的 printf 格式化传参字符串!
    可以直接传字符串
    他就是你的生成的设备文件的名字
    只不过支持格式化传递!

-- 函数返回值:
用于设备的销毁 也可以不要

-- 在代码中加入

    //4:先去搞一个 类结构体cls = class_create(THIS_MODULE,"beep19");//5:生成设备文件device_create(cls,NULL,devnumber,NULL,"xydbeep");

补充:那个卸载函数采用倒序

-- 也就是先申请设备号函数,就最后取消申请设备号函数。

--

alt text

static void __exit myled_exit()
{//符合倒叙gpio_free(36);device_destroy(cls,mydevnum);class_destroy(cls);cdev_del(&mycdev);unregister_chrdev_region(mydevnum,1);}
小结:
  • 申请设备号函数:

int alloc_chrdev_region(dev_t *, unsigned, unsigned, const char *);

  • 取消申请的设备号

void unregister_chrdev_region(dev_t, unsigned);

  • cdev 的结构体的初始化

cdev_init

  • cdev 结构体的注册

cdev_add

  • cdev 结构体删除

cdev_del

  • 类结构体的创建

class_create(THIS_MODULE,"xyd_class");

  • 类结构体的销毁

class_destroy(cls);

  • 设备文件的创建

device_create(cls,NULL,mydevnum,NULL,"xydbeep");
//就会生成一个 设备文件 /dev/xydbeep

  • 设备文件的销毁

device_destroy(cls,mydevnum);

编写一个 Linux2.6 的代码->蜂鸣器驱动(整体代码)

#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/miscdevice.h>
#include <linux/gpio.h>
#include <linux/fs.h>
#include <linux/cdev.h>
#include <linux/device.h>dev_t devnumber;
struct cdev mydev;struct file_operations ops;struct class * cls;//类结构体int mybeep_open(struct inode *i, struct file *f)
{gpio_set_value(36,1);return 0;
}int mybeep_close(struct inode *i, struct file *f)
{gpio_set_value(36,0);return 0;
}static int __init mybeep_init(void)
{//1:申请一个可以用的设备号int ret = alloc_chrdev_region(&devnumber,88,1,"beep");if(ret < 0 ){printk("申请失败!\r\n");return -EINVAL;//如果你返回的是这个东西则代表加载失败  insmod加载失效}printk("申请成功,mydevnum = %d\r\n",devnumber);printk("主设备号 === %d\r\n",devnumber >> 20);//主设备号是设备号的高12位,现在将设备号向右移20位,将高12位放在低位,得到主设备号printk("次设备号 === %d\r\n",devnumber & 0xFFFFF);//次设备号是设备号的低20位,这里是将低20位与出来,然后将高12位清0.(16进制一个F是4位,2*5 = 20位)//2:初始化  cdev ops.owner = THIS_MODULE;ops.open = mybeep_open;ops.release = mybeep_close;cdev_init(&mydev,&ops);//3:往内核里面添加cdev 结构体ret = cdev_add(&mydev,devnumber,1);if(ret <0){printk("注册设备失败!\r\n");return  -EINVAL;}printk("设备注册成功!\r\n");//4:先去搞一个 类结构体cls = class_create(THIS_MODULE,"beep19");//5:生成设备文件device_create(cls,NULL,devnumber,NULL,"xydbeep");//6:申请GPIO 设置GPIOgpio_request(36,"beep");gpio_direction_output(36,0);return 0;
}static void __exit mybeep_exit(void)
{gpio_free(36);device_destroy(cls,devnumber);class_destroy(cls);cdev_del(&mydev);unregister_chrdev_region(devnumber,1);}module_init(mybeep_init);
module_exit(mybeep_exit);MODULE_LICENSE("GPL");

-- main.c


#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>int main()
{while(1){int fd = open("/dev/xydbeep",O_RDWR);//sleep(1);close(fd);sleep(1);}return 0;}

-- 效果图

-- 可以看出自动生成了设备文件

alt text

-- 执行main,蜂鸣器响

alt text

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

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

相关文章

硬件知识 cadence16.6 原理图输出为pdf 网络名下划线偏移 (ORCAD)

1. cadence原理图输出为PDF网络名下划线偏移 生这种情况的原因 1. 设计的原理图图纸大小比正常的 A4图纸大。 2. 打印为PDF 的时候&#xff0c;打印机的设置有问题。 2.cadence原理图输出为 PDF网络名下划线偏移的情况 可以看到上图&#xff0c;网络名往上漂移。 3. 解决办法 …

HarmonyOs DevEco Studio小技巧31--卡片的生命周期与卡片的开发

Form Kit简介 Form Kit&#xff08;卡片开发服务&#xff09;提供一种界面展示形式&#xff0c;可以将应用的重要信息或操作前置到服务卡片&#xff08;以下简称“卡片”&#xff09;&#xff0c;以达到服务直达、减少跳转层级的体验效果。卡片常用于嵌入到其他应用&#xff0…

SSRF漏洞利用

2.漏洞利用 2.1 SSRF中URL的伪协议 file:// 从⽂件系统中获取⽂件内容&#xff0c;如&#xff0c;file:///etc/passwd dict:// 字典服务器协议&#xff0c;访问字典资源&#xff0c;如dict://ip:6379/info sftp:// ssh⽂件传输协议或安全⽂件传输协议 ldap:// 轻量级⽬录访问…

nacos镜像启动时候报Public Key Retrieval is not allowed

在nacos的配置文件里加上一句allowPublicKeyRetrievaltrue

【pytorch-04】:线性回归案例(手动构建)

文章目录 1 构建数据集2 构建假设函数3 损失函数4 优化方法5 训练函数6.总结 1 构建数据集 为什么构建数据加载器&#xff1f; 在进行训练的时候都是采用的不是全部的数据&#xff0c;而是采用一个batch_size的数据进行训练&#xff0c;每次向模型当中送入batch_size数据&#…

实验室管理效率提升:Spring Boot技术的力量

2相关技术 2.1 MYSQL数据库 MySQL是一个真正的多用户、多线程SQL数据库服务器。 是基于SQL的客户/服务器模式的关系数据库管理系统&#xff0c;它的有点有有功能强大、使用简单、管理方便、安全可靠性高、运行速度快、多线程、跨平台性、完全网络化、稳定性等&#xff0c;非常…

STM32H7开发笔记(2)——H7外设之多路定时器中断

STM32H7开发笔记&#xff08;2&#xff09;——H7外设之多路定时器中断 文章目录 STM32H7开发笔记&#xff08;2&#xff09;——H7外设之多路定时器中断0.引言1.CubeMX配置2.软件编写 0.引言 本文PC端采用Win11STM32CubeMX4.1.0.0Keil5.24.2的配置&#xff0c;硬件使用STM32H…

springboot基于微信小程序的旧衣回收系统的设计与实现

摘 要 微信小程序的旧衣回收系统是一种专为环保生活设计的应用软件。这款小程序的主要功能包括&#xff1a;系统首页、个人中心、用户管理、回收人员管理、旧衣服分类管理、旧衣信息管理、回收预约管理、回收派单管理、回收订单管理、积分商品管理、积分兑换管理、管理员管理、…

路由缓存后跳转到新路由时,上一路由中的tip信息框不销毁问题解决

上一路由tip信息框不销毁问题解决 路由缓存篇问题描述及截图解决思路关键代码 路由缓存篇 传送门 问题描述及截图 路由缓存后跳转新路由时&#xff0c;上一个路由的tip信息框没销毁。 解决思路 在全局路由守卫中获取DOM元素&#xff0c;通过css去控制 关键代码 修改文…

40分钟学 Go 语言高并发:并发下载器开发实战教程

并发下载器开发实战教程 一、系统设计概述 1.1 功能需求表 功能模块描述技术要点分片下载将大文件分成多个小块并发下载goroutine池、分片算法断点续传支持下载中断后继续下载文件指针定位、临时文件管理进度显示实时显示下载进度和速度进度计算、速度统计错误处理处理下载过…

【前端】JavaScript中的indexOf()方法详解:基础概念与背后的应用思路

博客主页&#xff1a; [小ᶻZ࿆] 本文专栏: 前端 文章目录 &#x1f4af;前言&#x1f4af;什么是indexOf()方法&#xff1f;参数解释返回值示例 &#x1f4af;indexOf() 方法的工作原理&#x1f4af;特殊案例&#xff1a;undefined 的处理示例代码图示解释 &#x1f4af;i…

HarmonyOS4+NEXT星河版入门与项目实战------Button组件

文章目录 1、控件图解2、案例实现1、代码实现2、代码解释3、运行效果4、总结1、控件图解 这里我们用一张完整的图来汇整 Button 的用法格式、属性和事件,如下所示: 按钮默认类型就是胶囊类型。 2、案例实现 这里我们实现一个根据放大和缩小按钮来改变图片大小的功能。 功…

WPF窗体基本知识-笔记-命名空间

窗体程序关闭方式 命名空间:可以理解命名空间的作用为引用下面的控件对象 给控件命名:一般都用x:Name,也可以用Name但是有的控件不支持 布局控件(容器)的类型 布局控件继承于Panel的控件,其中下面的border不是布局控件,panel是抽象类 在重叠的情况下,Zindex值越大的就在上面 Z…

【Qt】QComboBox设置默认显示为空

需求 使用QComboBox&#xff0c;遇到一个小需求是&#xff0c;想要设置未点击出下拉列表时&#xff0c;内容显示为空。并且不想在下拉列表中添加一个空条目。 实现 使用setPlaceholderText()接口。我们先来看下帮助文档&#xff1a; 这里说的是&#xff0c;placeholderText是…

音频信号采集前端电路分析

音频信号采集前端电路 一、实验要求 要求设计一个声音采集系统 信号幅度&#xff1a;0.1mVpp到1Vpp 信号频率&#xff1a;100Hz到16KHz 搭建一个带通滤波器&#xff0c;滤除高频和低频部分 ADC采用套件中的AD7920&#xff0c;转换率设定为96Ksps &#xff1b;96*161536 …

[开源]1.2K star!中后台方向的低代码可视化平台,超赞!

大家好&#xff0c;我是JavaCodexPro&#xff01; “时间就是金钱&#xff0c;效率就是生命”&#xff0c;快速搭建高质量中后台的低代码可视化搭建平台尤为重要&#xff01; 今天JavaCodexPro给大家分享一款超赞的低代码可视化搭建平台 - Marsview &#xff0c;旨在简化开发…

Leetcode 完全二叉树的节点个数

不讲武德的解法 java 实现 class Solution {public int countNodes(TreeNode root) {if(root null) return 0;return countNodes(root.left) countNodes(root.right) 1;} }根据完全二叉树和满二叉树的性质做 class Solution {public int countNodes(TreeNode root) {if (r…

基于CVE安全公告号,全面修复麒麟ARM系统OpenSSH漏洞

前言&#xff1a;负责的其中一个从0开始搭建的某生产项目上线前需要做青藤安全扫描&#xff0c;过了后才允许上线&#xff0c;该项目从操作系统、中间件、数据库、容器等全国产信创化&#xff0c;公司公告为CVE安全公告号&#xff0c;而修复漏洞的责任归我&#xff0c;需要根据…

【每日 C/C++ 问题】

一、什么是 C 中的初始化列表&#xff1f;它的作用是什么&#xff1f; 作用&#xff1a;c提供了初始化列表语法&#xff0c;用来初始化属性 语法&#xff1a;构造函数&#xff08;&#xff09;&#xff1a;属性1&#xff08;值1&#xff09;&#xff0c;属性2&#xff08;值…

【前端知识】Javascript前端框架Vue入门

前端框架VUE入门 概述基础语法介绍组件特性组件注册Props 属性声明事件组件 v-model(双向绑定)插槽Slots内容与出口 组件生命周期样式文件使用1. 直接在<style>标签中写CSS2. 引入外部CSS文件3. 使用CSS预处理器4. 在main.js中全局引入CSS文件5. 使用CSS Modules6. 使用P…