Linux系统驱动(二)字符设备驱动

文章目录

  • 一、ioctl函数
    • (一)函数格式
    • (二)ioctl命令码的组成
      • 1. 命令码的组成
      • 2. 自己封装命令码
      • 2. 内核提供了封装命令码的宏
    • (三)使用示例
      • 1. 驱动
      • 2. 应用

一、ioctl函数

Linux内核开发者想要将数据的读写和设备的控制分开完成操作,

所以设计了ioctl函数。也就是说数据的读写在read/write中完成,
而设备的控制在ioctl中完成。

(一)函数格式

US:
#include <sys/ioctl.h>
int ioctl(int fd, unsigned long request, ...)
功能:设备的控制操作
参数:@fd:文件描述符@request:命令码@...:可变参,填写必须填写地址
返回值:成功返回0,失败返回-1,置位错误码
-----------------------------------------------
KS:
#include <linux/uaccess.h>
file_operations:long (*unlocked_ioctl) (struct file *, unsigned int, unsigned long);long myled_ioctl(struct file *file, unsigned int cmd, unsigned long arg);//request传给了cmd//...传递给arg,即arg存放的是参数的地址,接收到后需要强转为指针
  • 注:ioctl是应用层接口,内核层次也有与之对应的接口函数
  • 底层函数的返回值返回给上层应用层函数

(二)ioctl命令码的组成

  • 注:*Documentation目录下存放帮助文档,如果需要了解函数如何使用,就进这个目录下
    在这里插入图片描述

1. 命令码的组成

linux@ubuntu:~/linux-5.10.61/Documentation$ vi ./userspace-api/ioctl/ioctl-decoding.rst
在文档中可以看到如下信息:
在这里插入图片描述

在这里插入图片描述
[31:30] 读写标志位:01 — 写,10 — 读;11 — 读写;此处的方向位是相对用户来定的,用户具有写权限或者用户具有读权限
[29:16] 第三个参数占内存的大小,单位是字节;
[16:8] 每个驱动由唯一的ascii字符来标识;
[7:0] 功能码;

2. 自己封装命令码

#define LED_ON 0b01 00000000000100  01001100 00000001|			|			|		|--->功能码,自定义|			|			|-->此处以'L'为例0114--0b01001100|			|-->此处以int型数据为例,此处定义4字节|-->读写标志位
#define LED_OFF 0b01 00000000000100  01001100 00000000

2. 内核提供了封装命令码的宏

通过内核的宏封装命令码:
#define _IO(type,nr)        _IOC(_IOC_NONE,(type),(nr),0)
#define _IOR(type,nr,size)  _IOC(_IOC_READ,(type),(nr),sizeof(size))
#define _IOW(type,nr,size)  _IOC(_IOC_WRITE,(type),(nr),sizeof(size))
#define _IOWR(type,nr,size) _IOC(_IOC_READ|_IOC_WRITE,(type),(nr),sizeof(size))
参数:@type:类型,即用来标识驱动的字符@nr	 :功能码,来区分同一驱动不同的功能
  • 注:此处的size要传数据类型,因为这个宏传入的参数实际在_IOC这个宏中要使用sizeof来确定大小。
#define _IOC(dir,type,nr,size) \(((dir)  << 30) | \((type) << 8) | \((nr)   << 0) | \((size) << 16))

使用示例:

#define LED_ON   _IOW('L',0,int)
#define LED_OFF  _IOW('L',1,int)

(三)使用示例

功能需求:
通过ioctl函数实现LED灯的流水

需求分析:
用户层通过ioctl函数,将命令码和

代码实现:

1. 驱动

myioctl.h

