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

2024-05-25 15:36

本文主要是介绍【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");

这篇关于【linux-IMX6ULL-LED字符驱动框架完善】的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1001914

相关文章

Linux中压缩、网络传输与系统监控工具的使用完整指南

《Linux中压缩、网络传输与系统监控工具的使用完整指南》在Linux系统管理中,压缩与传输工具是数据备份和远程协作的桥梁,而系统监控工具则是保障服务器稳定运行的眼睛,下面小编就来和大家详细介绍一下它... 目录引言一、压缩与解压:数据存储与传输的优化核心1. zip/unzip:通用压缩格式的便捷操作2.

Linux中SSH服务配置的全面指南

《Linux中SSH服务配置的全面指南》作为网络安全工程师,SSH(SecureShell)服务的安全配置是我们日常工作中不可忽视的重要环节,本文将从基础配置到高级安全加固,全面解析SSH服务的各项参... 目录概述基础配置详解端口与监听设置主机密钥配置认证机制强化禁用密码认证禁止root直接登录实现双因素

Spring 框架之Springfox使用详解

《Spring框架之Springfox使用详解》Springfox是Spring框架的API文档工具,集成Swagger规范,自动生成文档并支持多语言/版本,模块化设计便于扩展,但存在版本兼容性、性... 目录核心功能工作原理模块化设计使用示例注意事项优缺点优点缺点总结适用场景建议总结Springfox 是

在Linux终端中统计非二进制文件行数的实现方法

《在Linux终端中统计非二进制文件行数的实现方法》在Linux系统中,有时需要统计非二进制文件(如CSV、TXT文件)的行数,而不希望手动打开文件进行查看,例如,在处理大型日志文件、数据文件时,了解... 目录在linux终端中统计非二进制文件的行数技术背景实现步骤1. 使用wc命令2. 使用grep命令

Linux如何快速检查服务器的硬件配置和性能指标

《Linux如何快速检查服务器的硬件配置和性能指标》在运维和开发工作中,我们经常需要快速检查Linux服务器的硬件配置和性能指标,本文将以CentOS为例,介绍如何通过命令行快速获取这些关键信息,... 目录引言一、查询CPU核心数编程(几C?)1. 使用 nproc(最简单)2. 使用 lscpu(详细信

linux重启命令有哪些? 7个实用的Linux系统重启命令汇总

《linux重启命令有哪些?7个实用的Linux系统重启命令汇总》Linux系统提供了多种重启命令,常用的包括shutdown-r、reboot、init6等,不同命令适用于不同场景,本文将详细... 在管理和维护 linux 服务器时,完成系统更新、故障排查或日常维护后,重启系统往往是必不可少的步骤。本文

Python的端到端测试框架SeleniumBase使用解读

《Python的端到端测试框架SeleniumBase使用解读》:本文主要介绍Python的端到端测试框架SeleniumBase使用,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全... 目录SeleniumBase详细介绍及用法指南什么是 SeleniumBase?SeleniumBase

基于Linux的ffmpeg python的关键帧抽取

《基于Linux的ffmpegpython的关键帧抽取》本文主要介绍了基于Linux的ffmpegpython的关键帧抽取,实现以按帧或时间间隔抽取关键帧,文中通过示例代码介绍的非常详细,对大家的学... 目录1.FFmpeg的环境配置1) 创建一个虚拟环境envjavascript2) ffmpeg-py

Linux脚本(shell)的使用方式

《Linux脚本(shell)的使用方式》:本文主要介绍Linux脚本(shell)的使用方式,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录概述语法详解数学运算表达式Shell变量变量分类环境变量Shell内部变量自定义变量:定义、赋值自定义变量:引用、修改、删

Linux链表操作方式

《Linux链表操作方式》:本文主要介绍Linux链表操作方式,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录一、链表基础概念与内核链表优势二、内核链表结构与宏解析三、内核链表的优点四、用户态链表示例五、双向循环链表在内核中的实现优势六、典型应用场景七、调试技巧与