热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

字符驱动开发之韦东山视频学习2——LED驱动框架的编写

1、普通驱动编写,一般实际操作简单的就可以直接填空,操作的最关键的地方就是init和readwriteioctrl。比如字符驱动我如果需要读取外部数据或者写入直接修

1、普通驱动编写,一般实际操作简单的就可以直接填空,操作的最关键的地方就是init和read write ioctrl。

比如字符驱动我如果需要读取外部数据或者写入直接修改自己定义的read write,这是你自己修改的最关键的地方,在init里ioremap,然后read write操作虚拟地址读写。

在你入口init函数里面通过register_chardev()和设备号注册,把fileoperation告诉内核,fileoperation里面是实例化的你自己写的read write ioctrl等,fileoperation虽然很关键,但实际写的时候这个里面只有实例化代码很少也很简单。

基本框架如下:

my_gpio_open{};my_gpio_write{};my_gpio_read{};my_gpio_ioctl{};static const struct file_operations my_gpio_fops{.owner = THIS_MODULE,.open = my_gpio_open,
.read = my_gpio_read,
.unlocked_ioctl = my_gpio_ioctl,};int __init fifo_init(void){fifo_Regs1 = ioremap(MY_GPIO_PHY_ADDR1,0xFFFF);ret = misc_register(&fifo_dev);//把fileoperation结构体告诉内核}void __exit my_gpio_exit(void)
{iounmap(GPIO_Regs1);iounmap(GPIO_Regs2);misc_deregister(&my_gpio_dev);printk("read_data_dev: Module exit\n");
}module_init(my_gpio_init);
module_exit(my_gpio_exit);MODULE_LICENSE("GPL");

2、上面那样写在多个设备的情况下修改比较麻烦,他这里给了两种通用的驱动编写框架

第一种很简单就是分成两层

一层leddrv.c和上下对接

另一层对下实现底层驱动ing,和我们最常用的直接写差不多

这样用A板子的时候就编译A和leddrv.c,B的时候就B和leddrv.c

第二种就是,以面向对象的思维,对第一种方法做个改进,把init和ctl包成一个结构体,对上层开放相当于一个对象。

 

led_opr.h

#ifndef _LED_OPR_H
#define _LED_OPR_Hstruct led_operations {int num;int (*init) (int which); /* 初始化LED, which-哪个LED */ int (*ctl) (int which, char status); /* 控制LED, which-哪个LED, status:1-亮,0-灭 */
};struct led_operations *get_board_led_opr(void);#endif

leddrv.c

#include #include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include #include "led_opr.h"/* 1. 确定主设备号 */
static int major = 0;
static struct class *led_class;
struct led_operations *p_led_opr;#define MIN(a, b) (a static ssize_t led_drv_read (struct file *file, char __user *buf, size_t size, loff_t *offset)
{printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);return 0;
}/* write(fd, &val, 1); */
static ssize_t led_drv_write (struct file *file, const char __user *buf, size_t size, loff_t *offset)
{int err;char status;struct inode *inode = file_inode(file);int minor = iminor(inode);printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);err = copy_from_user(&status, buf, 1);/* 根据次设备号和status控制LED */p_led_opr->ctl(minor, status);return 1;
}static int led_drv_open (struct inode *node, struct file *file)
{int minor = iminor(node);printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);/* 根据次设备号初始化LED */p_led_opr->init(minor);return 0;
}static int led_drv_close (struct inode *node, struct file *file)
{printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);return 0;
}/* 2. 定义自己的file_operations结构体 */
static struct file_operations led_drv = {.owner = THIS_MODULE,.open = led_drv_open,.read = led_drv_read,.write = led_drv_write,.release = led_drv_close,
};/* 4. 把file_operations结构体告诉内核:注册驱动程序 */
/* 5. 谁来注册驱动程序啊?得有一个入口函数:安装驱动程序时,就会去调用这个入口函数 */
static int __init led_init(void)
{int err;int i;printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);major = register_chrdev(0, "100ask_led", &led_drv); /* /dev/led */led_class = class_create(THIS_MODULE, "100ask_led_class");err = PTR_ERR(led_class);if (IS_ERR(led_class)) {printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);unregister_chrdev(major, "led");return -1;}p_led_opr = get_board_led_opr();for (i = 0; i num; i++)device_create(led_class, NULL, MKDEV(major, i), NULL, "100ask_led%d", i); /* /dev/100ask_led0,1,... */return 0;
}/* 6. 有入口函数就应该有出口函数:卸载驱动程序时,就会去调用这个出口函数 */
static void __exit led_exit(void)
{int i;printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);for (i = 0; i num; i++)device_destroy(led_class, MKDEV(major, i)); /* /dev/100ask_led0,1,... */device_destroy(led_class, MKDEV(major, 0));class_destroy(led_class);unregister_chrdev(major, "100ask_led");
}/* 7. 其他完善:提供设备信息,自动创建设备节点 */module_init(led_init);
module_exit(led_exit);MODULE_LICENSE("GPL");

