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)>;
  };
};



目录
相关文章
|
6月前
|
芯片 存储 C语言
STM32F103标准外设库——固件库 (三)
STM32F103标准外设库——固件库 (三)
410 0
STM32F103标准外设库——固件库 (三)
|
3月前
|
芯片 SoC
【驱动】【设备树】一个简单的系统级芯片(SoC)的设备树节点
【驱动】【设备树】一个简单的系统级芯片(SoC)的设备树节点
44 0
|
5月前
|
开发者
【经典案例】使用HAL库配置STM32F407的SPI外设
在嵌入式系统开发中,STM32F407是一款广泛应用的微控制器,而SPI(Serial Peripheral Interface)是一种常用的通信接口。本文将详细介绍如何使用STM32的硬件抽象层(HAL)库配置STM32F407的SPI外设,并提供完整的代码示例。
490 1
|
6月前
|
安全 测试技术
【ZYNQ】ZYNQ7000 全局定时器及其驱动示例
【ZYNQ】ZYNQ7000 全局定时器及其驱动示例
148 0
|
11月前
|
供应链 芯片
一种LED驱动专用控制电路
一、基本概述 TM1620是一种LED(发光二极管显示器)驱动控制专用IC,内部集成有MCU数字接口、数据锁存 器、LED驱动等电路。本产品质量可靠、稳定性好、抗干扰能力强。主要适用于家电设备(智能热 水器、微波炉、洗衣机、空调、电磁炉)、机顶盒、电子称、智能电表等数码管或LED显示设备。 二、基本的特性说明 采用CMOS工艺 显示模式(8 段×6 位~10段×4位) 辉度调节电路(8 级占空比可调) 串行接口(CLK,STB,DIN) 振荡方式:内置RC振荡 内置上电复位电路 内置数据锁存电路 内置针对LED反偏漏电导致暗亮问题优化电路 抗干扰能力强 封装形式:S
81 5
|
6月前
|
存储 芯片
一种LED驱动专用控制电路方案
一、基本的概述 TM1651 是一种带键盘扫描接口的LED(发光二极管显示器)驱动控制专用电路,内部集成有MCU 数字接口、数据锁存器、LED 高压驱动、键盘扫描等电路。本产品性能优良,质量可靠。采用SOP16/DIP16的封装形式。 二、特性说明 采用功率CMOS 工艺 显示模式(7字段×4 位),支持共阳数码管输出 键扫描(7×1bit),增强型抗干扰按键识别电路 辉度调节电路(占空比 8 级可调) 串行接口(CLK,DIO) 振荡方式:内置RC 振荡(450KHz±5%) 内置上电复位电路 内置自动消隐电路 封装形式:DIP16/SOP16 三、具体的应用
46 0
|
Linux 芯片
Linux驱动分析之SPI设备
前面我们对SPI控制器驱动进行了分析,接下来来分析SPI设备驱动。我们以DS1302驱动作为分析对象。DS1302是一款RTC芯片,估计很多人在学单片机时用到过。RTC芯片算是比较简单的,也方便分析理解。
Linux驱动分析之SPI设备
|
Linux
【Linux系统开发】 x210开发板 虚拟驱动创建流程(驱动编译进内核)
【Linux系统开发】 x210开发板 虚拟驱动创建流程(驱动编译进内核)
141 0
LED模板驱动程序的改造:总线设备驱动模型
LED模板驱动程序的改造:总线设备驱动模型
105 0
|
芯片
具体单板的按键驱动程序(查询方式)-以IMX6ull单板为例子
具体单板的按键驱动程序(查询方式)-以IMX6ull单板为例子
96 0