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

news2025/1/25 4:33:07

文章目录

  • 一、概念
    • (一)相关概念
    • (二)字符设备框架结构
    • (三)用户空间和内核空间数据传输
      • 1. 函数的参数对应关系
    • (四)字符设备相关的API
      • 1. 字符设备驱动
        • (1)注册字符设备驱动
        • (2)销毁字符设备驱动
      • 2. 用户和内核数据传输
        • (1)copy_to_user
        • (2)copy_from_user
      • 3. 物理地址映射虚拟地址
        • (1)地址映射
        • (2)取消地址映射
  • 二、驱动代码示例
    • (一)查看芯片手册
      • 1. 寄存器基地址
    • (一)驱动代码
    • (二)测试

一、概念

(一)相关概念

设备号:内核识别驱动的唯一的编号,设备号由32个bit位组成

设备号(32bit)=主设备号(高12bit)+次设备号(低20bit)
主设备号:代表的是哪一类设备
次设备号:代表同类中的哪一个设备
在这里插入图片描述

(二)字符设备框架结构

在这里插入图片描述
当用户在应用层面去调用open/read/write/close函数时,操作的是设备文件;
而创建设备文件时,通过设备号把设备文件和内核层面的设备驱动联系起来,又通过file_operations操作方法结构体将函数逐个对应;
在内核层面,驱动通过操作寄存器,最后实现对硬件设备的控制

(三)用户空间和内核空间数据传输

1. 函数的参数对应关系

在这里插入图片描述
用户在应用层通过write函数将buff的内容传给内核层中mycdev_write函数中ubuf,
为了保证内核的安全性,需要通过copy_from_user函数,将ubuf的数据拷贝一份到kbuf中,通过操作kbuf来使用用户传入的数据。
read函数同理,用户在应用层通过read函数读取buf,其实在内核层面。是通过copy_to_user函数,将内核空间数据拷贝一份到kbuf,通过kbuf来传递给用户

使用*ubuf会报错:地址不允许访问

(四)字符设备相关的API

1. 字符设备驱动

(1)注册字符设备驱动
#include <linux/fs.h>

int register_chrdev(unsigned int major,const char *name,const struct file_operations * fops)
功能:注册字符设备驱动; (register char device)
参数:
	@major:主设备号(申请到一个主设备号后,0-255的这256个次设备号会全部被分配)
		major > 0 用户指定主设备号,一般不使用,因为如果与系统已定义的主设备号冲突就会报错
		major = 0 系统动态分配主设备号
	@name:设备的名字
	@fops:操作方法结构体指针
	struct file_operations{
			int (*open) (struct inode *, struct file *);
            ssize_t (*read) (struct file *, char __user *, size_t, loff_t *);
            ssize_t (*write) (struct file *, const char __user *, size_t, loff_t *);
            int (*release) (struct inode *, struct file *);
		}
返回值:
	major > 0 ,成功返回0,失败返回错误码
  	major = 0 ,成功返回主设备号,失败返回错误码
  • 补:cat /proc/devices 查看设备名
    在这里插入图片描述
(2)销毁字符设备驱动
#include <linux/fs.h>

int unregister_chrdev(unsigned int major,const char *name)
功能:销毁字符设备驱动
参数:
	@major:主设备号
	@name:设备的名字
返回值:
	无

2. 用户和内核数据传输

(1)copy_to_user
#include <linux/uaccess.h>

int copy_to_user(void __user volatile *to, const void *from,unsigned long n)
功能:将数据从内核空间拷贝到用户空间(在驱动的在驱动的read中使用)
参数:
	@to:用户空间的数据首地址
 	@from:内核空间数据首地址
 	@n:大小,单位是字节
返回值:
	成功返回0,失败返回未拷贝的字节个数
(2)copy_from_user
int copy_from_user(void *to, const void __user volatile *from,unsigned long n)
功能:将数据从用户空间拷贝到内核空间(在驱动的在驱动的write中使用)
参数:
	@to:内核空间的数据首地址
 	@from:用户空间数据首地址
	@n:大小,单位是字节
