【嵌入式程序设计】——嵌入式驱动程序设计

时间:2021-01-17 22:29:44

发现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(
"\r\n===test device(%d) ok!===\r\n",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!\n");

//driver data test!
struct test_data *data = (struct test_data *)test_device->priv;
printf(
"test_data a:%d,b:%d!\n",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 !\r\n");
}

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!\r\n",test_device->id);

//driver struct test!
struct driver *test_driver = (struct driver *)test_device->driver;
printf(
"test init %d!\n",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