LED模板驱动程序的改造:总线设备驱动模型

本文涉及的产品
模型在线服务 PAI-EAS,A10/V100等 500元 1个月
模型训练 PAI-DLC,100CU*H 3个月
交互式建模 PAI-DSW,每月250计算时 3个月
简介: LED模板驱动程序的改造:总线设备驱动模型

LED模板驱动程序的改造:总线设备驱动模型

1.原来LED分层分离的思想

aa5560865dfb418f816b6ed6c4a7c9fd.png

2.现在要根据总线设备驱动模型实现的框架

f76024de3bbf4f108f603b1ccc2b80de.png


实现的代码

准备从开始调用led_drv开始一步一步的分析

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 */

这里声明了3个外部函数,创建设备号,销毁设备号,获得led_operations结构体。

leddrv.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 = 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");

leddrv.c程序实现了创建设备,销毁设备,获得led_operations结构体这三个函数,并将注册字符结构体file_operations,并实现file_operations结构体open,read,write,close四个函数。这里在open和write里面调用了p_led_opr这个led_operations结构体。这个opr通过register_led_operations这个函数在chip_demo_gpio.c里面进行调用。将chip_demo_gpio里面的led_operations结构体传递个leddrv.c里面。

EXPORT_SYMBOL()将一个函数声明为外部程序也可以调用这个函数。


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结构体,和get_board_led_opr函数

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 "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 resource *res;
    int i = 0;
    while (1)
    {
        res = platform_get_resource(pdev, IORESOURCE_IRQ, i++);
        if (!res)
            break;
        g_ledpins[g_ledcnt] = res->start;
        led_class_create_device(g_ledcnt);
        g_ledcnt++;
    }
    return 0;
}
static int chip_demo_gpio_remove(struct platform_device *pdev)
{
    struct resource *res;
    int i = 0;
    while (1)
    {
        res = platform_get_resource(pdev, IORESOURCE_IRQ, i);
        if (!res)
            break;
        led_class_destroy_device(i);
        i++;
        g_ledcnt--;
    }
    return 0;
}
static struct platform_driver chip_demo_gpio_driver = {
    .probe      = chip_demo_gpio_probe,
    .remove     = chip_demo_gpio_remove,
    .driver     = {
        .name   = "100ask_led",
    },
};
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");

主要实现了总线设备驱动模型的platform_driver_register,驱动函数,并往总线设备上面进行注册。并实现了设备驱动上面的初始化操作。也实现了led_operations结构体并实现了其内部函数的操作(对应引脚应该执行什么操作)。

led_resource.h

#ifndef _LED_RESOURCE_H
#define _LED_RESOURCE_H
/* GPIO3_0 */
/* bit[31:16] = group */
/* bit[15:0]  = which pin */
#define GROUP(x) (x>>16)
#define PIN(x)   (x&0xFFFF)
#define GROUP_PIN(g,p) ((g<<16) | (p))
#endif

实现了GROUP,PIN,GROUP_PIN等宏定义

board_A_led.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 "led_resource.h"
static void led_dev_release(struct device *dev)
{
}
static struct resource resources[] = {
        {
                .start = GROUP_PIN(3,1),
                .flags = IORESOURCE_IRQ,
                .name = "100ask_led_pin",
        },
        {
                .start = GROUP_PIN(5,8),
                .flags = IORESOURCE_IRQ,
                .name = "100ask_led_pin",
        },
};
static struct platform_device board_A_led_dev = {
        .name = "100ask_led",
        .num_resources = ARRAY_SIZE(resources),
        .resource = resources,
        .dev = {
                .release = led_dev_release,
         },
};
static int __init led_dev_init(void)
{
    int err;
    err = platform_device_register(&board_A_led_dev);   
    return 0;
}
static void __exit led_dev_exit(void)
{
    platform_device_unregister(&board_A_led_dev);
}
module_init(led_dev_init);
module_exit(led_dev_exit);
MODULE_LICENSE("GPL");

这里实现了总线设备驱动模型的设备注册。并定义了platform_device 和实现resource的资源定义。

目录
相关文章
|
Linux
Linux驱动开发(使用I2C总线设备驱动模型编写AT24C02驱动程序)
Linux驱动开发(使用I2C总线设备驱动模型编写AT24C02驱动程序)
163 0
|
6月前
|
异构计算 内存技术
FPGA进阶(1):基于SPI协议的Flash驱动控制(一)
FPGA进阶(1):基于SPI协议的Flash驱动控制(一)
241 0
|
6月前
|
异构计算 内存技术
FPGA进阶(1):基于SPI协议的Flash驱动控制(二)
FPGA进阶(1):基于SPI协议的Flash驱动控制
71 0
|
6月前
|
数据采集 计算机视觉 异构计算
FPGA进阶(2):基于I2C协议的EEPROM驱动控制
FPGA进阶(2):基于I2C协议的EEPROM驱动控制
88 0
|
8月前
|
传感器 芯片
PCF8574芯片介绍及驱动方法
PCF8574芯片介绍及驱动方法
470 0
|
供应链 芯片
一种LED驱动专用控制电路
一、基本概述 TM1620是一种LED(发光二极管显示器)驱动控制专用IC,内部集成有MCU数字接口、数据锁存 器、LED驱动等电路。本产品质量可靠、稳定性好、抗干扰能力强。主要适用于家电设备(智能热 水器、微波炉、洗衣机、空调、电磁炉)、机顶盒、电子称、智能电表等数码管或LED显示设备。 二、基本的特性说明 采用CMOS工艺 显示模式(8 段×6 位~10段×4位) 辉度调节电路(8 级占空比可调) 串行接口(CLK,STB,DIN) 振荡方式:内置RC振荡 内置上电复位电路 内置数据锁存电路 内置针对LED反偏漏电导致暗亮问题优化电路 抗干扰能力强 封装形式:S
|
8月前
|
存储 芯片
一种LED驱动专用控制电路方案
一、基本的概述 TM1651 是一种带键盘扫描接口的LED(发光二极管显示器)驱动控制专用电路,内部集成有MCU 数字接口、数据锁存器、LED 高压驱动、键盘扫描等电路。本产品性能优良,质量可靠。采用SOP16/DIP16的封装形式。 二、特性说明 采用功率CMOS 工艺 显示模式(7字段×4 位),支持共阳数码管输出 键扫描(7×1bit),增强型抗干扰按键识别电路 辉度调节电路(占空比 8 级可调) 串行接口(CLK,DIO) 振荡方式:内置RC 振荡(450KHz±5%) 内置上电复位电路 内置自动消隐电路 封装形式:DIP16/SOP16 三、具体的应用
|
存储 Go 芯片
单片机外围模块漫谈之四,USB总线基本概念。
单片机外围模块漫谈之四,USB总线基本概念。
|
Web App开发 芯片
USB2S可编程USB转串口适配器的开发原理
USB2S可编程USB转串口适配器的开发原理主要涉及USB接口协议、USB控制器芯片以及串口通信协议等方面。
USB2S可编程USB转串口适配器的开发原理
|
XML 测试技术 网络安全
开发调试工具:可编程USB转IIC/I2C/SPI/UART适配器模块开发板
发个方便测试I2C、SPI、1Wire接口的工具模块 总的思路是通过USB或者UART接口发送一些协议字符串,由模块转换成上面几种接口的硬件时序电信号,实现与这几种接口芯片、设备的快速测试。

热门文章

最新文章

下一篇
开通oss服务