board_rk3399.c

#include #include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include "led_opr.h"static volatile unsigned int *CRU_CLKGATE_CON31;
static volatile unsigned int *GRF_GPIO2D_IOMUX ;
static volatile unsigned int *GPIO2_SWPORTA_DDR;
static volatile unsigned int *GPIO2_SWPORTA_DR ;static int board_demo_led_init (int which) /* 初始化LED, which-哪个LED */
{//printk("%s %s line %d, led %d\n", __FILE__, __FUNCTION__, __LINE__, which);if (which &#61;&#61; 0){if (!CRU_CLKGATE_CON31){CRU_CLKGATE_CON31 &#61; ioremap(0xFF760000 &#43; 0x037c, 4);GRF_GPIO2D_IOMUX &#61; ioremap(0xFF770000 &#43; 0x0e00c, 4);GPIO2_SWPORTA_DDR &#61; ioremap(0xFF780000 &#43; 0x0004, 4);GPIO2_SWPORTA_DR &#61; ioremap(0xFF780000 &#43; 0x0000, 4);}/* rk3399 GPIO2_D3 *//* a. 使能GPIO2* set CRU to enable GPIO2* CRU_CLKGATE_CON31 0xFF760000 &#43; 0x037c* (1<<(3&#43;16)) | (0<<3)*/*CRU_CLKGATE_CON31 &#61; (1<<(3&#43;16)) | (0<<3);/* b. 设置GPIO2_D3用于GPIO* set PMU/GRF to configure GPIO2_D3 as GPIO* GRF_GPIO2D_IOMUX 0xFF770000 &#43; 0x0e00c* bit[7:6] &#61; 0b00* (3<<(6&#43;16)) | (0<<6)*/*GRF_GPIO2D_IOMUX &#61; (3<<(6&#43;16)) | (0<<6);/* c. 设置GPIO2_D3作为output引脚* set GPIO_SWPORTA_DDR to configure GPIO2_D3 as output* GPIO_SWPORTA_DDR 0xFF780000 &#43; 0x0004* bit[27] &#61; 0b1*/ *GPIO2_SWPORTA_DDR |&#61; (1<<27);}return 0;
}static int board_demo_led_ctl (int which, char status) /* 控制LED, which-哪个LED, status:1-亮, 0-灭*/
{//printk("%s %s line %d, led %d, %s\n", __FILE__, __FUNCTION__, __LINE__, which, status ? "on" : "off");if (which &#61;&#61; 0){if (status) /* on: output 1 */{ /* d. 设置GPIO2_D3输出高电平* set GPIO_SWPORTA_DR to configure GPIO2_D3 output 1* GPIO_SWPORTA_DR 0xFF780000 &#43; 0x0000* bit[27] &#61; 0b1*/ *GPIO2_SWPORTA_DR |&#61; (1<<27);}else /* off : output 0 */{/* e. 设置GPIO2_D3输出低电平* set GPIO_SWPORTA_DR to configure GPIO2_D3 output 0* GPIO_SWPORTA_DR 0xFF780000 &#43; 0x0000* bit[27] &#61; 0b0*/*GPIO2_SWPORTA_DR &&#61; ~(1<<27);}}return 0;
}static struct led_operations board_demo_led_opr &#61; {.num &#61; 1,.init &#61; board_demo_led_init,.ctl &#61; board_demo_led_ctl,
};struct led_operations *get_board_led_opr(void)
{return &board_demo_led_opr;
}

 