#ifndef __MYIOCTL_H__
#define __MYIOCTL_H__#define CHRNAME "myioctl"
//寄存器基地址---物理地址(使用前需要先转换为虚拟地址)
#define RCC_AHB4_BASE   0x50000A28
#define GPIOE_BASE      0x50006000
#define GPIOF_BASE      0x50007000/***RCC_AHB4***/
typedef struct{unsigned int gpioA:1;unsigned int gpioB:1;unsigned int gpioC:1;unsigned int gpioD:1;unsigned int gpioE:1;unsigned int gpioF:1;unsigned int gpioG:1;unsigned int gpioH:1;unsigned int gpioI:1;unsigned int gpioJ:1;
}rcc_bit_1_t;
typedef struct{rcc_bit_1_t rcc_ahb4;
}rcc_ahb4_t;//使用位域填充寄存器,方便操作
//对应1个bit管理一个引脚的寄存器
//unsigned int是四字节大小,刚好等于一个寄存器的大小,使用位域也不会再对unsigned int类型进行压缩
//即使没有占满四字节,该结构体类型仍然占用四字节
typedef struct{unsigned int gpiox0:1;unsigned int gpiox1:1;unsigned int gpiox2:1;unsigned int gpiox3:1;unsigned int gpiox4:1;unsigned int gpiox5:1;unsigned int gpiox6:1;unsigned int gpiox7:1;unsigned int gpiox8:1;unsigned int gpiox9:1;unsigned int gpiox10:1;unsigned int gpiox11:1;unsigned int gpiox12:1;unsigned int gpiox13:1;unsigned int gpiox14:1;unsigned int gpiox15:1;
}gpio_bit_1_t;
//对应两bit管理一个引脚的寄存器
typedef struct{unsigned int gpiox0:2;unsigned int gpiox1:2;unsigned int gpiox2:2;unsigned int gpiox3:2;unsigned int gpiox4:2;unsigned int gpiox5:2;unsigned int gpiox6:2;unsigned int gpiox7:2;unsigned int gpiox8:2;unsigned int gpiox9:2;unsigned int gpiox10:2;unsigned int gpiox11:2;unsigned int gpiox12:2;unsigned int gpiox13:2;unsigned int gpiox14:2;unsigned int gpiox15:2;
}gpio_bit_2_t;
//定义gpio的类型
typedef struct{gpio_bit_2_t MODER;gpio_bit_1_t OTYPER;gpio_bit_2_t OSPEEDR;gpio_bit_2_t PUPDR;gpio_bit_1_t IDR;gpio_bit_1_t ODR;
}gpio_t;/***定义命令码***/
#define LED1_ON _IOW('L',00,int)
#define LED1_OFF _IOW('L',01,int)
#define LED2_ON _IOW('L',10,int)
#define LED2_OFF _IOW('L',11,int)
#define LED3_ON _IOW('L',20,int)
#define LED3_OFF _IOW('L',21,int)#endif

myioctl.c

