【linux-IMX6ULL-LED字符驱动框架完善】

目录

  • 1.简介
  • 2.前置知识
    • 2.1 重要函数及结构体
    • 2.2 程序框架流程
  • 3. 代码详解:

1.简介

  在上节,我对linux-IMX6ULL-字符设备驱动简单框架实验进行了说明和构建,但是也存在几个问题;

  • 需要手动指定设备号,不能自动申请;
  • 需要在linux端手动创造设备节点,也就是要用maknod命令;
  • 没有引入实际设备;
      因此这节内容就根据上节的驱动框架,然后结合LED,实现设备号的自动分配和设备节点的自动创建;

2.前置知识

  由于本篇博客不属于教程类博客,只是作为学习总结和复盘,因此先把相关的重点知识给提前说明,也能起到一个便于快速回顾的目的;

2.1 重要函数及结构体

 下面的函数均进行了实参带入,具体原定义可以参考源码;

  • static void __iomem *IMX6U_CCM_CCGR1;
  • IMX6U_CCM_CCGR1 = ioremap(CCM_CCGR1_BASE,4);
  • register_chrdev_region(newchrled.devid,NEWCHRLED_COUNT,NEWCHRLED_NAME);
  • alloc_chrdev_region(&newchrled.devid,0,NEWCHRLED_COUNT,NEWCHRLED_NAME);
  • struct cdev cdev;
  • struct class *class;
  • struct device *device;
  • cdev_init(&newchrled.cdev, &newchrled_fops);
  • cdev_add(&newchrled.cdev, newchrled.devid,1);
  • class_create(THIS_MODULE, NEWCHRLED_NAME);
  • device_create(newchrled.class, NULL, newchrled.devid, NULL, NEWCHRLED_NAME);

2.2 程序框架流程

3. 代码详解:

  注意几个点:

  1. 在写驱动程序时不能直接操控物理寄存器,我们只能操控虚拟化的地址,然后虚拟化的地址通过映射间接操控真实的寄存器;
  2. 操控虚拟化的寄存器地址时是通过read(),write()函数来完成的,不能直接赋值;
  3. 我们接收用户端的写的数据时要通过copy_from_user(databuf,buffer,count)函数来实现,不能直接获取;
  4. 注意出口函数里面的注销和删除顺序是有要求的,我们最开始是先注册的设备号,然后注册操作结构体,但是我们在出口函数里面是先删除操作结构体,然后再删除设备号,注意顺序是有要求的,其它也是一样的;