返回值:
	成功返回0,失败返回未拷贝的字节个数
  • 注:两个函数都是第一个参数是拷贝到哪,第二个参数是从哪里拷贝

3. 物理地址映射虚拟地址

如果要将开发板上的LED点亮,那就必须操作LED灯对应的寄存器,寄存器的地址是物理地址。
驱动运行在3-4G的虚拟地址空间,所以没有办法直接控制LED的亮灭。如果想要在内核空间操作LED那就必须将LED灯的物理地址映射成虚拟地址,以后在内核空间操作这块虚拟地址就相当于在操作LED的物理地址。

(1)地址映射
#include <linux/io.h>

void *ioremap(unsigned long phy_addr, unsigned long size)
功能:将物理地址映射成虚拟地址
参数:
	@phy_addr:物理地址
 	@size:映射的大小,单位是字节
返回值:
	成功返回内核空间的虚拟地址,失败返回NULL
  • 注:
  • ioremap一次最多映射4k字节地址
(2)取消地址映射
void iounmap(void *virt_addr)
功能:取消地址映射
参数:
	@virt_addr:虚拟地址
返回值:
	无
  • 注:地址映射后,如果不取消映射就会造成内存泄漏

二、驱动代码示例

(一)查看芯片手册

1. 寄存器基地址

图片1

(一)驱动代码

功能需求:实现LED1,LED2,LED3交替亮1s
需求分析
LED1—PE10
LED2—PF10
LED3—PE8
GPIOE基地址—0x50006000
GPIOF基地址—0x50007000
RCC_AHB4基地址—0x50000A28
代码实现

myled.h

#ifndef __MYLED_H__
#define __MYLED_H__

#define GPIOE_BASE 0x50006000
#define GPIOF_BASE 0x50007000
#define RCC        0x50000A28
typedef struct{
    unsigned int gpiox_0:1;
    unsigned int gpiox_1:1;
    unsigned int gpiox_2:1;
    unsigned int gpiox_3:1;
    unsigned int gpiox_4:1;
    unsigned int gpiox_5:1;
    unsigned int gpiox_6:1;
    unsigned int gpiox_7:1;
    unsigned int gpiox_8:1;
    unsigned int gpiox_9:1;
    unsigned int gpiox_10:1;
    unsigned int gpiox_11:1;
    unsigned int gpiox_12:1;
    unsigned int gpiox_13:1;
    unsigned int gpiox_14:1;
    unsigned int gpiox_15:1;
}bitf1_t;
typedef struct{
    unsigned int gpiox_0:2;
    unsigned int gpiox_1:2;
    unsigned int gpiox_2:2;
    unsigned int gpiox_3:2;
    unsigned int gpiox_4:2;
    unsigned int gpiox_5:2;
    unsigned int gpiox_6:2;
    unsigned int gpiox_7:2;
    unsigned int gpiox_8:2;
    unsigned int gpiox_9:2;
    unsigned int gpiox_10:2;
    unsigned int gpiox_11:2;
    unsigned int gpiox_12:2;
    unsigned int gpiox_13:2;
    unsigned int gpiox_14:2;
    unsigned int gpiox_15:2;
}bitf2_t;

typedef struct{
    volatile bitf2_t MODER;
    volatile bitf1_t OTYPER;
    volatile bitf2_t OSPEEDR;
    volatile bitf2_t PUPDR;
    volatile bitf1_t IDR;
    volatile bitf1_t ODR;
}gpio_t;

#define LED1 1
#define LED2 2
#define LED3 3

#endif

myled.c

#include <linux/init.h>
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/io.h>
#include "myled.h"

#define CNAME "myled"
int major; //设备号

gpio_t *gpioe;
gpio_t *gpiof;
unsigned int *rcc;

