platform驱动模型(工作流程及代码)

一、总线驱动模型

1.概念

        linux中将一个挂载在总线上的驱动的驱动模型分为三部分: devicedriverbus

        device是用来保存设备信息的对象,存放在内核中一个klist_device链表中进行管理。

        driver当前设备的驱动信息对象,存放在内核中一个klist_driver链表中进行管理。

        bus是当前设备挂载的总线的总线驱动

2.流程

        bus负责完成device和driver到的匹配,这一步通过总线驱动中的match函数来实现。当device和driver匹配成功后执行driver端的probe函数,在probe函数中完成驱动的注册、设备节点的创建、以及后续的硬件控制工作。

platform驱动模型(工作流程及代码)_第1张图片

二、platform驱动模型

1.引入的原因

        为了让没有挂载在总线上的设备也能够按照总线驱动模型进行驱动的编写

2.platform总线驱动模型的部分

        设备端、驱动端、总线端

3.过程

        总线负责完成驱动和设备信息的匹配,当匹配成功之后会执行驱动端probe函数。在probe函数中实现驱动的注册、设备节点的创建以及后续的硬件控制工作

4.设备端API

1)分配设备信息对象并且初始化

        platform驱动模型(工作流程及代码)_第2张图片

2)注册设备信息对象

        int platform_device_register(struct platform_device *pdev)

3)注销设备对象

        void platform_device_unregister(struct platform_device *pdev)

5.驱动端API

platform驱动模型(工作流程及代码)_第3张图片

三、名字表

1.概述

        为了能够让驱动更加适配,我们可以在驱动端构建一个名字表。只要设备的名字和名字表中的任何一个名字一样,都可以执行驱动端probe函数

2.名字表的构建

struct platform_device_id idtable[]= {

        {"aaaaa",0},

        {"bbbbb",1},

        {"ccccc",2},

         {"ddddd",3},

         { },                   //防止数组越界

}

四、设备树匹配

1.概述

        内核3.10版本以后要求将所有的设备信息都保存在设备树中,所有以后驱动端获取设备信息都在设备树中获取,所以需要使用驱动端的设备树匹配方式

2.设备树匹配匹配项的类型

        platform驱动模型(工作流程及代码)_第4张图片

3.准备

1)添加用于设备树匹配的设备树节点

2)构建用于设备树匹配的表

        struct of_device_id odtable[] = {

                { .compatible = "hqyj,myplatform", },

                { /* end node */ },       //防止数组越界

                };

五、任务

platform驱动模型(工作流程及代码)_第5张图片

pdrv.c
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include
#include
 
//定义一个等待队列头
wait_queue_head_t wq_head;
unsigned int condition=0;
unsigned int major;
struct class *cls;
struct device *dev;
char kbuf[128] = {0};
struct resource *res;
unsigned int irqno;
struct gpio_desc *gpiono;
unsigned int number = 0;
 
// 封装操作方法
int mycdev_open(struct inode *inode, struct file *file)
{
    printk("%s:%s:%d\n", __FILE__, __func__, __LINE__);
    return 0;
}
 
ssize_t mycdev_read(struct file *file, char *ubuf, size_t size, loff_t *lof)
{
    int ret;
    //判断IO方式
    if(file->f_flags&O_NONBLOCK)//非阻塞
    {}
    else//阻塞
    {
        wait_event_interruptible(wq_head,condition); //先检查condition再将进程休眠 
    }
    ret = copy_to_user(ubuf, kbuf, size);
    if (ret)
    {
        printk("copy_to_ user err\n");
        return -EIO;
    }
    condition=0;//下一次硬件数据没有就绪
 
    return 0;
}
ssize_t mycdev_write(struct file *file, const char *ubuf, size_t size, loff_t *lof)
{
    printk("%s:%s:%d\n", __FILE__, __func__, __LINE__);
    return 0;
}
 
int mycdev_close(struct inode *inode, struct file *file)
{
    printk("%s:%s:%d\n", __FILE__, __func__, __LINE__);
    return 0;
}
 
struct file_operations fops = {
    .open = mycdev_open,
    .read = mycdev_read,
    .write = mycdev_write,
    .release = mycdev_close,
};
 