#define LED_MAJOR 200  
#define NEWCHRLED_NAME "newchrled1"
#define NEWCHRLED_COUNT 1/*物理地址*/
#define CCM_CCGR1_BASE				(0x020C406C)
#define SW_MUX_GPIO1_IO03_BASE		(0x020E0068)
#define SW_PAD_GPIO1_IO03_BASE		(0x020E02F4)
#define GPIO1_DR_BASE				(0x0209C000)
#define GPIO1_GDIR_BASE				(0x0209C004)/*虚拟地址,这些地址用于存储物理地址映射的虚拟地址*/
static void __iomem *IMX6U_CCM_CCGR1;
static void __iomem *SW_MUX_GPIO1_IO03;
static void __iomem *SW_PAD_GPIO1_IO03;
static void __iomem *GPIO1_DR;
static void __iomem *GPIO1_GDIR;/*宏定义,开关*/
#define	LEDOFF		0
#define LEDON		1/**LED 设备结构体**/
struct newchrled_dev{struct cdev cdev;     /*创建设备结构体*/struct class *class;  /*返回值都是指针类型*/struct device *device; /*创建设备的返回值,是个结构体指针*/dev_t devid;			/*设备号*/int major;				/*主设备号*/int minor;			/*次设备号*/
};
/*创建LED设备的结构体,这里没有初始化*/
struct newchrled_dev newchrled;
/*led开关函数封装*/
void led_switch(u8 sta)
{u32 val=0;if(sta==LEDON){val = readl(GPIO1_DR);val &= ~(1<<3);writel(val,GPIO1_DR);}if(sta==LEDOFF){val = readl(GPIO1_DR);val |= (1<<3);writel(val,GPIO1_DR);}
}
/*led初始化封装*/
void led_inti(void)
{unsigned int val = 0;/*把物理地址进行虚拟化映射,映射完后把虚拟地址赋值给前面定义的虚拟地址*/IMX6U_CCM_CCGR1 	= ioremap(CCM_CCGR1_BASE,4);SW_MUX_GPIO1_IO03 	= ioremap(SW_MUX_GPIO1_IO03_BASE,4);SW_PAD_GPIO1_IO03 	= ioremap(SW_PAD_GPIO1_IO03_BASE,4);GPIO1_DR 			= ioremap(GPIO1_DR_BASE,4);GPIO1_GDIR 			= ioremap(GPIO1_GDIR_BASE,4);/*开时钟*/val=readl(IMX6U_CCM_CCGR1);val &= ~(3<<26);/*clear*/val |= (3<<26);/*set bit 27 26 into 1*/writel(val,IMX6U_CCM_CCGR1);/*配置寄存器*/writel(0x5,SW_MUX_GPIO1_IO03);writel(0x10B0,SW_PAD_GPIO1_IO03);val = readl(GPIO1_GDIR);val |= (1<<3);writel(val,GPIO1_GDIR);
}static int newchrled_release(struct inode *inode, struct file *file)
{printk("Close ok\r\n");//struct newchrled_dev *dev=(struct newchrled_dev*)file->private_data;return 0;
}static int newchrled_open(struct inode *inode, struct file *file)
{printk("Open ok\r\n");//file->private_data = &newchrled;return 0;
}static ssize_t newchrled_write(struct file *file, const char __user *buffer,size_t count, loff_t *pos)
{unsigned int retvalue;unsigned char databuf[1];/*从用户哪里获取写入的数据,这里不能直接获得,要通过下面的函数进行获得*/retvalue=copy_from_user(databuf,buffer,count);if(retvalue<0){printk("Kernel write failed!\r\n");return -EFAULT;}/*判断是开灯还是关灯*/led_switch(databuf[0]);return 0;
}static const struct file_operations newchrled_fops={.owner 		= 	THIS_MODULE,.write		=	newchrled_write,.open		=	newchrled_open,.release	=	newchrled_release,
};/**into**/
static int __init newchrled_init(void)
{int ret = 0;printk("newchrled init!\r\n");/*1.初始化LED灯,地址映射*/	led_inti();/*2.注册设备号*/newchrled.major = 0;if(newchrled.major){newchrled.devid = MKDEV(newchrled.major,0);ret = register_chrdev_region(newchrled.devid,NEWCHRLED_COUNT,NEWCHRLED_NAME);}else{ret = alloc_chrdev_region(&newchrled.devid,0,NEWCHRLED_COUNT,NEWCHRLED_NAME);newchrled.major = MAJOR(newchrled.devid);newchrled.minor = MINOR(newchrled.devid);}if(ret<0){printk("newchrled chrdev err!\r\n");return -1;}printk("major=%d,minor=%d\r\n",newchrled.major,newchrled.minor);/*3 注册操作函数*/newchrled.cdev.owner=THIS_MODULE;cdev_init(&newchrled.cdev, &newchrled_fops);cdev_add(&newchrled.cdev, newchrled.devid,1);/*添加到linux内核中*///  第二步和第三歩本来在前两节是通过下面的函数实现的://  register_chrdev(LED_MAJOR, LED_NAME,&led_fops);//  这里改写成了改写成了两步,第一步是申请设备号,第二步是注册设备操作函数/*4.自动创建设备节点*/newchrled.class = class_create(THIS_MODULE, NEWCHRLED_NAME);if (IS_ERR(newchrled.class)){return PTR_ERR(newchrled.class);}/*5.创建一个设备*/newchrled.device = device_create(newchrled.class, NULL, newchrled.devid,NULL,NEWCHRLED_NAME);if (IS_ERR(newchrled.device)){return PTR_ERR(newchrled.device);}return 0;
}/**exit**/
static void __exit newchrled_exit(void)
{printk("newchrled exit!\r\n");/*1.注销字符操作函数*/cdev_del(&newchrled.cdev);/*2.注销设备号*/unregister_chrdev_region(newchrled.devid,NEWCHRLED_COUNT);/*3.先摧毁设备*/device_destroy(newchrled.class, newchrled.devid);/*4.后摧毁类*/class_destroy(newchrled.class);
}module_init(newchrled_init);
module_exit(newchrled_exit);
MODULE_LICENSE("GPL");

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

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

