Linux驱动开发2 - 内核定时器驱动

背景

所有驱动开发都是基于全志T507(Android 10)进行开发,用于记录驱动开发过程。

简介

定时器是比较常用的一个功能,用来执行周期性任务。一般不太精确的定时可以用系统提供的延时函数进行。如果需要进行较为精确的延时,就要考虑使用系统内核提供的定时器中断。

在linux中ktime可以实现ms级定时,如果需要us、ns级就没法使用这个了(这个我们后续在讨论)。

驱动实现

首先在Linux驱动开发1 - Platform设备中已经实现了Platform设备驱动的编写,本文是基于这个驱动进行的定时器功能开发。

步骤1、配置时钟频率

首先在内核中可以配置定时器中断周期,T507中默认配置为300HZ,即最大精度为3.33ms。如上图所示,timer最大支持的精度为1ms,可以通过启用CONFIG_HZ_1000进行设置。本文就以默认的300HZ频率为例。

也可以用make menuconfig在图形化界面进行配置。

配置文件路径为/kernel/linux-4.9/arch/arm64/configs/xxx

步骤2、实现platform_driver 结构体。

在longan/kernel/linux-4.9/drivers目录下创建自己的文件夹,定义一个timer_led_drv.c文件。

这里可以不用修改,只修改了驱动名称

/* platform驱动结构体 */
static struct platform_driver led_timer_driver = {.driver		= {.name	= "devledtimer",			        	// 无设备树时,用于设备和驱动间的匹配.of_match_table	= led_timer_of_match,     // 有设备树后,利用设备树匹配表},.probe		= led_timer_probe,.remove		= led_timer_remove,
};

在设备数创建设备节点。这里以PI0为例

