【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

相关文章

解决IDEA报错:编码GBK的不可映射字符问题

《解决IDEA报错:编码GBK的不可映射字符问题》:本文主要介绍解决IDEA报错:编码GBK的不可映射字符问题,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录IDEA报错:编码GBK的不可映射字符终端软件问题描述原因分析解决方案方法1:将命令改为方法2:右下jav

Linux之systemV共享内存方式

《Linux之systemV共享内存方式》:本文主要介绍Linux之systemV共享内存方式,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录一、工作原理二、系统调用接口1、申请共享内存(一)key的获取(二)共享内存的申请2、将共享内存段连接到进程地址空间3、将

快速修复一个Panic的Linux内核的技巧

《快速修复一个Panic的Linux内核的技巧》Linux系统中运行了不当的mkinitcpio操作导致内核文件不能正常工作,重启的时候,内核启动中止于Panic状态,该怎么解决这个问题呢?下面我们就... 感谢China编程(www.chinasem.cn)网友 鸢一雨音 的投稿写这篇文章是有原因的。为了配置完

usb接口驱动异常问题常用解决方案

《usb接口驱动异常问题常用解决方案》当遇到USB接口驱动异常时,可以通过多种方法来解决,其中主要就包括重装USB控制器、禁用USB选择性暂停设置、更新或安装新的主板驱动等... usb接口驱动异常怎么办,USB接口驱动异常是常见问题,通常由驱动损坏、系统更新冲突、硬件故障或电源管理设置导致。以下是常用解决

Linux命令之firewalld的用法

《Linux命令之firewalld的用法》:本文主要介绍Linux命令之firewalld的用法,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录linux命令之firewalld1、程序包2、启动firewalld3、配置文件4、firewalld规则定义的九大

Linux之计划任务和调度命令at/cron详解

《Linux之计划任务和调度命令at/cron详解》:本文主要介绍Linux之计划任务和调度命令at/cron的使用,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录linux计划任务和调度命令at/cron一、计划任务二、命令{at}介绍三、命令语法及功能 :at

Linux下如何使用C++获取硬件信息

《Linux下如何使用C++获取硬件信息》这篇文章主要为大家详细介绍了如何使用C++实现获取CPU,主板,磁盘,BIOS信息等硬件信息,文中的示例代码讲解详细,感兴趣的小伙伴可以了解下... 目录方法获取CPU信息:读取"/proc/cpuinfo"文件获取磁盘信息:读取"/proc/diskstats"文

Linux内核参数配置与验证详细指南

《Linux内核参数配置与验证详细指南》在Linux系统运维和性能优化中,内核参数(sysctl)的配置至关重要,本文主要来聊聊如何配置与验证这些Linux内核参数,希望对大家有一定的帮助... 目录1. 引言2. 内核参数的作用3. 如何设置内核参数3.1 临时设置(重启失效)3.2 永久设置(重启仍生效

kali linux 无法登录root的问题及解决方法

《kalilinux无法登录root的问题及解决方法》:本文主要介绍kalilinux无法登录root的问题及解决方法,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,... 目录kali linux 无法登录root1、问题描述1.1、本地登录root1.2、ssh远程登录root2、

Linux ls命令操作详解

《Linuxls命令操作详解》通过ls命令,我们可以查看指定目录下的文件和子目录,并结合不同的选项获取详细的文件信息,如权限、大小、修改时间等,:本文主要介绍Linuxls命令详解,需要的朋友可... 目录1. 命令简介2. 命令的基本语法和用法2.1 语法格式2.2 使用示例2.2.1 列出当前目录下的文