Linux驱动开发之杂项设备注册和Linux2.6设备注册

目录

一、杂项设备注册

杂项设备注册简介

杂项设备注册特点:

杂项设备注册相关API

misc_register()

misc_deregister()

杂项设备注册相关例程

例程简介

源码分享

二、Linux 2.6设备注册

Linux2.6设备注册简介

Linux 2.6设备注册特点

Linux2.6设备注册流程

​Linux2.6设备注册相关函数

alloc_chrdev_region()

unregister_chrdev_region()

cdev_init()

cdev_add()

cdev_del()

class_create()

device_create()

device_destroy()

class_destroy()

Linux2.6设备注册相关例程

例程简介

源码分享

三、选择建议


       Linux设备驱动开发中,需要向内核正确注册设备,才能创建设备节点,以供应用层访问。本文将详细介绍Linux下的两种设备注册方法:杂项设备注册Linux 2.6新方法注册

一、杂项设备注册

杂项设备注册简介

        杂项设备注册是Linux驱动开发中的一种设备注册方式。在Linux系统中,杂项设备是一类没有明确分类的设备,它们不属于字符设备、块设备或网络设备等特定类型。杂项设备可以包括各种不同类型的设备,如传感器、LED灯、温度计等。

        杂项设备注册的目的是将这些杂项设备描述成设备文件,以便通过文件操作来控制和访问设备。通过设备文件,应用程序可以打开、关闭、读取和写入杂项设备,实现对设备的控制和数据交互。

        在杂项设备注册中,使用​​struct miscdevice​​结构体来描述设备的属性和设备文件的名字。该结构体包含了主设备号、次设备号、设备文件名和文件操作集合等信息。通过调用​​misc_register​​函数,将杂项设备的核心结构体注册到内核中,完成设备的注册过程。

        杂项设备注册的好处是它可以自动生成设备文件,无需手动创建。系统会根据设备的主设备号和次设备号,在​​/dev​​目录下自动创建对应的设备文件。这样,应用程序可以直接通过设备文件来访问和控制杂项设备,简化了设备的管理和使用。

杂项设备注册特点:

  • 主设备号固定为10,次设备号自动分配
  • 注册后在/dev目录自动创建设备节点
  • 每个主机最多256个杂项设备

杂项设备注册相关API

misc_register()
功能向内核注册一个杂项设备
头文件Linux/miscdevice.h
原型int misc_register(struct miscdevice * misc)
参数struct miscdevice * misc:杂项设备注册的核心结构体
返回值成功    0失败    负数struct miscdevice  {//杂项注册核心结构体int minor;         //次设备号const char *name;  //设备的名字 会出现在  /dev/目录下const structfile_operations *fops;  操作集合结构体//以下内容可以不管struct list_head list;struct device *parent;struct device*this_device;const char *nodename;umode_t mode;
};
+++++++++++++++++++++++++++++++++++++++++++++++++++++
struct file_operations {//操作集合结构体,使用时需要实现内核编程接口:open/close/read/write等。structmodule *owner;                  //固定填写  THIS_MODULEint(*open) (struct inode *, struct file *);   //内核层的open函数int(*release) (struct inode *, struct file *); //内核层的close函数……………………………………..};
misc_deregister()
功能    取消注册的杂项设备
头文件Linux/miscdevice.h
原型int misc_deregister(struct miscdevice*misc)
参数struct miscdevice *misc:杂项设备的核心结构体
返回值成功    0失败    负数                                     

杂项设备注册相关例程

例程简介

这个例程的作用是实现一个蜂鸣器的控制功能。它包括了一个内核模块和一个用户态程序。

内核模块部分:

  • 在模块初始化函数​​bp_init​​中,首先进行了地址映射,将控制寄存器和数据寄存器映射到内核空间。
  • 然后设置了蜂鸣器的相关寄存器,包括将控制寄存器的低四位设置为1,将数据寄存器的最低位设置为0。
  • 接下来,通过​​misc_register​​函数向内核注册了一个杂项设备。设置了该设备的打开和关闭函数,并指定了设备文件名和次设备号。
  • 在模块退出函数​​bp_exit​​中,调用​​misc_deregister​​函数取消注册杂项设备。

用户态程序部分:

  • 在​​main​​函数中,通过​​open​​函数打开设备文件​​/dev/bp_dev​​,并指定了读写权限。
  • 然后进入一个循环,每隔一秒钟打开蜂鸣器并输出提示信息,再关闭蜂鸣器并输出提示信息。
  • 循环会一直执行,直到程序被手动终止。
源码分享
/*驱动层相关源码*/
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/miscdevice.h>
#include <linux/fs.h>
#include <linux/io.h>void __iomem *GPD0_CON = NULL; //控制寄存器
void __iomem *GPD0_DAT = NULL; //数据寄存器struct file_operations bp_fops;
struct miscdevice bp_misc;//开启蜂鸣器函数
int bp_open (struct inode *inode, struct file *file){
printk("蜂鸣器开启\n");
*((unsigned int *)GPD0_DAT) |= 0x1;
return 0;
}//蜂鸣器关闭函数
int bp_close (struct inode *inode, struct file *file){
printk("蜂鸣器关闭\n");
*((unsigned int *)GPD0_DAT) &= ~(0x1);
return 0;
}static int __init bp_init(void){
printk("加载蜂鸣器模块\n");//地址映射
GPD0_CON = ioremap(0x114000a0, 2);
GPD0_DAT = ioremap(0x114000a4, 1);//寄存器设置
*((unsigned int *)GPD0_CON) &= ~(0xf);
*((unsigned int *)GPD0_CON) |= (0x1);
*((unsigned int *)GPD0_DAT) &= (0x1);//向内核注册一个杂项设备
bp_fops.open = bp_open;	//打开蜂鸣器
bp_fops.release = bp_close;	//关闭蜂鸣器
bp_fops.owner = THIS_MODULE;	//模块所有者bp_misc.minor = 255;	//系统分配次设备号
bp_misc.name = "bp_dev";	//设备文件名
bp_misc.fops = &bp_fops;	//文件操作集合
misc_register(&bp_misc);return 0;
}static void __exit bp_exit(void){
printk("卸载蜂鸣器模块\n");
misc_deregister(&bp_misc);}module_init(bp_init);
module_exit(bp_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("YYY");
/*用户层相关源码*/#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>int main(){int bp_fd  =0;
while(1){bp_fd = open("/dev/bp_dev", O_RDWR);if(bp_fd < 0){printf("打开bp_dev失败\n");perror("open");}printf("开启蜂鸣器\n");sleep(1);close(bp_fd);printf("关闭蜂鸣器\n");sleep(1);
}
return 0;
}

二、Linux 2.6设备注册

Linux2.6设备注册简介

        Linux 2.6内核引入了一种更灵活的设备注册机制,提供了设备模型来管理和注册设备。这种设备注册方式相对于旧版本的Linux内核更加面向对象,具有更好的可维护性和扩展性,也是当前比较最流行的设备注册方法。

Linux 2.6设备注册特点

  • 提供更灵活的设备注册机制,主次设备号不再限定
  • 注册后需要自行创建设备节点
  • 支持模块化和多实例设计
  • 可以灵活配置主次设备号
  • 支持多实例和模块化设计
  • 更好地处理多个设备的情况

Linux2.6设备注册流程