int kbuf[2];
int mychrdev_open(struct inode *inode, struct file *file){
    pr_info("%s:%d\n",__func__,__LINE__);
    //将虚拟地址转换为物理地址
    rcc=ioremap(RCC,4);
    if(NULL == rcc){
        pr_err("RCC ioremap error");
        return -ENOMEM;
    }
    gpioe=ioremap(GPIOE_BASE,sizeof(gpio_t));
    if(NULL == gpioe){
        pr_err("GPIOE_BASE ioremap error");
        return -ENOMEM;
    }
    gpiof=ioremap(GPIOF_BASE,sizeof(gpio_t));
    if(NULL == gpiof){
        pr_err("GPIOF_BASE ioremap error");
        return -ENOMEM;
    }
    /***初始化LED灯***/
    //使能时钟源
    *rcc |= 0x3<<4;//RCC使能GPIOE和GPIOF
    //PE10和PE8输出模式01
    gpioe->MODER.gpiox_10=1;
    gpioe->MODER.gpiox_8=1;
    gpiof->MODER.gpiox_10=1;
    //输出低电平
    gpioe->ODR.gpiox_10=0;
    gpioe->ODR.gpiox_8=0;
    gpiof->ODR.gpiox_10=0;
    return 0;
}
ssize_t mychrdev_read(struct file *file, char __user *ubuf, size_t size, loff_t *offset){
    int ret;
    pr_info("%s:%s:%d\n", __FILE__, __func__, __LINE__);
    if (size > sizeof(kbuf))
        size = sizeof(kbuf);
    ret = copy_to_user(ubuf, kbuf, size);
    if (ret) {
        pr_err("copy_to_user error\n");
        return -EIO;
    }
    return 0;
}
ssize_t mychrdev_write(struct file *file, const char __user *ubuf, size_t size, loff_t *offset){
    int ret;
    pr_info("%s:%s:%d\n", __FILE__, __func__, __LINE__);
    memset(kbuf, 0, sizeof(kbuf));
    if (size > sizeof(kbuf))
        size = sizeof(kbuf);
    ret = copy_from_user(kbuf, ubuf, size);
    if (ret) {
        pr_err("copy_from_user error\n");
        return -EIO;
    }
    switch(kbuf[0]){
        case LED1:
            kbuf[1]==1?(gpioe->ODR.gpiox_10=1):(gpioe->ODR.gpiox_10=0);
            break;
        case LED2:
            kbuf[1]==1?(gpiof->ODR.gpiox_10=1):(gpiof->ODR.gpiox_10=0);
            break;
        case LED3:
            kbuf[1]==1?(gpioe->ODR.gpiox_8=1):(gpioe->ODR.gpiox_8=0);
            break;
    }
    return 0;
}
int mychrdev_close(struct inode *inode, struct file *file){
    pr_info("%s:%d\n",__func__,__LINE__);
    //取消虚拟映射
    iounmap(rcc);
    iounmap(gpioe);
    iounmap(gpiof);
    return 0;
}

const struct file_operations myfops = {
    .open=mychrdev_open,
    .read=mychrdev_read,
    .write=mychrdev_write,
    .release=mychrdev_close,
};

static int __init mychrdev_init(void){
    //注册设备
    major = register_chrdev(0,CNAME,&myfops);
    if(major < 0){
        pr_err("register error:%d\n",major);
        return major;//返回错误码
    }
    pr_info("major = %d\n",major);//打印设备号
    return 0;
}
static void __exit mychrdev_exit(void){
    //注销设备
    unregister_chrdev(major,CNAME);
}

module_init(mychrdev_init);
module_exit(mychrdev_exit);
MODULE_LICENSE("GPL");
  • 注:使用的交叉编译器版本比较老,对语法检查要求更高,要求变量只能在函数开头定义,否则报错。

(二)测试

在这里插入图片描述

  • 补:错误码 /include/uapi/asm0generic/errno-base.h
    在这里插入图片描述

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

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

相关文章

stl容器 vector的使用与模拟实现

1.vector构造 1.1默认构造函数 vector<int>是vector类模版类型&#xff0c;尖括号里的类型是指生成什么类型的vector的类&#xff0c;实质上vector可以看做一个数组&#xff0c;vector<int>实质上就是生成了一个存int类型的数组&#xff0c;而tamp是这个数组的名字…

