嵌入式驱动程序设计

发现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

原文地址:https://www.cnblogs.com/dong1/p/6786056.html