LED模板驱动程序的改造:设备树

简介: LED模板驱动程序的改造:设备树

LED模板驱动程序的改造:设备树

1.总结3种写驱动程序的方法

1.1资源和驱动写在同一个文件中

52e0869d239b4959b461ea5d2eaacfc3.png

1.2资源用platform_device指定,驱动在platform_driver实现

250f736a8f524697a9cb658d59a8360c.png


1.3资源用设备数指定驱动在platform_driver实现


fc6c389cf308461592bde8290bcd8aaa.png

核心永远是file_operations结构体。

上面的三种方式只是指定的硬件资源的方式不一样。platform_device/platform_driver只是编程的技巧,不涉及驱动的核心。

怎么使用设备树写驱动程序

设备树节点要与platform_driver能匹配

在我们的工作中,驱动要求设备树节点提供什么,我们就得按这要求去编写

设备树。

但是,匹配过程所要求的东西是固定的:

设备树要有 compatible 属性,它的值是一个字符串

platform_driver 中要有 of_match_table,其中一项的.compatible 成员设置

为一个字符串

上述 2 个字符串要一致

e5a1dd2c3655448f83a6a5fd93fe5f86.png


设备树节点指定资源,platform_driver 获得资源

如果在设备树节点里使用reg属性,那么内核生成对应的platform_device

时会用 reg 属性来设置 IORESOURCE_MEM 类型的资源。

如果在设备树节点里使用 interrupts 属性,那么内核生成对应的

platform_device 时会用 reg 属性来设置 IORESOURCE_IRQ 类型的资源。对于

interrupts 属性,内核会检查它的有效性,所以不建议在设备树里使用该属性

来表示其他资源。

在我们的工作中,驱动要求设备树节点提供什么,我们就得按这要求去编写

设备树。驱动程序中根据 pin 属性来确定引脚,那么我们就在设备树节点中添加

pin 属性。

设备树节点中:

41135bc45cd54e85986294c92c2b7cf0.png

驱动程序中,可以从 platform_device 中得到 device_node,再用of_property_read_u32 得到属性的值

2e7fea557691454aa948cf0b1655c127.png


程序

Makefile

# 1. 使用不同的开发板内核时, 一定要修改KERN_DIR
# 2. KERN_DIR中的内核要事先配置、编译, 为了能编译内核, 要先设置下列环境变量:
# 2.1 ARCH,          比如: export ARCH=arm64
# 2.2 CROSS_COMPILE, 比如: export CROSS_COMPILE=aarch64-linux-gnu-
# 2.3 PATH,          比如: export PATH=$PATH:/home/book/100ask_roc-rk3399-pc/ToolChain-6.3.1/gcc-linaro-6.3.1-2017.05-x86_64_aarch64-linux-gnu/bin 
# 注意: 不同的开发板不同的编译器上述3个环境变量不一定相同,
#       请参考各开发板的高级用户使用手册
KERN_DIR = /home/book/100ask_roc-rk3399-pc/linux-4.4
all:
  make -C $(KERN_DIR) M=`pwd` modules 
  $(CROSS_COMPILE)gcc -o ledtest ledtest.c 
clean:
  make -C $(KERN_DIR) M=`pwd` modules clean
  rm -rf modules.order
  rm -f ledtest
# 参考内核源码drivers/char/ipmi/Makefile
# 要想把a.c, b.c编译成ab.ko, 可以这样指定:
# ab-y := a.o b.o
# obj-m += ab.o
obj-m += leddrv.o chip_demo_gpio.o

这里只是指定了leddrv.o和chip_demo_gpio.o这两个文件参加驱动程序的编译

leddrv.h

