驱动开发(五)——驱动设计思想(面向对象/分层/分离)


设计思想

面向对象

  1. 字符设备驱动程序抽象出一个file_operations结构体
  2. 我们写的程序针对硬件部分抽象出led_operations结构体

所谓面向对象就是指的根据某一类事物的属性,进行抽象。比如字符设备的操作大多都需要read,write,open,close等函数,但是具体的操作不同,因此内核将共有的属性进行抽象出来,就是file_operations结构体,这样当我们需要编写具体的字符设备驱动的时候,我们就可以继承file_operations结构体,然后根据我们的需求自己实现对应的功能

分层

上下分层,比如我们在前面所写的LED驱动程序就分为2层:

  1. 上层实现硬件无关操作,比如注册字符设备驱动
  2. 下层实现硬件相关操作,比如实现具体单板的LED操作
    在这里插入图片描述

分离

设想一下,如果我们在board_A中突然想要在硬件上换一个引脚来控制LED怎么办呢?
如果直接修改boardA.c是不是显得非常的难受不不愿呢!
实际情况是,每一款芯片它的GPIO操作都是类似的。
既然引脚操作是有规律的,且与主芯片有关,那么我们就可以针对该芯片写出比较通用的硬件操作代码
比如board_A使用的芯片chipY,那么就可以写出:chipY_gpio.c,它实现芯片Y的GPIO操作,适用于使用芯片Y的所有单板的gpio操作;使用时,我们只需要在board_A_led.c中去指定使用那个芯片即可
如下图
在这里插入图片描述

实验

本实验基于STM32_MP_157开发板

手册资源

原理图 找到GREEN LED and YELLOW LED
在这里插入图片描述
数据手册
在这里插入图片描述通过查看MP157A框图可知,GPIOA与GPIOG组挂载在AHB总线上

开发参考手册
阅读芯片手册内存和总线架构中内存映射组织环节下的外设集群章节描述可知
在这里插入图片描述
AHB4用于大多数GPIO的控制
因此在清除控制通路以后,我们接下来就需要的是使能和配置时钟
查看时钟部分,我们需要使能时钟给外设GPIO,并将GPIO资源分配给MPU,因为我们的MP157开发板是M4核与A7核共享开发板的资源。
阅读参考手册/用户手册,查看RCC部分
在这里插入图片描述配置PLL4时钟源
在这里插入图片描述在这里插入图片描述
显然这里我们只需要进行控制即可,默认配置就行。
在这里插入图片描述我们需要使能PLL4,就需要设置控制寄存器的Bit0为1,同时等待Bit1为1使时钟稳定

将GPIO资源分配给MPU
在这里插入图片描述
我们使用的引脚使PA10和PG8,因此我们需要将GPIOA组和GPIOG组资源分配给MPU,即将设置寄存器中的Bit0和Bit6设置为1
清0寄存器
在这里插入图片描述

再来看看GPIO寄存器
第一个模式寄存器,设置该GPIO的工作模式
在这里插入图片描述
设置输出类型寄存器
在这里插入图片描述
输出速度寄存器
在这里插入图片描述
在这里插入图片描述

数据寄存器

在这里插入图片描述
但是为了方便操作,我们一般使用设置与重置寄存器
在这里插入图片描述在这里插入图片描述

从上面我们可以知晓各个寄存器的偏移地址,下面我们需要确定其基地址
在这里插入图片描述

下面我们就可以进行编程了

驱动程序 led_drv.c

// this file is true driver that is hardware independent
#include <linux/module.h>
#include <linux/init.h>
#include <linux/fs.h>
#include <linux/uaccess.h>
#include <linux/device.h>
#include <linux/errno.h>
#include <linux/err.h>
#include <linux/stat.h>
#include "led_opr.h"
#define LED_NUM 2

int major=0;
//allow user pass parameter to major
module_param(major,int,0);
//create assist information to driver ,because it can automatically create device nodes
static struct class *led_class;
struct led_operations *p_led_opr;

static int led_open(struct inode *node, struct file *fp)
{
    //get minor by node
    int minor;
    printk("%s %s line %d\n",__FILE__,__FUNCTION__ ,__LINE__);
    minor = iminor(node);
    p_led_opr->init(minor);
    return 0;
}

static int led_close(struct inode *node, struct file *fp)
{
    printk("%s %s line %d\n",__FILE__,__FUNCTION__ ,__LINE__);
    return 0;
}

static ssize_t led_write (struct file *fp, const char __user *buffer, size_t len, loff_t *offset)
{
    int minor;
    char status;
    int err;
    struct inode *inode = file_inode(fp);
    minor = iminor(inode);
    err = copy_from_user(&status,buffer,1);
    p_led_opr->ctl(minor,status);
    return err;
}
static ssize_t led_read (struct file *fp, char __user *buffer, size_t len, loff_t * offset)
{
    printk("%s %s line %d\n",__FILE__,__FUNCTION__ ,__LINE__);
    return 0;
}