#include <linux/init.h>
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/io.h>
#include <linux/uaccess.h>
#include "myioctl.h"int major; //因为该参数不仅要在注册设备的函数中使用,还要再销毁设备的函数中使用,因此必须定义为全局变量
rcc_ahb4_t *mrcc_ahb4;
gpio_t * mgpioe;
gpio_t * mgpiof;
//实现驱动层的打开函数
int myioctl_open(struct inode *inode, struct file *file){//1. 将物理地址映射为虚拟地址mrcc_ahb4 = ioremap(RCC_AHB4_BASE,sizeof(rcc_ahb4_t));//ioremap函数第一个参数是unsigned long类型的参数if(NULL == mrcc_ahb4){//失败返回NULLpr_err("ioremap error");return -ENOMEM;}mgpioe=ioremap(GPIOE_BASE,sizeof(gpio_t));if(NULL == mrcc_ahb4){//失败返回NULLpr_err("ioremap error");return -ENOMEM;}mgpiof=ioremap(GPIOF_BASE,sizeof(gpio_t));if(NULL == mrcc_ahb4){//失败返回NULLpr_err("ioremap error");return -ENOMEM;}//2. 初始化LED灯,并且将其初始值设为熄灭状态//时钟使能mrcc_ahb4->rcc_ahb4.gpioE=1;mrcc_ahb4->rcc_ahb4.gpioF=1;//输出模式01mgpioe->MODER.gpiox10=1;mgpioe->MODER.gpiox8=1;mgpiof->MODER.gpiox10=1;//输出低电平:熄灭mgpioe->ODR.gpiox10=0;mgpiof->ODR.gpiox10=0;mgpioe->ODR.gpiox8=0;return 0; //注意注意!!!!驱动中如果函数返回值不是void,就必须加return
}
//实现驱动层的关闭函数
int myioctl_close(struct inode *inode, struct file *file){//取消地址映射iounmap(mrcc_ahb4);iounmap(mgpioe);iounmap(mgpiof);return 0;
}
//实现驱动层的ioctol函数
long myioctl_ioctl(struct file *file, unsigned int cmd, unsigned long arg){//arg:第三个参数的首地址,由cmd[29:16]决定大小,此处无需第三个参数switch(cmd){case LED1_ON://判断是否有写权限if(cmd & (0x1<<30)){mgpioe->ODR.gpiox10=1;}else{pr_err("NO Write pression\n");}break;case LED1_OFF://判断是否有写权限if(cmd & (0x1<<30)){mgpioe->ODR.gpiox10=0;}else{pr_err("NO Write pression\n");}break;case LED2_ON://判断是否有写权限if(cmd & (0x1<<30)){mgpiof->ODR.gpiox10=1;}else{pr_err("NO Write pression\n");}break;case LED2_OFF://判断是否有写权限if(cmd & (0x1<<30)){mgpiof->ODR.gpiox10=0;}else{pr_err("NO Write pression\n");}break;case LED3_ON://判断是否有写权限if(cmd & (0x1<<30)){mgpioe->ODR.gpiox8=1;}else{pr_err("NO Write pression\n");}break;case LED3_OFF://判断是否有写权限if(cmd & (0x1<<30)){mgpioe->ODR.gpiox8=1;}else{pr_err("NO Write pression\n");}break;}return 0; //应用层ioctl成功返回0; 失败返回-1
}//file_operations结构体
const struct file_operations myfops={.open=myioctl_open,.release=myioctl_close,.unlocked_ioctl=myioctl_ioctl,
};/***驱动三要素***/
static int __init myioctl_init(void){//入口注册设备major=register_chrdev(0,CHRNAME,&myfops); //第一个参数为0,表示由系统分配主设备号,此时返回值就是系统分配的主设备号if(major < 0){//说明出错,返回了错误码,错误码均为负数pr_err("register_chrdev error:%d\n",major);return major; //失败返回错误码}printk("major=%d\n",major);return 0; //成功返回0
}static void __exit myioctl_exit(void){//出口销毁设备unregister_chrdev(major,CHRNAME);
}module_init(myioctl_init);
module_exit(myioctl_exit);
MODULE_LICENSE("GPL");

2. 应用

test.h

#ifndef __TEST_H__
#define __TEST_H__#include <stdio.h>
#include <sys/ioctl.h> //yingyong
#include <my_head.h>/***定义命令码***/
#define LED1_ON _IOW('L',00,int)
#define LED1_OFF _IOW('L',01,int)
#define LED2_ON _IOW('L',10,int)
#define LED2_OFF _IOW('L',11,int)
#define LED3_ON _IOW('L',20,int)
#define LED3_OFF _IOW('L',21,int)#endif

test.c

#include "test.h"int main(int argc, char const *argv[])
{//入参合法性检查if(2!=argc){printf("Usage:%s dev_file!\n",argv[0]);exit(-1);}int fd=0;fd=open(argv[1],O_RDWR);if(0 > fd){printf("open error\n");exit(-1);}//使用死循环,实现三个LED灯轮流点亮while(1){ioctl(fd,LED1_ON);sleep(1);ioctl(fd,LED1_OFF);ioctl(fd,LED2_ON);sleep(1);ioctl(fd,LED2_OFF);ioctl(fd,LED3_ON);sleep(1);ioctl(fd,LED3_OFF);}return 0;
}
  • 注:在宏定义中##表示字符串拼接

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

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

相关文章