#ifndef _LEDDRV_H
#define _LEDDRV_H
#include "led_opr.h"
void led_class_create_device(int minor);
void led_class_destroy_device(int minor);
void register_led_operations(struct led_operations *opr);
#endif /* _LEDDRV_H */
```定义了三个外部程序能够调用的函数
###  leddrv.c
```c
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/errno.h>
#include <linux/miscdevice.h>
#include <linux/kernel.h>
#include <linux/major.h>
#include <linux/mutex.h>
#include <linux/proc_fs.h>
#include <linux/seq_file.h>
#include <linux/stat.h>
#include <linux/init.h>
#include <linux/device.h>
#include <linux/tty.h>
#include <linux/kmod.h>
#include <linux/gfp.h>
#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 < b ? a : b)
void led_class_create_device(int minor)
{
  device_create(led_class, NULL, MKDEV(major, minor), NULL, "100ask_led%d", minor); /* /dev/100ask_led0,1,... */
}
void led_class_destroy_device(int minor)
{
  device_destroy(led_class, MKDEV(major, minor));
}
void register_led_operations(struct led_operations *opr)
{
  p_led_opr =  ;
}
EXPORT_SYMBOL(led_class_create_device);
EXPORT_SYMBOL(led_class_destroy_device);
EXPORT_SYMBOL(register_led_operations);
/* 3. 实现对应的open/read/write等函数,填入file_operations结构体                   */
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;
  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;
  }
  return 0;
}
/* 6. 有入口函数就应该有出口函数:卸载驱动程序时,就会去调用这个出口函数           */
static void __exit led_exit(void)
{
  printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);
  class_destroy(led_class);
  unregister_chrdev(major, "100ask_led");
}
/* 7. 其他完善:提供设备信息,自动创建设备节点                                     */
module_init(led_init);
module_exit(led_exit);
MODULE_LICENSE("GPL");

实现驱动函数,所用的硬件资源从struct led_operations *p_led_opr;结构体中获得。 register_led_operations(&board_demo_led_opr);在chip_demo_gpio.c中注册函数里面或者单板硬件的信息。而board_demo_led_opr则为led_operations结构体。static int chip_demo_gpio_probe(struct platform_device *pdev)函数则从设备树上获取硬件信息,通过比对of_match_table和设备树上的compatible属性。

led_opr.h