static struct file_operations f_opr ={
        .owner   = THIS_MODULE,
        .open    = led_open,
        .release = led_close,
        .write   = led_write,
        .read    = led_read,
};


static __init int led_init(void){
    int i;
    printk("%s %s line %d\n",__FILE__,__FUNCTION__ ,__LINE__);
    if(major){
        register_chrdev(major,"jacky_led",&f_opr);
    }else{
        major=register_chrdev(0,"jacky_led",&f_opr);
    }
    led_class = class_create(THIS_MODULE,"led_class");
    //check for led_class errors
    if(IS_ERR(led_class)){
        printk("error append in %s line %d\n",__FUNCTION__ ,__LINE__);
        unregister_chrdev(major,"jacky_led");
        return -1;
    }
    p_led_opr = get_board_led_opr();
    if(p_led_opr==NULL){
        printk("error append in %s line %d\n",__FUNCTION__ ,__LINE__);
        unregister_chrdev(major,"jacky_led");
        return -1;
    }
    // create device nodes
    for(i=0;i<LED_NUM;i++){
        device_create(led_class,NULL,MKDEV(major,i),NULL,"led_dev%d",i);
    }
    return 0;
}

static __exit void 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));
    }
    class_destroy(led_class);
    unregister_chrdev(major,"jacky_led");
    return;
}

module_init(led_init);
module_exit(led_exit);
MODULE_LICENSE("GPL");




led_operations.h


#ifndef _LED_OPR_H
#define _LED_OPR_H
#include "led_resource.h"

//define struct led_operations
struct led_operations{
    int (*init)(int minor);//initialize the led , which is which one gpio group
    int (*ctl)(int minor,char status);//control the gpio pin, status is led status
};

struct led_operations* get_board_led_opr(void);

#endif

芯片相关chip_gpio.c

/*
 * to realize led_operations for hierarchy(分层)
 * */
#include "led_opr.h"
#include "led_resource.h"
#include <linux/module.h>
#include <asm/io.h>

static volatile unsigned int *RCC_PLL4CR; //PLL4 clock control register
static volatile unsigned int *RCC_MP_AHB4ENSETR;//make gpio belong to MPU enable register
static volatile unsigned int *GPIO_MODER; //GPIO mode control register
static volatile unsigned int *GPIO_BSRR;//gpio data set/reset register

//get stm32MP157 led resource from board_MP157_led.c
static struct led_resource* board_led_res;

static int board_led_init(int minor)
{
    printk("%s %s line %d\n",__FILE__,__FUNCTION__ ,__LINE__);
    if(!board_led_res){
        board_led_res = get_led_resource();
    }
    printk("led resource:group = %d, pin = %d \n",
           GROUP(board_led_res->pin[minor]),PIN(board_led_res->pin[minor]));

    if(!RCC_PLL4CR){//init clock
        RCC_PLL4CR = ioremap(ADDRESS_RCC_PLL4CR,4);
        RCC_MP_AHB4ENSETR = ioremap(ADDRESS_RCC_MP_AHB4ENSETR,4);
        //enable clock
        *RCC_PLL4CR |= (1<<0);
        //ensure clock is stabilize(稳定)
        while ((*RCC_PLL4CR & (1<<1)) == 0);
    }

    switch (minor) {
        case 0:
            GPIO_MODER = ioremap(ADDRESS_GPIOA_MODER,4);
            GPIO_BSRR = ioremap(ADDRESS_GPIOA_BSRR,4);
            break;
        case 1:
            GPIO_MODER = ioremap(ADDRESS_GPIOG_MODER,4);
            GPIO_BSRR = ioremap(ADDRESS_GPIOG_BSRR,4);
            break;
        default:
            break;
    }
    //set gpio group work mode
    // enable RCC for gpio belong to MPU
    *RCC_MP_AHB4ENSETR |= (1<<GROUP(board_led_res->pin[minor]));
    //clear  data
    *GPIO_MODER &= ~(3 << (PIN(board_led_res->pin[minor]) * 2)) ;
    //set data
    *GPIO_MODER |= (1 << (PIN(board_led_res->pin[minor]) * 2));

    return 0;
}

static int board_led_ctl(int minor,char status)
{
    printk("%s %s line %d\n",__FILE__,__FUNCTION__ ,__LINE__);
    if(status){ //reset 1 is led light on
        *GPIO_BSRR = (1<<(PIN(board_led_res->pin[minor])+16));
    } else{ // set 1 is led light off
        *GPIO_BSRR = (1<<PIN(board_led_res->pin[minor]));
    }
    return 0;
}

