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

news2024/11/16 10:27:16

目录

  • 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.coloradmin.cn/o/1693268.html

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

相关文章

Android Studio添加依赖 新版 和 旧版 的添加方式(Gradle添加依赖)(Java)

旧版的&#xff08;在线添加&#xff09; 1找 文件 在项目的build.gradle文件中添加依赖(在下面的节点中添加库 格式 ’ 组 &#xff1a;名字 &#xff1a; 版本号 ‘ ) dependencies {implementation com.example:library:1.0.0 }implementation 组:名字:版本…

手机版AI写作软件哪个好用?5款AI写作软件分享

在这个快节凑的时代&#xff0c;人们对于高效、便捷的创作方式很是追求。尤其是在人工智能技术发展迅速的今天&#xff0c;AI写作软件的出现&#xff0c;让很多自媒体创作者都会想到在手机上面进内容创作&#xff0c;这样不仅能提高工作效率&#xff0c;而且工作的自由度会更高…

Centos7静态路由和动态路由

路由&#xff0c;即路由选择&#xff08;Routing&#xff09;&#xff0c;是指在计算机网络中选择数据传输路径的过程。路由器&#xff08;Router&#xff09;是执行路由选择功能的网络设备。路由的主要目的是在复杂的网络结构中&#xff0c;选择最佳路径将数据包从源节点传递到…

等保三级 腾讯云控制台检查项设置

1、地址限制 2、角色里加三权用户&#xff08;查看日志权限&#xff09; 3、登录失败处理和超时退出 4、密码复杂度和口令有效期 5、双因素验证 6、操作审计

Vue3实战笔记(43)—Vue3组合式API下封装可复用ECharts图表组件

文章目录 前言一、封装echart图标钩子二、使用步骤总结 前言 接上文&#xff0c;已经安装好了ECharts&#xff0c;开始封装组件方便使用。 一、封装echart图标钩子 首先应用我们之前学习的钩子方式&#xff0c;在hooks目录下创建一个名为 useECharts.js 的文件&#xff0c;用…

边框渐变样式

实现样式&#xff1a; 对应代码&#xff1a; div {min-height: 40vh;border: 10px solid transparent;background-image: linear-gradient(#222, #222), var(--gradient);background-origin: border-box;background-clip: padding-box, border-box;border-radius: 10px;positi…

【深度学习】yolov8 seg实例分割训练,交通灯

文章目录 一、类别定义二、标注后再清洗数据三、训练yolov8 seg四、部署五、代码资料 一、类别定义 类别0&#xff1a; 类别1&#xff1a; 类别2&#xff1a; 类别3&#xff1a; 类别4&#xff1a; 类别5&#xff1a; 类别6&#xff1a; 类别7&#xff1a; 二、标注后再清洗…

kubernetes之prometheus kube-controller-manager。 scheduler报错问题

项目场景&#xff1a; prometheus scheduler及kube-controller-manager监控报错 问题描述 kubeadm搭建完kube-prometheus 会有这个报错 原因分析&#xff1a; rootmaster2:~# kubectl describe servicemonitor -n kube-system kube-controller-manager通过以上图片我们发现 k…

数据结构_链式二叉树(Chained binary tree)基础

✨✨所属专栏&#xff1a;数据结构✨✨ ✨✨作者主页&#xff1a;嶔某✨✨ 二叉树的遍历 前序、中序以及后序遍历 学习二叉树结构&#xff0c;最简单的方式就是遍历。所谓二叉树遍历(Traversal)是按照某种特定的规则&#xff0c;依次对二叉树中的结点进行相应的操作&#xff…

自己手写一个栈【C风格】

#include <iostream> //栈 #define MAX_SIZE 20 #define OK 1 #define ERROR 0 #define TRUE 1 #define FALSE 0typedef int Status;//状态类型 typedef int ElemType;//元素类型typedef struct SqStack {ElemType data[MAX_SIZE];int top; };//初始化&#xff0c;方法1 …

安装 Ubuntu桌面版,详细步骤(附引导 U盘制作工具)