SpringSecurity-2(认证和授权+SpringSecurity入门案例+自定义认证+数据库认证)

SpringSecurity使用自定义认证页面 4 SpringSecurity使用自定义认证页面4.1 在SpringSecurity主配置文件中指定认证页面配置信息4.1.1 配置springSecurity.xml配置文件4.1.2 定义login.jsp 4.2 SpringSecurity的csrf防护机制4.2.1 SpringSecurity中CsrfFilter过滤器说明4.2.2 禁…

苹果iPhone 16 Pro系列有望支持Wi-Fi 7,再也不说苹果信号不好了

苹果公司始终以其创新技术引领智能手机市场的发展。 随着新一代iPhone 16系列的即将发布&#xff0c;特别是iPhone 16 Pro系列&#xff0c;预计将带来一系列令人瞩目的升级和新功能。 其中最引人注目的是Wi-Fi 7技术的应用&#xff0c;这将为用户带来前所未有的无线网络速度。…

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

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

飞书API 2-7:如何将 MySQL 数据库的查询结果写入多维表(下)

一、引入 上一篇&#xff0c;解决了数据持续插入更新的问题。在一些场景下&#xff0c;如果数据量较大&#xff0c;需要跑多个任务调用接口插入&#xff0c;但是逐个跑任务又太久&#xff0c;又该怎么提高执行速度呢&#xff1f;没错&#xff01;就是多线程。 本文就来探讨下…

【Nuxt】服务端渲染 SSR

SSR 概述 服务器端渲染全称是&#xff1a;Server Side Render&#xff0c;在服务器端渲染页面&#xff0c;并将渲染好HTML返回给浏览器呈现。 SSR应用的页面是在服务端渲染的&#xff0c;用户每请求一个SSR页面都会先在服务端进行渲染&#xff0c;然后将渲染好的页面&#xf…

STM32 | ADC+RS485(第十天)

点击上方"蓝字"关注我们 01、ADC概述 ADC, Analog-to-Digital Converter的缩写,指模/数转换器或者模拟/数字转换器。是指将连续变量的模拟信号转换为离散的数字信号的器件。真实世界的模拟信号.例如温度、压力、声音或者图像等,需要转换成更容易储存、处理和发射的…

【公考新手教程】公考新手小白备考规划

公考 公考相关考试国考省考 行测常识判断言语理解与表达数量关系判断推理&#xff08;重中之重&#xff09;图推定义判断类比逻辑判断 资料分析&#xff08;重中之重&#xff09; 申论&#xff08;很重要&#xff0c;提升困难&#xff09;公基推荐考公软件粉笔华图在线bilibili…

零基础开始学习鸿蒙开发-文章推荐栏获取接口数据并展示

目录 1.新建文章列表布局页面&#xff0c;通过静态数据&#xff0c;编写好布局页面。 1.1 通过行ArticleCard布局构建单个文章展示的item项 1.2 使用了ObjectLink装饰器&#xff08;尽管这不是ArkUI标准API的一部分&#xff0c;特定框架或自定义的扩展&#xff09;&#xff0c…

在自定义数据集上训练现有的Detectron2模型

这段内容介绍了将使用一个气球分割数据集&#xff08;仅包含一个类别&#xff1a;气球&#xff09;来训练一个气球分割模型。训练过程将以一个预训练在COCO数据集上的模型为基础&#xff0c;这些模型可以在Detectron2的模型库中获取。需要注意的是&#xff0c;COCO数据集本身并…

神仙公司名单(苏州)

神仙公司&#xff08;苏州&#xff09; 前几天虽辟谣了 苏州佳能 N12/2N12 的裁员赔偿&#xff0c;但也同时指出了苏州是外企偏多&#xff0c;就业环境和生活节奏平衡的好城市。 但提到苏州公司&#xff0c;大家第一印象是微软、思科 和 Zoom 此类以 WLB 闻名的明星企业。 确实…