LabVIEW与CANopen实现自动化生产线的设备控制与数据采集

在某工厂的自动化生产线上&#xff0c;多个设备通过CANopen网络进行通信和控制。这些设备包括传感器、执行器和PLC&#xff0c;它们共同负责监测和控制生产过程中的关键参数&#xff0c;如温度、压力、速度等。为了实现对整个生产线的集中监控和管理&#xff0c;工厂决定使用La…

计算机毕业设计选题推荐-校园服务系统-Java/Python项目实战

✨作者主页&#xff1a;IT研究室✨ 个人简介&#xff1a;曾从事计算机专业培训教学&#xff0c;擅长Java、Python、微信小程序、Golang、安卓Android等项目实战。接项目定制开发、代码讲解、答辩教学、文档编写、降重等。 ☑文末获取源码☑ 精彩专栏推荐⬇⬇⬇ Java项目 Python…

小程序开发_02项目构成

一、项目的基本结构 二、小程序的页面组成部分 三、json配置文件 ① project.config.json文件 作用&#xff1a;项目的配置文件&#xff0c;用来记录对小程序开发工具所作的个性化配置 ② sitemap.json 作用&#xff1a;是否允许被微信引擎搜索,不希望被搜索dis ③ app.jso…

AI图文创作革命:10步快速掌握自动化内容生成技巧

1.背景 新媒体时代&#xff0c;内容变得非常容易传播&#xff0c;主题及内容的质量直接影响访问量&#xff0c;如果按传统方式写一篇好的文章及配图&#xff0c;至少2天。 Ai 既然有海量的数据&#xff0c;且能够自动生成图文&#xff0c;我们需要给作者提供一个工具&#xff…

XML 学习笔记

简介&#xff1a; &#xff08;1&#xff09;XML&#xff1a;可扩展性标记语言&#xff0c;用于传输和存储数据&#xff0c;而不是展示数据&#xff0c;是W3C 推举的数据传输格式。 XML的标签必须自定义&#xff0c;但是在写标签名的时候一定要有含义。 XML 只能有一个根节点…

Linux驱动----总线

总线相关 总线注册和注销总线device对象----描述设备信息&#xff0c;包括地址&#xff0c;中断号和其他的一些自定义数据注册和注销device对象----指将device注册到mybus总线 driver对象----描述设备驱动的方法&#xff08;操作地址和中断&#xff09;注册和注销driver对象---…

MySQL第3讲--数据类型和表的修改和删除

文章目录 前言数据类型数值类型整数类型浮点数和定点数 字符串类型字符类型&#xff1a;文本类型&#xff1a;二进制数据类型 日期和时间类型实例分析 表的操作添加字段修改字段删除字段修改表名删除表 DDL总结DDL数据库操作DDL表操作 前言 上一节在MySQL第2讲–关系型数据库以…

WebSocket 协议介绍

