git-svn-id: svn://svn.code.sf.net/p/chibios/svn/trunk@15288 27425a3e-05d8-49a3-a47f-9c15f0e5edd8
This commit is contained in:
parent
d14f5f15e9
commit
325637c093
|
@ -29,6 +29,11 @@
|
|||
<type>2</type>
|
||||
<locationURI>CHIBIOS/os/hal/boards/ST_NUCLEO64_G474RE</locationURI>
|
||||
</link>
|
||||
<link>
|
||||
<name>fatfs</name>
|
||||
<type>2</type>
|
||||
<locationURI>CHIBIOS/ext/fatfs</locationURI>
|
||||
</link>
|
||||
<link>
|
||||
<name>os</name>
|
||||
<type>2</type>
|
||||
|
|
|
@ -27,9 +27,9 @@ MEMORY
|
|||
flash5 (rx) : org = 0x00000000, len = 0
|
||||
flash6 (rx) : org = 0x00000000, len = 0
|
||||
flash7 (rx) : org = 0x00000000, len = 0
|
||||
ram0 (wx) : org = 0x20000000, len = 640k - 384k /* Host data. */
|
||||
ram1 (wx) : org = 0x20040000, len = 192k /* Sandbox 1 data. */
|
||||
ram2 (wx) : org = 0x20070000, len = 192k /* Sandbox 2 data. */
|
||||
ram0 (wx) : org = 0x20000000, len = 640k - 256k /* Host data. */
|
||||
ram1 (wx) : org = 0x20060000, len = 128k /* Sandbox 1 data. */
|
||||
ram2 (wx) : org = 0x20080000, len = 128k /* Sandbox 2 data. */
|
||||
ram3 (wx) : org = 0x00000000, len = 0
|
||||
ram4 (wx) : org = 0x00000000, len = 0
|
||||
ram5 (wx) : org = 0x00000000, len = 0
|
||||
|
|
|
@ -293,7 +293,7 @@
|
|||
#define STM32_SERIAL_USE_USART3 FALSE
|
||||
#define STM32_SERIAL_USE_UART4 FALSE
|
||||
#define STM32_SERIAL_USE_UART5 FALSE
|
||||
#define STM32_SERIAL_USE_LPUART1 FALSE
|
||||
#define STM32_SERIAL_USE_LPUART1 TRUE
|
||||
|
||||
/*
|
||||
* SIO driver system settings.
|
||||
|
|
|
@ -26,33 +26,49 @@
|
|||
|
||||
#include "startup_defs.h"
|
||||
|
||||
#include "sdmon.h"
|
||||
|
||||
/*===========================================================================*/
|
||||
/* VFS-related. */
|
||||
/*===========================================================================*/
|
||||
|
||||
#if VFS_CFG_ENABLE_DRV_FATFS == TRUE
|
||||
/* VFS FatFS driver object representing the root directory.*/
|
||||
static vfs_fatfs_driver_c root_driver;
|
||||
/* VFS FatFS driver used by all file systems.*/
|
||||
static vfs_fatfs_driver_c fatfs_driver;
|
||||
#endif
|
||||
|
||||
/* VFS overlay driver object representing the root directory.*/
|
||||
/* VFS overlay driver object representing the absolute root directory.*/
|
||||
static vfs_overlay_driver_c root_overlay_driver;
|
||||
|
||||
/* VFS streams driver object representing the /dev directory.*/
|
||||
static vfs_streams_driver_c dev_driver;
|
||||
/* Segregated roots for the two sandboxes.*/
|
||||
static vfs_overlay_driver_c sb1_root_overlay_driver;
|
||||
static vfs_overlay_driver_c sb2_root_overlay_driver;
|
||||
|
||||
/* Shared directory between the two sandboxes.*/
|
||||
static vfs_overlay_driver_c sb_shared_overlay_driver;
|
||||
|
||||
/* VFS streams driver objects representing the /dev private directories.*/
|
||||
static vfs_streams_driver_c sb1_dev_driver;
|
||||
static vfs_streams_driver_c sb2_dev_driver;
|
||||
|
||||
/* VFS API will use this object as implicit root, defining this
|
||||
symbol is expected.*/
|
||||
vfs_driver_c *vfs_root = (vfs_driver_c *)&root_overlay_driver;
|
||||
|
||||
/* Used for /dev/null.*/
|
||||
static NullStream nullstream;
|
||||
|
||||
/* Stream to be exposed under /dev as files.*/
|
||||
static const drv_streams_element_t streams[] = {
|
||||
/* Streams to be exposed under /dev as files.*/
|
||||
static const drv_streams_element_t sb1_streams[] = {
|
||||
{"VSD1", (BaseSequentialStream *)&SD2},
|
||||
{"null", (BaseSequentialStream *)&nullstream},
|
||||
{NULL, NULL}
|
||||
};
|
||||
static const drv_streams_element_t sb2_streams[] = {
|
||||
{"VSD1", (BaseSequentialStream *)&LPSD1},
|
||||
{"null", (BaseSequentialStream *)&nullstream},
|
||||
{NULL, NULL}
|
||||
};
|
||||
|
||||
/*===========================================================================*/
|
||||
/* SB-related. */
|
||||
|
@ -72,7 +88,7 @@ static const sb_config_t sb_config1 = {
|
|||
.writeable = true
|
||||
}
|
||||
},
|
||||
.vfs_driver = (vfs_driver_c *)&root_overlay_driver
|
||||
.vfs_driver = (vfs_driver_c *)&sb1_root_overlay_driver
|
||||
};
|
||||
|
||||
/* Sandbox 2 configuration.*/
|
||||
|
@ -89,7 +105,7 @@ static const sb_config_t sb_config2 = {
|
|||
.writeable = true
|
||||
}
|
||||
},
|
||||
.vfs_driver = (vfs_driver_c *)&root_overlay_driver
|
||||
.vfs_driver = (vfs_driver_c *)&sb2_root_overlay_driver
|
||||
};
|
||||
|
||||
/* Sandbox objects.*/
|
||||
|
@ -98,6 +114,8 @@ sb_class_t sbx1, sbx2;
|
|||
static THD_WORKING_AREA(waUnprivileged1, 512);
|
||||
static THD_WORKING_AREA(waUnprivileged2, 512);
|
||||
|
||||
static thread_t *utp1, *utp2;
|
||||
|
||||
/*===========================================================================*/
|
||||
/* Main and generic code. */
|
||||
/*===========================================================================*/
|
||||
|
@ -117,15 +135,34 @@ static THD_FUNCTION(Thread1, arg) {
|
|||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* SB termination event.
|
||||
*/
|
||||
static void SBHandler(eventid_t id) {
|
||||
|
||||
(void)id;
|
||||
|
||||
if (chThdTerminatedX(utp1)) {
|
||||
chprintf((BaseSequentialStream *)&SD2, "SB1 terminated\r\n");
|
||||
}
|
||||
|
||||
if (chThdTerminatedX(utp2)) {
|
||||
chprintf((BaseSequentialStream *)&SD2, "SB2 terminated\r\n");
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Application entry point.
|
||||
*/
|
||||
int main(void) {
|
||||
unsigned i = 1U;
|
||||
thread_t *utp1, *utp2;
|
||||
event_listener_t el1;
|
||||
event_listener_t el0, el1, el2;
|
||||
vfs_file_node_c *fnp;
|
||||
msg_t ret;
|
||||
static const evhandler_t evhndl[] = {
|
||||
sdmonInsertHandler,
|
||||
sdmonRemoveHandler,
|
||||
SBHandler
|
||||
};
|
||||
|
||||
/*
|
||||
* System initializations.
|
||||
|
@ -142,27 +179,64 @@ int main(void) {
|
|||
sbHostInit();
|
||||
|
||||
/*
|
||||
* Starting a serial port for I/O, initializing other streams too.
|
||||
* Starting a serial ports for I/O, initializing other streams too.
|
||||
*/
|
||||
sdStart(&SD2, NULL);
|
||||
sdStart(&LPSD1, NULL);
|
||||
nullObjectInit(&nullstream);
|
||||
|
||||
/*
|
||||
* Activates the card insertion monitor.
|
||||
*/
|
||||
sdmonInit();
|
||||
|
||||
/*
|
||||
* Creating a blinker thread.
|
||||
*/
|
||||
chThdCreateStatic(waThread1, sizeof(waThread1), NORMALPRIO+10, Thread1, NULL);
|
||||
|
||||
/*
|
||||
* Initializing an overlay VFS object as a root, no overlaid driver,
|
||||
* registering a streams VFS driver on the VFS overlay root as "/dev".
|
||||
* Initializing an overlay VFS object as a root on top of a FatFS driver.
|
||||
* This is accessible from kernel space and covers the whole file system.
|
||||
*/
|
||||
drvOverlayObjectInit(&root_overlay_driver, NULL, NULL);
|
||||
ret = drvOverlayRegisterDriver(&root_overlay_driver,
|
||||
drvStreamsObjectInit(&dev_driver, &streams[0]),
|
||||
drvOverlayObjectInit(&root_overlay_driver, (vfs_driver_c *)&fatfs_driver, NULL);
|
||||
|
||||
/*
|
||||
* Initializing overlay drivers for the two sandbox roots. Those also use
|
||||
* the FatFS driver but are restricted to "/sb1" and "/sb2" directories.
|
||||
*/
|
||||
drvOverlayObjectInit(&sb1_root_overlay_driver, (vfs_driver_c *)&fatfs_driver, "sb1");
|
||||
drvOverlayObjectInit(&sb2_root_overlay_driver, (vfs_driver_c *)&fatfs_driver, "sb2");
|
||||
ret = drvOverlayRegisterDriver(&sb1_root_overlay_driver,
|
||||
drvStreamsObjectInit(&sb1_dev_driver, &sb1_streams[0]),
|
||||
"dev");
|
||||
if (CH_RET_IS_ERROR(ret)) {
|
||||
chSysHalt("VFS");
|
||||
}
|
||||
ret = drvOverlayRegisterDriver(&sb2_root_overlay_driver,
|
||||
drvStreamsObjectInit(&sb2_dev_driver, &sb2_streams[0]),
|
||||
"dev");
|
||||
if (CH_RET_IS_ERROR(ret)) {
|
||||
chSysHalt("VFS");
|
||||
}
|
||||
|
||||
/*
|
||||
* Initializing overlay driver for the directory shared among the sandboxes.
|
||||
* It is seen as "/shared".
|
||||
*/
|
||||
drvOverlayObjectInit(&sb_shared_overlay_driver, (vfs_driver_c *)&fatfs_driver, "shared");
|
||||
ret = drvOverlayRegisterDriver(&sb1_root_overlay_driver,
|
||||
(vfs_driver_c *)&sb_shared_overlay_driver,
|
||||
"shared");
|
||||
if (CH_RET_IS_ERROR(ret)) {
|
||||
chSysHalt("VFS");
|
||||
}
|
||||
ret = drvOverlayRegisterDriver(&sb2_root_overlay_driver,
|
||||
(vfs_driver_c *)&sb_shared_overlay_driver,
|
||||
"shared");
|
||||
if (CH_RET_IS_ERROR(ret)) {
|
||||
chSysHalt("VFS");
|
||||
}
|
||||
|
||||
/*
|
||||
* Sandbox objects initialization.
|
||||
|
@ -171,16 +245,26 @@ int main(void) {
|
|||
sbObjectInit(&sbx2, &sb_config2);
|
||||
|
||||
/*
|
||||
* Associating standard input, output and error to sandboxes. Both sandboxes
|
||||
* use the same serial port in this setup.
|
||||
* Associating standard input, output and error to sandbox 1.
|
||||
*/
|
||||
ret = vfsOpenFile("/dev/VSD1", 0, &fnp);
|
||||
ret = vfsDrvOpenFile((vfs_driver_c *)&sb1_root_overlay_driver,
|
||||
"/dev/VSD1", 0, &fnp);
|
||||
if (CH_RET_IS_ERROR(ret)) {
|
||||
chSysHalt("VFS");
|
||||
}
|
||||
sbPosixRegisterFileDescriptor(&sbx1, STDIN_FILENO, (vfs_file_node_c *)roAddRef(fnp));
|
||||
sbPosixRegisterFileDescriptor(&sbx1, STDOUT_FILENO, (vfs_file_node_c *)roAddRef(fnp));
|
||||
sbPosixRegisterFileDescriptor(&sbx1, STDERR_FILENO, (vfs_file_node_c *)roAddRef(fnp));
|
||||
vfsCloseFile(fnp);
|
||||
|
||||
/*
|
||||
* Associating standard input, output and error to sandbox 1.
|
||||
*/
|
||||
ret = vfsDrvOpenFile((vfs_driver_c *)&sb2_root_overlay_driver,
|
||||
"/dev/VSD1", 0, &fnp);
|
||||
if (CH_RET_IS_ERROR(ret)) {
|
||||
chSysHalt("VFS");
|
||||
}
|
||||
sbPosixRegisterFileDescriptor(&sbx2, STDIN_FILENO, (vfs_file_node_c *)roAddRef(fnp));
|
||||
sbPosixRegisterFileDescriptor(&sbx2, STDOUT_FILENO, (vfs_file_node_c *)roAddRef(fnp));
|
||||
sbPosixRegisterFileDescriptor(&sbx2, STDERR_FILENO, (vfs_file_node_c *)roAddRef(fnp));
|
||||
|
@ -188,20 +272,24 @@ int main(void) {
|
|||
|
||||
/*
|
||||
* Creating **static** boxes using MPU.
|
||||
* Note: The two regions cover both sandbox 1 and 2, there is no
|
||||
* isolation among them.
|
||||
*/
|
||||
mpuConfigureRegion(MPU_REGION_0,
|
||||
0x08070000U,
|
||||
0x08180000,
|
||||
MPU_RASR_ATTR_AP_RO_RO |
|
||||
MPU_RASR_ATTR_CACHEABLE_WT_NWA |
|
||||
MPU_RASR_SIZE_64K |
|
||||
MPU_RASR_SIZE_512K |
|
||||
MPU_RASR_ENABLE);
|
||||
mpuConfigureRegion(MPU_REGION_1,
|
||||
0x2001E000U,
|
||||
0x20060000,
|
||||
MPU_RASR_ATTR_AP_RW_RW |
|
||||
MPU_RASR_ATTR_CACHEABLE_WB_WA |
|
||||
MPU_RASR_SIZE_8K |
|
||||
MPU_RASR_SIZE_128K |
|
||||
MPU_RASR_ENABLE);
|
||||
mpuConfigureRegion(MPU_REGION_2,
|
||||
0x20080000,
|
||||
MPU_RASR_ATTR_AP_RW_RW |
|
||||
MPU_RASR_ATTR_CACHEABLE_WB_WA |
|
||||
MPU_RASR_SIZE_128K |
|
||||
MPU_RASR_ENABLE);
|
||||
|
||||
/* Starting sandboxed thread 1.*/
|
||||
|
@ -223,13 +311,16 @@ int main(void) {
|
|||
/*
|
||||
* Listening to sandbox events.
|
||||
*/
|
||||
chEvtRegister(&sb.termination_es, &el1, (eventid_t)0);
|
||||
chEvtRegister(&sdmon_inserted_event, &el0, (eventid_t)0);
|
||||
chEvtRegister(&sdmon_removed_event, &el1, (eventid_t)1);
|
||||
chEvtRegister(&sb.termination_es, &el2, (eventid_t)2);
|
||||
|
||||
/*
|
||||
* Normal main() thread activity, in this demo it monitors the user button
|
||||
* and checks for sandboxes state.
|
||||
*/
|
||||
while (true) {
|
||||
chEvtDispatch(evhndl, chEvtWaitOneTimeout(ALL_EVENTS, TIME_MS2I(500)));
|
||||
|
||||
/* Checking for user button, launching test suite if pressed.*/
|
||||
if (palReadLine(LINE_BUTTON)) {
|
||||
|
@ -237,18 +328,7 @@ int main(void) {
|
|||
test_execute((BaseSequentialStream *)&SD2, &oslib_test_suite);
|
||||
}
|
||||
|
||||
/* Waiting for a sandbox event or timeout.*/
|
||||
if (chEvtWaitOneTimeout(ALL_EVENTS, TIME_MS2I(500)) != (eventmask_t)0) {
|
||||
|
||||
if (chThdTerminatedX(utp1)) {
|
||||
chprintf((BaseSequentialStream *)&SD2, "SB1 terminated\r\n");
|
||||
}
|
||||
|
||||
if (chThdTerminatedX(utp2)) {
|
||||
chprintf((BaseSequentialStream *)&SD2, "SB2 terminated\r\n");
|
||||
}
|
||||
}
|
||||
|
||||
#if 0
|
||||
if ((i & 1) == 0U) {
|
||||
if (!chThdTerminatedX(utp1)) {
|
||||
(void) sbSendMessageTimeout(&sbx1, (msg_t)i, TIME_MS2I(10));
|
||||
|
@ -260,5 +340,6 @@ int main(void) {
|
|||
}
|
||||
}
|
||||
i++;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,147 @@
|
|||
/*
|
||||
ChibiOS - Copyright (C) 2006..2018 Giovanni Di Sirio
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
#include "ch.h"
|
||||
#include "hal.h"
|
||||
#include "vfs.h"
|
||||
|
||||
/*===========================================================================*/
|
||||
/* Module local definitions. */
|
||||
/*===========================================================================*/
|
||||
|
||||
#define POLLING_INTERVAL 10
|
||||
#define POLLING_DELAY 10
|
||||
|
||||
/*===========================================================================*/
|
||||
/* Module exported variables. */
|
||||
/*===========================================================================*/
|
||||
|
||||
event_source_t sdmon_inserted_event, sdmon_removed_event;
|
||||
bool sdmon_ready;
|
||||
|
||||
/*===========================================================================*/
|
||||
/* Module local types. */
|
||||
/*===========================================================================*/
|
||||
|
||||
/*===========================================================================*/
|
||||
/* Module local variables. */
|
||||
/*===========================================================================*/
|
||||
|
||||
static virtual_timer_t tmr;
|
||||
static unsigned cnt;
|
||||
|
||||
/*===========================================================================*/
|
||||
/* Module local functions. */
|
||||
/*===========================================================================*/
|
||||
|
||||
/**
|
||||
* @brief Insertion monitor timer callback function.
|
||||
*
|
||||
* @param[in] p pointer to the @p BaseBlockDevice object
|
||||
*
|
||||
* @notapi
|
||||
*/
|
||||
static void tmrfunc(virtual_timer_t *vtp, void *p) {
|
||||
BaseBlockDevice *bbdp = p;
|
||||
|
||||
chSysLockFromISR();
|
||||
|
||||
if (cnt > 0) {
|
||||
if (blkIsInserted(bbdp)) {
|
||||
if (--cnt == 0) {
|
||||
chEvtBroadcastI(&sdmon_inserted_event);
|
||||
}
|
||||
}
|
||||
else
|
||||
cnt = POLLING_INTERVAL;
|
||||
}
|
||||
else {
|
||||
if (!blkIsInserted(bbdp)) {
|
||||
cnt = POLLING_INTERVAL;
|
||||
chEvtBroadcastI(&sdmon_removed_event);
|
||||
}
|
||||
}
|
||||
chVTSetI(vtp, TIME_MS2I(POLLING_DELAY), tmrfunc, bbdp);
|
||||
|
||||
chSysUnlockFromISR();
|
||||
}
|
||||
|
||||
/*===========================================================================*/
|
||||
/* Module exported functions. */
|
||||
/*===========================================================================*/
|
||||
|
||||
/**
|
||||
* @brief Polling monitor start.
|
||||
*
|
||||
* @notapi
|
||||
*/
|
||||
void sdmonInit(void) {
|
||||
|
||||
/* Activates the SDC driver using default configuration.*/
|
||||
sdcStart(&SDCD1, NULL);
|
||||
|
||||
chEvtObjectInit(&sdmon_inserted_event);
|
||||
chEvtObjectInit(&sdmon_removed_event);
|
||||
|
||||
sdmon_ready = false;
|
||||
cnt = POLLING_INTERVAL;
|
||||
chVTSet(&tmr, TIME_MS2I(POLLING_DELAY), tmrfunc, (void *)&SDCD1);
|
||||
}
|
||||
|
||||
/*
|
||||
* Card insertion event.
|
||||
*/
|
||||
void sdmonInsertHandler(eventid_t id) {
|
||||
msg_t err;
|
||||
|
||||
(void)id;
|
||||
|
||||
#if HAL_USE_SDC
|
||||
if (sdcConnect(&SDCD1)) {
|
||||
return;
|
||||
}
|
||||
#else
|
||||
if (mmcConnect(&MMCD1)) {
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
err = drvFatFSMount("0:", 1);
|
||||
if (CH_RET_IS_ERROR(err)) {
|
||||
#if HAL_USE_SDC
|
||||
sdcDisconnect(&SDCD1);
|
||||
#else
|
||||
if (mmcDisconnect(&MMCD1)) {
|
||||
#endif
|
||||
return;
|
||||
}
|
||||
sdmon_ready = true;
|
||||
}
|
||||
|
||||
/*
|
||||
* Card removal event.
|
||||
*/
|
||||
void sdmonRemoveHandler(eventid_t id) {
|
||||
|
||||
(void)id;
|
||||
|
||||
#if HAL_USE_SDC
|
||||
sdcDisconnect(&SDCD1);
|
||||
#else
|
||||
mmcDisconnect(&MMCD1);
|
||||
#endif
|
||||
sdmon_ready = false;
|
||||
}
|
|
@ -0,0 +1,63 @@
|
|||
/*
|
||||
ChibiOS - Copyright (C) 2006..2018 Giovanni Di Sirio
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
#ifndef SDMON_H
|
||||
#define SDMON_H
|
||||
|
||||
/*===========================================================================*/
|
||||
/* Module constants. */
|
||||
/*===========================================================================*/
|
||||
|
||||
/*===========================================================================*/
|
||||
/* Module pre-compile time settings. */
|
||||
/*===========================================================================*/
|
||||
|
||||
/*===========================================================================*/
|
||||
/* Derived constants and error checks. */
|
||||
/*===========================================================================*/
|
||||
|
||||
/*===========================================================================*/
|
||||
/* Module data structures and types. */
|
||||
/*===========================================================================*/
|
||||
|
||||
/*===========================================================================*/
|
||||
/* Module macros. */
|
||||
/*===========================================================================*/
|
||||
|
||||
/*===========================================================================*/
|
||||
/* External declarations. */
|
||||
/*===========================================================================*/
|
||||
|
||||
extern event_source_t sdmon_inserted_event, sdmon_removed_event;
|
||||
extern bool sdmon_ready;
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
void sdmonInit(void);
|
||||
void sdmonInsertHandler(eventid_t id);
|
||||
void sdmonRemoveHandler(eventid_t id);
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
/*===========================================================================*/
|
||||
/* External declarations. */
|
||||
/*===========================================================================*/
|
||||
|
||||
#endif /* SDMON_H */
|
||||
|
||||
/** @} */
|
|
@ -47,6 +47,10 @@
|
|||
|
||||
#if (CH_CFG_USE_MESSAGES == TRUE) || defined(__DOXYGEN__)
|
||||
|
||||
/*===========================================================================*/
|
||||
/* Module local definitions. */
|
||||
/*===========================================================================*/
|
||||
|
||||
/*===========================================================================*/
|
||||
/* Module exported variables. */
|
||||
/*===========================================================================*/
|
||||
|
|
Loading…
Reference in New Issue