Zephyr Kernel 裝置驅動和裝置模型(二)
C++語言有封裝、繼承、多型的特點。C語言也可以面向物件和應用設計模式,關鍵就在於如何實現面嚮物件語言的三個重要屬性,下面以watdog為例分析Zephyr裝置驅動和裝置模型。
struct device;
/**
* @brief Static device information (In ROM) Per driver instance
* @param name name of the device
* @param init init function for the driver
* @param config_info address of driver instance config information
*/
struct device_config {
char *name;
int (*init)(struct device *device);
void *config_info;
};
/**
* @brief Runtime device structure (In memory) Per driver instance
* @param device_config Build time config information
* @param driver_api pointer to structure containing the API functions for
* the device type. This pointer is filled in by the driver at init time.
* @param driver_data river instance data. For driver use only
*/
struct device {
struct device_config *config;
void *driver_api;
void *driver_data;
};
#define DEVICE_INIT(dev_name, drv_name, init_fn, data, cfg_info, level, prio) \
\
static struct device_config __config_##dev_name __used \
__attribute__((__section__(".devconfig.init"))) = { \
.name = drv_name, .init = (init_fn), \
.config_info = (cfg_info) \
}; \
\
static struct device (__device_##dev_name) __used \
__attribute__((__section__(".init_" #level STRINGIFY(prio)))) = { \
.config = &(__config_##dev_name), \
.driver_data = data \
}
(1)封裝性
- struct _Device_data;
- typedefvoid (*process)(struct _Device_data* pData);
- typedefstruct _Device_data
- {
- int value;
- process pProcess;
- }Data;
(2)繼承性
- typedefstruct _parent
- {
- int data_parent;
- }Parent;
- typedefstruct _Child
- {
- struct _parent parent;
- int data_child;
- }Child;
(3)多型
- typedefstruct _Play
- {
- void* pData;
- void (*start_play)(struct _Play* pPlay);
- }Play;
#include <nanokernel.h>
#include <init.h>#include <clock_control.h>
#include "wdt_dw.h"
#ifdef WDT_DW_INT_MASK
static inline void _wdt_dw_int_unmask(void)
{
sys_write32(sys_read32(WDT_DW_INT_MASK) & INT_UNMASK_IA,
WDT_DW_INT_MASK);
}
#else
#define _wdt_dw_int_unmask()
#endif
#ifdef CONFIG_WDT_DW_CLOCK_GATE
static inline void _wdt_dw_clock_config(struct device *dev)
{
char *drv = CONFIG_WDT_DW_CLOCK_GATE_DRV_NAME;
struct device *clk;
clk = device_get_binding(drv);
if (clk) {
struct wdt_dw_runtime *context = dev->driver_data;
context->clock = clk;
}
}
static inline void _wdt_dw_clock_on(struct device *dev)
{
struct wdt_dw_dev_config *config = dev->config->config_info;
struct wdt_dw_runtime *context = dev->driver_data;
clock_control_on(context->clock, config->clock_data);
}
static inline void _wdt_dw_clock_off(struct device *dev)
{
struct wdt_dw_dev_config *config = dev->config->config_info;
struct wdt_dw_runtime *context = dev->driver_data;
clock_control_off(context->clock, config->clock_data);
}
#else
#define _wdt_dw_clock_config(...)
#define _wdt_dw_clock_on(...)
#define _wdt_dw_clock_off(...)
#endif
/**
* Enables the clock for the peripheral watchdog
*/
static void wdt_dw_enable(struct device *dev)
{
_wdt_dw_clock_on(dev);
#if defined(CONFIG_SOC_QUARK_SE) || defined(CONFIG_SOC_QUARK_D2000)
sys_set_bit(SCSS_PERIPHERAL_BASE + SCSS_PERIPH_CFG0, 1);
#endif
}
static void wdt_dw_disable(struct device *dev)
{
_wdt_dw_clock_off(dev);
#if defined(CONFIG_SOC_QUARK_SE) || defined(CONFIG_SOC_QUARK_D2000)
sys_clear_bit(SCSS_PERIPHERAL_BASE + SCSS_PERIPH_CFG0, 1);
#endif
}
void wdt_dw_isr(void *arg)
{
struct device *dev = arg;
struct wdt_dw_runtime *context = dev->driver_data;
if (context->cb_fn) {
context->cb_fn(dev);
}
}
static void wdt_dw_get_config(struct device *dev, struct wdt_config *config)
{
struct wdt_dw_dev_config *wdt_dev = dev->config->config_info;
struct wdt_dw_runtime *context = dev->driver_data;
config->timeout = sys_read32(wdt_dev->base_address + WDT_TORR) &
WDT_TIMEOUT_MASK;
config->mode = (sys_read32(wdt_dev->base_address + WDT_CR) & WDT_MODE)
>> WDT_MODE_OFFSET;
config->interrupt_fn = context->cb_fn;
}
static void wdt_dw_reload(struct device *dev)
{
struct wdt_dw_dev_config *wdt_dev = dev->config->config_info;
sys_write32(WDT_CRR_VAL, wdt_dev->base_address + WDT_CRR);
}
static int wdt_dw_set_config(struct device *dev, struct wdt_config *config)
{
struct wdt_dw_dev_config *wdt_dev = dev->config->config_info;
struct wdt_dw_runtime *context = dev->driver_data;
sys_write32(config->timeout, wdt_dev->base_address + WDT_TORR);
/* Set response mode */
if (WDT_MODE_RESET == config->mode) {
sys_clear_bit(wdt_dev->base_address + WDT_CR, 1);
} else {
if (!config->interrupt_fn) {
return DEV_FAIL;
}
context->cb_fn = config->interrupt_fn;
sys_set_bit(wdt_dev->base_address + WDT_CR, 1);
}
/* Enable WDT, cannot be disabled until soc reset */
sys_set_bit(wdt_dev->base_address + WDT_CR, 0);
wdt_dw_reload(dev);
return DEV_OK;
}
static struct wdt_driver_api wdt_dw_funcs = {
.set_config = wdt_dw_set_config,
.get_config = wdt_dw_get_config,
.enable = wdt_dw_enable,
.disable = wdt_dw_disable,
.reload = wdt_dw_reload,
};
#if 0
#define DEVICE_INIT(dev_name, drv_name, init_fn, data, cfg_info, level, prio) \
\
static struct device_config __config_##dev_name __used \
__attribute__((__section__(".devconfig.init"))) = { \
.name = drv_name, .init = (init_fn), \
.config_info = (cfg_info) \
}; \
\
static struct device (__device_##dev_name) __used \
__attribute__((__section__(".init_" #level STRINGIFY(prio)))) = { \
.config = &(__config_##dev_name), \
.driver_data = data \
}
#endif
int wdt_dw_init(struct device *dev);
struct wdt_dw_runtime wdt_runtime;
struct wdt_dw_dev_config wdt_dev = {
.base_address = CONFIG_WDT_DW_BASE_ADDR,
#ifdef CONFIG_WDT_DW_CLOCK_GATE
.clock_data = UINT_TO_POINTER(CONFIG_WDT_DW_CLOCK_GATE_SUBSYS),
#endif
};
DEVICE_INIT(wdt, CONFIG_WDT_DW_DRV_NAME, &wdt_dw_init,
&wdt_runtime, &wdt_dev,
SECONDARY, CONFIG_KERNEL_INIT_PRIORITY_DEVICE);
int wdt_dw_init(struct device *dev)
{
dev->driver_api = &wdt_dw_funcs;
IRQ_CONNECT(CONFIG_WDT_DW_IRQ, CONFIG_WDT_DW_IRQ_PRI, wdt_dw_isr,
DEVICE_GET(wdt), 0);
irq_enable(CONFIG_WDT_DW_IRQ);
_wdt_dw_int_unmask();
_wdt_dw_clock_config(dev);
return 0;
}
=========================================================================================
application sample:
#include <zephyr.h>
#include <device.h>
#include <watchdog.h>
#include "board.h"
#include <misc/printk.h>
uint32_t wdt_fired;
#define WDT_DRIVER CONFIG_WDT_DW_DRV_NAME
/* WDT Requires a callback, there is no interrupt enable / disable. */
void wdt_example_cb(struct device *dev)
{
wdt_fired++;
printk("watchdog fired\n");
wdt_reload(dev);
}
void main(void)
{
struct wdt_config wr_cfg;
struct wdt_config cfg;
struct device *wdt_dev;
printk("Start watchdog test\n");
wr_cfg.timeout = WDT_2_27_CYCLES;
wr_cfg.mode = WDT_MODE_INTERRUPT_RESET;
wr_cfg.interrupt_fn = wdt_example_cb;
wdt_fired = 0;
wdt_dev = device_get_binding(WDT_DRIVER);
wdt_enable(wdt_dev);
wdt_set_config(wdt_dev, &wr_cfg);
wdt_get_config(wdt_dev, &cfg);
printk("timeout: %d\n", cfg.timeout);
printk("mode: %d\n", cfg.mode);
}