#ifndef _LED_OPR_H
#define _LED_OPR_H
struct led_operations {
  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

定义led_operations结构体和获得该结构体的函数

chip_demo_gpio.c

#include <linux/module.h>
#include <linux/fs.h>
#include <linux/errno.h>
#include <linux/miscdevice.h>
#include <linux/kernel.h>
#include <linux/major.h>
#include <linux/mutex.h>
#include <linux/proc_fs.h>
#include <linux/seq_file.h>
#include <linux/stat.h>
#include <linux/init.h>
#include <linux/device.h>
#include <linux/tty.h>
#include <linux/kmod.h>
#include <linux/gfp.h>
#include <linux/platform_device.h>
#include <linux/of.h>
#include "led_opr.h"
#include "leddrv.h"
#include "led_resource.h"
static int g_ledpins[100];
static int g_ledcnt = 0;
static int board_demo_led_init (int which) /* 初始化LED, which-哪个LED */       
{   
    //printk("%s %s line %d, led %d\n", __FILE__, __FUNCTION__, __LINE__, which);
    printk("init gpio: group %d, pin %d\n", GROUP(g_ledpins[which]), PIN(g_ledpins[which]));
    switch(GROUP(g_ledpins[which]))
    {
        case 0:
        {
            printk("init pin of group 0 ...\n");
            break;
        }
        case 1:
        {
            printk("init pin of group 1 ...\n");
            break;
        }
        case 2:
        {
            printk("init pin of group 2 ...\n");
            break;
        }
        case 3:
        {
            printk("init pin of group 3 ...\n");
            break;
        }
    }
    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");
    printk("set led %s: group %d, pin %d\n", status ? "on" : "off", GROUP(g_ledpins[which]), PIN(g_ledpins[which]));
    switch(GROUP(g_ledpins[which]))
    {
        case 0:
        {
            printk("set pin of group 0 ...\n");
            break;
        }
        case 1:
        {
            printk("set pin of group 1 ...\n");
            break;
        }
        case 2:
        {
            printk("set pin of group 2 ...\n");
            break;
        }
        case 3:
        {
            printk("set pin of group 3 ...\n");
            break;
        }
    }
    return 0;
}
static struct led_operations board_demo_led_opr = {
    .init = board_demo_led_init,
    .ctl  = board_demo_led_ctl,
};
struct led_operations *get_board_led_opr(void)
{
    return &board_demo_led_opr;
}
static int chip_demo_gpio_probe(struct platform_device *pdev)
{
    struct device_node *np;
    int err = 0;
    int led_pin;
    np = pdev->dev.of_node;
    if (!np)
        return -1;
    err = of_property_read_u32(np, "pin", &led_pin);
    g_ledpins[g_ledcnt] = led_pin;
    led_class_create_device(g_ledcnt);
    g_ledcnt++;
    return 0;
}
static int chip_demo_gpio_remove(struct platform_device *pdev)
{
    int i = 0;
    int err;
    struct device_node *np;
    int led_pin;
    np = pdev->dev.of_node;
    if (!np)
        return -1;
    err = of_property_read_u32(np, "pin", &led_pin);
    for (i = 0; i < g_ledcnt; i++)
    {
        if (g_ledpins[i] == led_pin)
        {
            led_class_destroy_device(i);
            g_ledpins[i] = -1;
            break;
        };
    }
    for (i = 0; i < g_ledcnt; i++)
    {
        if (g_ledpins[i] != -1)
            break;
    }
    if (i == g_ledcnt)
        g_ledcnt = 0;
    return 0;
}
static const struct of_device_id ask100_leds[] = {
    { .compatible = "100as,leddrv" },
    { },
};
static struct platform_driver chip_demo_gpio_driver = {
    .probe      = chip_demo_gpio_probe,
    .remove     = chip_demo_gpio_remove,
    .driver     = {
        .name   = "100ask_led",
        .of_match_table = ask100_leds,
    },
};
static int __init chip_demo_gpio_drv_init(void)
{
    int err;
    err = platform_driver_register(&chip_demo_gpio_driver); 
    register_led_operations(&board_demo_led_opr);
    return 0;
}
static void __exit lchip_demo_gpio_drv_exit(void)
{
    platform_driver_unregister(&chip_demo_gpio_driver);
}
module_init(chip_demo_gpio_drv_init);
module_exit(lchip_demo_gpio_drv_exit);
MODULE_LICENSE("GPL");

static struct platform_driver chip_demo_gpio_driver = {

.probe = chip_demo_gpio_probe,

.remove = chip_demo_gpio_remove,

.driver = {

.name = “100ask_led”,

.of_match_table = ask100_leds,

},

static const struct of_device_id ask100_leds[] = {

{ .compatible = “100as,leddrv” },

{ },

};

这个platform_driver通过of_match_table和跟设备树上的compatible进行匹配,获得硬件的资源。

100ask_led.dts

#define GROUP_PIN(g,p) ((g<<16) | (p))
/ {
  100ask_led@0 {
    compatible = "100as,leddrv";
    pin = <GROUP_PIN(3, 1)>;
  };
  100ask_led@1 {
    compatible = "100as,leddrv";
    pin = <GROUP_PIN(5, 8)>;
  };
};



目录
相关文章
|
Linux
Win或Linux系统下用conda安装Open Babel
Win或Linux系统下用conda安装Open Babel
2065 0
Win或Linux系统下用conda安装Open Babel
|
前端开发 JavaScript Java
Layui之入门
Layui之入门
272 0
|
2月前
|
数据采集 监控 网络协议
基于aiohttp的高并发爬虫实战:从原理到代码的完整指南
在数据驱动时代,传统同步爬虫效率低下,而基于Python的aiohttp库可构建高并发异步爬虫。本文通过实战案例解析aiohttp的核心组件与优化策略,包括信号量控制、连接池复用、异常处理等,并探讨代理集成、分布式架构及反爬应对方案,助你打造高性能、稳定可靠的网络爬虫系统。
135 0
|
11月前
|
开发框架 JavaScript 前端开发
鸿蒙NEXT开发声明式UI是咋回事?
【10月更文挑战第15天】鸿蒙NEXT的声明式UI基于ArkTS,提供高效简洁的开发体验。ArkTS扩展了TypeScript,支持声明式UI描述、自定义组件及状态管理。ArkUI框架则提供了丰富的组件、布局计算和动画能力。开发者仅需关注数据变化,UI将自动更新,简化了开发流程。此外,其前后端分层设计与编译时优化确保了高性能运行,利于生态发展。通过组件创建、状态管理和渲染控制等方式,开发者能快速构建高质量的鸿蒙应用。
420 3
|
11月前
|
并行计算 Ubuntu 开发工具
Jetson学习笔记(一):jetson 系列镜像下载、烧写、设置散热风扇、中文包、pip、中转英目录、软件源、显示CSI摄像头
关于NVIDIA Jetson系列设备的入门学习笔记,涵盖了从下载镜像、烧录、设置散热风扇、安装中文语言包、配置环境变量、安装CUDA和OpenCV,到显示CSI摄像头和增加Swap交换空间的详细步骤。
691 0
Jetson学习笔记(一):jetson 系列镜像下载、烧写、设置散热风扇、中文包、pip、中转英目录、软件源、显示CSI摄像头
|
10月前
|
机器学习/深度学习 存储 自然语言处理
使用深度学习模型进行情感分析!!!
本文介绍了如何使用深度学习模型进行中文情感分析。首先导入了必要的库,包括`transformers`、`pandas`、`jieba`和`re`。然后定义了一个`SentimentAnalysis`类,用于处理数据、加载真实标签和评估模型准确性。在主函数中,使用预训练的情感分析模型对处理后的数据进行预测,并计算模型的准确性。
308 0
|
数据采集 监控 数据挖掘
ERP系统中的数据分析与报表生成
【7月更文挑战第25天】 ERP系统中的数据分析与报表生成
895 2
|
11月前
|
缓存 Java API
API接口性能优化管理
在数字化时代,API性能优化对于提升软件效率和用户体验至关重要。本文介绍了多种优化方法:配置优化包括调整JVM参数等;代码层面减少重复调用并批量操作数据库;池化技术如线程池和HTTP连接池能有效利用资源;数据库优化通过索引提高查询速度;异步处理则使主流程业务不受阻塞;缓存策略如Redis缓存减少数据库访问;可观测性工具如日志平台和APM帮助监控性能。综合运用这些方法,可根据业务需求持续调整优化,显著提升API性能及用户体验。
|
搜索推荐 物联网 vr&ar
"电子书VS纸质书:一场跨越时空的阅读盛宴,你站哪队?揭秘数字与纸墨的终极对决,哪种阅读方式才是你的真爱?"
【8月更文挑战第14天】电子书与纸质书作为知识传播的载体,各具特色。电子书凭借便携性、个性化设置及互动功能受到欢迎;而纸质书则以其独特的感官体验、收藏价值和促进深度阅读的特点保持着吸引力。随着技术进步,两者正逐步融合,如电子书提供沉浸式体验,纸质书引入智能元素,共同丰富阅读世界。
230 0
|
缓存 网络协议 网络架构
以太网数据链路层、Ethernet_II帧格式、IEEE802.3帧格式,以太网的MAC地址的组成,ARP地址解析协议的工作原理,单播帧、组播帧、广播帧的区别
数据链路层,Ethernet_II帧格式、IEEE802.3帧格式,帧格式的区分以及链路层每种帧格式有什么作用,怎么区别分辨帧格式,以太网MAC地址的组成,ARP地址解析协议原理、什么是单播帧?什么是组播数据帧?什么是广播帧?...............
1278 0
以太网数据链路层、Ethernet_II帧格式、IEEE802.3帧格式,以太网的MAC地址的组成,ARP地址解析协议的工作原理,单播帧、组播帧、广播帧的区别

热门文章

最新文章