热门标签 | 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;
}

 


推荐阅读
  • 编译原理中的语法分析方法探讨
    本文探讨了在编译原理课程中遇到的复杂文法问题,特别是当使用SLR(1)文法时遇到的多重规约与移进冲突。文章讨论了可能的解决策略,包括递归下降解析、运算符优先级解析等,并提供了相关示例。 ... [详细]
  • 洛谷 P4009 汽车加油行驶问题 解析
    探讨了经典算法题目——汽车加油行驶问题,通过网络流和费用流的视角,深入解析了该问题的解决方案。本文将详细阐述如何利用最短路径算法解决这一问题,并提供详细的代码实现。 ... [详细]
  • 问题描述现在,不管开发一个多大的系统(至少我现在的部门是这样的),都会带一个日志功能;在实际开发过程中 ... [详细]
  • 本问题涉及在给定的无向图中寻找一个至少包含三个节点的环,该环上的节点不重复,并且环上所有边的长度之和最小。目标是找到并输出这个最小环的具体方案。 ... [详细]
  • 本题要求计算一组正整数的最小公倍数(LCM)。输入包括多组测试数据,每组数据首先给出一个正整数n,随后是n个正整数。 ... [详细]
  • linux网络子系统分析(二)—— 协议栈分层框架的建立
    目录一、综述二、INET的初始化2.1INET接口注册2.2抽象实体的建立2.3代码细节分析2.3.1socket参数三、其他协议3.1PF_PACKET3.2P ... [详细]
  • c语言二元插值,二维线性插值c语言
    c语言二元插值,二维线性插值c语言 ... [详细]
  • 本文详细介绍了如何在循环双链表的指定位置插入新元素的方法,包括必要的步骤和代码示例。 ... [详细]
  • 使用QT构建基础串口辅助工具
    本文详细介绍了如何利用QT框架创建一个简易的串口助手应用程序,包括项目的建立、界面设计与编程实现、运行测试以及最终的应用程序打包。 ... [详细]
  • 本文介绍了如何通过C#语言调用动态链接库(DLL)中的函数来实现IC卡的基本操作,包括初始化设备、设置密码模式、获取设备状态等,并详细展示了将TextBox中的数据写入IC卡的具体实现方法。 ... [详细]
  • 本文介绍如何手动实现一个字符串连接函数,该函数不依赖于C语言的标准字符串处理函数,如strcpy或strcat。函数原型为void concatenate(char *dest, char *src),其主要作用是将源字符串src追加到目标字符串dest的末尾。 ... [详细]
  • 本文通过分析一个具体的案例,探讨了64位Linux系统对32位应用程序的兼容性问题。案例涉及OpenVPN客户端在64位系统上的异常行为,通过逐步排查和代码测试,最终定位到了与TUN/TAP设备相关的系统调用兼容性问题。 ... [详细]
  • HTML:  将文件拖拽到此区域 ... [详细]
  • 本文介绍了如何利用X_CORBA实现远程对象调用,并通过多个示例程序展示了其功能与应用,包括基础的Hello World示例、文件传输工具以及一个完整的聊天系统。 ... [详细]
  • 实现系统调用
    实现系统调用一、实验环境​本次操作还是基于上次编译Linux0.11内核的实验环境进行操作。环境如下:二、实验目标​通过对上述实验原理的认识,相信 ... [详细]
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社区 版权所有