AcWing
  • 首页
  • 活动
  • 题库
  • 竞赛
  • 校园
  • 应用
  • 文章
    • 题解
    • 分享
    • 问答
  • 吐槽
  • 登录/注册

Linux 驱动开发基础---驱动设计思想---分层

作者: 作者的头像   也许 ,  2022-05-14 15:50:33 ,  所有人可见 ,  阅读 22


0


驱动程序分层的思想

想想前面写的LED驱动程序有什么缺点?

    只支持imx6ull单板,换一块其他开发板,驱动程序要改变,并且要深入阅读源码修改

    可扩展性差,与硬件绑定的太死了,能不能写一个驱动程序,可以兼容所有开发板?

想要兼容所有开发板,首先要把属于开发板的硬件操作那一块从驱动程序分离出去,驱动程序只负责注册file_operations

结构体,其中的open, read, write 函数调用单板的硬件操作函数。

如何使驱动程序可以调用所有开发板的硬件操作函数,毕竟不同开发板的函数名字,参数,返回值不同?

    约定一套函数接口,通过函数指针可以约定返回值,参数。这样开发板硬件操作函数按照这套接口实现,驱动程序就可

    以兼容所有开发板了,这就是模块的解耦合思想。

利用分层思想改进LED驱动程序

三个文件: (应用层)led_drv_test.c (内核调用)===> (驱动层)leddrv.c (函数接口)===> (硬件层)broad.c 

led_drv_test.c 应用层不变,使用write写入数据

leddrv.c:驱动层抽象一套函数接口,可以接收来自硬件层的函数

    struct led_operations {

        int (*init) (int which); /* 初始化LED, which-哪个LED */     

        int (*ctl) (int which, char status); /* 控制LED, which-哪个LED, status:1-亮,0-灭 */

    };

    如何接收硬件层的函数?

        1. 传参:在led_drv.c 的主调函数中,定义struct file_operations* fp 接收braod.c 的struct 

        file_operations*

        2. braod.c 的struct file_operations* 通过函数返回,在led_drv.c 用变量接收

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_drv_test.c


#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>
#include <string.h>

/*
 * ./ledtest /dev/100ask_led0 on
 * ./ledtest /dev/100ask_led0 off
 */
int main(int argc, char **argv)
{
    int fd;
    char status;

    /* 1. 判断参数 */
    if (argc != 3) 
    {
        printf("Usage: %s <dev> <on | off>\n", argv[0]);
        return -1;
    }

    /* 2. 打开文件 */
    fd = open(argv[1], O_RDWR);
    if (fd == -1)
    {
        printf("can not open file %s\n", argv[1]);
        return -1;
    }

    /* 3. 写文件 */
    if (0 == strcmp(argv[2], "on"))
    {
        status = 1;
        write(fd, &status, 1);
    }
    else
    {
        status = 0;
        write(fd, &status, 1);
    }

    close(fd);

    return 0;
}


led_drv.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"

#define LED_NUM 2

/* 1. 确定主设备号                                                                 */
static int major = 0;
static struct class *led_class;
struct led_operations *p_led_opr;


#define MIN(a, b) (a < b ? a : b)

/* 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;
    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, "100ask_led");
        return -1;
    }

    for (i = 0; i < LED_NUM; i++)
        device_create(led_class, NULL, MKDEV(major, i), NULL, "100ask_led%d", i); /* /dev/100ask_led0,1,... */

    p_led_opr = get_board_led_opr();

    return 0;
}

/* 6. 有入口函数就应该有出口函数:卸载驱动程序时,就会去调用这个出口函数           */
static void __exit led_exit(void)
{
    int i;
    printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);

    for (i = 0; i < LED_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");


braod.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"

static int board_demo_led_init (int which) /* 初始化LED, which-哪个LED */       
{

    printk("%s %s line %d, led %d\n", __FILE__, __FUNCTION__, __LINE__, which);
    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");
    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;
}



0 评论

你确定删除吗?
1024
x

© 2018-2022 AcWing 版权所有  |  京ICP备17053197号-1
用户协议  |  常见问题  |  联系我们
AcWing
请输入登录信息
更多登录方式: 微信图标 qq图标
请输入绑定的邮箱地址
请输入注册信息