• 嵌入式驱动程序设计


    发现intel curie平台的bsp部分驱动架构类似linux,今天花了一下午把curie bsp的驱动核心抽离出来了,并且做了几个小sample。

    最小驱动框架核心代码

    1、设备管理

    device.c

    #include <stdio.h>
    #include <stddef.h>
    #include <stdlib.h>
    #include <stdint.h>
    #include <errno.h>
    #include "../../bsp/soc/soc_config.h"
    #include "../../bsp/soc/device.h"
    
    static struct td_device **all_devices = NULL;
    static uint32_t all_devices_count = 0;
    
    void init_devices(struct td_device **_all_devices, uint32_t _all_devices_count)
    {
        if (all_devices != NULL)
            /* Devices already init */
            return;
    
        /* Link array with root device */
        all_devices = _all_devices;
        all_devices_count = _all_devices_count;
    
        uint32_t i;
        int ret = 0;
    
        for (i = 0; i < all_devices_count; ++i)
        {
            struct td_device *dev = all_devices[i];
    
            if (dev->driver->init && (ret = dev->driver->init(dev)))
            {
                dev->powerstate = PM_NOT_INIT;
                printf("dev(%d) is not init",dev->id);
            }
            dev->powerstate = PM_RUNNING;
        }
    }
    
    static void resume_devices_from_index(uint32_t i)
    {
        int ret = 0;
        struct td_device *dev = NULL;
    
        for (; i < all_devices_count; ++i)
        {
            dev = all_devices[i];
    
            printf("resume device %d", dev->id);
            if (dev->powerstate <= PM_SHUTDOWN)
            {
                ret = -EINVAL;
                goto err_resume_device;
            }
    
            if (dev->powerstate == PM_RUNNING)
                /* Device already running */
                continue;
    
            if (dev->driver->resume && (ret = dev->driver->resume(dev)))
                goto err_resume_device;
    
            /* Current device resumed */
            dev->powerstate = PM_RUNNING;
        }
    
        return;
    
    err_resume_device:
        printf("failed to resume device %d (%d)", dev->id,ret);
    
    }
    
    void resume_devices(void)
    {
        resume_devices_from_index(0);
    }
    
    int suspend_devices(PM_POWERSTATE state)
    {
        int32_t i;
        int ret = 0;
    
        /* Use the reverse order used for init, i.e. we suspend bus devices first,
         * then buses, then top level devices */
        for (i = all_devices_count - 1; i >= 0; --i)
        {
            struct td_device *dev = all_devices[i];
    
            // device already suspended
            if (dev->powerstate <= state)
                continue;
    
            printf("suspend dev %d", dev->id);
    
            if (!dev->driver->suspend)
            {
                dev->powerstate = state;
                continue;
            }
    
            ret = dev->driver->suspend(dev, state);
            if (!ret)
            {
                dev->powerstate = state;
                continue;
            }
    
            break;
        }
    
        if (!ret)
            return 0;
    
        /* Suspend aborted, resume all devices starting from where we had
         * an issue */
        if (state > PM_SHUTDOWN)
            resume_devices_from_index(i + 1);
    
        return -1;
    }

    device.h

    #ifndef __DEVICE_H_
    #define __DEVICE_H_
    
    #include <stdint.h>
    
    typedef enum
    {
        PM_NOT_INIT = 0,
        PM_SHUTDOWN,
        PM_SUSPENDED,
        PM_RUNNING,
        PM_COUNT
    } PM_POWERSTATE;
    
    struct td_device;
    struct driver;
    
    //struct __packed __aligned(4) td_device
    struct td_device
    {
        void *priv;
        struct driver *driver;
        PM_POWERSTATE powerstate : 8;
        uint8_t id;
    };
    
    struct driver
    {
        int (*init)(struct td_device *dev);
        int (*suspend)(struct td_device *dev, PM_POWERSTATE state);
        int (*resume)(struct td_device *dev);
    };
    
    int suspend_devices(PM_POWERSTATE state);
    void resume_devices(void);
    void init_devices(struct td_device **all_devices, uint32_t all_devices_count);
    void init_all_devices(void);
    
    #endif

    2、驱动程序配置文件,我这里配置了WDT , CLK , TEST 三个简单的驱动程序。

    soc_config.c

    #include <stdio.h>
    #include <stddef.h>
    #include <stdlib.h>
    #include <errno.h>
    #include "../soc/soc_config.h"
    #include "../soc/device.h"
    #include "../driver/wdt/wdt.h"
    #include "../driver/clk/clk.h"
    #include "../driver/test/test.h"
    
    typedef enum
    {
        WDT_ID = 0,
        CLK_ID=1,
        TEST_ID =2,
    } DEVICE_ID;
    
    struct td_device pf_device_wdt =
    {
        .id = WDT_ID,
        .driver = &watchdog_driver,
        .priv = &(struct wdt_pm_data){
            .a = 1,
            .b =2,
        },
    };
    
    struct td_device pf_device_clk =
    {
        .id = CLK_ID,
        .driver = &clk_driver,
        .priv = &(struct clk_data){
            .a=5,
            .b=6,
        },
    };
    
    struct td_device pf_device_test =
    {
        .id = TEST_ID,
        .driver = &test_driver,
        .priv = &(struct test_data){
            .a=3,
            .b=4,
        },
    };
    
    static struct td_device *platform_devices[] =
    {
        &pf_device_wdt,
        &pf_device_clk,
        &pf_device_test,
    };
    
    #define ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0]))
    void init_all_devices(void)
    {
        /* Init plateform devices and buses */
        init_devices(platform_devices, ARRAY_SIZE(platform_devices));
    
    }

    soc_config.h

    #ifndef __SOC_CONFIG_H_
    #define __SOC_CONFIG_H_
    
    extern struct td_device pf_device_wdt;
    extern struct td_device pf_device_clk;
    extern struct td_device pf_device_test;
    
    #endif

    3、以上就是驱动架构的最小系统,下面添加一个驱动程序例子test_driver

    test.c

    #include <stdio.h>
    #include <stdlib.h>
    #include "../../soc/soc_config.h"
    #include "../../soc/device.h"
    #include "../../driver/test/test.h"
    
    int test_init(struct td_device *dev)
    {
        return 0;
    }
    
    static int test_suspend(struct td_device *dev, PM_POWERSTATE state)
    {
        return 0;
    }
    
    static int test_resume(struct td_device *dev)
    {
        return 0;
    }
    
    struct driver test_driver =
    {
        .init = test_init,
        .suspend = test_suspend,
        .resume = test_resume
    };

    test.h

    #ifndef _TEST_H_
    #define _TEST_H_
    
    #include <stdint.h>
    
    extern struct driver test_driver;
    
    struct test_data
    {
        uint32_t a;
        uint32_t b;
    };
    
    #endif

    5、再写个驱动程序调用实例

    main.c

    #include <stdio.h>
    #include "../bsp/soc/device.h"
    #include "../bsp/soc/soc_config.h"
    #include "../bsp/driver/test/test.h"
    
    int main()
    {
        //driver framework test!
        init_all_devices();
    
        //driver struct test!
        struct td_device *test_device =(struct td_device *)&pf_device_test;
        printf("
    ===test device(%d) ok!===
    ",test_device->id);
    
        //driver api test!
        struct driver *test_driver = (struct driver *)test_device->driver;
        if(test_driver->init(wdt_device)==0)  printf("test init ok!
    ");
    
        //driver data test!
        struct test_data *data = (struct test_data *)test_device->priv;
        printf("test_data a:%d,b:%d!
    ",data->a,data->b);
    
        return 0;
    }

    项目工程放在github上了https://github.com/zhoudd1/driver

    用code::blocks可以直接编译运行。

    6.在test driver的基础上添加driver api

    首先在设备指针里添加driver api属性

    struct td_device pf_device_test =
    {
        .id = TEST_ID,
        .driver = &test_driver,
        .priv = &(struct test_data){
        },
    };

    struct test_data结构体是用户根据需求自定义的,这里仅增加了几个driver api ,留了个void *driver_data空指针备用。

    struct test_data
    {
        void *driver_api;
        void *driver_data;
    };

    然后更新test driver实例

    test.c

    #include <stdio.h>
    #include <stdlib.h>
    #include "../../soc/soc_config.h"
    #include "../../soc/device.h"
    #include "../../driver/test/test.h"
    
    
    static void test_open_cb(struct td_device *dev)
    {
        printf("test dev open sucss !
    ");
    }
    
    static void test_close_cb(struct td_device *dev)
    {
    }
    
    struct test_driver_api test_funcs = {
        .open = test_open_cb,
        .close = test_close_cb,
    };
    
    int test_init(struct td_device *dev)
    {
        struct test_data *data = (struct test_data *)dev->priv;
        data->driver_api= &test_funcs;
        return 0;
    }
    
    static int test_suspend(struct td_device *dev, PM_POWERSTATE state)
    {
        return 0;
    }
    
    static int test_resume(struct td_device *dev)
    {
        return 0;
    }
    
    struct driver test_driver =
    {
        .init = test_init,
        .suspend = test_suspend,
        .resume = test_resume
    };

    test.h

    #ifndef _TEST_H_
    #define _TEST_H_
    
    #include <stdint.h>
    
    typedef void (*test_api_open)(struct td_device *dev);
    typedef void (*test_api_close)(struct td_device *dev);
    
    struct test_driver_api {
        test_api_open open;
        test_api_close close;
    };
    
    struct test_data
    {
        void *driver_api;
        void *driver_data;
    };
    
    extern struct driver test_driver;
    
    #endif

    测试代码main.c

    #include <stdio.h>
    #include "../bsp/soc/device.h"
    #include "../bsp/soc/soc_config.h"
    #include "../bsp/driver/wdt/wdt.h"
    #include "../bsp/driver/clk/clk.h"
    #include "../bsp/driver/test/test.h"
    
    int main()
    {
        //device driver framework test!
        init_all_devices();
    
        //device struct test!
        struct td_device *test_device =(struct td_device *)&pf_device_test;
        printf("test device(%d) ok!
    ",test_device->id);
    
        //driver struct test!
        struct driver *test_driver = (struct driver *)test_device->driver;
        printf("test init %d!
    ",test_driver->init(test_device));
    
        //driver data test!
        struct test_data *data = (struct test_data *)test_device->priv;
    
        //driver api test!
        struct test_driver_api *b = data->driver_api;
        b->open(test_device);
    
        //driver api data test!
        int *d = (int*)data->driver_data;
    
        return 0;
    }

    代码有些凌乱,如果哪天需要在具体的SOC上重构BSP,再好好整理一下。

    https://github.com/zhoudd1/driver

  • 相关阅读:
    viewmodel
    jQuery.each的function中有哪些参数
    JS,Jquery获取各种屏幕的宽度和高度
    解决点击空<a>标签返回页面顶部的问题
    JS使用getComputedStyle()方法获取CSS属性值
    html5中画布和SVG的比较
    HTML中的SVG
    Canvas绘制时钟
    用Canvas实现动画效果
    绘制文字
  • 原文地址:https://www.cnblogs.com/dong1/p/6786056.html
Copyright © 2020-2023  润新知