前言 一.通用协议设计 参考链接 /* --------------------------------------------------------------- | 魔数 2byte | 协议版本号 1byte | 序列化算法 1byte | 报文类型 1byte | --------------------------------------------------------------- | 状态 1byte | …

从0开始搭建vue + flask 旅游景点数据分析系统( 六):搭建后端flask框架

这一期开始开发header部分&#xff0c;预期实现两个目标&#xff1a; 创建 Flask 项目导入旅游数据后端实现旅游数据的查询 1 python 环境 & 开发环境 python 安装和pycharm安装需要去网上找包&#xff0c;建议python使用3.8 或者3.9版本 2 新建项目 我们新建一个文件…

还没排上 SearchGPT ?比 Perplexity 更好用的国产开源平替了解一下?

有 AI 在的科技圈,似乎没有中场休息。除了大模型发布不断,各家科技大厂也在寻找着第一个「杀手级」AI 应用的落脚之地。 OpenAI 首先瞄准的是谷歌 1750 亿美元的搜索业务市场。7 月 25 日,OpenAI 带着 AI 搜索引擎——SearchGPT 高调入场。在演示 demo 中,搜索引擎的使用体…

贪吃蛇(使用QT)

贪吃蛇小游戏 一.项目介绍**[贪吃蛇项目地址](https://gitee.com/strandingzy/QT/tree/zyy/snake)**界面一&#xff1a;游戏大厅界面二&#xff1a;关卡选择界面界面三&#xff1a;游戏界面 二.项目实现2.1 游戏大厅2.2关卡选择界面2.3 游戏房间2.3.1 封装贪吃蛇数据结构2.3.2 …

【CTFWP】ctfshow-web40

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 题目介绍&#xff1a;题目分析&#xff1a;payload&#xff1a;payload解释&#xff1a;payload2&#xff1a;payload2解释&#xff1a;flag 题目介绍&#xff1a; …

第一阶段面试问题(后半部分)

1. c语言中const *p的用法 &#xff08;1&#xff09;const int *p; 或 int const *p; 指向常量整数的指针&#xff0c;通过这个指针不能修改它所指向的整数值&#xff0c;但可以修改指针本身来指向其他地址 const int a 10; const int *p &a; // *p 20; // 错误&…

vector中 resize()和reserve()

1.resize()改变容器大小 resize除了预留内存以外&#xff0c;还会调用容器元素的构造函数&#xff0c;不仅分配了N个对象的内存&#xff0c;还会构造N个对象。从这个层面上来说&#xff0c;resize()在时间效率上是比reserve()低的。 2.reserve()容器大小管理 用于预留内存。 …

Flask目录结构路由重定向简单实例讲解——轻量级的 Python Web 框架

假设一个flask目录结构如下&#xff1a; my_flask_app/ │ ├── app.py ├── routes/ │ ├── __init__.py │ ├── ZhejiangProvince/ │ │ ├── __init__.py │ │ ├── la.py │ │ └── el.py │ ├── GuangdongProvince/ │ │ ├…

C语言项目实战FTP文件传输(windows网络编程基础)

文章目录 前言一、客户端和服务端介绍二、客户端和服务器之间进行通信的过程客户端和服务器建立通信的流程通信过程的示例图流程说明 三、客户端代码编写代码解释 四、服务端代码编写代码解释 总结 前言 本篇文章开始将带大家来学习FTP文件传输助手的项目实现&#xff0c;这个…

【单片机毕业设计选题24099】-室内空气质量检测及净化系统

系统功能: 系统上电后OLED显示“欢迎使用请稍后”两秒后进入正常界面显示&#xff0c; 第一行显示温湿度和MQ2值 第二行显示采集到的甲醛值 第三行显示采集到的PM2.5值 第四行显示设定的PM2.5值 短按B4按键增加PM2.5设定阈值 短按B5按键减小PM2.5设定阈值 如果PM2.5采集…

达梦数据库dsc集群动态添加节点

前提条件&#xff1a;在安装好的的dsc集群&#xff1a;达梦数据库dsc集群保姆级部署文档_达梦数据库文档-CSDN博客上动态添加节点 1、环境信息 扩展节点信息&#xff1a; 操作环境&#xff1a;VMware Workstation 16 Pro dmdsc集群 机器ip 主机名 操作系统 资源配置 实…

【传知代码】基于标签相关性的多标签学习(论文复现)

在当今信息爆炸的时代&#xff0c;数据中包含的标签信息对于理解和分析复杂问题至关重要。在诸如文本分类、图像识别和推荐系统等应用中&#xff0c;如何有效地利用标签相关性提升多标签学习的效果成为了研究的热点之一。基于标签相关性的多标签学习方法&#xff0c;通过挖掘不…

JAVA项目基于SpringBoot的外卖点餐管理系统

目录 一、前言 二、技术介绍 三、系统实现 四、论文参考 五、核心代码 六、源码获取 全栈码农以及毕业设计实战开发&#xff0c;CSDN平台Java领域新星创作者&#xff0c;专注于大学生项目实战开发、讲解和毕业答疑辅导。获取源码联系方式请查看文末 一、前言 随着生活节…