相关文章

TCP 与 UDP

0. tcp 与 udp 的 异同特性 TCPUDPname传输控制协议用户数据报协议面向连接&#xff1f; 需要 传输数据前建立连接传输完毕后断开连接不需要可靠的传输数据&#xff1f; 可靠 有确认机制&#xff08;三次握手&#xff09; 有确认、窗口、重传、拥塞控制的机制保证数据可靠传输…

操作视频号小店,新手最关心的问题,一篇给你讲解清楚!

大家好&#xff0c;我是电商小V 新手去做视频号小店的时候&#xff0c;心里面一定是有很多疑问的&#xff0c;会反复咨询一些最关心的问题&#xff0c;因为他们要做好准备&#xff0c;以防后续做店过程中出现问题&#xff0c;其实新手关心的问题就那几个&#xff0c;咱们今天就…

springboot2+mybatis-plus+vue3创建入门小项目[学生管理系统]02[实战篇]

创建一个 vue 项目 创建这个新的文件夹 创建前端项目 eggbox 数据库 SQL CREATE DATABASE IF NOT EXISTS egg DEFAULT CHARACTER SET utf8 COLLATE utf8_bin; USE egg;CREATE TABLE stu (id INT AUTO_INCREMENT, -- 自增主键name VARCHAR(64) NOT NULL, -- 非空姓名字段&a…

前端传参的三种方式

1、params 传参 参数拼接在地址 url 的后面给后台&#xff1b;地址栏中可见 案例1 地址栏&#xff1a;https://xxxxxxxx/admin/clues/detail?id558 接口代码&#xff1a; export function getClueDetail(query: any) {return request<clueItem>({url: /clues/detai…

Java:图书管理系统

目录 一.book 1.在book包中的Book 类用来定义和引用书的名字&#xff0c;作者&#xff0c;价格&#xff0c;类型等。 2.在book包中的第二个类是BookList是用来构建书架&#xff0c;和书架上的初始书本&#xff0c; 二、ioperations 1.AddOperation (增加图书) 2.BorrowOp…

Linux环境基础开发工具的使用(yum,vim,gcc/g++,make/Makefile,gdb)

Linux 软件包管理器-yum 什么是软件包及安装方式 在Linux下安装软件, 一个通常的办法是下载到程序的源代码, 并进行编译, 得到可执行程序。 但是这样太麻烦了, 于是有些人把一些常用的软件提前编译好, 做成软件包(可以理解成windows上的安装程序)放在一个服务器上, 通过包管理…

数据结构——栈(详细分析)

目录 &#x1f349;引言 &#x1f349;栈的本质和特点 &#x1f348;栈的基本操作 &#x1f348;栈的特点 &#x1f34d;后进先出 &#x1f34d;操作受限 &#x1f34d;动态调整 &#x1f348;栈的优缺点 &#x1f34d;优点 &#x1f34d;缺点 &#x1f349;栈的应用…

SQOOP详细讲解

SQOOP安装及使用 SQOOP安装及使用SQOOP安装1、上传并解压2、修改文件夹名字3、修改配置文件4、修改环境变量5、添加MySQL连接驱动6、测试准备MySQL数据登录MySQL数据库创建student数据库切换数据库并导入数据另外一种导入数据的方式使用Navicat运行SQL文件导出MySQL数据库impo…

数据结构------二叉树经典习题2

博主主页: 码农派大星. 关注博主带你了解更多数据结构知识 1.非递归的前序遍历 1.用栈来实现 2,前序遍历是根左右, 先是根节点入栈,,然后不为空时向左遍历,当为空时就返回向右遍历,右为空时直接出栈,依次循环即可. public void preOrderNot(TreeNode root){Stack<TreeNo…

科技赋能,打破视障人士的沟通壁垒

在探索如何增强盲人群体的社会参与度与幸福感的旅程中&#xff0c;盲人社交能力提升策略成为了不容忽视的一环。随着科技的不断进步&#xff0c;像“蝙蝠避障”这样的辅助软件&#xff0c;不仅在日常出行中为盲人提供了实时避障和拍照识别的便利&#xff0c;也在无形中为他们拓…

华为数通 HCIP-Datacom(H12-821)题库

最新 HCIP-Datacom&#xff08;H12-821&#xff09;完整题库请扫描上方二维码访问&#xff0c;持续更新中。 BGP路由的Update消息中可不包含以下哪些属性&#xff1f; A、Local Preference B、AS Path C、MED D、Origin 答案&#xff1a;AC 解析&#xff1a;as-path和ori…

Java17 --- SpringCloud之Sentinel

目录 一、Sentinel下载并运行 二、创建8401微服务整合Sentinel 三、流控规则 3.1、直接模式 3.2、关联模式 3.3、链路模式 3.3.1、修改8401代码 3.3.2、创建流控模式 3.4、Warm UP&#xff08;预热&#xff09; ​编辑 3.5、排队等待 四、熔断规则 4.1、慢调用比…

【C++】09.vector

一、vector介绍和使用 1.1 vector的介绍 vector是表示可变大小数组的序列容器。就像数组一样&#xff0c;vector也采用的连续存储空间来存储元素。也就是意味着可以采用下标对vector的元素进行访问&#xff0c;和数组一样高效。但是又不像数组&#xff0c;它的大小是可以动态改…

操作系统实验四 (综合实验)设计简单的Shell程序

前言 因为是一年前的实验&#xff0c;很多细节还有知识点我都已经遗忘了&#xff0c;但我还是尽可能地把各个细节讲清楚&#xff0c;请见谅。 1.实验目的 综合利用进程控制的相关知识&#xff0c;结合对shell功能的和进程间通信手段的认知&#xff0c;编写简易shell程序&…

Excel透视表:快速计算数据分析指标的利器

文章目录 概述1.数据透视表基本操作1.1准备数据&#xff1a;1.2创建透视表&#xff1a;1.3设置透视表字段&#xff1a;1.4多级分类汇总和交叉汇总的差别1.5计算汇总数据&#xff1a;1.6透视表美化&#xff1a;1.7筛选和排序&#xff1a;1.8更新透视表&#xff1a; 2.数据透视-数…

【B站 heima】小兔鲜Vue3 项目学习笔记Day02

文章目录 Pinia1.使用2. pinia-计数器案例3. getters实现4. 异步action5. storeToRefsx 数据解构保持响应式6. pinia 调试 项目起步1.项目初始化和git管理2. 使用ElementPlus3. ElementPlus 主题色定制4. axios 基础配置5. 路由设计6. 静态资源初始化和 Error lens安装7.scss自…

Github 2024-05-24 开源项目日报 Top10

根据Github Trendings的统计,今日(2024-05-24统计)共有10个项目上榜。根据开发语言中项目的数量,汇总情况如下: 开发语言项目数量Python项目3非开发语言项目2TypeScript项目2JavaScript项目1Kotlin项目1C#项目1C++项目1Shell项目1Microsoft PowerToys: 最大化Windows系统生产…

软件设计师备考笔记(十):网络与信息安全基础知识

文章目录 一、网络概述二、网络互连硬件&#xff08;一&#xff09;网络的设备&#xff08;二&#xff09;网络的传输介质&#xff08;三&#xff09;组建网络 三、网络协议与标准&#xff08;一&#xff09;网络的标准与协议&#xff08;二&#xff09;TCP/IP协议簇 四、Inter…

某神,云手机启动?

某神自从上线之后&#xff0c;热度不减&#xff0c;以其丰富的内容和独特的魅力吸引着众多玩家&#xff1b; 但是随着剧情无法跳过&#xff0c;长草期过长等原因&#xff0c;近年脱坑的玩家多之又多&#xff0c;之前米家推出了一款云某神的app&#xff0c;目标是为了减少用户手…

RedisTemplateAPI:String

文章目录 ⛄1 String 介绍⛄2 命令⛄3 对应 RedisTemplate API❄️❄️ 3.1 添加缓存❄️❄️ 3.2 设置过期时间(单独设置)❄️❄️ 3.3 获取缓存值❄️❄️ 3.4 删除key❄️❄️ 3.5 顺序递增❄️❄️ 3.6 顺序递减 ⛄4 以下是一些常用的API⛄5 应用场景 ⛄1 String 介绍 Str…