阿里「轨迹可控版Sora」,告别「抽卡」,让视频生成更符合物理规律

目前&#xff0c;扩散模型能够生成多样化且高质量的图像或视频。此前&#xff0c;视频扩散模型采用 U-Net 架构 &#xff0c;主要侧重于合成有限时长&#xff08;通常约为两秒&#xff09;的视频&#xff0c;并且分辨率和纵横比受到固定限制。 Sora 的出现打破了这一限制&#…

【教学类-73-01】20240804镂空瓶子01

背景需求&#xff1a; 瓶子里的春天呀&#xff01; - 小红书 (xiaohongshu.com)https://www.xiaohongshu.com/explore/63ef87f8000000000703acae?app_platformandroid&ignoreEngagetrue&app_version8.47.0&share_from_user_hiddentrue&xsec_sourceapp_share&…

谷粒商城实战笔记-vagrant避坑指南

文章目录 一&#xff0c;虚拟机磁盘空间不足问题原因解决方案 二&#xff0c;虚拟机导致C盘空间不足 一&#xff0c;虚拟机磁盘空间不足 使用vagrant管理虚拟机的过程中遇到了一个问题&#xff0c;虚拟机安装完成后&#xff0c;很快磁盘dev/sda1就满了&#xff0c;40G的空间&a…

链表篇: 04-寻找两个链表的第一个公共结点

解题思路&#xff1a; 方案一&#xff1a;长度统计法 从题目中可以看到&#xff0c;两个链表有长度差&#xff0c;这里可以先让长度比较长的链表先把长度差走完&#xff0c;这里假设为 pHead1, 先让 pHead1 把长度差走完&#xff0c;之后让两个链表同时往后进行遍历&#xff…

01:【stm32】软件安装及stm32的简要介绍

软件安装及stm32的简要介绍 1、软件安装1.1、安装Keil5 MDK软件1.2、安装DFP1.3、安装ARMCC编译器1.4、安装ST-Link驱动1.5、程序下载 2、stm32的介绍 1、软件安装 1.1、安装Keil5 MDK软件 ①先在D盘新建一个名为Keil5的文件夹。然后在Keil5文件夹里面新建2个文件夹&#xff0…

Vue从入门到精通全网最强保姆级教程

Vue是什么&#xff1f;为什么要学习他 Vue是什么&#xff1f; Vue是前端优秀框架&#xff0c; 是一套用于构建用户界面的渐进式框架 为什么要学习Vue 1 Vue是目前前端最火的框架之一 2 Vue是目前企业技术栈中要求的知识点 3 Vue可以提升开发体验 4 Vue学习难度较低 5 ..…

使用GCC编译Notepad++的插件

Notepad的本体1是支持使用MSVC和GCC编译的2&#xff0c;但是Notepad插件的官方文档3里却只给出了MSVC的编译指南4。 网上也没有找到相关的讨论&#xff0c;所以我尝试在 Windows 上使用 MinGW&#xff0c;基于 GCC-8.1.0 的 posix-sjlj 线程版本5&#xff0c;研究一下怎么编译…

【Kubernetes】Deployment 的创建和使用(实战)

Deployment 的创建和使用 创建 deployment-demo.yaml 文件&#xff0c;并在其中输入以下内容&#xff1a; apiVersion: apps/v1 kind: Deployment metadata:name: deployment-demolabels:app: nginx spec:replicas: 3selector:matchLabels:app: nginxtemplate:metadata:labels…

Go语言加Vue3零基础入门全栈班15 gin+gorm+vue3用户管理系统实战录播课 2024年08月04日 课程笔记

预览 登录页面&#xff1a; 首页&#xff1a; 用户列表&#xff1a; 新增用户&#xff1a; 删除用户&#xff1a; 暗黑模式&#xff1a; 概述 如果您没有Golang的基础&#xff0c;应该学习如下前置课程。 01 Golang零基础入门课_20240726_149元02 Golang面向对象…