static struct led_operations chip_led_opr = {
        .init = board_led_init ,
        .ctl  = board_led_ctl,
};

struct led_operations* get_board_led_opr(void )
{
    return &chip_led_opr;
}

led_resource.h

#ifndef _LED_RESOURCE_H
#define _LED_RESOURCE_H
/*GPIOA_10 and GPIOG_8
 *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))
#define led_max 100

#define ADDRESS_RCC_PLL4CR (0x50000000 + 0x894)
#define ADDRESS_RCC_MP_AHB4ENSETR  (0x50000000 + 0xA28)
#define ADDRESS_GPIOA_MODER  (0x50002000 + 0x00)
#define ADDRESS_GPIOA_BSRR  (0x50002000 + 0x18)
#define ADDRESS_GPIOG_MODER  (0x50008000 + 0x00)
#define ADDRESS_GPIOG_BSRR  (0x50008000 + 0x18)

struct led_resource{
    int pin_num;
    int pin[led_max];
};

struct led_resource* get_led_resource(void);
#endif


单板相关 board_MP157_led.c

//set STM32MP_157 gpio resource
#include "led_resource.h"

static struct led_resource MP157_led_res = {
        .pin_num = 2,
        .pin[0] = GROUP_PIN(0,10),
        .pin[1] = GROUP_PIN(6,8),
};

struct led_resource* get_led_resource(void)
{
    return &MP157_led_res;
}

应用测试程序

#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
/* you can designer led by yourself
#define file_1 "/dev/led_dev0"
#define file_2 "/dev/led_dev1"
*/

int main(int argc ,char **argv){
    char status;
    int fd;
    if(argc!=3){
        printf("Usage :%s <dev> <on | off>\n",argv[0]);
        return -1;
    }

    fd=open(argv[1],O_RDWR);
    if(fd<0){
        perror("[open file error]");
        return -1;
    }

    if(strncmp("on",argv[2],2)==0){
        status = 1;
    }else{
        status = 0;
    }

    int res =write(fd,&status,1);
    if(res<0){
        perror("[write error]");
        return -1;
    }
    close(fd);
    return 0;
}


Makefile

ARCH := arm
CROSS_COMPILE := arm-buildroot-linux-gnueabihf-
CFLAGS :=-g -Wall
SOURCE := led_test.c
TARGET := build
OUTPUT := /home/jacky/nfs_rootfs/chardev/led

KERNEL_DIR := /home/jacky/100ask_stm32mp157_pro-sdk/Linux-5.4
TARGET_MODULE := jacky_led.ko
all:
	make -C $(KERNEL_DIR) M=`pwd` modules
	$(CROSS_COMPILE)gcc $(CFLAGS) -o $(TARGET) $(SOURCE)
	cp -f $(TARGET) $(TARGET_MODULE) $(OUTPUT)
.PHONY:clean
clean:
	make -C $(KERNEL_DIR) M=`pwd` modules clean
	rm -rf modules.order
	rm -rf $(TARGET)

CONFIG_MODULE_SIG=n
jacky_led-y := led_drv.o chip_gpio.o board_MP157_led.o
obj-m +=jacky_led.o

实验测试

编译

在这里插入图片描述编译成功并成功拷贝到nfs目录下

挂载驱动

挂载网络文件系统
在这里插入图片描述

关闭系统默认的绿灯设置(心跳灯)

在这里插入图片描述
开始挂载驱动
在这里插入图片描述CONFIG_MODULE_SIG=n
当然,对我们的程序并不会造成太大的影响,你卸载了然后在挂载就不会出现污染内核的警告了

测试应用程序

检查到了我们的设备,下面进行验证我们的应用程序
在这里插入图片描述

