1. 程式人生 > >Zephyr Kernel 裝置驅動和裝置模型(二)

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)封裝性

  1. struct _Device_data;  
  2. typedefvoid (*process)(struct  _Device_data* pData);  
  3. typedefstruct  _Device_data  
  4. {  
  5.     int value;  
  6.     process pProcess;  
  7. }Data;  
    封裝性的意義在於,函式和資料是綁在一起的,資料和資料是綁在一起的。這樣,我們就可以通過簡單的一個結構指標訪問到所有的資料,遍歷所有的函式。封裝性,這是類擁有的屬性,當然也是資料結構體擁有的屬性。

(2)繼承性

  1. typedefstruct _parent  
  2. {  
  3.     int data_parent;  
  4. }Parent;  
  5. typedefstruct _Child  
  6. {  
  7.     struct _parent parent;  
  8.     int data_child;  
  9. }Child;  
    在設計C語言繼承性的時候,我們需要做的就是把基礎資料放在繼承的結構的首位置即可。這樣,不管是資料的訪問、資料的強轉、資料的訪問都不會有什麼問題。

(3)多型

  1. typedefstruct _Play  
  2. {  
  3.     void* pData;  
  4.     void (*start_play)(struct _Play* pPlay);  
  5. }Play;  
    多型,就是說用同一的介面程式碼處理不同的資料。比如說,這裡的Play結構就是一個通用的資料結構,我們也不清楚pData是什麼資料,start_play是什麼處理函式?但是,我們處理的時候只要呼叫pPlay->start_play(pPlay)就可以了。剩下來的事情我們不需要管,因為不同的介面會有不同的函式去處理,我們只要學會呼叫就可以了。

#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);
}