推荐阅读
  • 本文探讨了在C++中如何有效地清空输入缓冲区,确保程序只处理最近的输入并丢弃多余的输入。我们将介绍一种不阻塞的方法,并提供一个具体的实现方案。 ... [详细]
  • 本文介绍了一种解决二元可满足性(2-SAT)问题的方法。通过具体实例,详细解释了如何构建模型、应用算法,并提供了编程实现的细节和优化建议。 ... [详细]
  • 数据结构入门:栈的基本概念与操作
    本文详细介绍了栈这一重要的数据结构,包括其基本概念、顺序存储结构、栈的基本操作(如入栈、出栈、清空栈和销毁栈),以及如何利用栈实现二进制到十进制的转换。通过具体代码示例,帮助读者更好地理解和应用栈的相关知识。 ... [详细]
  • 在Java中,this是一个引用当前对象的关键字。如何通过this获取并显示其所指向的对象的属性和方法?本文详细解释了this的用法及其背后的原理。 ... [详细]
  • 本文介绍了Linux系统中的文件IO操作,包括文件描述符、基本文件操作函数以及目录操作。详细解释了各个函数的参数和返回值,并提供了代码示例。 ... [详细]
  • 本文深入探讨了HTTP请求和响应对象的使用,详细介绍了如何通过响应对象向客户端发送数据、处理中文乱码问题以及常见的HTTP状态码。此外,还涵盖了文件下载、请求重定向、请求转发等高级功能。 ... [详细]
  • 本文将详细探讨Linux pinctrl子系统的各个关键数据结构,帮助读者深入了解其内部机制。通过分析这些数据结构及其相互关系,我们将进一步理解pinctrl子系统的工作原理和设计思路。 ... [详细]
  • 本题旨在通过给定的评级信息,利用拓扑排序和并查集算法来确定全球 Tetris 高手排行榜。题目要求判断是否可以根据提供的信息生成一个明确的排名表,或者是否存在冲突或信息不足的情况。 ... [详细]
  • 本文介绍了如何使用Java中的同步方法和同步代码块来实现两个线程的交替打印。一个线程负责打印1到52的数字,另一个线程负责打印A到Z的字母,确保输出顺序为12A34B...5152Z。 ... [详细]
  • 不确定性|放入_华为机试题 HJ9提取不重复的整数
    不确定性|放入_华为机试题 HJ9提取不重复的整数 ... [详细]
  • 20100423:Fixes:更新批处理,以兼容WIN7。第一次系统地玩QT,于是诞生了此预备式:【QT版本4.6.0&#x ... [详细]
  • 开发笔记:9.八大排序
    开发笔记:9.八大排序 ... [详细]
  • PHP 过滤器详解
    本文深入探讨了 PHP 中的过滤器机制,包括常见的 $_SERVER 变量、filter_has_var() 函数、filter_id() 函数、filter_input() 函数及其数组形式、filter_list() 函数以及 filter_var() 和其数组形式。同时,详细介绍了各种过滤器的用途和用法。 ... [详细]
  • Ihaveastringwithquotesaroundthepathasfollows:我在路径周围有一个带引号的字符串,如下所示:C:\ProgramFiles(x ... [详细]
  • 本文详细介绍了C语言的起源、发展及其标准化过程,涵盖了从早期的BCPL和B语言到现代C语言的演变,并探讨了其在操作系统和跨平台编程中的重要地位。 ... [详细]
author-avatar
辣妈最___潮
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有