  • 申请设备号(alloc_chrdev_region) 设备号由主设备号和次设备号组成,用于唯一标识一个设备。可以使用​​alloc_chrdev_region​​​函数动态申请字符设备号,或者使​​register_chrdev_region​​函数静态注册一个已知的设备号。
  • 初始化cdev结构体(cdev_init) ​cdev​​​结构体表示字符设备。开发者需要初始化这个结构体,并将它与设备的文件操作函数关联起来。这可以通过​​cdev_init​​​和​​cdev_add​​函数来完成。
  • 注册cdev结构体(cdev_add) ​cdev_add​​​函数用于将​​cdev​​结构体添加到系统中,使得内核可以管理和操作这个设备。
  • 创建设备类(class_create) 使用​​class_create​​​函数创建一个设备类,这个类将出现在​​/sys/class​​​目录下。
  • 创建设备节点(device_create) 用​​device_create​​​函数自动创建设备节点(通常位于​​/dev​​​目录下),而不是手动使用​​mknod​​命令。

​Linux2.6设备注册相关函数

alloc_chrdev_region()
功能向内核申请设备号
头文件Linux/fs.h
原型int alloc_chrdev_region(dev_t * dev, unsigned baseminor, unsigned count, const char * name)
参数dev_t *dev,         用来存放获取到的设备号的变量unsigned baseminor,  次设备号的起始位置 unsigned count,      要申请的设备号的数量 constchar *name     设备的名字
返回值成功    0失败    非零
unregister_chrdev_region()
功能释放设备号
头文件Linux/fs.h
原型void unregister_chrdev_region(dev_t from, unsigned count)
参数dev_t from,      要释放的设备号的起始位置 unsigned count   要释放的设备号的个数
返回值无
cdev_init()
功能初始化linux2.6的核心结构体
头文件Linux/cdev.h
原型void cdev_init(struct cdev *cdev, conststruct file_operations *fops);
参数struct cdev *cdev,               linux2.6的核心结构体const struct file_operations *fops 操作集合结构体
返回值无
cdev_add()
功能注册一个linux2.6的核心结构体
头文件Linux/cdev.h
原型int cdev_add(struct cdev *, dev_t,unsigned);
参数struct cdev *cdev,      linux2.6的核心结构体dev_t dev,                        设备号unsigned   count      向内核注册的linux2.6的核心结构体的个数  一般填1 
返回值成功    0失败    错误码
cdev_del()
功能取消linux2.6核心结构体的注册
头文件Linux/cdev.h
原型void cdev_del(struct cdev *p)
参数struct cdev *p:linux2.6的核心结构体指针
返回值无
class_create()
功能创建类结构体
头文件Linux/device.h
原型struct class *class_create( struct module*owner, const char *name)
参数struct module *owner,    :THIS_MODULEconst char *name        :类结构体的名字  
返回值成功    类结构体指针失败    NULL                                    
device_create()
功能自动生成设备文件
头文件Linux/device.h
原型struct device *device_create(struct class*class, struct device *parent,dev_t devt, void *drvdata, const char*fmt, ...)
参数struct class *class,      类结构体的名字struct device *parent,   NULLdev_t devt,                        设备号void*drvdata,          传给设备的私有数据  NULLconst char *fmt,...       设备的名字  会出现在/dev/目录下 
返回值成功    设备的结构体指针失败    NULL               
device_destroy()
功能销毁设备文件
头文件Linux/device.h
原型void device_destroy(struct class *class,dev_t devt)
参数struct class *class,   类结构体dev_t devt                  设备号
返回值无
class_destroy()
功能销毁类结构体
头文件Linux/device.h
原型void class_destroy(struct class *cls)
参数struct class *cls:类结构体 
返回值无

Linux2.6设备注册相关例程

例程简介

这个Linux内核模块的例程实现了一个简单的 LED 流水灯控制。