下载镜像 安装Ubuntu首先要下载镜像包&#xff0c;访问下面网址下载镜像包 https://releases.ubuntu.com/ 选择你要安装的Ubuntu版本 将 .iso 文件保存到所需位置&#xff0c;下面会使用此文件创建可引导 U盘。 制作 Ubuntu 引导 U 盘 首先要找到一个大于4G的U盘&#xff…

用Python Pygame做的一些好玩的小游戏

有些游戏的代码比较长就不公布了 1.简简单单 1.疯狂的鸡哥 你要准备的图片&#xff1a; 命名为&#xff1a;ji.png 代码&#xff1a; import pygame import random as r pygame.init() pygame.display.set_caption(aaa) pm pygame.display.set_mode((800,600))class Ls(py…

三前奏:获取/ 读取/ 评估数据【数据分析】

各位大佬好 &#xff0c;这里是阿川的博客 &#xff0c; 祝您变得更强 个人主页&#xff1a;在线OJ的阿川 大佬的支持和鼓励&#xff0c;将是我成长路上最大的动力 阿川水平有限&#xff0c;如有错误&#xff0c;欢迎大佬指正 前面的博客 数据分析—技术栈和开发环境搭建 …

Excel提取某一列的唯一值

点击【筛选】&#xff08;【高级筛选】&#xff09;&#xff0c;参数里&#xff1a; 列表区域&#xff1a;为需要选择唯一值的那一列复制到&#xff1a;生成唯一值的目标区域 据说新版本的excel有了unique()函数&#xff0c;可以很快捷的选择某一列的唯一值&#xff0c;但是博…

深度学习500问——Chapter09:图像分割(3)

文章目录 9.8 PSPNet 9.9 DeepLab系列 9.9.1 DeepLabv1 9.9.2 DeepLabv2 9.9.3 DeeoLabv3 9.9.4 DeepLabv3 9.8 PSPNet 场景解析对于无限制的开放词汇和不同场景来说是具有挑战性的。本文使用文中的 pyramid pooling module 实现基于不同区域的上下文集成&#xff0c;提出了PS…

Java进阶学习笔记14——模板方法设计模式

面试和看源码。 谈到设计模式&#xff1a; 1、解决了什么问题&#xff1f; 2、怎么写&#xff1f; 模板方法设计模式解决了什么问题&#xff1f; 解决方法中存在重复代码的问题。 写法&#xff1a; 1&#xff09;定义一个抽象类&#xff1a; 2&#xff09;在里面定义两个方…

阅读笔记——《ProFuzzBench: A Benchmark for Stateful Protocol Fuzzing》

【参考文献】Natella R, Pham V T. Profuzzbench: A benchmark for stateful protocol fuzzing[C]//Proceedings of the 30th ACM SIGSOFT international symposium on software testing and analysis. 2021: 662-665.【注】本文仅为作者个人学习笔记&#xff0c;如有冒犯&…

Java反射角度简单理解spring IOC容器

概述 Java反射&#xff08;Reflection&#xff09;是Java编程语言的一个特性&#xff0c;它允许在运行时对类、接口、字段和方法进行动态查询和操作。反射提供了一种在运行时查看和修改程序行为的能力&#xff0c;这通常用于实现一些高级功能&#xff0c;如框架(Spring)、ORM&…

【正点原子Linux连载】 第四十七章 音频驱动实验摘自【正点原子】ATK-DLRK3568嵌入式Linux驱动开发指南

1&#xff09;实验平台&#xff1a;正点原子ATK-DLRK3568开发板 2&#xff09;平台购买地址&#xff1a;https://detail.tmall.com/item.htm?id731866264428 3&#xff09;全套实验源码手册视频下载地址&#xff1a; http://www.openedv.com/docs/boards/xiaoxitongban 第四十…

EEGLAB的相关使用

目录 概念 1.安装EEGLAB 2.文件实例演示 导入数据集处理 &#xff08;1&#xff09;导入数据集 &#xff08;2&#xff09;画图 &#xff08;3&#xff09; 修改并存储数据集 &#xff08;4&#xff09; 保存数据集 &#xff08;5&#xff09; 删除数据集 &#xff0…