// 定义中断处理函数
irqreturn_t key_handler(int irq, void *dev)
{
    if (number == 0)
    {
        number++;
        kbuf[0] = '1';
        // 灯亮
        gpiod_set_value(gpiono,1);
    }
    else if (number == 1)
    {
        number = 0;
        kbuf[0] = '0';
        // 灯灭
        gpiod_set_value(gpiono,0);
    }
    
    condition=1;//表示硬件数据就绪
    wake_up_interruptible(&wq_head);
 
    return IRQ_HANDLED;
}
 
// 封装probe函数
int pdri_probe(struct platform_device *pdev)
{
     //初始化等待队列
    init_waitqueue_head(&wq_head);
 
    // 1字符设备驱动注册
    major = register_chrdev(0, "myled", &fops);
    if (major < 0)
    {
        printk("字符设备驱动注册失败\n");
        return major;
    }
    printk("字符设备驱动注册成功:major=%d\n", major);
 
    // 2向上提交目录
    cls = class_create(THIS_MODULE, "myled");
    if (IS_ERR(cls))
    {
        printk("向上提交目录失败\n");
        return -PTR_ERR(cls);
    }
    printk("向上提交目录成功\n");
 
    // 3向上提交设备节点信息
    dev = device_create(cls, NULL, MKDEV(major, 0), NULL, "myled0");
    if (IS_ERR(dev))
    {
        printk("向上提交设备节点信息失败\n");
        return -PTR_ERR(dev);
    }
 
    printk("向上提交设备节点信息成功\n");
 
    // 基于设备数节点信息获取gpio_desc对象指针
    gpiono = gpiod_get_from_of_node(pdev->dev.of_node, "led3-gpio", 0, GPIOD_OUT_LOW, NULL);
    if (IS_ERR(gpiono))
    {
        printk("解析GPIO管脚信息失败\n");
        return -ENXIO;
    }

    printk("解析GPIO管脚信息成功\n");
 
    // 获取中断类型的资源,中断号
    irqno = platform_get_irq(pdev, 0);
    if (irqno < 0)
    {
        printk("获取中断类型资源失败\n");
        return -ENXIO;
    }
    printk("key1_irq资源:%d\n", irqno);
 
    // 注册按键1中断
    int ret = request_irq(irqno, key_handler, IRQF_TRIGGER_FALLING, "key1_int", NULL);
    if (ret < 0)
    {
        printk("注册按键1中断失败\n");
        return ret;
    }
 
    printk("注册按键1中断成功\n");
 
    printk("%s-%s-%d\n", __FILE__, __func__, __LINE__);
 
    return 0;
}
// 封装remove函数
int pdri_remove(struct platform_device *pdev)
{
    // 注销中断
    free_irq(irqno, NULL);
 
    // 1销毁设备节点信息
    device_destroy(cls, MKDEV(major, 0));
 
    // 2销毁目录信息
    class_destroy(cls);
 
    // 3字符设备驱动注销
    unregister_chrdev(major, "myled");
 
    printk("%s:%s:%d\n", __FILE__, __func__, __LINE__);
    return 0;
}
// 构建设备树匹配表
struct of_device_id oftable[] = {
    {.compatible = "hqyj,myplatform"},
    {}, // 防止数组越界
};
 
// 定义驱动信息对象并初始化
struct platform_driver pdri = {
    .probe = pdri_probe,
    .remove = pdri_remove,
    .driver = {
        .name = "ccc",
        .of_match_table = oftable,
    },
};
 
module_platform_driver(pdri); // 一键注册宏
MODULE_LICENSE("GPL");
test.c
#include 
#include 
#include 
#include 
#include 
#include 
#include 
 
int main(int argc,char const *argv[])
{
    char buf[128]={0};
    int fd = open("/dev/myled0",O_RDWR);
 
    if(fd < 0)
    {
        printf("设备文件打开失败\n");
        exit(-1);
    }
    while(1)
    {
        //读取number的值
        read(fd,buf,sizeof(buf));
        printf("number = %s\n",buf);
    }
 
    close(fd);
    
    return 0;  
}
测试现象:

platform驱动模型(工作流程及代码)_第6张图片

你可能感兴趣的:(驱动开发,stm32,linux)