  1. Linux内核模块部分:
  • 定义了一个字符设备驱动,设备名为"myled"。这个驱动通过myopenmyclose函数控制LED灯的开关,并实现了流水灯效果。
  • myled_init函数中,首先通过alloc_chrdev_region函数申请设备号,然后初始化cdev结构体并通过cdev_add函数将其添加到系统中。接着创建一个类结构体,并在该类下创建设备文件。
  • myled_exit函数中,注销设备文件,注销类结构体,取消cdev的注册,并释放设备号。
  1. 用户空间程序部分:
  • 这个程序通过打开"/dev/myled"设备文件来控制LED灯的开关。每次打开设备文件时,LED灯会按照流水灯的效果亮起,每次关闭设备文件时,LED灯会熄灭。
  • 程序会无限循环打开和关闭设备文件,从而实现LED灯的不断闪烁。
源码分享
/*驱动层源码*/
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/gpio.h>
#include <linux/cdev.h>
#include <linux/device.h>dev_t mydev;
int count;
struct cdev mycdev;
struct file_operations myfops;
struct class *myclass = NULL;//open函数实现,开灯,流水灯效果
int myopen (struct inode *inode, struct file *file){
if(count == 4)
count = 0;
gpio_set_value(EXYNOS4X12_GPM4(count), 0);
gpio_set_value(EXYNOS4_GPD0(0), 1);
count++;
printk("开灯%d\n", count);
return 0;
}//close函数实现,关灯,流水灯效果
int myclose (struct inode *inode, struct file *file){
gpio_set_value(EXYNOS4X12_GPM4(count - 1), 1);
gpio_set_value(EXYNOS4_GPD0(0), 0);
printk("关灯%d\n", count - 1);
return 0;
}static int __init myled_init(void){
//申请设备号alloc_chrdev_region(&mydev, 0, 1, "myled");
printk("申请到的设备号为 %d\n", mydev);
printk("主设备号 %d\n", MAJOR(mydev));
printk("次设备号 %d\n", MINOR(mydev));//初始化Linux2.6的核心结构体
myfops.owner = THIS_MODULE;
myfops.open = myopen;
myfops.release = myclose;
cdev_init(&mycdev, &myfops);//向内核注册一个Linux2.6核心结构体
cdev_add(&mycdev, mydev, 1);//创建一个类结构体
myclass = class_create(THIS_MODULE, "myled");
if(myclass == NULL){printk("创建类结构体失败\n");return -1;
}//创建设备文件
device_create(myclass, NULL, mydev, NULL, "myled");
return 0;
}static void __exit myled_exit(void){
//注销设备文件
device_destroy(myclass, mydev);//注销类结构体
class_destroy(myclass);//取消Linux2.6注册
cdev_del(&mycdev);//释放设备号
unregister_chrdev_region(mydev, 1);
}module_init(myled_init);
module_exit(myled_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("YYY");
/*用户层源码*/
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>int main(){int led_fd = 0;
while(1){led_fd = open("/dev/myled", O_RDWR);if(led_fd < 0){printf("打开myled失败\n");perror("open");}printf("开灯\n");sleep(1);close(led_fd);printf("关灯\n");sleep(1);
}
return 0;
}
//申请设备号 
int alloc_chrdev_region(dev_t *dev, unsigned baseminor, unsigned count, const char *name);//初始化注册cdev
void cdev_init(struct cdev *cdev, const struct file_operations *fops);
int cdev_add(struct cdev *cdev, dev_t dev, unsigned count);//创建设备节点
struct device *device_create(struct class *cls, struct device *parent, dev_t devt, void *drvdata, const char *fmt,..);

三、选择建议

  • 简单的字符设备推荐使用杂项设备注册
  • 需要灵活控制主次设备号的复杂设备使用Linux 2.6方法
  • 在模块化设计方面,Linux 2.6方式更优秀

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

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

相关文章

sklearn 中matplotlib编制图表

代码 # 导入pandas库&#xff0c;并为其设置别名pd import pandas as pd import matplotlib.pyplot as plt# 使用pandas的read_csv函数读取名为iris.csv的文件&#xff0c;将数据存储在iris_data变量中 iris_data pd.read_csv(data/iris.txt,sep\t)# 使用groupby方法按照&quo…

人机交互中信息数量与质量

在人机交互中&#xff0c;信息的数量和质量都是非常重要的因素。 信息的数量指的是交互过程中传递的信息的多少。信息的数量直接影响到交互的效率和效果&#xff0c;如果交互中传递的信息量太少&#xff0c;可能导致交互过程中的信息不足&#xff0c;用户无法得到想要的结果或者…

三菱人机交互GT Designer的使用(二,开关,指示灯,数值显示,数值输入)

今天也开始每日一学&#xff0c;内容为开关&#xff0c;指示灯&#xff0c;数值显示&#xff0c;数值输入&#xff0c;以为这篇文章比较长&#xff0c;所有小编决分为3篇内容写完&#xff0c;谢谢大家阅读&#xff0c;不足之处&#xff0c;欢迎指正。 目录 开关 位&#xff0c…

【UE5蓝图】读取本地json文件修改窗口大小

效果 插件 蓝图 1.判断文件存在 2.1文件不存在&#xff0c;生成文件 {"ResolutionX":540, "ResolutionY":960} 2.2文件存在&#xff0c;直接读取 3.设置窗口大小 遇到的坑 1.分辨率太大&#xff0c;导致效果不理想&#xff0c;建议先往小填写。 2.选对…

JSON 详解

文章目录 JSON 的由来JSON 的基本语法JSON 的序列化简单使用stringify 方法之 replacerstringify 方法之 replacer 参数传入回调函数stringify 方法之 spacestringify 方法之 toJSONparse 方法之 reviver 利用 stringify 和 parse 实现深拷贝 json 相信大家一定耳熟能详&#x…

React Hooks 面试题 | 08.精选React Hooks面试题

&#x1f90d; 前端开发工程师&#xff08;主业&#xff09;、技术博主&#xff08;副业&#xff09;、已过CET6 &#x1f368; 阿珊和她的猫_CSDN个人主页 &#x1f560; 牛客高级专题作者、在牛客打造高质量专栏《前端面试必备》 &#x1f35a; 蓝桥云课签约作者、已在蓝桥云…

原生微信小程序如何动态配置主题颜色及如何调用子组件的方法

一、最终效果 二、步骤 1、在初始化进入项目时&#xff0c;获取当前主题色 2、把主题色定义成全局变量&#xff08;即在app.js中设置&#xff09; 3、tabBar也需要定义全局变量&#xff0c;在首页时需要重新赋值 三、具体实现 1、app.js onLaunch () {//获取主题数据this.set…

MySQL数据库导入100万数据不同方式的性能差异

本文将介绍MySQL数据库导入100万数据的三种方式性能比较。 三种方式分别为&#xff1a; &#xff08;1&#xff09;逐条INSERT &#xff08;2&#xff09;批量INSERT提交 &#xff08;3&#xff09;通过mysql自带的load data命令 应用场景&#xff1a;假设需要向100万个号码…

neo4j运维管理

管理数据库 概念 Neo4j 5(从v4.0)&#xff0c;可以同时创建和使用多个活动数据库。 DBMS Neo4j是一个数据库管理系统(DBMS)&#xff0c;能够管理多个数据库。DBMS可以管理一个独立的服务器&#xff0c;也可以管理集群中的一组服务器。 实例 Neo4j实例是运行Neo4j服务器代…

gitLab页面打tag操作步骤

作者&#xff1a;moical 链接&#xff1a;gitLab页面打tag简单使用 - 掘金 (juejin.cn) 来源&#xff1a;稀土掘金 著作权归作者所有。商业转载请联系作者获得授权&#xff0c;非商业转载请注明出处。 ---------------------------------------------------------------------…

Linux lpr命令教程:如何使用lpr命令打印文件(附案例详解和注意事项)

Linux lpr命令介绍 lpr命令在Unix-like操作系统中用于提交打印任务。如果在命令行中指定了文件名&#xff0c;那么这些文件将被发送到指定的打印机&#xff08;如果没有指定目的地&#xff0c;则发送到默认目的地&#xff09;。如果命令行中没有列出文件&#xff0c;lpr将从标…

RK3568测试tdd

RK3568测试tdd 一、门禁取包二、烧录三、跑tdd用例四、查看结果参考资料 一、门禁取包 右键复制链接&#xff0c;粘贴下载&#xff1b;解压到文件夹&#xff1b; 二、烧录 双击\windows\RKDevTool.exe打开烧写工具&#xff0c;工具界面击烧写步骤如图所示&#xff1a; 推荐…

1229-方法引用(简化lambda)-stream流-单元测试-单例模式

方法引用stream流单元测试&#xff08;junit&#xff09;单例模式 方法引用 概念&#xff1a;对特殊的lambda简化的一种语法 使用时机&#xff1a;&#xff08;&#xff09;-> {lambda体} 特殊的lambda&#xff1a;当lambda体只有1句代码&#xff0c;并且这句代码是在调用…

二叉树BFS

前置知识 二叉树节点的定义 二叉树是递归定义的 /*** Definition for a binary tree node.&#xff08;LeetCode&#xff09;*/public class TreeNode {int val;TreeNode left;TreeNode right;TreeNode() {}TreeNode(int val) { this.val val; }TreeNode(int val, TreeNode…

【java爬虫】获取个股详细数据并用echarts展示

前言 前面一篇文章介绍了获取个股数据的方法&#xff0c;本文将会对获取的接口进行一些优化&#xff0c;并且添加查询数据的接口&#xff0c;并且基于后端返回数据编写一个前端页面对数据进行展示。 具体的获取个股数据的接口可以看上一篇文章 【java爬虫】基于springbootjd…

Leetcode的AC指南 —— 双指针:18. 四数之和

摘要&#xff1a; Leetcode的AC指南 —— 双指针&#xff1a;18. 四数之和。题目介绍&#xff1a;给你一个由 n 个整数组成的数组 nums &#xff0c;和一个目标值 target 。请你找出并返回满足下述全部条件且不重复的四元组 [nums[a], nums[b], nums[c], nums[d]] &#xff08;…

Android : 使用GestureOverlayView进行手势识别—简单应用

示例图&#xff1a; GestureOverlayView介绍&#xff1a; GestureOverlayView 是 Android 开发中用于识别和显示手势的视图组件。它允许用户在屏幕上绘制手势&#xff0c;并且应用程序可以检测和响应这些手势。以下是关于 GestureOverlayView 的主要特点&#xff1a; 手势识别…

Canal的学习

Canal 基本概念整合SpringBoot 基本概念 Canal是一个基于MySQL数据库增量日志解析&#xff0c;提供增量数据订阅和消费&#xff0c;支持将增量数据投递到下游消费者&#xff08;如Kafka、RocketMQ等&#xff09;或者存储&#xff08;如 Elasticsearch、HBase 等&#xff09;的…

nodejs+vue+微信小程序+python+PHP特困救助供养信息管理系统-计算机毕业设计推荐

通过走访某特困救助供养机构实际情况&#xff0c;整理特困救助供养机构管理的业务流程&#xff0c;分析当前特困救助供养机构管理存在的各种问题&#xff0c;利用软件开发思想对特困救助供养机构特困救助供养机构管理进行系统设计分析。通过服务端程序框架进行设计&#xff0c;…

Python基础语法笔记 tkinter的简单使用

语法 物质 动态类型语言,不需要声明类型 数字 类型int float bool 操作 //整除 **幂 字符串 str1 "Hello python" str2 "world" print(str1 * 3) # 重复输出 print(str1[1]) # 索引访问 print(str1 " " str2) # 拼接 print(str1[2…