可以看到我们开发板的灯是完全受到测试程序的控制的

  • 1
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
0.基础的基础 |-学习WIN64驱动开发的硬件准备 |-配置驱动开发环境 ------------------------------ 1.驱动级HelloWorld |-配置驱动测试环境 |-编译和加载内核HelloWorld ------------------------------ 2.内核编程基础 |-WIN64内核编程的基本规则 |-驱动程序与应用程序通信 |-内核里使用内存 |-内核里操作字符串 |-内核里操作文件 |-内核里操作注册表 |-内核里操作进线程 |-驱动里的其它常用代码 ------------------------------ 3.内核HOOK与UNHOOK |-系统调用、WOW64与兼容模式 |-编程实现突破WIN7的PatchGuard |-系统服务描述表结构详解 |-SSDT HOOK和UNHOOK |-SHADOW SSDT HOOK和UNHOOK |-INLINE HOOK和UNHOOK ------------------------------ 4.无HOOK监控技术 |-无HOOK监控进线程启动和退出 |-无HOOK监控模块加载 |-无HOOK监控注册表操作 |-无HOOK监控文件操作 |-无HOOK监控进线程句柄操作 |-使用对象回调监视文件访问 |-无HOOK监控网络访问 |-无HOOK监视修改时间 ------------------------------ 5.零散内容 |-驱动里实现内嵌汇编 |-DKOM隐藏进程+保护进程 |-枚举和隐藏内核模块 |-强制结束进程 |-强制读写进程内存 |-枚举消息钩子 |-强制解锁文件 |-初步探索PE32+格式文件 ------------------------------ 6.用户态HOOK与UNHOOK |-RING3注射DLL到系统进程 |-RING3的INLINE HOOK和UNHOOK |-RING3的EAT HOOK和IAT HOOK ------------------------------ 7.反回调 |-枚举与删除创建进线程回调 |-枚举与删除加载映像回调 |-枚举与删除注册表回调 |-枚举与对抗MiniFilter |-枚举与删除对象回调
### 回答1: Linux驱动分离分层是指将不同的驱动程序分离开来,分别放置在不同的层次结构中,以便于管理和维护。这样可以使得不同的驱动程序之间相互独立,互不干扰,同时也可以提高系统的可靠性和稳定性。在Linux系统中,驱动程序一般分为以下几个层次:硬件层、设备驱动层、总线驱动层、文件系统层等。每个层次都有其特定的功能和作用,通过分层的方式可以使得驱动程序的开发和维护更加方便和高效。 ### 回答2: Linux驱动分离分层是指将Linux内核中的驱动程序分成不同的功能层级并独立开发的过程。在Linux开发社区中,这种分离分层开发模式被广泛采纳,以提高整个系统的稳定性和可维护性。 Linux驱动分离分层主要包括以下几个层级: 1. 用户空间驱动层:用户空间驱动程序是指运行在用户空间的驱动程序,如USB驱动等。这些驱动程序通常在内核提供的接口上实现,通过系统调用和驱动程序接口对硬件进行操作。 2. 设备驱动层:设备驱动层是指连接硬件和内核的驱动程序,用来管理计算机的输入和输出设备。这一层级的驱动程序通常是编写最复杂的。 3. 驱动控制器层:驱动控制器层是指连接设备驱动程序和硬件的控制器层,比如PCI控制器、DMA控制器等。这些控制器集成在计算机的芯片组中,工作在硬件和软件之间的接口。 4. 硬件抽象层:硬件抽象层是指对硬件的抽象描述,这一层级的驱动程序提供了一个统一的接口来存取硬件设备。 总体来说,Linux驱动分离分层可以提高系统的可维护性和可靠性,同时也为内核扩展提供了可靠的机制。因此,Linux开发社区鼓励发展和维护一个分离分层驱动程序生态系统。 ### 回答3: 在Linux系统中,驱动程序是操作系统与硬件之间的桥梁,它们向操作系统提供了与硬件设备交互的能力。为了使Linux操作系统的驱动程序更加模块化和灵活,Linux设备驱动设计时采用了分离分层的策略。 分离是指将不同类型的驱动程序放在不同的模块中,这样可以让相似的驱动程序共享一些关键代码,减少代码量,并且提高了代码的重用性。对于Linux系统中的设备,常见的驱动程序有字符设备驱动、块设备驱动以及网络设备驱动等。每种类型的驱动程序都有自己的特点和工作方式,因此将它们分离出来更便于管理和维护。 分层是指将驱动程序设计成多个层次,每个层次依赖于下一个层次,以便在需要时实现相应的功能。通常把驱动程序分为个层次:应用程序接口层、驱动程序核心层、总线接口层、体系结构层和硬件控制层。每个层次的驱动程序都有自己的职责和功能,它们通过下一层向上提供服务,并从上一层向下接收请求。 应用程序接口层是驱动程序提供给应用程序的接口,应用程序可以通过这些接口来控制硬件设备。驱动程序核心层是驱动程序的核心部分,它在驱动程序的设计中起到桥梁的作用,它管理整个驱动程序的请求过程,同时也与硬件设备交互。总线接口层负责管理总线设备的操作,比如对于PCI总线来说,总线接口层可以管理PCI卡中的设备。体系结构层是根据系统体系结构设计驱动程序层次,比如ARM体系结构、MIPS体系结构等。硬件控制层是直接与硬件设备交互的层次,它负责整个驱动程序对硬件设备的控制。 总之,Linux设备驱动程序的分离分层策略可以提高驱动程序的模块化和灵活性,同时也更利于维护和管理驱动程序。这种策略已经被广泛应用于各种Linux系统中,使得Linux操作系统更加强大和稳定。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Jacky~~

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值