    led_timer {compatible = "devledtimer";pinctrl-names = "default";status = "okay";//pinctrl-0 = <&timer_led_pin_a>;ledtimer {gpios = <&pio PI 0 1 2 0 1>;state = "on";};};

在PIO节点下定义pinctrl_led_test节点,主要用于配置IO口。

    soc@03000000 {pio: pinctrl@0300b000 {...timer_led_pin_a: led_timer_grp@0 {allwinner,pins = "PI0";allwinner,function = "led_timer_grp";allwinner,muxsel = <0x01>;allwinner,drive = <0x00>;allwinner,pull = <0x01>;allwinner,data = <0x01>;};...}}

步骤3、定义设备节点

主要定义一下led_timer_priv,用来存放节点信息,用于生成/dev/xxx节点。

这里用到了定时器,因此增加了timer_list和timeperiod用来启用定时器及控制定时器周期。

另外还用到了自旋锁,作用自己查资料,这里就不详述了。

/* 存放led信息的结构体 */
struct led_timer_data 
{char                    name[16];       // 设备名字int                     pin;            // gpio编号int                     active;         // 控制亮灭的标志int                     enable;         // 定时器使能标志位
};/* 存放led的私有属性 */
struct led_timer_priv 
{struct cdev             cdev;           // cdev结构体struct class            *dev_class;     // 自动创建设备节点的类int                     num_leds;       // led的数量struct led_timer_data   led;            // 存放led信息的结构体数组int                     timeperiod;     // 定时周期struct timer_list       timer;          // 定义一个定时器spinlock_t              lock;           // 定义一个自旋锁};

步骤4、初始化设备

定义一个初始化方法gpio_led_probe,并绑定到步骤2中的.probe

static int led_timer_probe(struct platform_device *pdev)

这个函数和 Linux驱动开发1 - Platform设备中gpio_led_probe一样。

parser_init_led_timer用于获取设备树中的节点信息,并进行初始化。

步骤5、移除设备

常规操作,将所有设备依次注销回收。

增加del_timer_sync(&priv->timer);用于在移除设备时删除定时器。

static int led_timer_remove(struct platform_device *pdev)
{struct led_timer_priv *priv = platform_get_drvdata(pdev);int i;dev_t devno = MKDEV(dev_major, 0);/* 注销设备结构体,class结构体和cdev结构体 */for(i=0; i<priv->num_leds; i++){devno = MKDEV(dev_major, i);device_destroy(priv->dev_class, devno);}class_destroy(priv->dev_class);cdev_del(&priv->cdev); unregister_chrdev_region(MKDEV(dev_major, 0), priv->num_leds);/* 将led的状态设置为灭 */for (i = 0; i < priv->num_leds; i++) {gpio_set_value(priv->led.pin, 0);}   del_timer_sync(&priv->timer);printk("led_timer_ioctl success to remove driver[major=%d]!\n", dev_major);return 0;
} 

步骤6、字符设备描述file_operations


static struct file_operations led_fops = 
{.owner = THIS_MODULE,.open = led_open,.release = led_release,.unlocked_ioctl = led_ioctl,.compat_ioctl = led_timer_ioctl,
};

实现led_open、led_release、led_ioctl。

在led_open中初始化自旋锁及初始化定时器timer。修改ioctl逻辑,增加定时器控制命令。

static int led_open(struct inode *inode, struct file *file)
{struct led_timer_priv *priv;priv = container_of(inode->i_cdev, struct led_timer_priv, cdev);file->private_data = priv;priv->timeperiod = 1000;/* 初始化自旋锁 */spin_lock_init(&priv->lock);/* 初始化timer */setup_timer(&priv->timer, timer_do_in_time, &priv->timer);return 0;
}static int led_release(struct inode *inode, struct file *file)
{return 0;
}static long led_timer_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
{struct led_timer_priv *priv;priv = file->private_data;unsigned int flags;printk(">>>>>  led_timer_ioctl command=%d arg=%ld\n", cmd, arg);switch (cmd){case 10:/* 关闭定时器 */del_timer_sync(&priv->timer);priv->led.enable = 0;gpio_set_value(priv->led.pin, 0);priv->led.active = 0;printk(">>>>>  led_timer_ioctl del_timer_sync\n");break;case 11:/* 打开定时器 */del_timer_sync(&priv->timer);mod_timer(&priv->timer, jiffies + msecs_to_jiffies(priv->timeperiod));priv->led.enable = 1;printk(">>>>>  led_timer_ioctl mod_timer = %ld\n", jiffies);break;case 12:/* 设置定时器周期 */spin_lock_irqsave(&priv->lock, flags);priv->timeperiod = arg;spin_unlock_irqrestore(&priv->lock, flags);if (priv->led.enable == 0){del_timer_sync(&priv->timer);mod_timer(&priv->timer, jiffies + msecs_to_jiffies(priv->timeperiod));priv->led.enable = 1;}printk(">>>>>  led_timer_ioctl period = %d\n", priv->timeperiod);break;default:printk("Ioctl command=%d can't be supported\n", cmd);break;}return 0;}

实现定时器中断处理函数

void timer_do_in_time(unsigned long arg)
{struct timer_list *mytimer = (struct timer_list *)arg;struct led_timer_priv *priv = container_of(mytimer, struct led_timer_priv, timer);// TODO 在这里处理需要做的事情// 根据标志位处理是否重启定时器if (priv->led.enable == 1){mod_timer(&priv->timer, jiffies + msecs_to_jiffies(priv->timeperiod)); }
}

步骤7、编译环境MakeFile

在自定义目录下创建Makefile文件。这里默认加载设备。

obj-y	+= timer_led_drv.o

遇到的问题

问题1、中断函数中获取led_timer_priv结构体信息。

主要用container_of获取到内存中struct led_timer_priv *priv类型指针,然后使用priv中的led_timer_data或者timer进行操作。

源码

#include <linux/init.h>                  
#include <linux/module.h>          
#include <linux/errno.h>             
#include <linux/types.h>                  
#include <linux/kernel.h>   
#include <linux/timer.h>
#include <linux/jiffies.h>      
#include <linux/fs.h>                  
#include <linux/cdev.h>           
#include <linux/device.h>               
#include <linux/of_gpio.h>             
#include <linux/platform_device.h>    
#include <linux/gpio.h>
#include <linux/of_device.h>
#include <linux/slab.h>#define LED_OFF                     0       
#define LED_ON                      1static int dev_major = 0;/* 存放led信息的结构体 */
struct led_timer_data 
{char                    name[16];       // 设备名字int                     pin;            // gpio编号int                     active;         // 控制亮灭的标志int                     enable;         // 定时器使能标志位
};/* 存放led的私有属性 */
struct led_timer_priv 
{struct cdev             cdev;           // cdev结构体struct class            *dev_class;     // 自动创建设备节点的类int                     num_leds;       // led的数量struct led_timer_data   led;            // 存放led信息的结构体数组int                     timeperiod;     // 定时周期struct timer_list       timer;          // 定义一个定时器spinlock_t              lock;           // 定义一个自旋锁};void timer_do_in_time(unsigned long arg)
{struct timer_list *mytimer = (struct timer_list *)arg;struct led_timer_priv *priv = container_of(mytimer, struct led_timer_priv, timer);/* 每次都取反,实现LED灯反转 */priv->led.active = !priv->led.active;		gpio_set_value(priv->led.pin, priv->led.active);/* 重启定时器 */if (priv->led.enable == 1){mod_timer(&priv->timer, jiffies + msecs_to_jiffies(priv->timeperiod)); }
}static inline int sizeof_led_timers_priv(int num_leds)
{return sizeof(struct led_timer_priv) + (sizeof(struct led_timer_priv) * num_leds);
}int parser_init_led_timer(struct platform_device *pdev)
{struct device_node *np = pdev->dev.of_node;     // 当前设备节点struct device_node *child;                      // 当前设备节点的子节点struct led_timer_priv *priv;                    // 存放私有属性int num_leds, gpio;                             // led数量和gpio编号                num_leds = 1;if(num_leds <= 0) {dev_err(&pdev->dev, "fail to find child node\n");return -EINVAL;}priv = devm_kzalloc(&pdev->dev, sizeof_led_timers_priv(num_leds), GFP_KERNEL);if (!priv){return -ENOMEM;}priv->num_leds = 1;/* 找到子节点并传给child */child = of_get_child_by_name(np, "ledtimer");/* 解析dts并且获取gpio口,函数返回值就得到gpio号,并且读取gpio现在的标志 */gpio = of_get_named_gpio(child, "gpios", 0);/* 将子节点的名字,传给私有属性结构体中的led信息结构体中的name属性 */strncpy(priv->led.name, child->name, sizeof(priv->led.name)); printk(">>>>>  led_timer_ioctl gpio=%d name=%s\n", gpio, priv->led.name);/* 将gpio编号和控制亮灭的标志传给结构体* active属性,1代表亮,0代表灭,初始属性为亮*/priv->led.active = 1; priv->led.pin = gpio; /* 申请gpio口,相较于gpio_request增加了gpio资源获取与释放功能 */gpio_request(priv->led.pin, "ledtimer");/* 设置gpio为输出模式,并设置初始状态 */gpio_direction_output(priv->led.pin, 1);/* 将led的私有属性放入platform_device结构体的device结构体中的私有数据中 */platform_set_drvdata(pdev, priv);return 0;}static int led_open(struct inode *inode, struct file *file)
{struct led_timer_priv *priv;priv = container_of(inode->i_cdev, struct led_timer_priv, cdev);file->private_data = priv;priv->timeperiod = 1000;/* 初始化自旋锁 */spin_lock_init(&priv->lock);/* 初始化timer */setup_timer(&priv->timer, timer_do_in_time, &priv->timer);return 0;
}static int led_release(struct inode *inode, struct file *file)
{return 0;
}static long led_timer_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
{struct led_timer_priv *priv;priv = file->private_data;unsigned int flags;switch (cmd){case 10:/* 关闭定时器 */del_timer_sync(&priv->timer);priv->led.enable = 0;gpio_set_value(priv->led.pin, 0);priv->led.active = 0;break;case 11:/* 打开定时器 */del_timer_sync(&priv->timer);mod_timer(&priv->timer, jiffies + msecs_to_jiffies(priv->timeperiod));priv->led.enable = 1;break;case 12:/* 设置定时器周期 */spin_lock_irqsave(&priv->lock, flags);priv->timeperiod = arg;spin_unlock_irqrestore(&priv->lock, flags);if (priv->led.enable == 0){del_timer_sync(&priv->timer);mod_timer(&priv->timer, jiffies + msecs_to_jiffies(priv->timeperiod));priv->led.enable = 1;}break;default:printk("Ioctl command=%d can't be supported\n", cmd);break;}return 0;}static struct file_operations led_fops = 
{.owner = THIS_MODULE,.open = led_open,.release = led_release,.unlocked_ioctl = led_timer_ioctl,.compat_ioctl = led_timer_ioctl,
};static int led_timer_probe(struct platform_device *pdev)
{struct led_timer_priv   *priv;          // 临时存放私有属性的结构体struct device           *dev;           // 设备结构体dev_t                   devno;          // 设备的主次设备号int                     i, rv = 0;    /* 1)解析设备树并初始化led状态 */rv = parser_init_led_timer(pdev);if( rv < 0 )return rv;/* 将之前存入的私有属性,放入临时的结构体中 */priv = platform_get_drvdata(pdev);/* 2)分配主次设备号 */if (0 != dev_major) {   /* 静态分配主次设备号 */devno = MKDEV(dev_major, 0); rv = register_chrdev_region(devno, priv->num_leds, "devledtimer"); }   else {   /* 动态分配主次设备号 */rv = alloc_chrdev_region(&devno, 0, priv->num_leds, "devledtimer"); dev_major = MAJOR(devno); }   if (rv < 0) {   dev_err(&pdev->dev, "major can't be allocated\n"); return rv; }   /* 3)分配cdev结构体 */cdev_init(&priv->cdev, &led_fops);priv->cdev.owner  = THIS_MODULE;rv = cdev_add (&priv->cdev, devno , priv->num_leds); if( rv < 0) {dev_err(&pdev->dev, "struture cdev can't be allocated\n");goto undo_major;}/* 4)创建类,实现自动创建设备节点 */priv->dev_class = class_create(THIS_MODULE, "ledtimer");if( IS_ERR(priv->dev_class) ) {dev_err(&pdev->dev, "fail to create class\n");rv = -ENOMEM;goto undo_cdev;}/* 5)创建设备 */for(i=0; i<priv->num_leds; i++){devno = MKDEV(dev_major, i);dev = device_create(priv->dev_class, NULL, devno, NULL, "ledtimer");if( IS_ERR(dev) ) {dev_err(&pdev->dev, "fail to create device\n");rv = -ENOMEM;goto undo_class;}}printk("led_timer_ioctl success to install driver[major=%d]!\n", dev_major);return 0;undo_class:class_destroy(priv->dev_class);undo_cdev:cdev_del(&priv->cdev);undo_major:unregister_chrdev_region(devno, priv->num_leds);return rv;}static int led_timer_remove(struct platform_device *pdev)
{struct led_timer_priv *priv = platform_get_drvdata(pdev);int i;dev_t devno = MKDEV(dev_major, 0);/* 注销设备结构体,class结构体和cdev结构体 */for(i=0; i<priv->num_leds; i++){devno = MKDEV(dev_major, i);device_destroy(priv->dev_class, devno);}class_destroy(priv->dev_class);cdev_del(&priv->cdev); unregister_chrdev_region(MKDEV(dev_major, 0), priv->num_leds);/* 将led的状态设置为灭 */for (i = 0; i < priv->num_leds; i++) {gpio_set_value(priv->led.pin, 0);}   del_timer_sync(&priv->timer);printk("led_timer_ioctl success to remove driver[major=%d]!\n", dev_major);return 0;
} /* 匹配列表 */
static const struct of_device_id led_timer_of_match[] = {{ .compatible = "devledtimer" },{}
};MODULE_DEVICE_TABLE(of, led_timer_of_match);/* platform驱动结构体 */
static struct platform_driver led_timer_driver = {.driver		= {.name	= "devledtimer",			        	// 无设备树时,用于设备和驱动间的匹配.of_match_table	= led_timer_of_match,     // 有设备树后,利用设备树匹配表},.probe		= led_timer_probe,.remove		= led_timer_remove,
};module_platform_driver(led_timer_driver);MODULE_LICENSE("GPL");

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/web/77483.shtml

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

相关文章

Dify 使用 excel 或者 csv 文件创建知识库

Dify 使用 excel 或者 csv 文件创建知识库 1. 创建知识库2. 创建聊天助手3. 其他 1. 创建知识库 创建知识库&#xff0c;导入excel/csv文件&#xff0c; 文件内容&#xff0c; 单击 “预览块”&#xff0c;可以确认会生成多个键值对的块&#xff0c; 配置 Embedding 模型和检索…

23种设计模式-行为型模式之迭代器模式(Java版本)

Java 迭代器模式&#xff08;Iterator Pattern&#xff09;详解 &#x1f9e0; 什么是迭代器模式&#xff1f; 迭代器模式是一种行为型设计模式&#xff0c;它提供一种方法顺序访问一个聚合对象中的各个元素&#xff0c;而不暴露该对象的内部表示。 &#x1f3af; 使用场景 …

使用nodeJs的express+axios+cors做代理

使用nodeJs的expressaxioscors做代理 前端在请求后端时通常会遇到跨域cors问题&#xff0c;如果只在本地开发可以通过webpack或vite的proxy设置。但如果需要在线上或者其他地方绕过跨域&#xff0c;可以使用代理的方法。 1. 创建文件夹 并创建以下文件 package.json {"…

T检验、F检验及样本容量计算学习总结

目录 〇、碎语一、假设检验1.1 两种错误1.2 z检验和t检验1.3 t检验1.3.1 单样本t检验1.3.2 配对样本t检验1.3.3 独立样本t检验1.4 方差齐性检验1.4 卡方检验二、样本容量的计算2.1 AB测试主要的两种应用场景2.2 绝对量的计算公式2.3 率的计算公式参考资料〇、碎语 听到最多的检…

tensorflow使用详解

一、TensorFlow基础环境搭建 安装与验证 # 安装CPU版本 pip install tensorflow# 安装GPU版本&#xff08;需CUDA 11.x和cuDNN 8.x&#xff09; pip install tensorflow-gpu# 验证安装 python -c "import tensorflow as tf; print(tf.__version__)"核心概念 Tensor…

Redis的阻塞

Redis的阻塞 Redis的阻塞问题主要分为内在原因和外在原因两大类&#xff0c;以下从这两个维度展开分析&#xff1a; 一、内在原因 1. 不合理使用API或数据结构 Redis 慢查询 Redis 慢查询的界定 定义&#xff1a;Redis 慢查询指命令执行时间超过预设阈值&#xff08;默认 10m…

SLAM学习系列——ORB-SLAM3安装(Ubuntu20-ROS/Noetic)

ORB-SLAM3学习&#xff08;Ubuntu20-ROS&#xff09; 0 主要参考文献1 ORB-SLAM3安装环境配置1.0 前言1.0.0 关于ORB-SLAM3安装版本选择1.0.1 本文配置操作汇总(快速配置)1.0.1.1 ORB_SLAM3环境配置&#xff1a;1.0.1.2 ORB_SLAM3安装1.0.1.3 ORB_SLAM的ROS接口 1.1 C&#xff…

【应用密码学】实验二 分组密码(2)

一、实验要求与目的 1&#xff09; 学习AES密码算法原理 2&#xff09; 学习AES密码算法编程实现 二、实验内容与步骤记录&#xff08;只记录关键步骤与结果&#xff0c;可截图&#xff0c;但注意排版与图片大小&#xff09; 字符串加解密 运行python程序&#xff0c;输入…

区块链基石解码:分布式账本的运行奥秘与技术架构

区块链技术的革命性源于其核心组件——分布式账本&#xff08;Distributed Ledger&#xff09;。这一技术通过去中心化、透明性和不可篡改性&#xff0c;重塑了传统数据存储与交易验证的方式。本文将从分布式账本的核心概念、实现原理、应用场景及挑战等方面展开&#xff0c;揭…

AUTOSAR_RS_ClassicPlatformDebugTraceProfile

AUTOSAR经典平台调试、跟踪与分析支持 AUTOSAR组件调试、跟踪与分析功能详解 目录 简介ARTI核心扩展 核心特定ARTI扩展结构核心参数定义 操作系统和任务扩展 OS特定ARTI扩展任务特定ARTI扩展软件组件特定扩展 总体架构 组件结构接口定义 错误处理 默认错误跟踪器(DET) 总结 1.…

SpringBoot配置RestTemplate并理解单例模式详解

在日常开发中&#xff0c;RestTemplate 是一个非常常用的工具&#xff0c;用来发起HTTP请求。今天我们通过一个小例子&#xff0c;不仅学习如何在SpringBoot中配置RestTemplate&#xff0c;还会深入理解单例模式在Spring中的实际应用。 1. 示例代码 我们首先来看一个基础的配置…

DPIN在AI+DePIN孟买峰会阐述全球GPU生态系统的战略愿景

DPIN基金会在3月29日于印度孟买举行的AIDePIN峰会上展示了其愿景和未来5年的具体发展计划&#xff0c;旨在塑造去中心化算力的未来。本次活动汇集了DPIN、QPIN、社区成员和Web3行业资深顾问&#xff0c;深入探讨DPIN构建全球领先的去中心化GPU算力网络的战略&#xff0c;该网络…

央视两次采访报道爱藏评级,聚焦生肖钞市场升温,评级币成交易安全“定心丸”

CCTV央视财经频道《经济信息联播》《第一时间》两档节目分别对生肖贺岁钞进行了5分钟20秒的专题报道。长期以来&#xff0c;我国一直保持着发行生肖纪念钞和纪念币的传统&#xff0c;生肖纪念钞和纪念币在收藏市场保持着较高的热度。特别是2024年初&#xff0c;央行发行了首张贺…

【计算机哲学故事1-2】输入输出(I/O):你吸收什么,便成为什么

“我最近&#xff0c;是不是废了……”她瘫在沙发上&#xff0c;手机扣在胸口&#xff0c;盯着天花板自言自语。 我坐在一旁&#xff0c;随手翻着桌上的杂志&#xff0c;没接话&#xff0c;等着她把情绪发泄完。 果然&#xff0c;几秒后&#xff0c;她重重地叹了口气&#xf…

封装el-autocomplete,接口调用

组件 <template><el-autocompletev-model"selectedValue":fetch-suggestions"fetchSuggestions":placeholder"placeholder"select"handleSelect"clearablev-bind"$attrs"/> </template><script lang&…

GPUStack昇腾Atlas300I duo部署模型DeepSeek-R1【GPUStack实战篇2】

2025年4月25日GPUStack发布了v0.6版本&#xff0c;为昇腾芯片910B&#xff08;1-4&#xff09;和310P3内置了MinIE推理&#xff0c;新增了310P芯片的支持&#xff0c;很感兴趣&#xff0c;所以我马上来捣鼓玩玩看哈 官方文档&#xff1a;https://docs.gpustack.ai/latest/insta…

Linux进程详细解析

1.操作系统 概念 任何计算机系统都包含⼀个基本的程序集合&#xff0c;称为操作系统(OS)。笼统的理解&#xff0c;操作系统包括&#xff1a; • 内核&#xff08;进程管理&#xff0c;内存管理&#xff0c;文件管理&#xff0c;驱动管理&#xff09; • 其他程序&#xff08…

解决两个技术问题后小有感触-QZ Tray使用经验小总结

老朋友都知道&#xff0c;我现在是一家软件公司销售部门的项目经理和全栈开发工程师&#xff0c;就是这么“奇怪”的岗位&#xff0c;大概我是公司销售团队里比较少有技术背景、销售业绩又不那么理想的销售。 近期在某个票务系统项目上驻场&#xff0c;原来我是这个项目的项目…

Centos 7.6安装redis-6.2.6

1. 安装依赖 确保系统已经安装了必要的编译工具和库&#xff1a; sudo yum groupinstall "Development Tools" -y sudo yum install gcc make tcl -y 2. 解压 Redis 源码包 进入 /usr/local/ 目录并解压 redis-6.2.6.tar.gz 文件&#xff1a; cd /usr/local/ sudo ta…

Ejs模版引擎介绍,什么是模版引擎,什么是ejs,ejs基本用法

** EJS 模板引擎**&#xff0c;让你彻底搞明白什么是模板引擎、什么是 EJS、怎么用、语法、最佳实践等等&#xff1a; &#x1f4da; 一、什么是模板引擎&#xff1f; 模板引擎是前后端分离之前的一种服务器端“渲染技术”。它的主要作用是&#xff1a; 将 HTML 页面和后端传递…