first
This commit is contained in:
226
rt-thread/components/utilities/Kconfig
Normal file
226
rt-thread/components/utilities/Kconfig
Normal file
@@ -0,0 +1,226 @@
|
||||
menu "Utilities"
|
||||
|
||||
config RT_USING_RYM
|
||||
bool "Enable Ymodem"
|
||||
default n
|
||||
|
||||
if RT_USING_RYM
|
||||
config YMODEM_USING_CRC_TABLE
|
||||
bool "Enable CRC Table in Ymodem"
|
||||
default n
|
||||
|
||||
config YMODEM_USING_FILE_TRANSFER
|
||||
bool "Enable file transfer feature"
|
||||
depends on RT_USING_DFS
|
||||
default y
|
||||
endif
|
||||
|
||||
menuconfig RT_USING_ULOG
|
||||
bool "Enable ulog"
|
||||
default n
|
||||
|
||||
if RT_USING_ULOG
|
||||
if !ULOG_USING_SYSLOG
|
||||
choice
|
||||
prompt "The static output log level."
|
||||
default ULOG_OUTPUT_LVL_D
|
||||
help
|
||||
When the log level is less than this option and it will stop output.
|
||||
These log will not compile into ROM when using LOG_X api.
|
||||
NOTE: It's not available on syslog mode.
|
||||
config ULOG_OUTPUT_LVL_A
|
||||
bool "Assert"
|
||||
config ULOG_OUTPUT_LVL_E
|
||||
bool "Error"
|
||||
config ULOG_OUTPUT_LVL_W
|
||||
bool "Warning"
|
||||
config ULOG_OUTPUT_LVL_I
|
||||
bool "Information"
|
||||
config ULOG_OUTPUT_LVL_D
|
||||
bool "Debug"
|
||||
endchoice
|
||||
endif
|
||||
|
||||
if ULOG_USING_SYSLOG
|
||||
choice
|
||||
prompt "The static output log level."
|
||||
default ULOG_OUTPUT_LVL_DEBUG
|
||||
help
|
||||
When the log level is less than this option and it will stop output.
|
||||
These log will not compile into ROM when using LOG_X api.
|
||||
NOTE: It's not available on syslog mode.
|
||||
config ULOG_OUTPUT_LVL_EMERG
|
||||
bool "EMERG"
|
||||
config ULOG_OUTPUT_LVL_ALERT
|
||||
bool "ALERT"
|
||||
config ULOG_OUTPUT_LVL_CRIT
|
||||
bool "CRIT"
|
||||
config ULOG_OUTPUT_LVL_ERROR
|
||||
bool "ERR"
|
||||
config ULOG_OUTPUT_LVL_WARNING
|
||||
bool "WARNING"
|
||||
config ULOG_OUTPUT_LVL_NOTICE
|
||||
bool "NOTICE"
|
||||
config ULOG_OUTPUT_LVL_INFO
|
||||
bool "INFO"
|
||||
config ULOG_OUTPUT_LVL_DEBUG
|
||||
bool "DEBUG"
|
||||
endchoice
|
||||
endif
|
||||
|
||||
config ULOG_OUTPUT_LVL
|
||||
int
|
||||
default 0 if ULOG_OUTPUT_LVL_A
|
||||
default 0 if ULOG_OUTPUT_LVL_EMERG
|
||||
default 1 if ULOG_OUTPUT_LVL_ALERT
|
||||
default 2 if ULOG_OUTPUT_LVL_CRIT
|
||||
default 3 if ULOG_OUTPUT_LVL_E
|
||||
default 3 if ULOG_OUTPUT_LVL_ERROR
|
||||
default 4 if ULOG_OUTPUT_LVL_W
|
||||
default 4 if ULOG_OUTPUT_LVL_WARNING
|
||||
default 5 if ULOG_OUTPUT_LVL_NOTICE
|
||||
default 6 if ULOG_OUTPUT_LVL_I
|
||||
default 6 if ULOG_OUTPUT_LVL_INFO
|
||||
default 7 if ULOG_OUTPUT_LVL_D
|
||||
default 7 if ULOG_OUTPUT_LVL_DEBUG
|
||||
default 7
|
||||
|
||||
config ULOG_USING_ISR_LOG
|
||||
bool "Enable ISR log."
|
||||
default n
|
||||
help
|
||||
The log output API can using in ISR (Interrupt Service Routines) also.
|
||||
|
||||
config ULOG_ASSERT_ENABLE
|
||||
bool "Enable assert check."
|
||||
default y
|
||||
|
||||
config ULOG_LINE_BUF_SIZE
|
||||
int "The log's max width."
|
||||
default 128
|
||||
help
|
||||
The buffer size for every line log.
|
||||
|
||||
config ULOG_USING_ASYNC_OUTPUT
|
||||
bool "Enable async output mode."
|
||||
default n
|
||||
help
|
||||
When enable asynchronous output mode. The log output is not immediately and the log will stored to buffer.
|
||||
The another thread (Such as idle) will read the buffer and output the log. So it will using more RAM.
|
||||
|
||||
if ULOG_USING_ASYNC_OUTPUT
|
||||
config ULOG_ASYNC_OUTPUT_BUF_SIZE
|
||||
int "The async output buffer size."
|
||||
default 2048
|
||||
|
||||
config ULOG_ASYNC_OUTPUT_BY_THREAD
|
||||
bool "Enable async output by thread."
|
||||
default y
|
||||
help
|
||||
This thread will output the asynchronous logs. The logs can output by other user thread when this option is disable.
|
||||
|
||||
if ULOG_ASYNC_OUTPUT_BY_THREAD
|
||||
|
||||
config ULOG_ASYNC_OUTPUT_THREAD_STACK
|
||||
int "The async output thread stack size."
|
||||
default 1024
|
||||
|
||||
config ULOG_ASYNC_OUTPUT_THREAD_PRIORITY
|
||||
int "The async output thread stack priority."
|
||||
range 0 RT_THREAD_PRIORITY_MAX
|
||||
default 30
|
||||
|
||||
endif
|
||||
endif
|
||||
|
||||
menu "log format"
|
||||
config ULOG_OUTPUT_FLOAT
|
||||
bool "Enable float number support. It will using more thread stack."
|
||||
default n
|
||||
select PKG_USING_RT_VSNPRINTF_FULL
|
||||
help
|
||||
The default formater is using rt_vsnprint and it not supported float number.
|
||||
When enable this option then it will enable libc. The formater will change to vsnprint on libc.
|
||||
|
||||
if !ULOG_USING_SYSLOG
|
||||
config ULOG_USING_COLOR
|
||||
bool "Enable color log."
|
||||
default y
|
||||
help
|
||||
The log will has different color by level.
|
||||
endif
|
||||
|
||||
config ULOG_OUTPUT_TIME
|
||||
bool "Enable time information."
|
||||
default y
|
||||
|
||||
config ULOG_TIME_USING_TIMESTAMP
|
||||
bool "Enable timestamp format for time."
|
||||
default n
|
||||
depends on ULOG_OUTPUT_TIME
|
||||
|
||||
config ULOG_OUTPUT_LEVEL
|
||||
bool "Enable level information."
|
||||
default y
|
||||
|
||||
config ULOG_OUTPUT_TAG
|
||||
bool "Enable tag information."
|
||||
default y
|
||||
|
||||
config ULOG_OUTPUT_THREAD_NAME
|
||||
bool "Enable thread information."
|
||||
default n
|
||||
endmenu
|
||||
|
||||
config ULOG_BACKEND_USING_CONSOLE
|
||||
bool "Enable console backend."
|
||||
default y
|
||||
help
|
||||
The low level output using rt_kprintf().
|
||||
|
||||
config ULOG_BACKEND_USING_FILE
|
||||
bool "Enable file backend."
|
||||
select RT_USING_DFS
|
||||
default n
|
||||
help
|
||||
The file backend of ulog.
|
||||
|
||||
config ULOG_USING_FILTER
|
||||
bool "Enable runtime log filter."
|
||||
default n
|
||||
help
|
||||
It will enable the log filter.
|
||||
Such as level filter, log tag filter, log kw filter and tag's level filter.
|
||||
|
||||
config ULOG_USING_SYSLOG
|
||||
bool "Enable syslog format log and API."
|
||||
select ULOG_OUTPUT_TIME
|
||||
select ULOG_USING_FILTER
|
||||
default n
|
||||
endif
|
||||
|
||||
config RT_USING_UTEST
|
||||
bool "Enable utest (RT-Thread test framework)"
|
||||
default n
|
||||
|
||||
if RT_USING_UTEST
|
||||
config UTEST_THR_STACK_SIZE
|
||||
int "The utest thread stack size"
|
||||
default 4096
|
||||
config UTEST_THR_PRIORITY
|
||||
int "The utest thread priority"
|
||||
default 20
|
||||
endif
|
||||
|
||||
config RT_USING_VAR_EXPORT
|
||||
bool "Enable Var Export"
|
||||
default n
|
||||
|
||||
config RT_USING_RESOURCE_ID
|
||||
bool "Enable resource id"
|
||||
default n
|
||||
|
||||
rsource "libadt/Kconfig"
|
||||
rsource "rt-link/Kconfig"
|
||||
|
||||
endmenu
|
15
rt-thread/components/utilities/SConscript
Normal file
15
rt-thread/components/utilities/SConscript
Normal file
@@ -0,0 +1,15 @@
|
||||
# RT-Thread building script for bridge
|
||||
|
||||
import os
|
||||
from building import *
|
||||
|
||||
cwd = GetCurrentDir()
|
||||
objs = []
|
||||
list = os.listdir(cwd)
|
||||
|
||||
for d in list:
|
||||
path = os.path.join(cwd, d)
|
||||
if os.path.isfile(os.path.join(path, 'SConscript')):
|
||||
objs = objs + SConscript(os.path.join(d, 'SConscript'))
|
||||
|
||||
Return('objs')
|
24
rt-thread/components/utilities/libadt/Kconfig
Normal file
24
rt-thread/components/utilities/libadt/Kconfig
Normal file
@@ -0,0 +1,24 @@
|
||||
menuconfig RT_USING_ADT
|
||||
bool "Enable ADT(abstract data type)"
|
||||
default y if ARCH_MM_MMU
|
||||
default n
|
||||
|
||||
config RT_USING_ADT_AVL
|
||||
bool "AVL tree"
|
||||
depends on RT_USING_ADT
|
||||
default y
|
||||
|
||||
config RT_USING_ADT_BITMAP
|
||||
bool "Bitmap"
|
||||
depends on RT_USING_ADT
|
||||
default y
|
||||
|
||||
config RT_USING_ADT_HASHMAP
|
||||
bool "HashMap"
|
||||
depends on RT_USING_ADT
|
||||
default y
|
||||
|
||||
config RT_USING_ADT_REF
|
||||
bool "Reference API"
|
||||
depends on RT_USING_ADT
|
||||
default y
|
15
rt-thread/components/utilities/libadt/SConscript
Normal file
15
rt-thread/components/utilities/libadt/SConscript
Normal file
@@ -0,0 +1,15 @@
|
||||
from building import *
|
||||
|
||||
cwd = GetCurrentDir()
|
||||
list = os.listdir(cwd)
|
||||
objs = []
|
||||
|
||||
if not GetDepend(['RT_USING_ADT']):
|
||||
Return('objs')
|
||||
|
||||
for d in list:
|
||||
path = os.path.join(cwd, d)
|
||||
if os.path.isfile(os.path.join(path, 'SConscript')):
|
||||
objs = objs + SConscript(os.path.join(d, 'SConscript'))
|
||||
|
||||
Return('objs')
|
9
rt-thread/components/utilities/libadt/avl/SConscript
Normal file
9
rt-thread/components/utilities/libadt/avl/SConscript
Normal file
@@ -0,0 +1,9 @@
|
||||
from building import *
|
||||
|
||||
cwd = GetCurrentDir()
|
||||
src = Glob('*.c')
|
||||
CPPPATH = [cwd]
|
||||
group = []
|
||||
|
||||
group = DefineGroup('LIBADT', src, depend = ['RT_USING_ADT_AVL'], CPPPATH = CPPPATH)
|
||||
Return('group')
|
242
rt-thread/components/utilities/libadt/avl/avl.c
Normal file
242
rt-thread/components/utilities/libadt/avl/avl.c
Normal file
@@ -0,0 +1,242 @@
|
||||
/*
|
||||
* Copyright (c) 2006-2022, RT-Thread Development Team
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*
|
||||
* Change Logs:
|
||||
* Date Author Notes
|
||||
* 2019-10-12 Jesven first version
|
||||
* 2022-11-14 WangXiaoyao Optimize footprint and performance
|
||||
* Export as ADT for generic use case
|
||||
*/
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include "avl.h"
|
||||
|
||||
#define HEIGHT_OF(node) ((node) ? (node)->height : 0)
|
||||
#define IS_RCHILD(node) (!((node) - ((node)->parent->avl_right)))
|
||||
#define IS_LCHILD(node) (!((node) - ((node)->parent->avl_left)))
|
||||
#define NODE_PLACE(node) \
|
||||
IS_LCHILD(node) ? &(node)->parent->avl_left : &(node)->parent->avl_right
|
||||
|
||||
static inline void rotate_right(struct util_avl_struct *axis,
|
||||
struct util_avl_struct *lchild,
|
||||
struct util_avl_struct *lrchild,
|
||||
struct util_avl_struct **nodeplace,
|
||||
size_t lrheight)
|
||||
{
|
||||
axis->avl_left = lrchild;
|
||||
lchild->avl_right = axis;
|
||||
|
||||
axis->height = lrheight + 1;
|
||||
lchild->height = axis->height + 1;
|
||||
|
||||
lchild->parent = axis->parent;
|
||||
axis->parent = lchild;
|
||||
|
||||
*nodeplace = lchild;
|
||||
if (lrchild != NULL)
|
||||
lrchild->parent = axis;
|
||||
}
|
||||
|
||||
static inline void midmount_right(struct util_avl_struct *axis,
|
||||
struct util_avl_struct *lchild,
|
||||
struct util_avl_struct *lrchild,
|
||||
struct util_avl_struct **nodeplace,
|
||||
size_t lrheight)
|
||||
{
|
||||
lchild->avl_right = lrchild->avl_left;
|
||||
axis->avl_left = lrchild->avl_right;
|
||||
lrchild->avl_left = lchild;
|
||||
lrchild->avl_right = axis;
|
||||
|
||||
lrchild->height = lchild->height;
|
||||
lchild->height = lrheight;
|
||||
axis->height = lrheight;
|
||||
|
||||
lrchild->parent = axis->parent;
|
||||
lchild->parent = lrchild;
|
||||
axis->parent = lrchild;
|
||||
if (lchild->avl_right != NULL)
|
||||
lchild->avl_right->parent = lchild;
|
||||
if (axis->avl_left != NULL)
|
||||
axis->avl_left->parent = axis;
|
||||
*nodeplace = lrchild;
|
||||
}
|
||||
|
||||
static inline void rotate_left(struct util_avl_struct *axis,
|
||||
struct util_avl_struct *rchild,
|
||||
struct util_avl_struct *rlchild,
|
||||
struct util_avl_struct **nodeplace,
|
||||
size_t rlheight)
|
||||
{
|
||||
axis->avl_right = rlchild;
|
||||
rchild->avl_left = axis;
|
||||
|
||||
axis->height = rlheight + 1;
|
||||
rchild->height = axis->height + 1;
|
||||
|
||||
rchild->parent = axis->parent;
|
||||
axis->parent = rchild;
|
||||
|
||||
*nodeplace = rchild;
|
||||
if (rlchild != NULL)
|
||||
rlchild->parent = axis;
|
||||
}
|
||||
|
||||
static inline void midmount_left(struct util_avl_struct *axis,
|
||||
struct util_avl_struct *rchild,
|
||||
struct util_avl_struct *rlchild,
|
||||
struct util_avl_struct **nodeplace,
|
||||
size_t rlheight)
|
||||
{
|
||||
rchild->avl_left = rlchild->avl_right;
|
||||
axis->avl_right = rlchild->avl_left;
|
||||
rlchild->avl_right = rchild;
|
||||
rlchild->avl_left = axis;
|
||||
|
||||
rlchild->height = rchild->height;
|
||||
rchild->height = rlheight;
|
||||
axis->height = rlheight;
|
||||
|
||||
rlchild->parent = axis->parent;
|
||||
rchild->parent = rlchild;
|
||||
axis->parent = rlchild;
|
||||
if (rchild->avl_left != NULL)
|
||||
rchild->avl_left->parent = rchild;
|
||||
if (axis->avl_right != NULL)
|
||||
axis->avl_right->parent = axis;
|
||||
|
||||
*nodeplace = rlchild;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief avl insertion & delete conceptually contain 2 stage
|
||||
* 1. insertion/delete of reference
|
||||
* 2. rebalance
|
||||
*/
|
||||
|
||||
void util_avl_rebalance(struct util_avl_struct *node,
|
||||
struct util_avl_root *root)
|
||||
{
|
||||
if (!node)
|
||||
return;
|
||||
|
||||
struct util_avl_struct *axis = node;
|
||||
struct util_avl_struct **nodeplace;
|
||||
do
|
||||
{
|
||||
struct util_avl_struct *lchild = axis->avl_left;
|
||||
struct util_avl_struct *rchild = axis->avl_right;
|
||||
nodeplace = axis->parent ? NODE_PLACE(axis) : &root->root_node;
|
||||
int lheight = HEIGHT_OF(lchild);
|
||||
int rheight = HEIGHT_OF(rchild);
|
||||
if (rheight + 1 < lheight)
|
||||
{
|
||||
struct util_avl_struct *lrchild = lchild->avl_right;
|
||||
size_t lrheight = HEIGHT_OF(lrchild);
|
||||
if (HEIGHT_OF(lchild->avl_left) >= lrheight)
|
||||
{
|
||||
rotate_right(axis, lchild, lrchild, nodeplace, lrheight);
|
||||
axis = lchild->parent;
|
||||
}
|
||||
else
|
||||
{
|
||||
midmount_right(axis, lchild, lrchild, nodeplace, lrheight);
|
||||
axis = lrchild->parent;
|
||||
}
|
||||
}
|
||||
else if (lheight + 1 < rheight)
|
||||
{
|
||||
struct util_avl_struct *rlchild = rchild->avl_left;
|
||||
size_t rlheight = HEIGHT_OF(rlchild);
|
||||
if (HEIGHT_OF(rchild->avl_right) >= rlheight)
|
||||
{
|
||||
rotate_left(axis, rchild, rlchild, nodeplace, rlheight);
|
||||
axis = rchild->parent;
|
||||
}
|
||||
else
|
||||
{
|
||||
midmount_left(axis, rchild, rlchild, nodeplace, rlheight);
|
||||
axis = rlchild->parent;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
int height = (lheight < rheight ? rheight : lheight) + 1;
|
||||
if (height == axis->height)
|
||||
break;
|
||||
axis->height = height;
|
||||
axis = axis->parent;
|
||||
}
|
||||
} while (axis);
|
||||
}
|
||||
|
||||
void util_avl_remove(struct util_avl_struct *node, struct util_avl_root *root)
|
||||
{
|
||||
struct util_avl_struct **nodeplace;
|
||||
|
||||
if (root->root_node == NULL)
|
||||
return;
|
||||
|
||||
if (node->parent != NULL)
|
||||
{
|
||||
nodeplace = NODE_PLACE(node);
|
||||
}
|
||||
else
|
||||
{
|
||||
nodeplace = &root->root_node;
|
||||
}
|
||||
|
||||
/* deletion */
|
||||
if (node->avl_right == NULL)
|
||||
{
|
||||
*nodeplace = node->avl_left;
|
||||
if (node->avl_left != NULL)
|
||||
node->avl_left->parent = node->parent;
|
||||
node = node->parent;
|
||||
}
|
||||
else
|
||||
{
|
||||
struct util_avl_struct *rchild = node->avl_right;
|
||||
if (rchild->avl_left == NULL)
|
||||
{
|
||||
*nodeplace = rchild;
|
||||
rchild->avl_left = node->avl_left;
|
||||
if (rchild->avl_left != NULL)
|
||||
rchild->avl_left->parent = rchild;
|
||||
rchild->parent = node->parent;
|
||||
util_avl_rebalance(rchild, root);
|
||||
node = rchild->parent;
|
||||
}
|
||||
else
|
||||
{
|
||||
struct util_avl_struct *successor = rchild->avl_left;
|
||||
struct util_avl_struct *sparent = rchild;
|
||||
while (successor->avl_left != NULL)
|
||||
{
|
||||
sparent = successor;
|
||||
successor = successor->avl_left;
|
||||
}
|
||||
*nodeplace = successor;
|
||||
sparent->avl_left = successor->avl_right;
|
||||
successor->avl_left = node->avl_left;
|
||||
successor->avl_right = node->avl_right;
|
||||
|
||||
if (successor->avl_left != NULL)
|
||||
successor->avl_left->parent = successor;
|
||||
successor->avl_right->parent = successor;
|
||||
|
||||
if (sparent->avl_left != NULL)
|
||||
sparent->avl_left->parent = sparent;
|
||||
successor->parent = node->parent;
|
||||
util_avl_rebalance(sparent, root);
|
||||
node = successor;
|
||||
}
|
||||
}
|
||||
|
||||
/* rebalance */
|
||||
util_avl_rebalance(node, root);
|
||||
return;
|
||||
}
|
116
rt-thread/components/utilities/libadt/avl/avl.h
Normal file
116
rt-thread/components/utilities/libadt/avl/avl.h
Normal file
@@ -0,0 +1,116 @@
|
||||
/*
|
||||
* Copyright (c) 2006-2020, RT-Thread Development Team
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*
|
||||
* Change Logs:
|
||||
* Date Author Notes
|
||||
* 2019-10-12 Jesven first version
|
||||
* 2022-11-14 WangXiaoyao Optimize for generic use case
|
||||
* and performance
|
||||
*/
|
||||
#ifndef __UTIL_TREE_AVL_H__
|
||||
#define __UTIL_TREE_AVL_H__
|
||||
|
||||
#include <rtdef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
struct util_avl_struct
|
||||
{
|
||||
struct util_avl_struct *avl_left;
|
||||
struct util_avl_struct *avl_right;
|
||||
struct util_avl_struct *parent;
|
||||
size_t height;
|
||||
};
|
||||
|
||||
#define AVL_ROOT ((struct util_avl_struct *)0)
|
||||
|
||||
struct util_avl_root
|
||||
{
|
||||
struct util_avl_struct *root_node;
|
||||
};
|
||||
|
||||
void util_avl_rebalance(struct util_avl_struct *node,
|
||||
struct util_avl_root *root);
|
||||
|
||||
void util_avl_remove(struct util_avl_struct *node, struct util_avl_root *root);
|
||||
|
||||
static inline void util_avl_link(struct util_avl_struct *new_node,
|
||||
struct util_avl_struct *parent,
|
||||
struct util_avl_struct **nodeplace)
|
||||
{
|
||||
new_node->avl_left = AVL_ROOT;
|
||||
new_node->avl_right = AVL_ROOT;
|
||||
new_node->parent = parent;
|
||||
new_node->height = 1;
|
||||
*nodeplace = new_node;
|
||||
}
|
||||
|
||||
static inline struct util_avl_struct *util_avl_next(
|
||||
struct util_avl_struct *node)
|
||||
{
|
||||
struct util_avl_struct *successor = 0;
|
||||
if (node)
|
||||
{
|
||||
if (node->avl_right)
|
||||
{
|
||||
node = node->avl_right;
|
||||
while (node->avl_left)
|
||||
node = node->avl_left;
|
||||
successor = node;
|
||||
}
|
||||
else
|
||||
{
|
||||
while ((successor = node->parent) && (node == successor->avl_right))
|
||||
node = successor;
|
||||
}
|
||||
}
|
||||
return successor;
|
||||
}
|
||||
|
||||
static inline struct util_avl_struct *util_avl_prev(
|
||||
struct util_avl_struct *node)
|
||||
{
|
||||
struct util_avl_struct *predecessor = 0;
|
||||
if (node)
|
||||
{
|
||||
if (node->avl_left)
|
||||
{
|
||||
node = node->avl_left;
|
||||
while (node->avl_right)
|
||||
node = node->avl_right;
|
||||
predecessor = node;
|
||||
}
|
||||
else
|
||||
{
|
||||
while ((predecessor = node->parent) &&
|
||||
(node == predecessor->avl_left))
|
||||
node = predecessor;
|
||||
}
|
||||
}
|
||||
return predecessor;
|
||||
}
|
||||
|
||||
static inline struct util_avl_struct *util_avl_first(struct util_avl_root *root)
|
||||
{
|
||||
struct util_avl_struct *first = root->root_node;
|
||||
if (first)
|
||||
{
|
||||
while (first->avl_left)
|
||||
first = first->avl_left;
|
||||
}
|
||||
return first;
|
||||
}
|
||||
|
||||
static inline struct util_avl_struct *util_avl_last(struct util_avl_root *root)
|
||||
{
|
||||
struct util_avl_struct *last = root->root_node;
|
||||
if (last)
|
||||
{
|
||||
while (last->avl_right)
|
||||
last = last->avl_right;
|
||||
}
|
||||
return last;
|
||||
}
|
||||
|
||||
#endif /* __UTIL_TREE_AVL_H__ */
|
9
rt-thread/components/utilities/libadt/bitmap/SConscript
Normal file
9
rt-thread/components/utilities/libadt/bitmap/SConscript
Normal file
@@ -0,0 +1,9 @@
|
||||
from building import *
|
||||
|
||||
cwd = GetCurrentDir()
|
||||
src = list()
|
||||
CPPPATH = [cwd]
|
||||
group = []
|
||||
|
||||
group = DefineGroup('LIBADT', src, depend = ['RT_USING_ADT_BITMAP'], CPPPATH = CPPPATH)
|
||||
Return('group')
|
77
rt-thread/components/utilities/libadt/bitmap/bitmap.h
Normal file
77
rt-thread/components/utilities/libadt/bitmap/bitmap.h
Normal file
@@ -0,0 +1,77 @@
|
||||
/*
|
||||
* Copyright (c) 2006-2022, RT-Thread Development Team
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*
|
||||
* Change Logs:
|
||||
* Date Author Notes
|
||||
* 2022-6-27 GuEe-GUI first version
|
||||
*/
|
||||
|
||||
#ifndef __UTIL_BITMAP_H__
|
||||
#define __UTIL_BITMAP_H__
|
||||
|
||||
#include <rtdef.h>
|
||||
|
||||
typedef rt_ubase_t rt_bitmap_t;
|
||||
|
||||
#define RT_BITMAP_BITS_MIN (sizeof(rt_bitmap_t) * 8)
|
||||
#define RT_BITMAP_LEN(bits) (((bits) + (RT_BITMAP_BITS_MIN) - 1) / (RT_BITMAP_BITS_MIN))
|
||||
#define RT_BITMAP_BIT_LEN(nr) (nr * RT_BITMAP_BITS_MIN)
|
||||
#define RT_BITMAP_DECLARE(name, bits) rt_bitmap_t name[RT_BITMAP_LEN(bits)]
|
||||
|
||||
rt_inline void rt_bitmap_set_bit(rt_bitmap_t *bitmap, rt_uint32_t bit)
|
||||
{
|
||||
bitmap[bit / RT_BITMAP_BITS_MIN] |= (1UL << (bit & (RT_BITMAP_BITS_MIN - 1)));
|
||||
}
|
||||
|
||||
rt_inline rt_bool_t rt_bitmap_test_bit(rt_bitmap_t *bitmap, rt_uint32_t bit)
|
||||
{
|
||||
return !!(bitmap[bit / RT_BITMAP_BITS_MIN] & (1UL << (bit & (RT_BITMAP_BITS_MIN - 1))));
|
||||
}
|
||||
|
||||
rt_inline void rt_bitmap_clear_bit(rt_bitmap_t *bitmap, rt_uint32_t bit)
|
||||
{
|
||||
bitmap[bit / RT_BITMAP_BITS_MIN] &= ~(1UL << (bit & (RT_BITMAP_BITS_MIN - 1)));
|
||||
}
|
||||
|
||||
rt_inline rt_size_t rt_bitmap_next_set_bit(rt_bitmap_t *bitmap, rt_size_t start, rt_size_t limit)
|
||||
{
|
||||
rt_size_t bit;
|
||||
|
||||
for (bit = start; bit < limit && !rt_bitmap_test_bit(bitmap, bit); ++bit)
|
||||
{
|
||||
}
|
||||
|
||||
return bit;
|
||||
}
|
||||
|
||||
rt_inline rt_size_t rt_bitmap_next_clear_bit(rt_bitmap_t *bitmap, rt_size_t start, rt_size_t limit)
|
||||
{
|
||||
rt_size_t bit;
|
||||
|
||||
for (bit = start; bit < limit && rt_bitmap_test_bit(bitmap, bit); ++bit)
|
||||
{
|
||||
}
|
||||
|
||||
return bit;
|
||||
}
|
||||
|
||||
#define rt_bitmap_for_each_bit_from(state, bitmap, from, bit, limit) \
|
||||
for ((bit) = rt_bitmap_next_##state##_bit((bitmap), (from), (limit)); \
|
||||
(bit) < (limit); \
|
||||
(bit) = rt_bitmap_next_##state##_bit((bitmap), (bit + 1), (limit)))
|
||||
|
||||
#define rt_bitmap_for_each_set_bit_from(bitmap, from, bit, limit) \
|
||||
rt_bitmap_for_each_bit_from(set, bitmap, from, bit, limit)
|
||||
|
||||
#define rt_bitmap_for_each_set_bit(bitmap, bit, limit) \
|
||||
rt_bitmap_for_each_set_bit_from(bitmap, 0, bit, limit)
|
||||
|
||||
#define rt_bitmap_for_each_clear_bit_from(bitmap, from, bit, limit) \
|
||||
rt_bitmap_for_each_bit_from(clear, bitmap, from, bit, limit)
|
||||
|
||||
#define rt_bitmap_for_each_clear_bit(bitmap, bit, limit) \
|
||||
rt_bitmap_for_each_clear_bit_from(bitmap, 0, bit, limit)
|
||||
|
||||
#endif /* __UTIL_BITMAP_H__ */
|
9
rt-thread/components/utilities/libadt/hashmap/SConscript
Normal file
9
rt-thread/components/utilities/libadt/hashmap/SConscript
Normal file
@@ -0,0 +1,9 @@
|
||||
from building import *
|
||||
|
||||
cwd = GetCurrentDir()
|
||||
src = list()
|
||||
CPPPATH = [cwd]
|
||||
group = []
|
||||
|
||||
group = DefineGroup('LIBADT', src, depend = ['RT_USING_ADT_HASHMAP'], CPPPATH = CPPPATH)
|
||||
Return('group')
|
41
rt-thread/components/utilities/libadt/hashmap/hashmap.h
Normal file
41
rt-thread/components/utilities/libadt/hashmap/hashmap.h
Normal file
@@ -0,0 +1,41 @@
|
||||
/*
|
||||
* Copyright (c) 2006-2022, RT-Thread Development Team
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*
|
||||
* Change Logs:
|
||||
* Date Author Notes
|
||||
* 2022-08-01 GuEe-GUI first version
|
||||
*/
|
||||
|
||||
#ifndef __UTIL_HASHMAP_H__
|
||||
#define __UTIL_HASHMAP_H__
|
||||
|
||||
#include <rtdef.h>
|
||||
|
||||
/*
|
||||
* http://www.citi.umich.edu/techreports/reports/citi-tr-00-1.pdf
|
||||
*
|
||||
* GoldenRatio = ~(Math.pow(2, 32) / ((Math.sqrt(5) - 1) / 2)) + 1
|
||||
*/
|
||||
#define RT_HASHMAP_GOLDEN_RATIO_32 0x61C88647
|
||||
#define RT_HASHMAP_GOLDEN_RATIO_64 0X61C8864680B583EBULL
|
||||
|
||||
rt_inline rt_uint32_t rt_hashmap_32(rt_uint32_t val, rt_uint32_t bits)
|
||||
{
|
||||
/* High bits are more random, so use them. */
|
||||
return (val * RT_HASHMAP_GOLDEN_RATIO_32) >> (32 - bits);
|
||||
}
|
||||
|
||||
rt_inline rt_uint32_t rt_hashmap_64(rt_uint64_t val, rt_uint32_t bits)
|
||||
{
|
||||
#ifdef ARCH_CPU_64BIT
|
||||
/* 64x64-bit multiply is efficient on all 64-bit processors */
|
||||
return val * RT_HASHMAP_GOLDEN_RATIO_64 >> (64 - bits);
|
||||
#else
|
||||
/* Hash 64 bits using only 32x32-bit multiply. */
|
||||
return rt_hashmap_32((rt_uint32_t)val ^ ((val >> 32) * RT_HASHMAP_GOLDEN_RATIO_32), bits);
|
||||
#endif
|
||||
}
|
||||
|
||||
#endif /* __UTIL_HASHMAP_H__ */
|
9
rt-thread/components/utilities/libadt/ref/SConscript
Normal file
9
rt-thread/components/utilities/libadt/ref/SConscript
Normal file
@@ -0,0 +1,9 @@
|
||||
from building import *
|
||||
|
||||
cwd = GetCurrentDir()
|
||||
src = list()
|
||||
CPPPATH = [cwd]
|
||||
group = []
|
||||
|
||||
group = DefineGroup('LIBADT', src, depend = ['RT_USING_ADT_REF'], CPPPATH = CPPPATH)
|
||||
Return('group')
|
75
rt-thread/components/utilities/libadt/ref/ref.h
Normal file
75
rt-thread/components/utilities/libadt/ref/ref.h
Normal file
@@ -0,0 +1,75 @@
|
||||
/*
|
||||
* Copyright (c) 2006-2023, RT-Thread Development Team
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*
|
||||
* Change Logs:
|
||||
* Date Author Notes
|
||||
* 2022-3-1 zhouxiaohu first version
|
||||
* 2023-5-18 GuEe-GUI implemented by rtatomic
|
||||
*/
|
||||
|
||||
#ifndef __UTIL_REF_H__
|
||||
#define __UTIL_REF_H__
|
||||
|
||||
#include <rtatomic.h>
|
||||
|
||||
/**
|
||||
* struct ref must be embedded in an object.
|
||||
* it acts as a reference counter for the object.
|
||||
*/
|
||||
struct rt_ref
|
||||
{
|
||||
rt_atomic_t refcount;
|
||||
};
|
||||
|
||||
#define RT_REF_INIT(n) { .refcount = n, }
|
||||
|
||||
rt_inline void rt_ref_init(struct rt_ref *r)
|
||||
{
|
||||
rt_atomic_store(&r->refcount, 1);
|
||||
}
|
||||
|
||||
rt_inline unsigned int rt_ref_read(struct rt_ref *r)
|
||||
{
|
||||
return rt_atomic_load(&r->refcount);
|
||||
}
|
||||
|
||||
/**
|
||||
* ref_get
|
||||
* increment reference counter for object.
|
||||
*/
|
||||
rt_inline void rt_ref_get(struct rt_ref *r)
|
||||
{
|
||||
rt_atomic_add(&r->refcount, 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* ref_put
|
||||
* decrement reference counter for object.
|
||||
* If the reference counter is zero, call release().
|
||||
*
|
||||
* Return 1 means the object's reference counter is zero and release() is called.
|
||||
*/
|
||||
rt_inline int rt_ref_put(struct rt_ref *r, void (*release)(struct rt_ref *r))
|
||||
{
|
||||
if (rt_atomic_dec_and_test(&r->refcount))
|
||||
{
|
||||
release(r);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* ref_get_unless_zero - Increment refcount for object unless it is zero.
|
||||
* Return non-zero if the increment succeeded. Otherwise return 0.
|
||||
*/
|
||||
rt_inline int rt_ref_get_unless_zero(struct rt_ref *r)
|
||||
{
|
||||
return (int)rt_atomic_inc_not_zero(&r->refcount);
|
||||
}
|
||||
|
||||
#endif /* __UTIL_REF_H__ */
|
9
rt-thread/components/utilities/libadt/uthash/SConscript
Normal file
9
rt-thread/components/utilities/libadt/uthash/SConscript
Normal file
@@ -0,0 +1,9 @@
|
||||
from building import *
|
||||
|
||||
cwd = GetCurrentDir()
|
||||
src = Glob('*.c')
|
||||
CPPPATH = [cwd]
|
||||
group = []
|
||||
|
||||
group = DefineGroup('LIBADT', src, depend = [], CPPPATH = CPPPATH)
|
||||
Return('group')
|
16
rt-thread/components/utilities/libadt/uthash/dict.h
Normal file
16
rt-thread/components/utilities/libadt/uthash/dict.h
Normal file
@@ -0,0 +1,16 @@
|
||||
/*
|
||||
* Copyright (c) 2006-2023, RT-Thread Development Team
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*
|
||||
* Change Logs:
|
||||
* Date Author Notes
|
||||
* 2023-11-01 Shell Init ver.
|
||||
*/
|
||||
|
||||
#ifndef __LIBADT_DICT_H__
|
||||
#define __LIBADT_DICT_H__
|
||||
|
||||
#include "rt_uthash.h"
|
||||
|
||||
#endif
|
57
rt-thread/components/utilities/libadt/uthash/rt_uthash.h
Normal file
57
rt-thread/components/utilities/libadt/uthash/rt_uthash.h
Normal file
@@ -0,0 +1,57 @@
|
||||
/*
|
||||
* Copyright (c) 2006-2023, RT-Thread Development Team
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*
|
||||
* Change Logs:
|
||||
* Date Author Notes
|
||||
* 2023-11-01 Shell Porting to RTT API
|
||||
*/
|
||||
#ifndef __LIBADT_RT_UTHASH_H__
|
||||
#define __LIBADT_RT_UTHASH_H__
|
||||
|
||||
#include <rtthread.h>
|
||||
|
||||
#define uthash_malloc(sz) rt_malloc(sz)
|
||||
#define uthash_free(ptr, sz) rt_free(ptr)
|
||||
|
||||
/**
|
||||
* for performance consideration, using libc implementations
|
||||
* as the default case. If you care about the compatibility
|
||||
* problem, define the RT_UTHASH_CONFIG_COMPATIBILITY_FIRST
|
||||
* before including the rt_uthash.h.
|
||||
*/
|
||||
#ifndef RT_UTHASH_CONFIG_COMPATIBILITY_FIRST
|
||||
#define uthash_bzero(a, n) memset(a, '\0', n)
|
||||
#define uthash_strlen(s) strlen(s)
|
||||
|
||||
#else
|
||||
#define uthash_bzero(a, n) rt_memset(a, '\0', n)
|
||||
#define uthash_strlen(s) rt_strlen(s)
|
||||
|
||||
#endif /* RT_UTHASH_CONFIG_COMPATIBILITY_FIRST */
|
||||
|
||||
/* if any fatal happen, throw an exception and return a failure */
|
||||
#define uthash_fatal(msg) \
|
||||
do \
|
||||
{ \
|
||||
LOG_E(msg); \
|
||||
return -RT_ENOMEM; \
|
||||
} while (0)
|
||||
|
||||
#include "uthash.h"
|
||||
|
||||
#define DEFINE_RT_UTHASH_TYPE(entry_name, key_type, key_name) \
|
||||
typedef struct entry_name \
|
||||
{ \
|
||||
key_type key_name; \
|
||||
UT_hash_handle hh; \
|
||||
} *entry_name##_t;
|
||||
|
||||
#define RT_UTHASH_ADD(head, key_member, keylen_in, value) \
|
||||
HASH_ADD(hh, head, key_member, keylen_in, value)
|
||||
#define RT_UTHASH_FIND(head, key_ptr, keylen_in, pval) \
|
||||
HASH_FIND(hh, head, key_ptr, keylen_in, pval)
|
||||
#define RT_UTHASH_DELETE(head, pobj) HASH_DELETE(hh, head, pobj)
|
||||
|
||||
#endif /* __LIBADT_RT_UTHASH_H__ */
|
1140
rt-thread/components/utilities/libadt/uthash/uthash.h
Normal file
1140
rt-thread/components/utilities/libadt/uthash/uthash.h
Normal file
File diff suppressed because it is too large
Load Diff
12
rt-thread/components/utilities/resource/SConscript
Normal file
12
rt-thread/components/utilities/resource/SConscript
Normal file
@@ -0,0 +1,12 @@
|
||||
from building import *
|
||||
|
||||
cwd = GetCurrentDir()
|
||||
src = Glob('*.c')
|
||||
CPPPATH = [cwd]
|
||||
|
||||
if GetDepend('RT_USING_ADT_BITMAP') == False:
|
||||
SrcRemove(src, ['rid_bitmap.c'])
|
||||
|
||||
group = DefineGroup('Utilities', src, depend = ['RT_USING_RESOURCE_ID'], CPPPATH = CPPPATH)
|
||||
|
||||
Return('group')
|
63
rt-thread/components/utilities/resource/resource_id.c
Normal file
63
rt-thread/components/utilities/resource/resource_id.c
Normal file
@@ -0,0 +1,63 @@
|
||||
/*
|
||||
* Copyright (c) 2006-2021, RT-Thread Development Team
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*
|
||||
* Change Logs:
|
||||
* Date Author Notes
|
||||
* 2021-08-25 RT-Thread First version
|
||||
* 2023-09-15 xqyjlj perf rt_hw_interrupt_disable/enable
|
||||
*/
|
||||
#include <rthw.h>
|
||||
#include <rtthread.h>
|
||||
#include <resource_id.h>
|
||||
|
||||
void resource_id_init(resource_id_t *mgr, int size, void **res)
|
||||
{
|
||||
if (mgr)
|
||||
{
|
||||
mgr->size = size;
|
||||
mgr->_res = res;
|
||||
mgr->noused = 0;
|
||||
mgr->_free = RT_NULL;
|
||||
rt_spin_lock_init(&(mgr->spinlock));
|
||||
}
|
||||
}
|
||||
|
||||
int resource_id_get(resource_id_t *mgr)
|
||||
{
|
||||
rt_base_t level;
|
||||
void **cur;
|
||||
|
||||
level = rt_spin_lock_irqsave(&(mgr->spinlock));
|
||||
if (mgr->_free)
|
||||
{
|
||||
cur = mgr->_free;
|
||||
mgr->_free = (void **)*mgr->_free;
|
||||
rt_spin_unlock_irqrestore(&(mgr->spinlock), level);
|
||||
return cur - mgr->_res;
|
||||
}
|
||||
else if (mgr->noused < mgr->size)
|
||||
{
|
||||
cur = &mgr->_res[mgr->noused++];
|
||||
rt_spin_unlock_irqrestore(&(mgr->spinlock), level);
|
||||
return cur - mgr->_res;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
void resource_id_put(resource_id_t *mgr, int no)
|
||||
{
|
||||
rt_base_t level;
|
||||
void **cur;
|
||||
|
||||
if (no >= 0 && no < mgr->size)
|
||||
{
|
||||
level = rt_spin_lock_irqsave(&(mgr->spinlock));
|
||||
cur = &mgr->_res[no];
|
||||
*cur = (void *)mgr->_free;
|
||||
mgr->_free = cur;
|
||||
rt_spin_unlock_irqrestore(&(mgr->spinlock), level);
|
||||
}
|
||||
}
|
33
rt-thread/components/utilities/resource/resource_id.h
Normal file
33
rt-thread/components/utilities/resource/resource_id.h
Normal file
@@ -0,0 +1,33 @@
|
||||
/*
|
||||
* Copyright (c) 2006-2021, RT-Thread Development Team
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*
|
||||
* Change Logs:
|
||||
* Date Author Notes
|
||||
* 2021-08-25 RT-Thread First version
|
||||
* 2023-09-15 xqyjlj perf rt_hw_interrupt_disable/enable
|
||||
*/
|
||||
|
||||
#ifndef RESOURCE_ID_H__
|
||||
#define RESOURCE_ID_H__
|
||||
|
||||
#include <rthw.h>
|
||||
#include <rtthread.h>
|
||||
|
||||
#define RESOURCE_ID_INIT(size, pool) {size, pool, 0, RT_NULL, RT_SPINLOCK_INIT}
|
||||
|
||||
typedef struct
|
||||
{
|
||||
int size;
|
||||
void **_res;
|
||||
int noused;
|
||||
void **_free;
|
||||
struct rt_spinlock spinlock;
|
||||
} resource_id_t;
|
||||
|
||||
void resource_id_init(resource_id_t *mgr, int size, void **res);
|
||||
int resource_id_get(resource_id_t *mgr);
|
||||
void resource_id_put(resource_id_t *mgr, int no);
|
||||
|
||||
#endif /*RESOURCE_ID_H__*/
|
116
rt-thread/components/utilities/resource/rid_bitmap.c
Normal file
116
rt-thread/components/utilities/resource/rid_bitmap.c
Normal file
@@ -0,0 +1,116 @@
|
||||
/*
|
||||
* Copyright (c) 2006-2023, RT-Thread Development Team
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*
|
||||
* Change Logs:
|
||||
* Date Author Notes
|
||||
* 2023-12-07 Shell First version
|
||||
*/
|
||||
|
||||
#include "rid_bitmap.h"
|
||||
#include <bitmap.h>
|
||||
#include <rtdef.h>
|
||||
|
||||
void rid_bitmap_init(rid_bitmap_t mgr, int min_id, int total_id_count,
|
||||
rt_bitmap_t *set, struct rt_mutex *id_lock)
|
||||
{
|
||||
mgr->min_id = min_id;
|
||||
mgr->total_id_count = total_id_count;
|
||||
mgr->bitset = set;
|
||||
mgr->id_lock = id_lock;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
long rid_bitmap_get(rid_bitmap_t mgr)
|
||||
{
|
||||
long id;
|
||||
long overflow;
|
||||
if (mgr->id_lock)
|
||||
{
|
||||
rt_mutex_take(mgr->id_lock, RT_WAITING_FOREVER);
|
||||
}
|
||||
|
||||
overflow = mgr->total_id_count;
|
||||
id = rt_bitmap_next_clear_bit(mgr->bitset, 0, overflow);
|
||||
if (id == overflow)
|
||||
{
|
||||
id = -1;
|
||||
}
|
||||
else
|
||||
{
|
||||
rt_bitmap_set_bit(mgr->bitset, id);
|
||||
id += mgr->min_id;
|
||||
}
|
||||
|
||||
if (mgr->id_lock)
|
||||
{
|
||||
rt_mutex_release(mgr->id_lock);
|
||||
}
|
||||
return id;
|
||||
}
|
||||
|
||||
long rid_bitmap_get_named(rid_bitmap_t mgr, long no)
|
||||
{
|
||||
long id_relative;
|
||||
long overflow;
|
||||
long min;
|
||||
|
||||
if (mgr->id_lock)
|
||||
{
|
||||
rt_mutex_take(mgr->id_lock, RT_WAITING_FOREVER);
|
||||
}
|
||||
|
||||
min = mgr->min_id;
|
||||
id_relative = no - min;
|
||||
overflow = mgr->total_id_count;
|
||||
if (id_relative >= min && id_relative < overflow)
|
||||
{
|
||||
if (rt_bitmap_test_bit(mgr->bitset, id_relative))
|
||||
{
|
||||
id_relative = -RT_EBUSY;
|
||||
}
|
||||
else
|
||||
{
|
||||
rt_bitmap_set_bit(mgr->bitset, id_relative);
|
||||
id_relative += min;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
id_relative = -1;
|
||||
}
|
||||
|
||||
if (mgr->id_lock)
|
||||
{
|
||||
rt_mutex_release(mgr->id_lock);
|
||||
}
|
||||
return id_relative;
|
||||
}
|
||||
|
||||
void rid_bitmap_put(rid_bitmap_t mgr, long no)
|
||||
{
|
||||
long id_relative;
|
||||
long overflow;
|
||||
long min;
|
||||
|
||||
if (mgr->id_lock)
|
||||
{
|
||||
rt_mutex_take(mgr->id_lock, RT_WAITING_FOREVER);
|
||||
}
|
||||
|
||||
min = mgr->min_id;
|
||||
id_relative = no - min;
|
||||
overflow = mgr->total_id_count;
|
||||
if (id_relative >= min && id_relative < overflow &&
|
||||
rt_bitmap_test_bit(mgr->bitset, id_relative))
|
||||
{
|
||||
rt_bitmap_clear_bit(mgr->bitset, id_relative);
|
||||
}
|
||||
|
||||
if (mgr->id_lock)
|
||||
{
|
||||
rt_mutex_release(mgr->id_lock);
|
||||
}
|
||||
}
|
34
rt-thread/components/utilities/resource/rid_bitmap.h
Normal file
34
rt-thread/components/utilities/resource/rid_bitmap.h
Normal file
@@ -0,0 +1,34 @@
|
||||
/*
|
||||
* Copyright (c) 2006-2023, RT-Thread Development Team
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*
|
||||
* Change Logs:
|
||||
* Date Author Notes
|
||||
* 2023-12-07 Shell First version
|
||||
*/
|
||||
|
||||
#ifndef __RID_BITMAP_H__
|
||||
#define __RID_BITMAP_H__
|
||||
|
||||
#include <rthw.h>
|
||||
#include <rtthread.h>
|
||||
#include <bitmap.h>
|
||||
|
||||
#define RESOURCE_ID_INIT {0}
|
||||
|
||||
typedef struct rid_bitmap
|
||||
{
|
||||
struct rt_mutex *id_lock;
|
||||
long min_id;
|
||||
long total_id_count;
|
||||
rt_bitmap_t *bitset;
|
||||
} *rid_bitmap_t;
|
||||
|
||||
void rid_bitmap_init(rid_bitmap_t mgr, int min_id, int total_id_count,
|
||||
rt_bitmap_t *set, struct rt_mutex *id_lock);
|
||||
long rid_bitmap_get(rid_bitmap_t mgr);
|
||||
long rid_bitmap_get_named(rid_bitmap_t mgr, long no);
|
||||
void rid_bitmap_put(rid_bitmap_t mgr, long no);
|
||||
|
||||
#endif /* __RID_BITMAP_H__ */
|
25
rt-thread/components/utilities/rt-link/Kconfig
Normal file
25
rt-thread/components/utilities/rt-link/Kconfig
Normal file
@@ -0,0 +1,25 @@
|
||||
# Kconfig file for rt_link
|
||||
menuconfig RT_USING_RT_LINK
|
||||
bool "RT-Link"
|
||||
default n
|
||||
|
||||
if RT_USING_RT_LINK
|
||||
choice
|
||||
prompt"use hw crc device or not"
|
||||
default RT_LINK_USING_SF_CRC
|
||||
|
||||
config RT_LINK_USING_SF_CRC
|
||||
bool "use software crc table"
|
||||
config RT_LINK_USING_HW_CRC
|
||||
bool "use hardware crc device"
|
||||
endchoice
|
||||
|
||||
menu "rt link debug option"
|
||||
config USING_RT_LINK_DEBUG
|
||||
bool "Enable RT-Link debug"
|
||||
default n
|
||||
config USING_RT_LINK_HW_DEBUG
|
||||
bool "Enable RT-Link hw debug"
|
||||
default n
|
||||
endmenu
|
||||
endif
|
15
rt-thread/components/utilities/rt-link/SConscript
Normal file
15
rt-thread/components/utilities/rt-link/SConscript
Normal file
@@ -0,0 +1,15 @@
|
||||
# RT-Thread building script for bridge
|
||||
|
||||
import os
|
||||
from building import *
|
||||
|
||||
cwd = GetCurrentDir()
|
||||
objs = []
|
||||
list = os.listdir(cwd)
|
||||
|
||||
for d in list:
|
||||
path = os.path.join(cwd, d)
|
||||
if os.path.isfile(os.path.join(path, 'SConscript')):
|
||||
objs = objs + SConscript(os.path.join(d, 'SConscript'))
|
||||
|
||||
Return('objs')
|
210
rt-thread/components/utilities/rt-link/inc/rtlink.h
Normal file
210
rt-thread/components/utilities/rt-link/inc/rtlink.h
Normal file
@@ -0,0 +1,210 @@
|
||||
/*
|
||||
* Copyright (c) 2006-2021, RT-Thread Development Team
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*
|
||||
* Change Logs:
|
||||
* Date Author Notes
|
||||
* 2021-02-02 xiangxistu the first version
|
||||
* 2021-03-19 Sherman Streamline the struct rt_link_session
|
||||
*/
|
||||
|
||||
#ifndef __RT_LINK_H__
|
||||
#define __RT_LINK_H__
|
||||
|
||||
#include <rtdef.h>
|
||||
|
||||
#define RT_LINK_VER "0.2.0"
|
||||
|
||||
#define RT_LINK_AUTO_INIT
|
||||
|
||||
#define RT_LINK_FLAG_ACK 0x01U
|
||||
#define RT_LINK_FLAG_CRC 0x02U
|
||||
|
||||
#define RT_LINK_FRAME_HEAD 0x15U
|
||||
#define RT_LINK_FRAME_HEAD_MASK 0x1FU
|
||||
/* The maximum number of split frames for a long package */
|
||||
#define RT_LINK_FRAMES_MAX 0x03U
|
||||
/* The length in the rt_link_frame_head structure occupies 11 bits,
|
||||
so the value range after 4-byte alignment is 0-2044.*/
|
||||
#define RT_LINK_MAX_FRAME_LENGTH 1024U
|
||||
|
||||
#define RT_LINK_ACK_MAX 0x07U
|
||||
#define RT_LINK_CRC_LENGTH 4U
|
||||
#define RT_LINK_HEAD_LENGTH 4U
|
||||
#define RT_LINK_EXTEND_LENGTH 4U
|
||||
|
||||
#define RT_LINK_MAX_DATA_LENGTH (RT_LINK_MAX_FRAME_LENGTH - \
|
||||
RT_LINK_HEAD_LENGTH - \
|
||||
RT_LINK_EXTEND_LENGTH - \
|
||||
RT_LINK_CRC_LENGTH)
|
||||
#define RT_LINK_RECEIVE_BUFFER_LENGTH (RT_LINK_MAX_FRAME_LENGTH * \
|
||||
RT_LINK_FRAMES_MAX + \
|
||||
RT_LINK_HEAD_LENGTH + \
|
||||
RT_LINK_EXTEND_LENGTH)
|
||||
|
||||
typedef enum
|
||||
{
|
||||
RT_LINK_SERVICE_RTLINK = 0,
|
||||
RT_LINK_SERVICE_SOCKET = 1,
|
||||
RT_LINK_SERVICE_WIFI = 2,
|
||||
RT_LINK_SERVICE_MNGT = 3,
|
||||
RT_LINK_SERVICE_MSHTOOLS = 4,
|
||||
|
||||
/* Expandable to a maximum of 31 */
|
||||
RT_LINK_SERVICE_MAX
|
||||
} rt_link_service_e;
|
||||
|
||||
typedef enum
|
||||
{
|
||||
RT_LINK_RESEND_FRAME = 0,
|
||||
RT_LINK_CONFIRM_FRAME = 1,
|
||||
|
||||
RT_LINK_HANDSHAKE_FRAME = 2,
|
||||
RT_LINK_DETACH_FRAME = 3, /* service is not online */
|
||||
RT_LINK_SESSION_END = 4, /* The retring failed to end the session */
|
||||
|
||||
RT_LINK_LONG_DATA_FRAME = 5,
|
||||
RT_LINK_SHORT_DATA_FRAME = 6,
|
||||
|
||||
RT_LINK_RESERVE_FRAME = 7
|
||||
} rt_link_frame_attr_e;
|
||||
|
||||
typedef enum
|
||||
{
|
||||
/* receive event */
|
||||
RT_LINK_READ_CHECK_EVENT = 1 << 0,
|
||||
RT_LINK_RECV_TIMEOUT_FRAME_EVENT = 1 << 1,
|
||||
RT_LINK_RECV_TIMEOUT_LONG_EVENT = 1 << 2,
|
||||
|
||||
/* send event */
|
||||
RT_LINK_SEND_READY_EVENT = 1 << 4,
|
||||
RT_LINK_SEND_OK_EVENT = 1 << 5,
|
||||
RT_LINK_SEND_FAILED_EVENT = 1 << 6,
|
||||
RT_LINK_SEND_TIMEOUT_EVENT = 1 << 7
|
||||
} rt_link_notice_e;
|
||||
|
||||
typedef enum
|
||||
{
|
||||
RT_LINK_INIT = 0,
|
||||
RT_LINK_DISCONN = 1,
|
||||
RT_LINK_CONNECT = 2,
|
||||
} rt_link_linkstate_e;
|
||||
|
||||
typedef enum
|
||||
{
|
||||
RT_LINK_EOK = 0,
|
||||
RT_LINK_ERR = 1,
|
||||
RT_LINK_ETIMEOUT = 2,
|
||||
RT_LINK_EFULL = 3,
|
||||
RT_LINK_EEMPTY = 4,
|
||||
RT_LINK_ENOMEM = 5,
|
||||
RT_LINK_EIO = 6,
|
||||
RT_LINK_ESESSION = 7,
|
||||
RT_LINK_ESERVICE = 8,
|
||||
|
||||
RT_LINK_EMAX
|
||||
} rt_link_err_e;
|
||||
|
||||
struct rt_link_receive_buffer
|
||||
{
|
||||
/* rt-link receive data buffer */
|
||||
rt_uint8_t data[RT_LINK_RECEIVE_BUFFER_LENGTH];
|
||||
rt_uint8_t *read_point;
|
||||
rt_uint8_t *write_point;
|
||||
rt_uint8_t *end_point;
|
||||
};
|
||||
|
||||
struct rt_link_frame_head
|
||||
{
|
||||
rt_uint8_t magicid : 5;
|
||||
rt_uint8_t extend : 1;
|
||||
rt_uint8_t crc : 1;
|
||||
rt_uint8_t ack : 1;
|
||||
|
||||
rt_uint8_t sequence;
|
||||
rt_uint16_t service: 5;
|
||||
rt_uint16_t length : 11; /* range 0~2047 */
|
||||
};
|
||||
|
||||
/* record frame information that opposite */
|
||||
struct rt_link_record
|
||||
{
|
||||
rt_uint8_t rx_seq; /* record the opposite sequence */
|
||||
rt_uint8_t total; /* the number of long frame number */
|
||||
rt_uint8_t long_count; /* long packet recv counter */
|
||||
rt_uint8_t *dataspace; /* the space of long frame */
|
||||
};
|
||||
|
||||
struct rt_link_extend
|
||||
{
|
||||
rt_uint16_t attribute; /* rt_link_frame_attr_e */
|
||||
rt_uint16_t parameter;
|
||||
};
|
||||
|
||||
struct rt_link_frame
|
||||
{
|
||||
struct rt_link_frame_head head; /* frame head */
|
||||
struct rt_link_extend extend; /* frame extend data */
|
||||
rt_uint8_t *real_data; /* the origin data */
|
||||
rt_uint32_t crc; /* CRC result */
|
||||
|
||||
rt_uint16_t data_len; /* the length of frame length */
|
||||
rt_uint16_t attribute; /* rt_link_frame_attr_e */
|
||||
|
||||
rt_uint8_t issent;
|
||||
rt_uint8_t index; /* the index frame for long frame */
|
||||
rt_uint8_t total; /* the total frame for long frame */
|
||||
|
||||
rt_slist_t slist; /* the frame will hang on the send list on session */
|
||||
};
|
||||
|
||||
struct rt_link_service
|
||||
{
|
||||
rt_int32_t timeout_tx;
|
||||
void (*send_cb)(struct rt_link_service *service, void *buffer);
|
||||
void (*recv_cb)(struct rt_link_service *service, void *data, rt_size_t size);
|
||||
void *user_data;
|
||||
|
||||
rt_uint8_t flag; /* Whether to use the CRC and ACK */
|
||||
rt_link_service_e service;
|
||||
rt_link_linkstate_e state; /* channel link state */
|
||||
rt_link_err_e err;
|
||||
};
|
||||
|
||||
struct rt_link_session
|
||||
{
|
||||
struct rt_event event;
|
||||
struct rt_link_service *service[RT_LINK_SERVICE_MAX];
|
||||
|
||||
rt_uint8_t tx_seq; /* Sequence number of the send data frame */
|
||||
rt_slist_t tx_data_slist;
|
||||
rt_uint8_t sendbuffer[RT_LINK_MAX_FRAME_LENGTH];
|
||||
struct rt_event sendevent;
|
||||
struct rt_timer sendtimer;
|
||||
|
||||
struct rt_link_record rx_record; /* the memory of receive status */
|
||||
struct rt_timer recvtimer; /* receive a frame timer for rt link */
|
||||
struct rt_timer longframetimer; /* receive long frame timer for rt link */
|
||||
|
||||
struct rt_link_receive_buffer *rx_buffer;
|
||||
rt_uint32_t (*calculate_crc)(rt_uint8_t using_buffer_ring, rt_uint8_t *data, rt_size_t size);
|
||||
rt_link_linkstate_e state; /* Link status */
|
||||
};
|
||||
|
||||
#define SERV_ERR_GET(service) (service->err)
|
||||
|
||||
/* rtlink init and deinit, default is automatic initialization*/
|
||||
int rt_link_init(void);
|
||||
rt_err_t rt_link_deinit(void);
|
||||
|
||||
rt_size_t rt_link_send(struct rt_link_service *service, const void *data, rt_size_t size);
|
||||
|
||||
/* rtlink service attach and detach */
|
||||
rt_err_t rt_link_service_attach(struct rt_link_service *service);
|
||||
rt_err_t rt_link_service_detach(struct rt_link_service *service);
|
||||
|
||||
/* Private operator function */
|
||||
struct rt_link_session *rt_link_get_scb(void);
|
||||
|
||||
#endif /* __RT_LINK_H__ */
|
41
rt-thread/components/utilities/rt-link/inc/rtlink_dev.h
Normal file
41
rt-thread/components/utilities/rt-link/inc/rtlink_dev.h
Normal file
@@ -0,0 +1,41 @@
|
||||
/*
|
||||
* Copyright (c) 2006-2021, RT-Thread Development Team
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*
|
||||
* Change Logs:
|
||||
* Date Author Notes
|
||||
* 2021-06-15 Sherman the first version
|
||||
*/
|
||||
|
||||
#include <rtthread.h>
|
||||
#include <rtlink.h>
|
||||
|
||||
#define RT_LINK_RX_NONBLOCKING 0x1000
|
||||
#define RT_LINK_RX_BLOCKING 0x2000
|
||||
#define RT_LINK_TX_NONBLOCKING 0x4000
|
||||
#define RT_LINK_TX_BLOCKING 0x8000
|
||||
#define RT_LINK_DEVICE_MASK 0xf000
|
||||
|
||||
struct rtlink_recv_list
|
||||
{
|
||||
void *data;
|
||||
rt_size_t size;
|
||||
rt_size_t index;
|
||||
struct rt_slist_node list;
|
||||
};
|
||||
|
||||
struct rt_link_device
|
||||
{
|
||||
struct rt_device parent;
|
||||
struct rt_link_service service;
|
||||
struct rt_slist_node recv_head; /* recv data list, struct rtlink_recv_list */
|
||||
};
|
||||
|
||||
/*
|
||||
* rtlink device register
|
||||
*/
|
||||
rt_err_t rt_link_dev_register(struct rt_link_device *rtlink,
|
||||
const char *name,
|
||||
rt_uint32_t flag,
|
||||
void *data);
|
26
rt-thread/components/utilities/rt-link/inc/rtlink_hw.h
Normal file
26
rt-thread/components/utilities/rt-link/inc/rtlink_hw.h
Normal file
@@ -0,0 +1,26 @@
|
||||
/*
|
||||
* Copyright (c) 2006-2021, RT-Thread Development Team
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*
|
||||
* Change Logs:
|
||||
* Date Author Notes
|
||||
* 2021-02-02 xiangxistu the first version
|
||||
* 2021-07-13 Sherman add reconnect API
|
||||
*
|
||||
*/
|
||||
#ifndef __RT_LINK_HW_H__
|
||||
#define __RT_LINK_HW_H__
|
||||
|
||||
#include <rtdef.h>
|
||||
|
||||
rt_size_t rt_link_hw_recv_len(struct rt_link_receive_buffer *buffer);
|
||||
void rt_link_hw_copy(rt_uint8_t *dst, rt_uint8_t *src, rt_size_t count);
|
||||
void rt_link_hw_buffer_point_shift(rt_uint8_t **pointer_address, rt_size_t length);
|
||||
|
||||
rt_err_t rt_link_hw_init(void);
|
||||
rt_err_t rt_link_hw_deinit(void);
|
||||
rt_err_t rt_link_hw_reconnect(void);
|
||||
rt_size_t rt_link_hw_send(void *data, rt_size_t length);
|
||||
|
||||
#endif /* _RT_LINK_PORT_INTERNAL_H_ */
|
33
rt-thread/components/utilities/rt-link/inc/rtlink_port.h
Normal file
33
rt-thread/components/utilities/rt-link/inc/rtlink_port.h
Normal file
@@ -0,0 +1,33 @@
|
||||
/*
|
||||
* Copyright (c) 2006-2021, RT-Thread Development Team
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*
|
||||
* Change Logs:
|
||||
* Date Author Notes
|
||||
* 2021-02-02 xiangxistu the first version
|
||||
* 2021-05-15 Sherman function rename
|
||||
* 2021-07-13 Sherman add reconnect API
|
||||
*/
|
||||
#ifndef __RT_LINK_PORT_H__
|
||||
#define __RT_LINK_PORT_H__
|
||||
|
||||
#include <rtdef.h>
|
||||
|
||||
/* Functions that need to be implemented at the hardware */
|
||||
rt_err_t rt_link_port_init(void);
|
||||
rt_err_t rt_link_port_deinit(void);
|
||||
rt_err_t rt_link_port_reconnect(void);
|
||||
rt_size_t rt_link_port_send(void *data, rt_size_t length);
|
||||
|
||||
#ifdef RT_LINK_USING_HW_CRC
|
||||
rt_err_t rt_link_hw_crc32_init(void);
|
||||
rt_err_t rt_link_hw_crc32_deinit(void);
|
||||
rt_err_t rt_link_hw_crc32_reset(void);
|
||||
rt_uint32_t rt_link_hw_crc32(rt_uint8_t *data, rt_size_t u32_size)
|
||||
#endif
|
||||
|
||||
/* Called when the hardware receives data and the data is transferred to RTLink */
|
||||
rt_size_t rt_link_hw_write_cb(void *data, rt_size_t length);
|
||||
|
||||
#endif /* __RT_LINK_PORT_H__ */
|
21
rt-thread/components/utilities/rt-link/inc/rtlink_utils.h
Normal file
21
rt-thread/components/utilities/rt-link/inc/rtlink_utils.h
Normal file
@@ -0,0 +1,21 @@
|
||||
/*
|
||||
* Copyright (c) 2006-2021, RT-Thread Development Team
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*
|
||||
* Change Logs:
|
||||
* Date Author Notes
|
||||
* 2021-05-15 Sherman the first version
|
||||
*/
|
||||
#ifndef __RT_LINK_UTILITIES_H__
|
||||
#define __RT_LINK_UTILITIES_H__
|
||||
|
||||
#include <rtthread.h>
|
||||
|
||||
/* Calculate the number of '1' */
|
||||
int rt_link_utils_num1(rt_uint32_t n);
|
||||
|
||||
rt_err_t rt_link_sf_crc32_reset(void);
|
||||
rt_uint32_t rt_link_sf_crc32(rt_uint8_t *data, rt_size_t len);
|
||||
|
||||
#endif /* __RT_LINK_UTILITIES_H__ */
|
13
rt-thread/components/utilities/rt-link/src/SConscript
Normal file
13
rt-thread/components/utilities/rt-link/src/SConscript
Normal file
@@ -0,0 +1,13 @@
|
||||
Import('rtconfig')
|
||||
from building import *
|
||||
|
||||
cwd = GetCurrentDir()
|
||||
src = Glob('*.c')
|
||||
CPPPATH = [cwd + '/../inc']
|
||||
|
||||
group = DefineGroup('rt-link', src, depend = ['RT_USING_RT_LINK'], CPPPATH = CPPPATH)
|
||||
|
||||
if os.path.isfile(os.path.join(cwd, 'hw', 'SConscript')):
|
||||
group = group + SConscript(os.path.join('hw', 'SConscript'))
|
||||
|
||||
Return('group')
|
1320
rt-thread/components/utilities/rt-link/src/rtlink.c
Normal file
1320
rt-thread/components/utilities/rt-link/src/rtlink.c
Normal file
File diff suppressed because it is too large
Load Diff
398
rt-thread/components/utilities/rt-link/src/rtlink_dev.c
Normal file
398
rt-thread/components/utilities/rt-link/src/rtlink_dev.c
Normal file
@@ -0,0 +1,398 @@
|
||||
/*
|
||||
* Copyright (c) 2006-2021, RT-Thread Development Team
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*
|
||||
* Change Logs:
|
||||
* Date Author Notes
|
||||
* 2021-06-15 Sherman the first version
|
||||
*/
|
||||
|
||||
#define DBG_TAG "RTLINK_DEV"
|
||||
#define DBG_LVL DBG_LOG
|
||||
#include <rtdbg.h>
|
||||
|
||||
#include <rthw.h>
|
||||
#include <rtthread.h>
|
||||
#include <rtdevice.h>
|
||||
#include <rtlink_dev.h>
|
||||
|
||||
#define RTLINK_SERV(dev) (((struct rt_link_device*)dev)->service)
|
||||
|
||||
#ifdef RT_USING_POSIX_DEVIO
|
||||
#include <unistd.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/statfs.h>
|
||||
#include <poll.h>
|
||||
|
||||
int rtlink_fops_open(struct dfs_file *fd)
|
||||
{
|
||||
rt_uint16_t flags = 0;
|
||||
rt_device_t device;
|
||||
|
||||
switch (fd->flags & O_ACCMODE)
|
||||
{
|
||||
case O_RDONLY:
|
||||
LOG_D("fops open: O_RDONLY!");
|
||||
flags = RT_DEVICE_FLAG_RDONLY;
|
||||
break;
|
||||
case O_WRONLY:
|
||||
LOG_D("fops open: O_WRONLY!");
|
||||
flags = RT_DEVICE_FLAG_WRONLY;
|
||||
break;
|
||||
case O_RDWR:
|
||||
LOG_D("fops open: O_RDWR!");
|
||||
flags = RT_DEVICE_FLAG_RDWR;
|
||||
break;
|
||||
default:
|
||||
LOG_E("fops open: unknown mode - %d!", fd->flags & O_ACCMODE);
|
||||
break;
|
||||
}
|
||||
|
||||
device = (rt_device_t)fd->vnode->data;
|
||||
if (fd->flags & O_NONBLOCK)
|
||||
{
|
||||
rt_device_control(device, RT_LINK_TX_NONBLOCKING | RT_LINK_RX_NONBLOCKING, RT_NULL);
|
||||
}
|
||||
|
||||
return rt_device_open(device, flags);
|
||||
}
|
||||
|
||||
int rtlink_fops_close(struct dfs_file *fd)
|
||||
{
|
||||
rt_device_t device;
|
||||
device = (rt_device_t)fd->vnode->data;
|
||||
|
||||
rt_device_set_rx_indicate(device, RT_NULL);
|
||||
return rt_device_close(device);
|
||||
}
|
||||
|
||||
int rtlink_fops_ioctl(struct dfs_file *fd, int cmd, void *args)
|
||||
{
|
||||
rt_device_t device;
|
||||
device = (rt_device_t)fd->vnode->data;
|
||||
|
||||
if (cmd == O_NONBLOCK)
|
||||
{
|
||||
return rt_device_control(device, RT_LINK_TX_NONBLOCKING | RT_LINK_RX_NONBLOCKING, RT_NULL);
|
||||
}
|
||||
else
|
||||
{
|
||||
return rt_device_control(device, cmd, args);
|
||||
}
|
||||
}
|
||||
|
||||
int rtlink_fops_read(struct dfs_file *fd, void *buf, size_t count)
|
||||
{
|
||||
int size = 0;
|
||||
rt_device_t device;
|
||||
device = (rt_device_t)fd->vnode->data;
|
||||
|
||||
size = rt_device_read(device, -1, buf, count);
|
||||
if (size <= 0)
|
||||
{
|
||||
size = -EAGAIN;
|
||||
}
|
||||
return size;
|
||||
}
|
||||
|
||||
int rtlink_fops_write(struct dfs_file *fd, const void *buf, size_t count)
|
||||
{
|
||||
int size = 0;
|
||||
rt_device_t device;
|
||||
device = (rt_device_t)fd->vnode->data;
|
||||
|
||||
size = rt_device_write(device, -1, buf, count);
|
||||
if (size <= 0)
|
||||
{
|
||||
size = -EAGAIN;
|
||||
}
|
||||
return size;
|
||||
}
|
||||
|
||||
int rtlink_fops_poll(struct dfs_file *fd, struct rt_pollreq *req)
|
||||
{
|
||||
int mask = 0;
|
||||
int flags = 0;
|
||||
rt_device_t device;
|
||||
struct rt_link_device *rtlink_dev;
|
||||
|
||||
device = (rt_device_t)fd->vnode->data;
|
||||
RT_ASSERT(device != RT_NULL);
|
||||
|
||||
rtlink_dev = (struct rt_link_device *)device;
|
||||
|
||||
flags = fd->flags & O_ACCMODE;
|
||||
if (flags == O_RDONLY || flags == O_RDWR)
|
||||
{
|
||||
rt_base_t level;
|
||||
rt_poll_add(&(device->wait_queue), req);
|
||||
|
||||
level = rt_hw_interrupt_disable();
|
||||
if (RT_NULL != rt_slist_first(&rtlink_dev->recv_head))
|
||||
mask |= POLLIN;
|
||||
rt_hw_interrupt_enable(level);
|
||||
}
|
||||
mask |= POLLOUT;
|
||||
|
||||
return mask;
|
||||
}
|
||||
|
||||
const static struct dfs_file_ops _rtlink_fops =
|
||||
{
|
||||
rtlink_fops_open,
|
||||
rtlink_fops_close,
|
||||
rtlink_fops_ioctl,
|
||||
rtlink_fops_read,
|
||||
rtlink_fops_write,
|
||||
RT_NULL, /* flush */
|
||||
RT_NULL, /* lseek */
|
||||
RT_NULL, /* getdents */
|
||||
rtlink_fops_poll,
|
||||
};
|
||||
#endif /* RT_USING_POSIX_DEVIO */
|
||||
|
||||
/* The event type for the service channel number,
|
||||
* which is used to wake up the service thread in blocking receive mode */
|
||||
struct rt_event recv_event;
|
||||
|
||||
static rt_err_t rt_link_event_send(struct rt_link_service *serv)
|
||||
{
|
||||
RT_ASSERT(serv != RT_NULL);
|
||||
RT_ASSERT(serv->service < RT_LINK_SERVICE_MAX);
|
||||
rt_uint32_t set = 0x01 << serv->service;
|
||||
return rt_event_send(&recv_event, set);
|
||||
}
|
||||
|
||||
static rt_err_t rt_link_event_recv(struct rt_link_service *service)
|
||||
{
|
||||
RT_ASSERT(service != RT_NULL);
|
||||
RT_ASSERT(service->service < RT_LINK_SERVICE_MAX);
|
||||
|
||||
rt_uint32_t set = 0x01 << service->service;
|
||||
rt_uint32_t recved = 0;
|
||||
rt_err_t ret = rt_event_recv(&recv_event,
|
||||
set,
|
||||
RT_EVENT_FLAG_AND | RT_EVENT_FLAG_CLEAR,
|
||||
RT_WAITING_FOREVER,
|
||||
&recved);
|
||||
if (recved & set)
|
||||
{
|
||||
return ret;
|
||||
}
|
||||
return -RT_ERROR;
|
||||
}
|
||||
|
||||
static void send_cb(struct rt_link_service *service, void *buffer)
|
||||
{
|
||||
RT_ASSERT(service != RT_NULL);
|
||||
RT_ASSERT(buffer != RT_NULL);
|
||||
struct rt_link_device *rtlink = (struct rt_link_device *)service->user_data;
|
||||
|
||||
if (rtlink && rtlink->parent.tx_complete)
|
||||
{
|
||||
rtlink->parent.tx_complete(&rtlink->parent, buffer);
|
||||
}
|
||||
}
|
||||
|
||||
static void recv_cb(struct rt_link_service *service, void *data, rt_size_t size)
|
||||
{
|
||||
RT_ASSERT(service != RT_NULL);
|
||||
struct rt_link_device *rtlink = (struct rt_link_device *)service->user_data;
|
||||
|
||||
if (rtlink)
|
||||
{
|
||||
struct rtlink_recv_list *node = rt_malloc(sizeof(struct rtlink_recv_list));
|
||||
node->data = data;
|
||||
node->size = size;
|
||||
node->index = 0;
|
||||
rt_slist_append(&rtlink->recv_head, &node->list);
|
||||
rt_link_event_send(service);
|
||||
|
||||
if (rtlink->parent.rx_indicate)
|
||||
{
|
||||
rtlink->parent.rx_indicate(&rtlink->parent, size);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
rt_free(data);
|
||||
}
|
||||
}
|
||||
|
||||
rt_err_t rt_link_dev_init(rt_device_t dev)
|
||||
{
|
||||
RT_ASSERT(dev != RT_NULL);
|
||||
|
||||
dev->rx_indicate = RT_NULL;
|
||||
dev->tx_complete = RT_NULL;
|
||||
|
||||
struct rt_link_device *rtlink = (struct rt_link_device *)dev;
|
||||
rtlink->service.service = RT_LINK_SERVICE_MAX;
|
||||
rtlink->service.recv_cb = RT_NULL;
|
||||
rtlink->service.send_cb = RT_NULL;
|
||||
rtlink->service.timeout_tx = RT_WAITING_NO;
|
||||
rtlink->service.user_data = (void *)dev;
|
||||
|
||||
rt_slist_init(&rtlink->recv_head);
|
||||
return RT_EOK;
|
||||
}
|
||||
|
||||
rt_err_t rt_link_dev_open(rt_device_t dev, rt_uint16_t oflag)
|
||||
{
|
||||
RT_ASSERT(dev != RT_NULL);
|
||||
struct rt_link_device *rtlink = (struct rt_link_device *)dev;
|
||||
|
||||
rtlink->service.recv_cb = recv_cb;
|
||||
rtlink->service.send_cb = send_cb;
|
||||
|
||||
dev->open_flag = oflag & RT_DEVICE_OFLAG_MASK;
|
||||
if (dev->open_flag == RT_DEVICE_OFLAG_RDONLY)
|
||||
{
|
||||
rtlink->service.send_cb = RT_NULL;
|
||||
}
|
||||
else if (dev->open_flag == RT_DEVICE_OFLAG_WRONLY)
|
||||
{
|
||||
rtlink->service.recv_cb = RT_NULL;
|
||||
}
|
||||
return rt_link_service_attach(&rtlink->service);
|
||||
}
|
||||
|
||||
rt_err_t rt_link_dev_close(rt_device_t dev)
|
||||
{
|
||||
RT_ASSERT(dev != RT_NULL);
|
||||
struct rt_link_device *rtlink = (struct rt_link_device *)dev;
|
||||
return rt_link_service_detach(&rtlink->service);
|
||||
}
|
||||
|
||||
rt_ssize_t rt_link_dev_read(rt_device_t dev, rt_off_t pos, void *buffer, rt_size_t size)
|
||||
{
|
||||
RT_ASSERT(dev != RT_NULL);
|
||||
RT_ASSERT(buffer != RT_NULL);
|
||||
RT_ASSERT(size != 0);
|
||||
|
||||
struct rt_link_device *rtlink = (struct rt_link_device *)dev;
|
||||
struct rtlink_recv_list *node;
|
||||
rt_size_t read_len = 0;
|
||||
rt_size_t unread_len = 0;
|
||||
|
||||
if (dev->rx_indicate == RT_NULL)
|
||||
{
|
||||
/* RT_LINK_RX_BLOCKING, wait service receive data event */
|
||||
rt_link_event_recv(&rtlink->service);
|
||||
}
|
||||
|
||||
if (rt_slist_first(&rtlink->recv_head) != RT_NULL)
|
||||
{
|
||||
node = rt_container_of(rt_slist_next(&rtlink->recv_head), struct rtlink_recv_list, list);
|
||||
unread_len = (node->size) - (node->index);
|
||||
read_len = (size > unread_len) ? unread_len : size;
|
||||
rt_memcpy(buffer, (rt_uint8_t *)node->data + node->index, read_len);
|
||||
node->index += read_len;
|
||||
|
||||
if (node->index >= node->size)
|
||||
{
|
||||
rt_slist_remove(&rtlink->recv_head, &node->list);
|
||||
node->index = 0;
|
||||
rt_free(node->data);
|
||||
rt_free(node);
|
||||
}
|
||||
if (rt_slist_first(&rtlink->recv_head) != RT_NULL)
|
||||
{
|
||||
rt_link_event_send(&rtlink->service);
|
||||
}
|
||||
}
|
||||
return read_len;
|
||||
}
|
||||
|
||||
rt_ssize_t rt_link_dev_write(rt_device_t dev, rt_off_t pos, const void *buffer, rt_size_t size)
|
||||
{
|
||||
RT_ASSERT(dev != RT_NULL);
|
||||
RT_ASSERT(buffer != RT_NULL);
|
||||
RT_ASSERT(size != 0);
|
||||
|
||||
return rt_link_send(&RTLINK_SERV(dev), buffer, size);
|
||||
}
|
||||
|
||||
rt_err_t rt_link_dev_control(rt_device_t dev, int cmd, void *args)
|
||||
{
|
||||
RT_ASSERT(dev != RT_NULL);
|
||||
|
||||
if (cmd & RT_DEVICE_CTRL_CONFIG)
|
||||
{
|
||||
if (args == RT_NULL)
|
||||
return -RT_EINVAL;
|
||||
RTLINK_SERV(dev).service = ((struct rt_link_service *)args)->service;
|
||||
RTLINK_SERV(dev).timeout_tx = ((struct rt_link_service *)args)->timeout_tx;
|
||||
RTLINK_SERV(dev).flag = ((struct rt_link_service *)args)->flag;
|
||||
}
|
||||
|
||||
if (cmd & RT_LINK_RX_BLOCKING)
|
||||
{
|
||||
dev->rx_indicate = RT_NULL;
|
||||
}
|
||||
if (cmd & RT_LINK_TX_BLOCKING)
|
||||
{
|
||||
RTLINK_SERV(dev).timeout_tx = RT_WAITING_FOREVER;
|
||||
dev->tx_complete = RT_NULL;
|
||||
}
|
||||
if (cmd & RT_LINK_TX_NONBLOCKING)
|
||||
{
|
||||
RTLINK_SERV(dev).timeout_tx = RT_WAITING_NO;
|
||||
}
|
||||
|
||||
return RT_EOK;
|
||||
}
|
||||
|
||||
#ifdef RT_USING_DEVICE_OPS
|
||||
const static struct rt_device_ops rtlink_ops =
|
||||
{
|
||||
rt_link_dev_init,
|
||||
rt_link_dev_open,
|
||||
rt_link_dev_close,
|
||||
rt_link_dev_read,
|
||||
rt_link_dev_write,
|
||||
rt_link_dev_control
|
||||
};
|
||||
#endif /* RT_USING_DEVICE_OPS */
|
||||
|
||||
/*
|
||||
* rtlink device register
|
||||
*/
|
||||
rt_err_t rt_link_dev_register(struct rt_link_device *rtlink,
|
||||
const char *name,
|
||||
rt_uint32_t flag,
|
||||
void *data)
|
||||
{
|
||||
rt_err_t ret;
|
||||
struct rt_device *device;
|
||||
RT_ASSERT(rtlink != RT_NULL);
|
||||
|
||||
device = (struct rt_device *)rtlink;
|
||||
device->type = RT_Device_Class_Char;
|
||||
device->rx_indicate = RT_NULL;
|
||||
device->tx_complete = RT_NULL;
|
||||
|
||||
#ifdef RT_USING_DEVICE_OPS
|
||||
device->ops = &rtlink_ops;
|
||||
#else
|
||||
device->init = rt_link_dev_init;
|
||||
device->open = rt_link_dev_open;
|
||||
device->close = rt_link_dev_close;
|
||||
device->read = rt_link_dev_read;
|
||||
device->write = rt_link_dev_write;
|
||||
device->control = rt_link_dev_control;
|
||||
#endif
|
||||
device->user_data = data;
|
||||
|
||||
/* register a character device */
|
||||
ret = rt_device_register(device, name, flag);
|
||||
|
||||
#ifdef RT_USING_POSIX_DEVIO
|
||||
/* set fops */
|
||||
device->fops = &_rtlink_fops;
|
||||
#endif
|
||||
|
||||
rt_event_init(&recv_event, "rtlink_dev", RT_IPC_FLAG_FIFO);
|
||||
return ret;
|
||||
}
|
296
rt-thread/components/utilities/rt-link/src/rtlink_hw.c
Normal file
296
rt-thread/components/utilities/rt-link/src/rtlink_hw.c
Normal file
@@ -0,0 +1,296 @@
|
||||
/*
|
||||
* Copyright (c) 2006-2021, RT-Thread Development Team
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*
|
||||
* Change Logs:
|
||||
* Date Author Notes
|
||||
* 2021-02-02 xiangxistu the first version
|
||||
* 2021-05-08 Sherman Optimize the operation function on the rt_link_receive_buffer
|
||||
*/
|
||||
|
||||
#include <rtthread.h>
|
||||
|
||||
#include <rtlink.h>
|
||||
#include <rtlink_hw.h>
|
||||
#include <rtlink_port.h>
|
||||
#include <rtlink_utils.h>
|
||||
|
||||
#define DBG_TAG "rtlink_hw"
|
||||
#ifdef USING_RT_LINK_HW_DEBUG
|
||||
#define DBG_LVL DBG_LOG
|
||||
#else
|
||||
#define DBG_LVL DBG_INFO
|
||||
#endif
|
||||
#define DBG_COLOR
|
||||
#include <rtdbg.h>
|
||||
|
||||
static struct rt_link_receive_buffer *rx_buffer = RT_NULL;
|
||||
|
||||
struct rt_link_receive_buffer *rt_link_hw_buffer_init(void *parameter)
|
||||
{
|
||||
rx_buffer = rt_malloc(sizeof(struct rt_link_receive_buffer));
|
||||
if (rx_buffer != RT_NULL)
|
||||
{
|
||||
rt_memset(rx_buffer, 0, sizeof(struct rt_link_receive_buffer));
|
||||
rx_buffer->read_point = rx_buffer->data;
|
||||
rx_buffer->write_point = rx_buffer->data;
|
||||
rx_buffer->end_point = rx_buffer->data + RT_LINK_RECEIVE_BUFFER_LENGTH; /* Point to memory that has no access rights */
|
||||
}
|
||||
else
|
||||
{
|
||||
LOG_E("receive buffer alloc failed, init failed.");
|
||||
}
|
||||
|
||||
return rx_buffer;
|
||||
}
|
||||
|
||||
static rt_ssize_t rt_link_hw_buffer_write(void *data, rt_size_t count)
|
||||
{
|
||||
rt_size_t surplus = 0;
|
||||
if (rx_buffer == RT_NULL)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
/* (data)----(r)----(w)----(end) */
|
||||
if (rx_buffer->write_point >= rx_buffer->read_point)
|
||||
{
|
||||
rt_size_t w2end = rx_buffer->end_point - rx_buffer->write_point;
|
||||
surplus = RT_LINK_RECEIVE_BUFFER_LENGTH - (rx_buffer->write_point - rx_buffer->read_point);
|
||||
count = count > surplus ? surplus : count;
|
||||
if (count >= w2end)
|
||||
{
|
||||
rt_memcpy(rx_buffer->write_point, data, w2end);
|
||||
rx_buffer->write_point = rx_buffer->data;
|
||||
|
||||
rt_memcpy(rx_buffer->write_point, (rt_uint8_t *)data + w2end, (count - w2end));
|
||||
rx_buffer->write_point += (count - w2end);
|
||||
}
|
||||
else
|
||||
{
|
||||
rt_memcpy(rx_buffer->write_point, data, count);
|
||||
rx_buffer->write_point += count;
|
||||
}
|
||||
}
|
||||
else /* (data)----(w)----(r)----(end) */
|
||||
{
|
||||
surplus = rx_buffer->read_point - rx_buffer->write_point;
|
||||
count = count > surplus ? surplus : count;
|
||||
rt_memcpy(rx_buffer->write_point, data, count);
|
||||
rx_buffer->write_point += count;
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
/* increases buffer pointer by one and circle around if necessary */
|
||||
void rt_link_hw_buffer_point_shift(rt_uint8_t **pointer_address, rt_size_t length)
|
||||
{
|
||||
rt_uint8_t *pointer = *pointer_address + length;
|
||||
|
||||
if (rx_buffer->write_point >= rx_buffer->read_point)
|
||||
{
|
||||
if (pointer >= rx_buffer->write_point)
|
||||
{
|
||||
*pointer_address = rx_buffer->write_point;
|
||||
}
|
||||
else
|
||||
{
|
||||
*pointer_address = pointer;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (pointer >= rx_buffer->end_point)
|
||||
{
|
||||
*pointer_address = rx_buffer->data;
|
||||
pointer = pointer - rx_buffer->end_point + rx_buffer->data;
|
||||
|
||||
if (pointer >= rx_buffer->write_point)
|
||||
{
|
||||
*pointer_address = rx_buffer->write_point;
|
||||
}
|
||||
else
|
||||
{
|
||||
*pointer_address = pointer;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
*pointer_address = pointer;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* copy data from receive buffer */
|
||||
void rt_link_hw_copy(rt_uint8_t *dst, rt_uint8_t *src, rt_size_t count)
|
||||
{
|
||||
rt_uint8_t *pointer = RT_NULL;
|
||||
|
||||
pointer = src + count;
|
||||
if (pointer >= rx_buffer->end_point)
|
||||
{
|
||||
rt_size_t offset = 0;
|
||||
offset = rx_buffer->end_point - src;
|
||||
rt_memcpy(dst, src, offset);
|
||||
rt_memcpy(dst + offset, rx_buffer->data, pointer - rx_buffer->end_point);
|
||||
}
|
||||
else
|
||||
{
|
||||
rt_memcpy(dst, src, count);
|
||||
}
|
||||
}
|
||||
|
||||
/* Length of data received */
|
||||
rt_size_t rt_link_hw_recv_len(struct rt_link_receive_buffer *buffer)
|
||||
{
|
||||
if (buffer == RT_NULL)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
if (buffer->write_point >= buffer->read_point)
|
||||
{
|
||||
return (buffer->write_point - buffer->read_point);
|
||||
}
|
||||
else
|
||||
{
|
||||
return (RT_LINK_RECEIVE_BUFFER_LENGTH - (buffer->read_point - buffer->write_point));
|
||||
}
|
||||
}
|
||||
|
||||
rt_err_t rt_link_reset_crc32(void)
|
||||
{
|
||||
#ifdef RT_LINK_USING_HW_CRC
|
||||
return rt_link_hw_crc32_reset();
|
||||
#else
|
||||
return rt_link_sf_crc32_reset();
|
||||
#endif
|
||||
}
|
||||
|
||||
rt_uint32_t rt_link_crc32(rt_uint8_t *data, rt_size_t u32_size)
|
||||
{
|
||||
#ifdef RT_LINK_USING_HW_CRC
|
||||
return rt_link_hw_crc32(data, u32_size);
|
||||
#else
|
||||
return rt_link_sf_crc32(data, u32_size);
|
||||
#endif
|
||||
}
|
||||
|
||||
rt_uint32_t rt_link_get_crc(rt_uint8_t using_buffer_ring, rt_uint8_t *data, rt_size_t size)
|
||||
{
|
||||
rt_uint32_t crc32 = 0x0;
|
||||
rt_size_t surplus = 0;
|
||||
|
||||
if (data == RT_NULL)
|
||||
{
|
||||
LOG_D("warning, the parameter error: %d, data: 0x%08d.", size, data);
|
||||
return 0;
|
||||
}
|
||||
|
||||
rt_link_reset_crc32();
|
||||
if (using_buffer_ring == 1)
|
||||
{
|
||||
/* modify the missing character */
|
||||
surplus = rx_buffer->end_point - data;
|
||||
if (surplus >= size)
|
||||
{
|
||||
crc32 = rt_link_crc32(data, size);
|
||||
}
|
||||
else
|
||||
{
|
||||
rt_link_crc32(data, surplus);
|
||||
crc32 = rt_link_crc32(rx_buffer->data, size - surplus);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
crc32 = rt_link_crc32(data, size);
|
||||
}
|
||||
return crc32;
|
||||
}
|
||||
|
||||
rt_size_t rt_link_hw_send(void *data, rt_size_t length)
|
||||
{
|
||||
rt_size_t send_len = 0;
|
||||
send_len = rt_link_port_send(data, length);
|
||||
if (send_len <= 0)
|
||||
{
|
||||
rt_link_port_reconnect();
|
||||
send_len = rt_link_port_send(data, length);
|
||||
}
|
||||
return send_len;
|
||||
}
|
||||
|
||||
rt_size_t rt_link_hw_write_cb(void *data, rt_size_t length)
|
||||
{
|
||||
/* write real data into rtlink receive buffer */
|
||||
rt_size_t len = rt_link_hw_buffer_write(data, length);
|
||||
struct rt_link_session *scb = rt_link_get_scb();
|
||||
if (scb)
|
||||
{
|
||||
rt_event_send(&scb->event, RT_LINK_READ_CHECK_EVENT);
|
||||
}
|
||||
return len;
|
||||
}
|
||||
|
||||
rt_err_t rt_link_hw_init(void)
|
||||
{
|
||||
struct rt_link_session *scb = rt_link_get_scb();
|
||||
if ((rx_buffer != RT_NULL) || (scb == RT_NULL))
|
||||
{
|
||||
return -RT_ERROR;
|
||||
}
|
||||
|
||||
/* alloc receive buffer to store data */
|
||||
if (rt_link_hw_buffer_init(RT_NULL) == RT_NULL)
|
||||
{
|
||||
return -RT_ENOMEM;
|
||||
}
|
||||
scb->rx_buffer = rx_buffer;
|
||||
scb->calculate_crc = rt_link_get_crc;
|
||||
|
||||
if (RT_EOK != rt_link_port_init())
|
||||
{
|
||||
return -RT_ERROR;
|
||||
}
|
||||
|
||||
#ifdef LINK_LAYER_USING_HW_CRC
|
||||
/* crc hardware device for mcu and node */
|
||||
if (RT_EOK != rt_link_hw_crc32_init())
|
||||
{
|
||||
return -RT_ERROR;
|
||||
}
|
||||
#endif
|
||||
|
||||
LOG_I("link layer hardware environment init successful.");
|
||||
return RT_EOK;
|
||||
}
|
||||
|
||||
rt_err_t rt_link_hw_deinit(void)
|
||||
{
|
||||
if (rx_buffer)
|
||||
{
|
||||
rt_free(rx_buffer);
|
||||
rx_buffer = RT_NULL;
|
||||
}
|
||||
struct rt_link_session *scb = rt_link_get_scb();
|
||||
if (scb)
|
||||
{
|
||||
scb->rx_buffer = rx_buffer;
|
||||
scb->calculate_crc = RT_NULL;
|
||||
}
|
||||
if (RT_EOK != rt_link_port_deinit())
|
||||
{
|
||||
return -RT_ERROR;
|
||||
}
|
||||
|
||||
#ifdef LINK_LAYER_USING_HW_CRC
|
||||
/* crc hardware device for mcu and node */
|
||||
if (RT_EOK != rt_link_hw_crc32_deinit())
|
||||
{
|
||||
return -RT_ERROR;
|
||||
}
|
||||
#endif
|
||||
|
||||
LOG_I("rtlink hardware deinit successful.");
|
||||
return RT_EOK;
|
||||
}
|
90
rt-thread/components/utilities/rt-link/src/rtlink_utils.c
Normal file
90
rt-thread/components/utilities/rt-link/src/rtlink_utils.c
Normal file
@@ -0,0 +1,90 @@
|
||||
/*
|
||||
* Copyright (c) 2006-2021, RT-Thread Development Team
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*
|
||||
* Change Logs:
|
||||
* Date Author Notes
|
||||
* 2021-05-15 Sherman the first version
|
||||
*/
|
||||
|
||||
#include <rtlink_utils.h>
|
||||
|
||||
/* Calculate the number of '1' */
|
||||
int rt_link_utils_num1(rt_uint32_t n)
|
||||
{
|
||||
int ret = 0;
|
||||
while (n)
|
||||
{
|
||||
n &= n - 1;
|
||||
ret++;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
#ifdef RT_LINK_USING_SF_CRC
|
||||
|
||||
static rt_uint32_t crc = 0xffffffff;
|
||||
const rt_uint32_t crc_table[256] =
|
||||
{
|
||||
0x00000000, 0x77073096, 0xEE0E612C, 0x990951BA, 0x076DC419,
|
||||
0x706AF48F, 0xE963A535, 0x9E6495A3, 0x0EDB8832, 0x79DCB8A4, 0xE0D5E91E, 0x97D2D988,
|
||||
0x09B64C2B, 0x7EB17CBD, 0xE7B82D07, 0x90BF1D91, 0x1DB71064, 0x6AB020F2, 0xF3B97148,
|
||||
0x84BE41DE, 0x1ADAD47D, 0x6DDDE4EB, 0xF4D4B551, 0x83D385C7, 0x136C9856, 0x646BA8C0,
|
||||
0xFD62F97A, 0x8A65C9EC, 0x14015C4F, 0x63066CD9, 0xFA0F3D63, 0x8D080DF5, 0x3B6E20C8,
|
||||
0x4C69105E, 0xD56041E4, 0xA2677172, 0x3C03E4D1, 0x4B04D447, 0xD20D85FD, 0xA50AB56B,
|
||||
0x35B5A8FA, 0x42B2986C, 0xDBBBC9D6, 0xACBCF940, 0x32D86CE3, 0x45DF5C75, 0xDCD60DCF,
|
||||
0xABD13D59, 0x26D930AC, 0x51DE003A, 0xC8D75180, 0xBFD06116, 0x21B4F4B5, 0x56B3C423,
|
||||
0xCFBA9599, 0xB8BDA50F, 0x2802B89E, 0x5F058808, 0xC60CD9B2, 0xB10BE924, 0x2F6F7C87,
|
||||
0x58684C11, 0xC1611DAB, 0xB6662D3D, 0x76DC4190, 0x01DB7106, 0x98D220BC, 0xEFD5102A,
|
||||
0x71B18589, 0x06B6B51F, 0x9FBFE4A5, 0xE8B8D433, 0x7807C9A2, 0x0F00F934, 0x9609A88E,
|
||||
0xE10E9818, 0x7F6A0DBB, 0x086D3D2D, 0x91646C97, 0xE6635C01, 0x6B6B51F4, 0x1C6C6162,
|
||||
0x856530D8, 0xF262004E, 0x6C0695ED, 0x1B01A57B, 0x8208F4C1, 0xF50FC457, 0x65B0D9C6,
|
||||
0x12B7E950, 0x8BBEB8EA, 0xFCB9887C, 0x62DD1DDF, 0x15DA2D49, 0x8CD37CF3, 0xFBD44C65,
|
||||
0x4DB26158, 0x3AB551CE, 0xA3BC0074, 0xD4BB30E2, 0x4ADFA541, 0x3DD895D7, 0xA4D1C46D,
|
||||
0xD3D6F4FB, 0x4369E96A, 0x346ED9FC, 0xAD678846, 0xDA60B8D0, 0x44042D73, 0x33031DE5,
|
||||
0xAA0A4C5F, 0xDD0D7CC9, 0x5005713C, 0x270241AA, 0xBE0B1010, 0xC90C2086, 0x5768B525,
|
||||
0x206F85B3, 0xB966D409, 0xCE61E49F, 0x5EDEF90E, 0x29D9C998, 0xB0D09822, 0xC7D7A8B4,
|
||||
0x59B33D17, 0x2EB40D81, 0xB7BD5C3B, 0xC0BA6CAD, 0xEDB88320, 0x9ABFB3B6, 0x03B6E20C,
|
||||
0x74B1D29A, 0xEAD54739, 0x9DD277AF, 0x04DB2615, 0x73DC1683, 0xE3630B12, 0x94643B84,
|
||||
0x0D6D6A3E, 0x7A6A5AA8, 0xE40ECF0B, 0x9309FF9D, 0x0A00AE27, 0x7D079EB1, 0xF00F9344,
|
||||
0x8708A3D2, 0x1E01F268, 0x6906C2FE, 0xF762575D, 0x806567CB, 0x196C3671, 0x6E6B06E7,
|
||||
0xFED41B76, 0x89D32BE0, 0x10DA7A5A, 0x67DD4ACC, 0xF9B9DF6F, 0x8EBEEFF9, 0x17B7BE43,
|
||||
0x60B08ED5, 0xD6D6A3E8, 0xA1D1937E, 0x38D8C2C4, 0x4FDFF252, 0xD1BB67F1, 0xA6BC5767,
|
||||
0x3FB506DD, 0x48B2364B, 0xD80D2BDA, 0xAF0A1B4C, 0x36034AF6, 0x41047A60, 0xDF60EFC3,
|
||||
0xA867DF55, 0x316E8EEF, 0x4669BE79, 0xCB61B38C, 0xBC66831A, 0x256FD2A0, 0x5268E236,
|
||||
0xCC0C7795, 0xBB0B4703, 0x220216B9, 0x5505262F, 0xC5BA3BBE, 0xB2BD0B28, 0x2BB45A92,
|
||||
0x5CB36A04, 0xC2D7FFA7, 0xB5D0CF31, 0x2CD99E8B, 0x5BDEAE1D, 0x9B64C2B0, 0xEC63F226,
|
||||
0x756AA39C, 0x026D930A, 0x9C0906A9, 0xEB0E363F, 0x72076785, 0x05005713, 0x95BF4A82,
|
||||
0xE2B87A14, 0x7BB12BAE, 0x0CB61B38, 0x92D28E9B, 0xE5D5BE0D, 0x7CDCEFB7, 0x0BDBDF21,
|
||||
0x86D3D2D4, 0xF1D4E242, 0x68DDB3F8, 0x1FDA836E, 0x81BE16CD, 0xF6B9265B, 0x6FB077E1,
|
||||
0x18B74777, 0x88085AE6, 0xFF0F6A70, 0x66063BCA, 0x11010B5C, 0x8F659EFF, 0xF862AE69,
|
||||
0x616BFFD3, 0x166CCF45, 0xA00AE278, 0xD70DD2EE, 0x4E048354, 0x3903B3C2, 0xA7672661,
|
||||
0xD06016F7, 0x4969474D, 0x3E6E77DB, 0xAED16A4A, 0xD9D65ADC, 0x40DF0B66, 0x37D83BF0,
|
||||
0xA9BCAE53, 0xDEBB9EC5, 0x47B2CF7F, 0x30B5FFE9, 0xBDBDF21C, 0xCABAC28A, 0x53B39330,
|
||||
0x24B4A3A6, 0xBAD03605, 0xCDD70693, 0x54DE5729, 0x23D967BF, 0xB3667A2E, 0xC4614AB8,
|
||||
0x5D681B02, 0x2A6F2B94, 0xB40BBE37, 0xC30C8EA1, 0x5A05DF1B, 0x2D02EF8D
|
||||
};
|
||||
|
||||
rt_err_t rt_link_sf_crc32_reset(void)
|
||||
{
|
||||
crc = 0xffffffff;
|
||||
return RT_EOK;
|
||||
}
|
||||
|
||||
rt_uint32_t rt_link_sf_crc32(rt_uint8_t *data, rt_size_t len)
|
||||
{
|
||||
rt_uint32_t x, y;
|
||||
x = 0;
|
||||
y = 0;
|
||||
rt_size_t i;
|
||||
|
||||
for (i = 0; i < len; i++)
|
||||
{
|
||||
y = (crc ^ data[i]) & 0xff;
|
||||
x = crc_table[y];
|
||||
crc = (crc >> 8) ^ x;
|
||||
}
|
||||
return (crc ^ 0xffffffff);
|
||||
}
|
||||
#endif /* RT_LINK_USING_SF_CRC */
|
20
rt-thread/components/utilities/ulog/SConscript
Normal file
20
rt-thread/components/utilities/ulog/SConscript
Normal file
@@ -0,0 +1,20 @@
|
||||
from building import *
|
||||
|
||||
cwd = GetCurrentDir()
|
||||
src = Glob('*.c')
|
||||
path = [cwd]
|
||||
|
||||
if GetDepend('ULOG_BACKEND_USING_CONSOLE'):
|
||||
src += ['backend/console_be.c']
|
||||
|
||||
if GetDepend('ULOG_BACKEND_USING_FILE'):
|
||||
path += [cwd + '/backend']
|
||||
src += ['backend/file_be.c']
|
||||
|
||||
if GetDepend('ULOG_USING_SYSLOG'):
|
||||
path += [cwd + '/syslog']
|
||||
src += Glob('syslog/*.c')
|
||||
|
||||
group = DefineGroup('Utilities', src, depend = ['RT_USING_ULOG'], CPPPATH = path)
|
||||
|
||||
Return('group')
|
40
rt-thread/components/utilities/ulog/backend/console_be.c
Normal file
40
rt-thread/components/utilities/ulog/backend/console_be.c
Normal file
@@ -0,0 +1,40 @@
|
||||
/*
|
||||
* Copyright (c) 2006-2022, RT-Thread Development Team
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*
|
||||
* Change Logs:
|
||||
* Date Author Notes
|
||||
* 2018-09-04 armink the first version
|
||||
*/
|
||||
|
||||
#include <rthw.h>
|
||||
#include <ulog.h>
|
||||
|
||||
#ifdef ULOG_BACKEND_USING_CONSOLE
|
||||
|
||||
#if defined(ULOG_ASYNC_OUTPUT_BY_THREAD) && ULOG_ASYNC_OUTPUT_THREAD_STACK < 384
|
||||
#error "The thread stack size must more than 384 when using async output by thread (ULOG_ASYNC_OUTPUT_BY_THREAD)"
|
||||
#endif
|
||||
|
||||
static struct ulog_backend console = { 0 };
|
||||
|
||||
void ulog_console_backend_output(struct ulog_backend *backend, rt_uint32_t level, const char *tag, rt_bool_t is_raw,
|
||||
const char *log, rt_size_t len)
|
||||
{
|
||||
rt_kputs(log);
|
||||
|
||||
}
|
||||
|
||||
int ulog_console_backend_init(void)
|
||||
{
|
||||
ulog_init();
|
||||
console.output = ulog_console_backend_output;
|
||||
|
||||
ulog_backend_register(&console, "console", RT_TRUE);
|
||||
|
||||
return 0;
|
||||
}
|
||||
INIT_PREV_EXPORT(ulog_console_backend_init);
|
||||
|
||||
#endif /* ULOG_BACKEND_USING_CONSOLE */
|
226
rt-thread/components/utilities/ulog/backend/file_be.c
Normal file
226
rt-thread/components/utilities/ulog/backend/file_be.c
Normal file
@@ -0,0 +1,226 @@
|
||||
/*
|
||||
* Copyright (c) 2006-2021, RT-Thread Development Team
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*
|
||||
* Change Logs:
|
||||
* Date Author Notes
|
||||
* 2021-01-07 ChenYong first version
|
||||
* 2021-12-20 armink add multi-instance version
|
||||
*/
|
||||
|
||||
#include <rtthread.h>
|
||||
#include <dfs_file.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <ulog.h>
|
||||
#include <ulog_be.h>
|
||||
|
||||
#ifdef ULOG_BACKEND_USING_FILE
|
||||
|
||||
#if defined(ULOG_ASYNC_OUTPUT_THREAD_STACK) && (ULOG_ASYNC_OUTPUT_THREAD_STACK < 2048)
|
||||
#error "The value of ULOG_ASYNC_OUTPUT_THREAD_STACK must be greater than 2048."
|
||||
#endif
|
||||
|
||||
/* rotate the log file xxx_n-1.log => xxx_n.log, and xxx.log => xxx_0.log */
|
||||
static rt_bool_t ulog_file_rotate(struct ulog_file_be *be)
|
||||
{
|
||||
#define SUFFIX_LEN 10
|
||||
/* mv xxx_n-1.log => xxx_n.log, and xxx.log => xxx_0.log */
|
||||
static char old_path[ULOG_FILE_PATH_LEN], new_path[ULOG_FILE_PATH_LEN];
|
||||
int index = 0, err = 0, file_fd = 0;
|
||||
rt_bool_t result = RT_FALSE;
|
||||
size_t base_len = 0;
|
||||
|
||||
rt_snprintf(old_path, ULOG_FILE_PATH_LEN, "%s/%s", be->cur_log_dir_path, be->parent.name);
|
||||
rt_snprintf(new_path, ULOG_FILE_PATH_LEN, "%s/%s", be->cur_log_dir_path, be->parent.name);
|
||||
base_len = rt_strlen(be->cur_log_dir_path) + rt_strlen(be->parent.name) + 1;
|
||||
|
||||
if (be->cur_log_file_fd >= 0)
|
||||
{
|
||||
close(be->cur_log_file_fd);
|
||||
}
|
||||
|
||||
for (index = be->file_max_num - 2; index >= 0; --index)
|
||||
{
|
||||
rt_snprintf(old_path + base_len, SUFFIX_LEN, index ? "_%d.log" : ".log", index - 1);
|
||||
rt_snprintf(new_path + base_len, SUFFIX_LEN, "_%d.log", index);
|
||||
/* remove the old file */
|
||||
if ((file_fd = open(new_path, O_RDONLY)) >= 0)
|
||||
{
|
||||
close(file_fd);
|
||||
unlink(new_path);
|
||||
}
|
||||
/* change the new log file to old file name */
|
||||
if ((file_fd = open(old_path , O_RDONLY)) >= 0)
|
||||
{
|
||||
close(file_fd);
|
||||
err = dfs_file_rename(old_path, new_path);
|
||||
}
|
||||
|
||||
if (err < 0)
|
||||
{
|
||||
result = RT_FALSE;
|
||||
goto __exit;
|
||||
}
|
||||
|
||||
result = RT_TRUE;
|
||||
}
|
||||
|
||||
__exit:
|
||||
/* reopen the file */
|
||||
be->cur_log_file_fd = open(be->cur_log_file_path, O_CREAT | O_RDWR | O_APPEND);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static void ulog_file_backend_flush_with_buf(struct ulog_backend *backend)
|
||||
{
|
||||
struct ulog_file_be *be = (struct ulog_file_be *) backend;
|
||||
rt_size_t file_size = 0, write_size = 0;
|
||||
|
||||
if (be->enable == RT_FALSE || be->buf_ptr_now == be->file_buf)
|
||||
{
|
||||
return;
|
||||
}
|
||||
if (be->cur_log_file_fd < 0)
|
||||
{
|
||||
/* check log file directory */
|
||||
if (access(be->cur_log_dir_path, F_OK) < 0)
|
||||
{
|
||||
mkdir(be->cur_log_dir_path, 0);
|
||||
}
|
||||
/* open file */
|
||||
rt_snprintf(be->cur_log_file_path, ULOG_FILE_PATH_LEN, "%s/%s.log", be->cur_log_dir_path, be->parent.name);
|
||||
be->cur_log_file_fd = open(be->cur_log_file_path, O_CREAT | O_RDWR | O_APPEND);
|
||||
if (be->cur_log_file_fd < 0)
|
||||
{
|
||||
rt_kprintf("ulog file(%s) open failed.", be->cur_log_file_path);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
file_size = lseek(be->cur_log_file_fd, 0, SEEK_END);
|
||||
if (file_size >= (be->file_max_size - be->buf_size * 2))
|
||||
{
|
||||
if (!ulog_file_rotate(be))
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
write_size = (rt_size_t)(be->buf_ptr_now - be->file_buf);
|
||||
/* write to the file */
|
||||
if (write(be->cur_log_file_fd, be->file_buf, write_size) != write_size)
|
||||
{
|
||||
return;
|
||||
}
|
||||
/* flush file cache */
|
||||
fsync(be->cur_log_file_fd);
|
||||
|
||||
/* point be->buf_ptr_now at the head of be->file_buf[be->buf_size] */
|
||||
be->buf_ptr_now = be->file_buf;
|
||||
}
|
||||
|
||||
static void ulog_file_backend_output_with_buf(struct ulog_backend *backend, rt_uint32_t level,
|
||||
const char *tag, rt_bool_t is_raw, const char *log, rt_size_t len)
|
||||
{
|
||||
struct ulog_file_be *be = (struct ulog_file_be *)backend;
|
||||
rt_size_t copy_len = 0, free_len = 0;
|
||||
const unsigned char *buf_ptr_end = be->file_buf + be->buf_size;
|
||||
|
||||
while (len)
|
||||
{
|
||||
/* free space length */
|
||||
free_len = buf_ptr_end - be->buf_ptr_now;
|
||||
/* copy the log to the mem buffer */
|
||||
if (len > free_len)
|
||||
{
|
||||
copy_len = free_len;
|
||||
}
|
||||
else
|
||||
{
|
||||
copy_len = len;
|
||||
}
|
||||
rt_memcpy(be->buf_ptr_now, log, copy_len);
|
||||
/* update data pos */
|
||||
be->buf_ptr_now += copy_len;
|
||||
len -= copy_len;
|
||||
log += copy_len;
|
||||
|
||||
RT_ASSERT(be->buf_ptr_now <= buf_ptr_end);
|
||||
/* check the log buffer remain size */
|
||||
if (buf_ptr_end == be->buf_ptr_now)
|
||||
{
|
||||
ulog_file_backend_flush_with_buf(backend);
|
||||
if (buf_ptr_end == be->buf_ptr_now)
|
||||
{
|
||||
/* There is no space, indicating that the data cannot be refreshed
|
||||
to the back end of the file Discard data and exit directly */
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* initialize the ulog file backend */
|
||||
int ulog_file_backend_init(struct ulog_file_be *be, const char *name, const char *dir_path, rt_size_t max_num,
|
||||
rt_size_t max_size, rt_size_t buf_size)
|
||||
{
|
||||
be->file_buf = rt_calloc(1, buf_size);
|
||||
if (!be->file_buf)
|
||||
{
|
||||
rt_kprintf("Warning: NO MEMORY for %s file backend\n", name);
|
||||
return -RT_ENOMEM;
|
||||
}
|
||||
/* temporarily store the start address of the ulog file buffer */
|
||||
be->buf_ptr_now = be->file_buf;
|
||||
be->cur_log_file_fd = -1;
|
||||
be->file_max_num = max_num;
|
||||
be->file_max_size = max_size;
|
||||
be->buf_size = buf_size;
|
||||
be->enable = RT_FALSE;
|
||||
rt_strncpy(be->cur_log_dir_path, dir_path, ULOG_FILE_PATH_LEN);
|
||||
/* the buffer length MUST less than file size */
|
||||
RT_ASSERT(be->buf_size < be->file_max_size);
|
||||
|
||||
be->parent.output = ulog_file_backend_output_with_buf;
|
||||
be->parent.flush = ulog_file_backend_flush_with_buf;
|
||||
ulog_backend_register((ulog_backend_t) be, name, RT_FALSE);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* uninitialize the ulog file backend */
|
||||
int ulog_file_backend_deinit(struct ulog_file_be *be)
|
||||
{
|
||||
if (be->cur_log_file_fd >= 0)
|
||||
{
|
||||
/* flush log to file */
|
||||
ulog_file_backend_flush_with_buf((ulog_backend_t)be);
|
||||
/* close */
|
||||
close(be->cur_log_file_fd);
|
||||
be->cur_log_file_fd = -1;
|
||||
}
|
||||
|
||||
if (!be->file_buf)
|
||||
{
|
||||
rt_free(be->file_buf);
|
||||
be->file_buf = RT_NULL;
|
||||
}
|
||||
|
||||
ulog_backend_unregister((ulog_backend_t)be);
|
||||
return 0;
|
||||
}
|
||||
|
||||
void ulog_file_backend_enable(struct ulog_file_be *be)
|
||||
{
|
||||
be->enable = RT_TRUE;
|
||||
}
|
||||
|
||||
void ulog_file_backend_disable(struct ulog_file_be *be)
|
||||
{
|
||||
be->enable = RT_FALSE;
|
||||
}
|
||||
|
||||
#endif /* ULOG_BACKEND_USING_FILE */
|
44
rt-thread/components/utilities/ulog/backend/ulog_be.h
Normal file
44
rt-thread/components/utilities/ulog/backend/ulog_be.h
Normal file
@@ -0,0 +1,44 @@
|
||||
/*
|
||||
* Copyright (c) 2006-2021, RT-Thread Development Team
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*
|
||||
* Change Logs:
|
||||
* Date Author Notes
|
||||
* 2021-01-07 ChenYong first version
|
||||
* 2021-12-20 armink add multi-instance version
|
||||
*/
|
||||
|
||||
#ifndef _ULOG_BE_H_
|
||||
#define _ULOG_BE_H_
|
||||
|
||||
#include <ulog.h>
|
||||
|
||||
#ifndef ULOG_FILE_PATH_LEN
|
||||
#define ULOG_FILE_PATH_LEN 128
|
||||
#endif
|
||||
|
||||
struct ulog_file_be
|
||||
{
|
||||
struct ulog_backend parent;
|
||||
int cur_log_file_fd;
|
||||
rt_size_t file_max_num;
|
||||
rt_size_t file_max_size;
|
||||
rt_size_t buf_size;
|
||||
rt_bool_t enable;
|
||||
|
||||
rt_uint8_t *file_buf;
|
||||
rt_uint8_t *buf_ptr_now;
|
||||
|
||||
char cur_log_file_path[ULOG_FILE_PATH_LEN];
|
||||
char cur_log_dir_path[ULOG_FILE_PATH_LEN];
|
||||
};
|
||||
|
||||
/* ulog file backend api */
|
||||
int ulog_file_backend_init(struct ulog_file_be *be, const char *name, const char *dir_path, rt_size_t max_num,
|
||||
rt_size_t max_size, rt_size_t buf_size);
|
||||
int ulog_file_backend_deinit(struct ulog_file_be *be);
|
||||
void ulog_file_backend_enable(struct ulog_file_be *be);
|
||||
void ulog_file_backend_disable(struct ulog_file_be *be);
|
||||
|
||||
#endif /* _ULOG_BE_H_ */
|
262
rt-thread/components/utilities/ulog/syslog/syslog.c
Normal file
262
rt-thread/components/utilities/ulog/syslog/syslog.c
Normal file
@@ -0,0 +1,262 @@
|
||||
/*
|
||||
* Copyright (c) 2006-2022, RT-Thread Development Team
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*
|
||||
* Change Logs:
|
||||
* Date Author Notes
|
||||
* 2018-09-07 armink the first version
|
||||
*/
|
||||
|
||||
#include <stdarg.h>
|
||||
#include <ulog.h>
|
||||
#include <rthw.h>
|
||||
#include <stdint.h>
|
||||
#include "syslog.h"
|
||||
|
||||
/*
|
||||
* reference:
|
||||
* http://pubs.opengroup.org/onlinepubs/7908799/xsh/syslog.h.html
|
||||
* https://www.gnu.org/software/libc/manual/html_node/Submitting-Syslog-Messages.html
|
||||
* http://man7.org/linux/man-pages/man3/syslog.3.html
|
||||
*/
|
||||
|
||||
#ifdef ULOG_USING_SYSLOG
|
||||
|
||||
#include <sys/time.h>
|
||||
|
||||
#ifndef ULOG_SYSLOG_IDENT_MAX_LEN
|
||||
#define ULOG_SYSLOG_IDENT_MAX_LEN ULOG_FILTER_TAG_MAX_LEN
|
||||
#endif
|
||||
|
||||
static char local_ident[ULOG_SYSLOG_IDENT_MAX_LEN + 1];
|
||||
static int local_facility = LOG_USER;
|
||||
static int local_option = LOG_USER;
|
||||
static rt_bool_t is_open = RT_FALSE;
|
||||
|
||||
/**
|
||||
* open connection to syslog
|
||||
*
|
||||
* @param ident is an arbitrary identification string which future syslog invocations will prefix to each message.
|
||||
* @param option is not using on ulog.
|
||||
* @param facility is the default facility code for this connection.
|
||||
*/
|
||||
void openlog(const char *ident, int option, int facility)
|
||||
{
|
||||
rt_base_t level;
|
||||
|
||||
ulog_init();
|
||||
|
||||
level = rt_hw_interrupt_disable();
|
||||
|
||||
rt_memset(local_ident, 0, sizeof(local_ident));
|
||||
if (ident)
|
||||
{
|
||||
rt_strncpy(local_ident, ident, ULOG_SYSLOG_IDENT_MAX_LEN);
|
||||
}
|
||||
else
|
||||
{
|
||||
rt_strncpy(local_ident, "rtt", ULOG_SYSLOG_IDENT_MAX_LEN);
|
||||
}
|
||||
|
||||
local_option = option;
|
||||
|
||||
if (facility)
|
||||
{
|
||||
local_facility = facility;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* default facility is LOG_USER */
|
||||
local_facility = LOG_USER;
|
||||
}
|
||||
/* output all level log */
|
||||
setlogmask(LOG_UPTO(LOG_DEBUG));
|
||||
|
||||
is_open = RT_TRUE;
|
||||
|
||||
rt_hw_interrupt_enable(level);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* This is functionally identical to syslog.
|
||||
*
|
||||
* @param priority log priority, can be generated by the macro LOG_MAKEPRI
|
||||
* @param format log format
|
||||
* @param args log arguments
|
||||
*/
|
||||
void vsyslog(int priority, const char *format, va_list args)
|
||||
{
|
||||
if (LOG_FAC(priority) == 0)
|
||||
{
|
||||
/* using local facility */
|
||||
priority |= local_facility;
|
||||
}
|
||||
|
||||
ulog_voutput(priority, local_ident, RT_TRUE, RT_NULL, 0, 0, 0, format, args);
|
||||
}
|
||||
|
||||
/**
|
||||
* generates a log message
|
||||
*
|
||||
* @param priority log priority, can be generated by the macro LOG_MAKEPRI
|
||||
* @param format log format, like printf()
|
||||
*/
|
||||
void syslog(int priority, const char *format, ...)
|
||||
{
|
||||
va_list args;
|
||||
|
||||
if (!is_open)
|
||||
{
|
||||
openlog(0, 0, 0);
|
||||
}
|
||||
/* args point to the first variable parameter */
|
||||
va_start(args, format);
|
||||
|
||||
vsyslog(priority, format, args);
|
||||
|
||||
va_end(args);
|
||||
}
|
||||
|
||||
/**
|
||||
* close the syslog
|
||||
*/
|
||||
void closelog(void)
|
||||
{
|
||||
ulog_deinit();
|
||||
|
||||
is_open = RT_FALSE;
|
||||
}
|
||||
|
||||
/**
|
||||
* set log priority mask
|
||||
*
|
||||
* @param mask The log priority mask which generate by macro LOG_MASK and LOG_UPTO.
|
||||
*
|
||||
* @return This function returns the previous log priority mask.
|
||||
*/
|
||||
int setlogmask(int mask)
|
||||
{
|
||||
static int old_mask = 0;
|
||||
int return_mask = old_mask;
|
||||
|
||||
ulog_tag_lvl_filter_set(local_ident, mask);
|
||||
|
||||
old_mask = mask;
|
||||
|
||||
return return_mask;
|
||||
}
|
||||
|
||||
static const char *get_month_str(uint8_t month)
|
||||
{
|
||||
switch(month)
|
||||
{
|
||||
case 1: return "Jan";
|
||||
case 2: return "Feb";
|
||||
case 3: return "Mar";
|
||||
case 4: return "Apr";
|
||||
case 5: return "May";
|
||||
case 6: return "June";
|
||||
case 7: return "July";
|
||||
case 8: return "Aug";
|
||||
case 9: return "Sept";
|
||||
case 10: return "Oct";
|
||||
case 11: return "Nov";
|
||||
case 12: return "Dec";
|
||||
default: return "Unknown";
|
||||
}
|
||||
}
|
||||
|
||||
rt_weak rt_size_t syslog_formater(char *log_buf, int level, const char *tag, rt_bool_t newline, const char *format, va_list args)
|
||||
{
|
||||
extern rt_size_t ulog_strcpy(rt_size_t cur_len, char *dst, const char *src);
|
||||
|
||||
rt_size_t log_len = 0, newline_len = rt_strlen(ULOG_NEWLINE_SIGN);
|
||||
int fmt_result;
|
||||
|
||||
RT_ASSERT(log_buf);
|
||||
RT_ASSERT(LOG_PRI(level) <= LOG_DEBUG);
|
||||
RT_ASSERT(tag);
|
||||
RT_ASSERT(format);
|
||||
|
||||
/* add time and priority (level) info */
|
||||
{
|
||||
time_t now = time(RT_NULL);
|
||||
struct tm *tm, tm_tmp;
|
||||
|
||||
tm = gmtime_r(&now, &tm_tmp);
|
||||
|
||||
#ifdef ULOG_OUTPUT_LEVEL
|
||||
rt_snprintf(log_buf + log_len, ULOG_LINE_BUF_SIZE - log_len, "<%d>%s%3d %02d:%02d:%02d", level,
|
||||
get_month_str(tm->tm_mon + 1), tm->tm_mday, tm->tm_hour, tm->tm_min, tm->tm_sec);
|
||||
#else
|
||||
rt_snprintf(log_buf + log_len, ULOG_LINE_BUF_SIZE - log_len, "%s%3d %02d:%02d:%02d",
|
||||
get_month_str(tm->tm_mon + 1), tm->tm_mday, tm->tm_hour, tm->tm_min, tm->tm_sec);
|
||||
#endif /* ULOG_OUTPUT_LEVEL */
|
||||
|
||||
log_len += rt_strlen(log_buf + log_len);
|
||||
}
|
||||
|
||||
#ifdef ULOG_OUTPUT_TAG
|
||||
/* add identification (tag) info */
|
||||
{
|
||||
log_len += ulog_strcpy(log_len, log_buf + log_len, " ");
|
||||
log_len += ulog_strcpy(log_len, log_buf + log_len, tag);
|
||||
}
|
||||
#endif /* ULOG_OUTPUT_TAG */
|
||||
|
||||
#ifdef ULOG_OUTPUT_THREAD_NAME
|
||||
/* add thread info */
|
||||
{
|
||||
log_len += ulog_strcpy(log_len, log_buf + log_len, " ");
|
||||
/* is not in interrupt context */
|
||||
if (rt_interrupt_get_nest() == 0)
|
||||
{
|
||||
log_len += ulog_strcpy(log_len, log_buf + log_len, rt_thread_self()->parent.name);
|
||||
}
|
||||
else
|
||||
{
|
||||
log_len += ulog_strcpy(log_len, log_buf + log_len, "ISR");
|
||||
}
|
||||
}
|
||||
#endif /* ULOG_OUTPUT_THREAD_NAME */
|
||||
|
||||
log_len += ulog_strcpy(log_len, log_buf + log_len, ": ");
|
||||
fmt_result = rt_vsnprintf(log_buf + log_len, ULOG_LINE_BUF_SIZE - log_len, format, args);
|
||||
|
||||
/* calculate log length */
|
||||
if ((log_len + fmt_result <= ULOG_LINE_BUF_SIZE) && (fmt_result > -1))
|
||||
{
|
||||
log_len += fmt_result;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* using max length */
|
||||
log_len = ULOG_LINE_BUF_SIZE;
|
||||
}
|
||||
|
||||
/* overflow check and reserve some space for newline sign and string end sign */
|
||||
if (log_len + newline_len + sizeof('\0') > ULOG_LINE_BUF_SIZE)
|
||||
{
|
||||
/* using max length */
|
||||
log_len = ULOG_LINE_BUF_SIZE;
|
||||
/* reserve some space for newline sign */
|
||||
log_len -= newline_len;
|
||||
/* reserve some space for string end sign */
|
||||
log_len -= sizeof('\0');
|
||||
}
|
||||
|
||||
/* package newline sign */
|
||||
if (newline)
|
||||
{
|
||||
log_len += ulog_strcpy(log_len, log_buf + log_len, ULOG_NEWLINE_SIGN);
|
||||
}
|
||||
|
||||
/* add string end sign */
|
||||
log_buf[log_len] = '\0';
|
||||
|
||||
return log_len;
|
||||
}
|
||||
|
||||
#endif /* ULOG_USING_SYSLOG */
|
100
rt-thread/components/utilities/ulog/syslog/syslog.h
Normal file
100
rt-thread/components/utilities/ulog/syslog/syslog.h
Normal file
@@ -0,0 +1,100 @@
|
||||
/*
|
||||
* Copyright (c) 2006-2021, RT-Thread Development Team
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*
|
||||
* Change Logs:
|
||||
* Date Author Notes
|
||||
* 2018-09-07 armink the first version
|
||||
*/
|
||||
|
||||
#ifndef _SYSLOG_H_
|
||||
#define _SYSLOG_H_
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/*
|
||||
* priorities/facilities are encoded into a single 32-bit quantity, where the
|
||||
* bottom 3 bits are the priority (0-7) and the top 28 bits are the facility
|
||||
* (0-big number). Both the priorities and the facilities map roughly
|
||||
* one-to-one to strings in the syslogd(8) source code. This mapping is
|
||||
* included in this file.
|
||||
*
|
||||
* priorities (these are ordered)
|
||||
*/
|
||||
#define LOG_EMERG 0 /* system is unusable */
|
||||
#define LOG_ALERT 1 /* action must be taken immediately */
|
||||
#define LOG_CRIT 2 /* critical conditions */
|
||||
#define LOG_ERR 3 /* error conditions */
|
||||
#define LOG_WARNING 4 /* warning conditions */
|
||||
#define LOG_NOTICE 5 /* normal but significant condition */
|
||||
#define LOG_INFO 6 /* informational */
|
||||
#define LOG_DEBUG 7 /* debug-level messages */
|
||||
|
||||
#define LOG_PRIMASK 0x07
|
||||
|
||||
#define LOG_PRI(p) ((p) & LOG_PRIMASK)
|
||||
#define LOG_MAKEPRI(fac, pri) (((fac) << 3) | (pri))
|
||||
|
||||
/* facility codes */
|
||||
#define LOG_KERN (0<<3) /* kernel messages */
|
||||
#define LOG_USER (1<<3) /* random user-level messages */
|
||||
#define LOG_MAIL (2<<3) /* mail system */
|
||||
#define LOG_DAEMON (3<<3) /* system daemons */
|
||||
#define LOG_AUTH (4<<3) /* security/authorization messages */
|
||||
#define LOG_SYSLOG (5<<3) /* messages generated internally by syslogd */
|
||||
#define LOG_LPR (6<<3) /* line printer subsystem */
|
||||
#define LOG_NEWS (7<<3) /* network news subsystem */
|
||||
#define LOG_UUCP (8<<3) /* UUCP subsystem */
|
||||
#define LOG_CRON (9<<3) /* clock daemon */
|
||||
#define LOG_AUTHPRIV (10<<3) /* security/authorization messages (private) */
|
||||
|
||||
/* other codes through 15 reserved for system use */
|
||||
#define LOG_LOCAL0 (16<<3) /* reserved for local use */
|
||||
#define LOG_LOCAL1 (17<<3) /* reserved for local use */
|
||||
#define LOG_LOCAL2 (18<<3) /* reserved for local use */
|
||||
#define LOG_LOCAL3 (19<<3) /* reserved for local use */
|
||||
#define LOG_LOCAL4 (20<<3) /* reserved for local use */
|
||||
#define LOG_LOCAL5 (21<<3) /* reserved for local use */
|
||||
#define LOG_LOCAL6 (22<<3) /* reserved for local use */
|
||||
#define LOG_LOCAL7 (23<<3) /* reserved for local use */
|
||||
|
||||
#define LOG_NFACILITIES 24 /* current number of facilities */
|
||||
#define LOG_FACMASK 0x03f8 /* mask to extract facility part */
|
||||
/* facility of pri */
|
||||
#define LOG_FAC(p) (((p) & LOG_FACMASK) >> 3)
|
||||
|
||||
/*
|
||||
* arguments to setlogmask.
|
||||
*/
|
||||
#define LOG_MASK(pri) (1 << (pri)) /* mask for one priority */
|
||||
#define LOG_UPTO(pri) ((1 << ((pri)+1)) - 1) /* all priorities through pri */
|
||||
|
||||
/*
|
||||
* Option flags for openlog.
|
||||
*
|
||||
* LOG_ODELAY no longer does anything.
|
||||
* LOG_NDELAY is the inverse of what it used to be.
|
||||
*/
|
||||
#define LOG_PID 0x01 /* log the pid with each message */
|
||||
#define LOG_CONS 0x02 /* log on the console if errors in sending */
|
||||
#define LOG_ODELAY 0x04 /* delay open until first syslog() (default) */
|
||||
#define LOG_NDELAY 0x08 /* don't delay open */
|
||||
#define LOG_NOWAIT 0x10 /* don't wait for console forks: DEPRECATED */
|
||||
#define LOG_PERROR 0x20 /* log to stderr as well */
|
||||
|
||||
#include <stdarg.h>
|
||||
|
||||
void closelog(void);
|
||||
void openlog(const char *ident, int option, int facility);
|
||||
int setlogmask(int mask);
|
||||
void syslog(int priority, const char *format, ...);
|
||||
void vsyslog(int priority, const char *format, va_list args);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* _SYSLOG_H_ */
|
1561
rt-thread/components/utilities/ulog/ulog.c
Normal file
1561
rt-thread/components/utilities/ulog/ulog.c
Normal file
File diff suppressed because it is too large
Load Diff
104
rt-thread/components/utilities/ulog/ulog.h
Normal file
104
rt-thread/components/utilities/ulog/ulog.h
Normal file
@@ -0,0 +1,104 @@
|
||||
/*
|
||||
* Copyright (c) 2006-2022, RT-Thread Development Team
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*
|
||||
* Change Logs:
|
||||
* Date Author Notes
|
||||
* 2018-08-25 armink the first version
|
||||
*/
|
||||
|
||||
#ifndef _ULOG_H_
|
||||
#define _ULOG_H_
|
||||
|
||||
#include <rtthread.h>
|
||||
#include "ulog_def.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/*
|
||||
* ulog init and deint
|
||||
*/
|
||||
int ulog_init(void);
|
||||
int ulog_async_init(void);
|
||||
void ulog_output_lock_enabled(rt_bool_t enabled);
|
||||
void ulog_deinit(void);
|
||||
|
||||
/*
|
||||
* output different level log by LOG_X API
|
||||
*
|
||||
* NOTE: The `LOG_TAG` and `LOG_LVL` must be defined before including the <ulog.h> when you want to use LOG_X API.
|
||||
*
|
||||
* #define LOG_TAG "example"
|
||||
* #define LOG_LVL LOG_LVL_DBG
|
||||
* #include <ulog.h>
|
||||
*
|
||||
* Then you can using LOG_X API to output log
|
||||
*
|
||||
* LOG_D("this is a debug log!");
|
||||
* LOG_E("this is a error log!");
|
||||
*/
|
||||
#define LOG_E(...) ulog_e(LOG_TAG, __VA_ARGS__)
|
||||
#define LOG_W(...) ulog_w(LOG_TAG, __VA_ARGS__)
|
||||
#define LOG_I(...) ulog_i(LOG_TAG, __VA_ARGS__)
|
||||
#define LOG_D(...) ulog_d(LOG_TAG, __VA_ARGS__)
|
||||
#define LOG_RAW(...) ulog_raw(__VA_ARGS__)
|
||||
#define LOG_HEX(name, width, buf, size) ulog_hex(name, width, buf, size)
|
||||
|
||||
/*
|
||||
* backend register and unregister
|
||||
*/
|
||||
rt_err_t ulog_backend_register(ulog_backend_t backend, const char *name, rt_bool_t support_color);
|
||||
rt_err_t ulog_backend_unregister(ulog_backend_t backend);
|
||||
rt_err_t ulog_backend_set_filter(ulog_backend_t backend, ulog_backend_filter_t filter);
|
||||
ulog_backend_t ulog_backend_find(const char *name);
|
||||
|
||||
#ifdef ULOG_USING_FILTER
|
||||
/*
|
||||
* log filter setting
|
||||
*/
|
||||
int ulog_tag_lvl_filter_set(const char *tag, rt_uint32_t level);
|
||||
rt_uint32_t ulog_tag_lvl_filter_get(const char *tag);
|
||||
rt_slist_t *ulog_tag_lvl_list_get(void);
|
||||
void ulog_global_filter_lvl_set(rt_uint32_t level);
|
||||
rt_uint32_t ulog_global_filter_lvl_get(void);
|
||||
void ulog_global_filter_tag_set(const char *tag);
|
||||
const char *ulog_global_filter_tag_get(void);
|
||||
void ulog_global_filter_kw_set(const char *keyword);
|
||||
const char *ulog_global_filter_kw_get(void);
|
||||
#endif /* ULOG_USING_FILTER */
|
||||
|
||||
/*
|
||||
* flush all backends's log
|
||||
*/
|
||||
void ulog_flush(void);
|
||||
|
||||
#ifdef ULOG_USING_ASYNC_OUTPUT
|
||||
/*
|
||||
* asynchronous output API
|
||||
*/
|
||||
void ulog_async_output(void);
|
||||
void ulog_async_output_enabled(rt_bool_t enabled);
|
||||
rt_err_t ulog_async_waiting_log(rt_int32_t time);
|
||||
#endif
|
||||
|
||||
/*
|
||||
* dump the hex format data to log
|
||||
*/
|
||||
void ulog_hexdump(const char *tag, rt_size_t width, const rt_uint8_t *buf, rt_size_t size, ...);
|
||||
|
||||
/*
|
||||
* Another log output API. This API is more difficult to use than LOG_X API.
|
||||
*/
|
||||
void ulog_voutput(rt_uint32_t level, const char *tag, rt_bool_t newline, const rt_uint8_t *hex_buf,
|
||||
rt_size_t hex_size, rt_size_t hex_width, rt_base_t hex_addr, const char *format, va_list args);
|
||||
void ulog_output(rt_uint32_t level, const char *tag, rt_bool_t newline, const char *format, ...);
|
||||
void ulog_raw(const char *format, ...);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* _ULOG_H_ */
|
222
rt-thread/components/utilities/ulog/ulog_def.h
Normal file
222
rt-thread/components/utilities/ulog/ulog_def.h
Normal file
@@ -0,0 +1,222 @@
|
||||
/*
|
||||
* Copyright (c) 2006-2022, RT-Thread Development Team
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*
|
||||
* Change Logs:
|
||||
* Date Author Notes
|
||||
* 2018-08-25 armink the first version
|
||||
*/
|
||||
|
||||
#ifndef _ULOG_DEF_H_
|
||||
#define _ULOG_DEF_H_
|
||||
|
||||
#include <rtdef.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/* logger level, the number is compatible for syslog */
|
||||
#define LOG_LVL_ASSERT 0
|
||||
#define LOG_LVL_ERROR 3
|
||||
#define LOG_LVL_WARNING 4
|
||||
#define LOG_LVL_INFO 6
|
||||
#define LOG_LVL_DBG 7
|
||||
|
||||
/* the output silent level and all level for filter setting */
|
||||
#ifndef ULOG_USING_SYSLOG
|
||||
#define LOG_FILTER_LVL_SILENT 0
|
||||
#define LOG_FILTER_LVL_ALL 7
|
||||
#else
|
||||
#define LOG_FILTER_LVL_SILENT 1
|
||||
#define LOG_FILTER_LVL_ALL 255
|
||||
#endif /* ULOG_USING_SYSLOG */
|
||||
|
||||
/* compatible for rtdbg */
|
||||
#undef LOG_D
|
||||
#undef LOG_I
|
||||
#undef LOG_W
|
||||
#undef LOG_E
|
||||
#undef LOG_RAW
|
||||
#undef DBG_ERROR
|
||||
#undef DBG_WARNING
|
||||
#undef DBG_INFO
|
||||
#undef DBG_LOG
|
||||
#undef dbg_log
|
||||
#define DBG_ERROR LOG_LVL_ERROR
|
||||
#define DBG_WARNING LOG_LVL_WARNING
|
||||
#define DBG_INFO LOG_LVL_INFO
|
||||
#define DBG_LOG LOG_LVL_DBG
|
||||
#define dbg_log(level, ...) \
|
||||
if ((level) <= LOG_LVL) \
|
||||
{ \
|
||||
ulog_output(level, LOG_TAG, RT_FALSE, __VA_ARGS__);\
|
||||
}
|
||||
|
||||
#if !defined(LOG_TAG)
|
||||
/* compatible for rtdbg */
|
||||
#if defined(DBG_TAG)
|
||||
#define LOG_TAG DBG_TAG
|
||||
#elif defined(DBG_SECTION_NAME)
|
||||
#define LOG_TAG DBG_SECTION_NAME
|
||||
#else
|
||||
#define LOG_TAG "NO_TAG"
|
||||
#endif
|
||||
#endif /* !defined(LOG_TAG) */
|
||||
|
||||
#if !defined(LOG_LVL)
|
||||
/* compatible for rtdbg */
|
||||
#if defined(DBG_LVL)
|
||||
#define LOG_LVL DBG_LVL
|
||||
#elif defined(DBG_LEVEL)
|
||||
#define LOG_LVL DBG_LEVEL
|
||||
#else
|
||||
#define LOG_LVL LOG_LVL_DBG
|
||||
#endif
|
||||
#endif /* !defined(LOG_LVL) */
|
||||
|
||||
#if (LOG_LVL >= LOG_LVL_DBG) && (ULOG_OUTPUT_LVL >= LOG_LVL_DBG)
|
||||
#define ulog_d(TAG, ...) ulog_output(LOG_LVL_DBG, TAG, RT_TRUE, __VA_ARGS__)
|
||||
#else
|
||||
#define ulog_d(TAG, ...)
|
||||
#endif /* (LOG_LVL >= LOG_LVL_DBG) && (ULOG_OUTPUT_LVL >= LOG_LVL_DBG) */
|
||||
|
||||
#if (LOG_LVL >= LOG_LVL_INFO) && (ULOG_OUTPUT_LVL >= LOG_LVL_INFO)
|
||||
#define ulog_i(TAG, ...) ulog_output(LOG_LVL_INFO, TAG, RT_TRUE, __VA_ARGS__)
|
||||
#else
|
||||
#define ulog_i(TAG, ...)
|
||||
#endif /* (LOG_LVL >= LOG_LVL_INFO) && (ULOG_OUTPUT_LVL >= LOG_LVL_INFO) */
|
||||
|
||||
#if (LOG_LVL >= LOG_LVL_WARNING) && (ULOG_OUTPUT_LVL >= LOG_LVL_WARNING)
|
||||
#define ulog_w(TAG, ...) ulog_output(LOG_LVL_WARNING, TAG, RT_TRUE, __VA_ARGS__)
|
||||
#else
|
||||
#define ulog_w(TAG, ...)
|
||||
#endif /* (LOG_LVL >= LOG_LVL_WARNING) && (ULOG_OUTPUT_LVL >= LOG_LVL_WARNING) */
|
||||
|
||||
#if (LOG_LVL >= LOG_LVL_ERROR) && (ULOG_OUTPUT_LVL >= LOG_LVL_ERROR)
|
||||
#define ulog_e(TAG, ...) ulog_output(LOG_LVL_ERROR, TAG, RT_TRUE, __VA_ARGS__)
|
||||
#else
|
||||
#define ulog_e(TAG, ...)
|
||||
#endif /* (LOG_LVL >= LOG_LVL_ERROR) && (ULOG_OUTPUT_LVL >= LOG_LVL_ERROR) */
|
||||
|
||||
#if (LOG_LVL >= LOG_LVL_DBG) && (ULOG_OUTPUT_LVL >= LOG_LVL_DBG)
|
||||
#define ulog_hex(TAG, width, buf, size) ulog_hexdump(TAG, width, buf, size)
|
||||
#else
|
||||
#define ulog_hex(TAG, width, buf, size)
|
||||
#endif /* (LOG_LVL >= LOG_LVL_DBG) && (ULOG_OUTPUT_LVL >= LOG_LVL_DBG) */
|
||||
|
||||
/* assert for developer. */
|
||||
#ifdef ULOG_ASSERT_ENABLE
|
||||
#define ULOG_ASSERT(EXPR) \
|
||||
if (!(EXPR)) \
|
||||
{ \
|
||||
ulog_output(LOG_LVL_ASSERT, LOG_TAG, RT_TRUE, "(%s) has assert failed at %s:%ld.", #EXPR, __FUNCTION__, __LINE__); \
|
||||
ulog_flush(); \
|
||||
while (1); \
|
||||
}
|
||||
#else
|
||||
#define ULOG_ASSERT(EXPR)
|
||||
#endif
|
||||
|
||||
/* ASSERT API definition */
|
||||
#if !defined(ASSERT)
|
||||
#define ASSERT ULOG_ASSERT
|
||||
#endif
|
||||
|
||||
/* compatible for elog */
|
||||
#undef assert
|
||||
#undef log_e
|
||||
#undef log_w
|
||||
#undef log_i
|
||||
#undef log_d
|
||||
#undef log_v
|
||||
#undef ELOG_LVL_ASSERT
|
||||
#undef ELOG_LVL_ERROR
|
||||
#undef ELOG_LVL_WARN
|
||||
#undef ELOG_LVL_INFO
|
||||
#undef ELOG_LVL_DEBUG
|
||||
#undef ELOG_LVL_VERBOSE
|
||||
#define assert ASSERT
|
||||
#define log_e LOG_E
|
||||
#define log_w LOG_W
|
||||
#define log_i LOG_I
|
||||
#define log_d LOG_D
|
||||
#define log_v LOG_D
|
||||
#define log_raw LOG_RAW
|
||||
#define log_hex LOG_HEX
|
||||
#define ELOG_LVL_ASSERT LOG_LVL_ASSERT
|
||||
#define ELOG_LVL_ERROR LOG_LVL_ERROR
|
||||
#define ELOG_LVL_WARN LOG_LVL_WARNING
|
||||
#define ELOG_LVL_INFO LOG_LVL_INFO
|
||||
#define ELOG_LVL_DEBUG LOG_LVL_DBG
|
||||
#define ELOG_LVL_VERBOSE LOG_LVL_DBG
|
||||
|
||||
/* setting static output log level */
|
||||
#ifndef ULOG_OUTPUT_LVL
|
||||
#define ULOG_OUTPUT_LVL LOG_LVL_DBG
|
||||
#endif
|
||||
|
||||
/* buffer size for every line's log */
|
||||
#ifndef ULOG_LINE_BUF_SIZE
|
||||
#define ULOG_LINE_BUF_SIZE 128
|
||||
#endif
|
||||
|
||||
/* output filter's tag max length */
|
||||
#ifndef ULOG_FILTER_TAG_MAX_LEN
|
||||
#define ULOG_FILTER_TAG_MAX_LEN 23
|
||||
#endif
|
||||
|
||||
/* output filter's keyword max length */
|
||||
#ifndef ULOG_FILTER_KW_MAX_LEN
|
||||
#define ULOG_FILTER_KW_MAX_LEN 15
|
||||
#endif
|
||||
|
||||
#ifndef ULOG_NEWLINE_SIGN
|
||||
#define ULOG_NEWLINE_SIGN "\r\n"
|
||||
#endif
|
||||
|
||||
#define ULOG_FRAME_MAGIC 0x10
|
||||
|
||||
/* tag's level filter */
|
||||
struct ulog_tag_lvl_filter
|
||||
{
|
||||
char tag[ULOG_FILTER_TAG_MAX_LEN + 1];
|
||||
rt_uint32_t level;
|
||||
rt_slist_t list;
|
||||
};
|
||||
typedef struct ulog_tag_lvl_filter *ulog_tag_lvl_filter_t;
|
||||
|
||||
struct ulog_frame
|
||||
{
|
||||
/* magic word is 0x10 ('lo') */
|
||||
rt_uint32_t magic:8;
|
||||
rt_uint32_t is_raw:1;
|
||||
rt_uint32_t log_len:23;
|
||||
rt_uint32_t level;
|
||||
const char *log;
|
||||
const char *tag;
|
||||
};
|
||||
typedef struct ulog_frame *ulog_frame_t;
|
||||
|
||||
struct ulog_backend
|
||||
{
|
||||
char name[RT_NAME_MAX];
|
||||
rt_bool_t support_color;
|
||||
rt_uint32_t out_level;
|
||||
void (*init) (struct ulog_backend *backend);
|
||||
void (*output)(struct ulog_backend *backend, rt_uint32_t level, const char *tag, rt_bool_t is_raw, const char *log, rt_size_t len);
|
||||
void (*flush) (struct ulog_backend *backend);
|
||||
void (*deinit)(struct ulog_backend *backend);
|
||||
/* The filter will be call before output. It will return TRUE when the filter condition is math. */
|
||||
rt_bool_t (*filter)(struct ulog_backend *backend, rt_uint32_t level, const char *tag, rt_bool_t is_raw, const char *log, rt_size_t len);
|
||||
rt_slist_t list;
|
||||
};
|
||||
typedef struct ulog_backend *ulog_backend_t;
|
||||
typedef rt_bool_t (*ulog_backend_filter_t)(struct ulog_backend *backend, rt_uint32_t level, const char *tag, rt_bool_t is_raw, const char *log, rt_size_t len);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* _ULOG_DEF_H_ */
|
8
rt-thread/components/utilities/utest/SConscript
Normal file
8
rt-thread/components/utilities/utest/SConscript
Normal file
@@ -0,0 +1,8 @@
|
||||
from building import *
|
||||
|
||||
cwd = GetCurrentDir()
|
||||
src = Glob('*.c')
|
||||
CPPPATH = [cwd]
|
||||
group = DefineGroup('UTest', src, depend = ['RT_USING_UTEST'], CPPPATH = CPPPATH)
|
||||
|
||||
Return('group')
|
460
rt-thread/components/utilities/utest/utest.c
Normal file
460
rt-thread/components/utilities/utest/utest.c
Normal file
@@ -0,0 +1,460 @@
|
||||
/*
|
||||
* Copyright (c) 2006-2021, RT-Thread Development Team
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*
|
||||
* Change Logs:
|
||||
* Date Author Notes
|
||||
* 2018-11-19 MurphyZhao the first version
|
||||
*/
|
||||
|
||||
#include <rtthread.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "utest.h"
|
||||
#include <utest_log.h>
|
||||
|
||||
#undef DBG_TAG
|
||||
#undef DBG_LVL
|
||||
|
||||
#define DBG_TAG "utest"
|
||||
#ifdef UTEST_DEBUG
|
||||
#define DBG_LVL DBG_LOG
|
||||
#else
|
||||
#define DBG_LVL DBG_INFO
|
||||
#endif
|
||||
#include <rtdbg.h>
|
||||
|
||||
#if RT_CONSOLEBUF_SIZE < 256
|
||||
#error "RT_CONSOLEBUF_SIZE is less than 256!"
|
||||
#endif
|
||||
|
||||
#ifdef UTEST_THR_STACK_SIZE
|
||||
#define UTEST_THREAD_STACK_SIZE UTEST_THR_STACK_SIZE
|
||||
#else
|
||||
#define UTEST_THREAD_STACK_SIZE (4096)
|
||||
#endif
|
||||
|
||||
#ifdef UTEST_THR_PRIORITY
|
||||
#define UTEST_THREAD_PRIORITY UTEST_THR_PRIORITY
|
||||
#else
|
||||
#define UTEST_THREAD_PRIORITY FINSH_THREAD_PRIORITY
|
||||
#endif
|
||||
|
||||
static rt_uint8_t utest_log_lv = UTEST_LOG_ALL;
|
||||
static utest_tc_export_t tc_table = RT_NULL;
|
||||
static rt_size_t tc_num;
|
||||
static rt_uint32_t tc_loop;
|
||||
static rt_uint8_t *tc_fail_list;
|
||||
static struct utest local_utest = {UTEST_PASSED, 0, 0};
|
||||
|
||||
#if defined(__ICCARM__) || defined(__ICCRX__) /* for IAR compiler */
|
||||
#pragma section="UtestTcTab"
|
||||
#elif defined(_MSC_VER)
|
||||
#pragma section("UtestTcTab$a", read)
|
||||
__declspec(allocate("UtestTcTab$a")) const struct utest_tc_export __tc_export_begin =
|
||||
{
|
||||
"__start",
|
||||
};
|
||||
|
||||
#pragma section("UtestTcTab$z", read)
|
||||
__declspec(allocate("UtestTcTab$z")) const struct utest_tc_export __tc_export_end =
|
||||
{
|
||||
"__end",
|
||||
};
|
||||
#endif
|
||||
|
||||
#define TC_FAIL_LIST_SIZE (RT_ALIGN(tc_num, 8) / 8)
|
||||
#define TC_FAIL_LIST_MARK_FAILED(index) (tc_fail_list[index / 8] |= (1UL << (index % 8)))
|
||||
#define TC_FAIL_LIST_IS_FAILED(index) (tc_fail_list[index / 8] & (1UL << (index % 8)))
|
||||
|
||||
void utest_log_lv_set(rt_uint8_t lv)
|
||||
{
|
||||
if (lv == UTEST_LOG_ALL || lv == UTEST_LOG_ASSERT)
|
||||
{
|
||||
utest_log_lv = lv;
|
||||
}
|
||||
}
|
||||
|
||||
int utest_init(void)
|
||||
{
|
||||
/* initialize the utest commands table.*/
|
||||
#if defined(__ARMCC_VERSION) /* ARM C Compiler */
|
||||
extern const int UtestTcTab$$Base;
|
||||
extern const int UtestTcTab$$Limit;
|
||||
tc_table = (utest_tc_export_t)&UtestTcTab$$Base;
|
||||
tc_num = (utest_tc_export_t)&UtestTcTab$$Limit - tc_table;
|
||||
#elif defined (__ICCARM__) || defined(__ICCRX__) /* for IAR Compiler */
|
||||
tc_table = (utest_tc_export_t)__section_begin("UtestTcTab");
|
||||
tc_num = (utest_tc_export_t)__section_end("UtestTcTab") - tc_table;
|
||||
#else
|
||||
unsigned int *ptr_begin, *ptr_end;
|
||||
#if defined(__GNUC__)
|
||||
extern const int __rt_utest_tc_tab_start;
|
||||
extern const int __rt_utest_tc_tab_end;
|
||||
ptr_begin = (unsigned int *)&__rt_utest_tc_tab_start;
|
||||
ptr_end = (unsigned int *)&__rt_utest_tc_tab_end;
|
||||
#elif defined(_MSC_VER)
|
||||
ptr_begin = (unsigned int *)&__tc_export_begin;
|
||||
ptr_end = (unsigned int *)&__tc_export_end;
|
||||
ptr_begin += (sizeof(struct utest_tc_export) / sizeof(unsigned int));
|
||||
#endif
|
||||
while (*ptr_begin == 0) ptr_begin++;
|
||||
ptr_end--;
|
||||
while (*ptr_end == 0) ptr_end--;
|
||||
/* copy tc_table from rodata section to ram */
|
||||
for (unsigned int *ptr = ptr_begin; ptr < ptr_end;)
|
||||
{
|
||||
if (!tc_table)
|
||||
tc_table = (utest_tc_export_t)rt_malloc(sizeof(struct utest_tc_export));
|
||||
else
|
||||
tc_table = (utest_tc_export_t)rt_realloc(tc_table, (tc_num + 1)* sizeof(struct utest_tc_export));
|
||||
RT_ASSERT(tc_table);
|
||||
tc_table[tc_num++] = *((utest_tc_export_t)ptr);
|
||||
ptr += (sizeof(struct utest_tc_export) / sizeof(unsigned int));
|
||||
while (*ptr == 0) ptr++;
|
||||
}
|
||||
#endif
|
||||
|
||||
LOG_I("utest is initialize success.");
|
||||
LOG_I("total utest testcase num: (%d)", tc_num);
|
||||
if (tc_num > 0)
|
||||
{
|
||||
tc_fail_list = rt_malloc(TC_FAIL_LIST_SIZE);
|
||||
if(!tc_fail_list)
|
||||
{
|
||||
LOG_E("no memory, tc_fail_list init failed!");
|
||||
}
|
||||
}
|
||||
return tc_num;
|
||||
}
|
||||
INIT_COMPONENT_EXPORT(utest_init);
|
||||
|
||||
static long utest_tc_list(void)
|
||||
{
|
||||
rt_size_t i = 0;
|
||||
|
||||
LOG_I("Commands list : ");
|
||||
|
||||
for (i = 0; i < tc_num; i++)
|
||||
{
|
||||
LOG_I("[testcase name]:%s; [run timeout]:%d", tc_table[i].name, tc_table[i].run_timeout);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
MSH_CMD_EXPORT_ALIAS(utest_tc_list, utest_list, output all utest testcase);
|
||||
|
||||
static const char *file_basename(const char *file)
|
||||
{
|
||||
char *end_ptr = RT_NULL;
|
||||
char *rst = RT_NULL;
|
||||
|
||||
if (!((end_ptr = strrchr(file, '\\')) != RT_NULL || \
|
||||
(end_ptr = strrchr(file, '/')) != RT_NULL) || \
|
||||
(rt_strlen(file) < 2))
|
||||
{
|
||||
rst = (char *)file;
|
||||
}
|
||||
else
|
||||
{
|
||||
rst = (char *)(end_ptr + 1);
|
||||
}
|
||||
return (const char *)rst;
|
||||
}
|
||||
|
||||
static int utest_help(void)
|
||||
{
|
||||
rt_kprintf("\n");
|
||||
rt_kprintf("Command: utest_run\n");
|
||||
rt_kprintf(" info: Execute test cases.\n");
|
||||
rt_kprintf(" format: utest_run [-thread or -help] [testcase name] [loop num]\n");
|
||||
rt_kprintf(" usage:\n");
|
||||
rt_kprintf(" 1. utest_run\n");
|
||||
rt_kprintf(" Do not specify a test case name. Run all test cases.\n");
|
||||
rt_kprintf(" 2. utest_run -thread\n");
|
||||
rt_kprintf(" Do not specify a test case name. Run all test cases in threaded mode.\n");
|
||||
rt_kprintf(" 3. utest_run testcaseA\n");
|
||||
rt_kprintf(" Run 'testcaseA'.\n");
|
||||
rt_kprintf(" 4. utest_run testcaseA 10\n");
|
||||
rt_kprintf(" Run 'testcaseA' ten times.\n");
|
||||
rt_kprintf(" 5. utest_run -thread testcaseA\n");
|
||||
rt_kprintf(" Run 'testcaseA' in threaded mode.\n");
|
||||
rt_kprintf(" 6. utest_run -thread testcaseA 10\n");
|
||||
rt_kprintf(" Run 'testcaseA' ten times in threaded mode.\n");
|
||||
rt_kprintf(" 7. utest_run test*\n");
|
||||
rt_kprintf(" support '*' wildcard. Run all test cases starting with 'test'.\n");
|
||||
rt_kprintf(" 8. utest_run -help\n");
|
||||
rt_kprintf(" Show utest help information\n");
|
||||
rt_kprintf("\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void utest_run(const char *utest_name)
|
||||
{
|
||||
rt_size_t i;
|
||||
rt_uint32_t index;
|
||||
rt_bool_t is_find;
|
||||
rt_uint32_t tc_fail_num = 0;
|
||||
rt_uint32_t tc_run_num = 0;
|
||||
|
||||
for (index = 0; index < tc_loop; index ++)
|
||||
{
|
||||
i = 0;
|
||||
is_find = RT_FALSE;
|
||||
|
||||
tc_fail_num = 0;
|
||||
tc_run_num = 0;
|
||||
if (tc_fail_list)
|
||||
{
|
||||
rt_memset(tc_fail_list, 0, TC_FAIL_LIST_SIZE);
|
||||
}
|
||||
|
||||
LOG_I("[==========] [ utest ] loop %d/%d", index + 1, tc_loop);
|
||||
LOG_I("[==========] [ utest ] started");
|
||||
while(i < tc_num)
|
||||
{
|
||||
if (utest_name)
|
||||
{
|
||||
int len = strlen(utest_name);
|
||||
if (utest_name[len - 1] == '*')
|
||||
{
|
||||
len -= 1;
|
||||
}
|
||||
if (rt_memcmp(tc_table[i].name, utest_name, len) != 0)
|
||||
{
|
||||
i++;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
is_find = RT_TRUE;
|
||||
|
||||
LOG_I("[----------] [ testcase ] (%s) started", tc_table[i].name);
|
||||
if (tc_table[i].init != RT_NULL)
|
||||
{
|
||||
if (tc_table[i].init() != RT_EOK)
|
||||
{
|
||||
LOG_E("[ FAILED ] [ result ] testcase (%s)", tc_table[i].name);
|
||||
goto __tc_continue;
|
||||
}
|
||||
}
|
||||
|
||||
if (tc_table[i].tc != RT_NULL)
|
||||
{
|
||||
tc_table[i].tc();
|
||||
if (local_utest.failed_num == 0)
|
||||
{
|
||||
LOG_I("[ PASSED ] [ result ] testcase (%s)", tc_table[i].name);
|
||||
}
|
||||
else
|
||||
{
|
||||
TC_FAIL_LIST_MARK_FAILED(i);
|
||||
tc_fail_num ++;
|
||||
LOG_E("[ FAILED ] [ result ] testcase (%s)", tc_table[i].name);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
LOG_E("[ FAILED ] [ result ] testcase (%s)", tc_table[i].name);
|
||||
}
|
||||
|
||||
if (tc_table[i].cleanup != RT_NULL)
|
||||
{
|
||||
if (tc_table[i].cleanup() != RT_EOK)
|
||||
{
|
||||
LOG_E("[ FAILED ] [ result ] testcase (%s)", tc_table[i].name);
|
||||
goto __tc_continue;
|
||||
}
|
||||
}
|
||||
|
||||
__tc_continue:
|
||||
LOG_I("[----------] [ testcase ] (%s) finished", tc_table[i].name);
|
||||
|
||||
tc_run_num ++;
|
||||
i++;
|
||||
}
|
||||
|
||||
if (i == tc_num && is_find == RT_FALSE && utest_name != RT_NULL)
|
||||
{
|
||||
LOG_I("[==========] [ utest ] Not find (%s)", utest_name);
|
||||
LOG_I("[==========] [ utest ] finished");
|
||||
break;
|
||||
}
|
||||
|
||||
LOG_I("[==========] [ utest ] finished");
|
||||
LOG_I("[==========] [ utest ] %d tests from %d testcase ran.", tc_run_num, tc_num);
|
||||
LOG_I("[ PASSED ] [ result ] %d tests.", tc_run_num - tc_fail_num);
|
||||
|
||||
if(tc_fail_list && (tc_fail_num > 0))
|
||||
{
|
||||
LOG_E("[ FAILED ] [ result ] %d tests, listed below:", tc_fail_num);
|
||||
for(i = 0; i < tc_num; i ++)
|
||||
{
|
||||
if (TC_FAIL_LIST_IS_FAILED(i))
|
||||
{
|
||||
LOG_E("[ FAILED ] [ result ] %s", tc_table[i].name);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void utest_thr_entry(const char *utest_name)
|
||||
{
|
||||
/* see commit:0dc7b9a for details */
|
||||
rt_thread_mdelay(1000);
|
||||
|
||||
utest_run(utest_name);
|
||||
}
|
||||
|
||||
long utest_testcase_run(int argc, char** argv)
|
||||
{
|
||||
|
||||
static char utest_name[UTEST_NAME_MAX_LEN];
|
||||
rt_memset(utest_name, 0x0, sizeof(utest_name));
|
||||
|
||||
tc_loop = 1;
|
||||
|
||||
if (argc == 1)
|
||||
{
|
||||
utest_run(RT_NULL);
|
||||
return 0;
|
||||
}
|
||||
else if (argc == 2 || argc == 3 || argc == 4)
|
||||
{
|
||||
if (rt_strcmp(argv[1], "-thread") == 0)
|
||||
{
|
||||
rt_thread_t tid = RT_NULL;
|
||||
if (argc == 3 || argc == 4)
|
||||
{
|
||||
rt_strncpy(utest_name, argv[2], sizeof(utest_name) -1);
|
||||
|
||||
if (argc == 4) tc_loop = atoi(argv[3]);
|
||||
}
|
||||
tid = rt_thread_create("utest",
|
||||
(void (*)(void *))utest_thr_entry, utest_name,
|
||||
UTEST_THREAD_STACK_SIZE, UTEST_THREAD_PRIORITY, 10);
|
||||
if (tid != NULL)
|
||||
{
|
||||
rt_thread_startup(tid);
|
||||
}
|
||||
}
|
||||
else if (rt_strcmp(argv[1], "-help") == 0)
|
||||
{
|
||||
utest_help();
|
||||
}
|
||||
else
|
||||
{
|
||||
rt_strncpy(utest_name, argv[1], sizeof(utest_name) -1);
|
||||
if (argc == 3) tc_loop = atoi(argv[2]);
|
||||
utest_run(utest_name);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
LOG_E("[ error ] at (%s:%d), in param error.", __func__, __LINE__);
|
||||
utest_help();
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
MSH_CMD_EXPORT_ALIAS(utest_testcase_run, utest_run, utest_run [-thread or -help] [testcase name] [loop num]);
|
||||
|
||||
utest_t utest_handle_get(void)
|
||||
{
|
||||
return (utest_t)&local_utest;
|
||||
}
|
||||
|
||||
void utest_unit_run(test_unit_func func, const char *unit_func_name)
|
||||
{
|
||||
// LOG_I("[==========] utest unit name: (%s)", unit_func_name);
|
||||
local_utest.error = UTEST_PASSED;
|
||||
local_utest.passed_num = 0;
|
||||
local_utest.failed_num = 0;
|
||||
|
||||
if (func != RT_NULL)
|
||||
{
|
||||
func();
|
||||
}
|
||||
}
|
||||
|
||||
void utest_assert(int value, const char *file, int line, const char *func, const char *msg)
|
||||
{
|
||||
if (!(value))
|
||||
{
|
||||
local_utest.error = UTEST_FAILED;
|
||||
local_utest.failed_num ++;
|
||||
LOG_E("[ ASSERT ] [ unit ] at (%s); func: (%s:%d); msg: (%s)", file_basename(file), func, line, msg);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (utest_log_lv == UTEST_LOG_ALL)
|
||||
{
|
||||
LOG_D("[ OK ] [ unit ] (%s:%d) is passed", func, line);
|
||||
}
|
||||
local_utest.error = UTEST_PASSED;
|
||||
local_utest.passed_num ++;
|
||||
}
|
||||
}
|
||||
|
||||
void utest_assert_string(const char *a, const char *b, rt_bool_t equal, const char *file, int line, const char *func, const char *msg)
|
||||
{
|
||||
if (a == RT_NULL || b == RT_NULL)
|
||||
{
|
||||
utest_assert(0, file, line, func, msg);
|
||||
}
|
||||
|
||||
if (equal)
|
||||
{
|
||||
if (rt_strcmp(a, b) == 0)
|
||||
{
|
||||
utest_assert(1, file, line, func, msg);
|
||||
}
|
||||
else
|
||||
{
|
||||
utest_assert(0, file, line, func, msg);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (rt_strcmp(a, b) == 0)
|
||||
{
|
||||
utest_assert(0, file, line, func, msg);
|
||||
}
|
||||
else
|
||||
{
|
||||
utest_assert(1, file, line, func, msg);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void utest_assert_buf(const char *a, const char *b, rt_size_t sz, rt_bool_t equal, const char *file, int line, const char *func, const char *msg)
|
||||
{
|
||||
if (a == RT_NULL || b == RT_NULL)
|
||||
{
|
||||
utest_assert(0, file, line, func, msg);
|
||||
}
|
||||
|
||||
if (equal)
|
||||
{
|
||||
if (rt_memcmp(a, b, sz) == 0)
|
||||
{
|
||||
utest_assert(1, file, line, func, msg);
|
||||
}
|
||||
else
|
||||
{
|
||||
utest_assert(0, file, line, func, msg);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (rt_memcmp(a, b, sz) == 0)
|
||||
{
|
||||
utest_assert(0, file, line, func, msg);
|
||||
}
|
||||
else
|
||||
{
|
||||
utest_assert(1, file, line, func, msg);
|
||||
}
|
||||
}
|
||||
}
|
184
rt-thread/components/utilities/utest/utest.h
Normal file
184
rt-thread/components/utilities/utest/utest.h
Normal file
@@ -0,0 +1,184 @@
|
||||
/*
|
||||
* Copyright (c) 2006-2021, RT-Thread Development Team
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*
|
||||
* Change Logs:
|
||||
* Date Author Notes
|
||||
* 2018-11-19 MurphyZhao the first version
|
||||
*/
|
||||
|
||||
#ifndef __UTEST_H__
|
||||
#define __UTEST_H__
|
||||
|
||||
#include <rtthread.h>
|
||||
#include <stdint.h>
|
||||
#include "utest_log.h"
|
||||
#include "utest_assert.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* utest_error
|
||||
*
|
||||
* @brief Test result.
|
||||
*
|
||||
* @member UTEST_PASSED Test success.
|
||||
* @member UTEST_FAILED Test failed.
|
||||
* @member UTEST_PASSED Test skipped.
|
||||
*
|
||||
*/
|
||||
enum utest_error
|
||||
{
|
||||
UTEST_PASSED = 0,
|
||||
UTEST_FAILED = 1,
|
||||
UTEST_SKIPPED = 2
|
||||
};
|
||||
typedef enum utest_error utest_err_e;
|
||||
|
||||
/**
|
||||
* utest
|
||||
*
|
||||
* @brief utest data structure.
|
||||
*
|
||||
* @member error Error number from enum `utest_error`.
|
||||
* @member passed_num Total number of tests passed.
|
||||
* @member failed_num Total number of tests failed.
|
||||
*
|
||||
*/
|
||||
struct utest
|
||||
{
|
||||
utest_err_e error;
|
||||
uint32_t passed_num;
|
||||
uint32_t failed_num;
|
||||
};
|
||||
typedef struct utest *utest_t;
|
||||
|
||||
/**
|
||||
* utest_tc_export
|
||||
*
|
||||
* @brief utest testcase data structure.
|
||||
* Will export the data to `UtestTcTab` section in flash.
|
||||
*
|
||||
* @member name Testcase name.
|
||||
* @member run_timeout Testcase maximum test time (Time unit: seconds).
|
||||
* @member init Necessary initialization before executing the test case function.
|
||||
* @member tc Total number of tests failed.
|
||||
* @member cleanup Total number of tests failed.
|
||||
*
|
||||
*/
|
||||
struct utest_tc_export {
|
||||
const char *name;
|
||||
uint32_t run_timeout;
|
||||
rt_err_t (*init)(void);
|
||||
void (*tc)(void);
|
||||
rt_err_t (*cleanup)(void);
|
||||
};
|
||||
typedef struct utest_tc_export *utest_tc_export_t;
|
||||
|
||||
/**
|
||||
* test_unit_func
|
||||
*
|
||||
* @brief Unit test handler function pointer.
|
||||
*
|
||||
*/
|
||||
typedef void (*test_unit_func)(void);
|
||||
|
||||
/**
|
||||
* utest_unit_run
|
||||
*
|
||||
* @brief Unit test function executor.
|
||||
* No need for the user to call this function directly
|
||||
*
|
||||
* @param func Unit test function.
|
||||
* @param unit_func_name Unit test function name.
|
||||
*
|
||||
* @return void
|
||||
*
|
||||
*/
|
||||
void utest_unit_run(test_unit_func func, const char *unit_func_name);
|
||||
|
||||
/**
|
||||
* utest_handle_get
|
||||
*
|
||||
* @brief Get the utest data structure handle.
|
||||
* No need for the user to call this function directly
|
||||
*
|
||||
* @param void
|
||||
*
|
||||
* @return utest_t type. (struct utest *)
|
||||
*
|
||||
*/
|
||||
utest_t utest_handle_get(void);
|
||||
|
||||
/**
|
||||
* UTEST_NAME_MAX_LEN
|
||||
*
|
||||
* @brief Testcase name maximum length.
|
||||
*
|
||||
*/
|
||||
#define UTEST_NAME_MAX_LEN (128u)
|
||||
|
||||
/**
|
||||
* UTEST_TC_EXPORT
|
||||
*
|
||||
* @brief Export testcase function to `UtestTcTab` section in flash.
|
||||
* Used in application layer.
|
||||
*
|
||||
* @param testcase The testcase function.
|
||||
* @param name The testcase name.
|
||||
* @param init The initialization function of the test case.
|
||||
* @param cleanup The cleanup function of the test case.
|
||||
* @param timeout Testcase maximum test time (Time unit: seconds).
|
||||
*
|
||||
* @return None
|
||||
*
|
||||
*/
|
||||
#ifdef _MSC_VER
|
||||
#pragma section("UtestTcTab$f",read)
|
||||
#define UTEST_TC_EXPORT(testcase, name, init, cleanup, timeout) \
|
||||
__declspec(allocate("UtestTcTab$f")) \
|
||||
static const struct utest_tc_export _utest_testcase = \
|
||||
{ \
|
||||
name, \
|
||||
timeout, \
|
||||
init, \
|
||||
testcase, \
|
||||
cleanup \
|
||||
}
|
||||
#pragma comment(linker, "/merge:UtestTcTab=tctext")
|
||||
#else
|
||||
#define UTEST_TC_EXPORT(testcase, name, init, cleanup, timeout) \
|
||||
rt_used static const struct utest_tc_export _utest_testcase \
|
||||
rt_section("UtestTcTab") = \
|
||||
{ \
|
||||
name, \
|
||||
timeout, \
|
||||
init, \
|
||||
testcase, \
|
||||
cleanup \
|
||||
}
|
||||
#endif /* _MSC_VER */
|
||||
|
||||
/**
|
||||
* UTEST_UNIT_RUN
|
||||
*
|
||||
* @brief Unit test function executor.
|
||||
* Used in `testcase` function in application.
|
||||
*
|
||||
* @param test_unit_func Unit test function
|
||||
*
|
||||
* @return None
|
||||
*
|
||||
*/
|
||||
#define UTEST_UNIT_RUN(test_unit_func) \
|
||||
utest_unit_run(test_unit_func, #test_unit_func); \
|
||||
if(utest_handle_get()->failed_num != 0) return;
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* __UTEST_H__ */
|
72
rt-thread/components/utilities/utest/utest_assert.h
Normal file
72
rt-thread/components/utilities/utest/utest_assert.h
Normal file
@@ -0,0 +1,72 @@
|
||||
/*
|
||||
* Copyright (c) 2006-2021, RT-Thread Development Team
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*
|
||||
* Change Logs:
|
||||
* Date Author Notes
|
||||
* 2018-11-19 MurphyZhao the first version
|
||||
*/
|
||||
|
||||
#ifndef __UTEST_ASSERT_H__
|
||||
#define __UTEST_ASSERT_H__
|
||||
|
||||
#include "utest.h"
|
||||
#include <rtthread.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/* No need for the user to use this function directly */
|
||||
void utest_assert(int value, const char *file, int line, const char *func, const char *msg);
|
||||
|
||||
/* No need for the user to use this function directly */
|
||||
void utest_assert_string(const char *a, const char *b, rt_bool_t equal, const char *file, int line, const char *func, const char *msg);
|
||||
void utest_assert_buf(const char *a, const char *b, rt_size_t sz, rt_bool_t equal, const char *file, int line, const char *func, const char *msg);
|
||||
|
||||
/* No need for the user to use this macro directly */
|
||||
#define __utest_assert(value, msg) utest_assert(value, __FILE__, __LINE__, __func__, msg)
|
||||
|
||||
/**
|
||||
* uassert_x macros
|
||||
*
|
||||
* @brief Get the utest data structure handle.
|
||||
* No need for the user to call this function directly.
|
||||
*
|
||||
* @macro uassert_true if @value is true, not assert, means passing.
|
||||
* @macro uassert_false if @value is false, not assert, means passing.
|
||||
* @macro uassert_null if @value is null, not assert, means passing.
|
||||
* @macro uassert_not_null if @value is not null, not assert, means passing.
|
||||
* @macro uassert_int_equal if @a equal to @b, not assert, means passing. Integer type test.
|
||||
* @macro uassert_int_not_equal if @a not equal to @b, not assert, means passing. Integer type test.
|
||||
* @macro uassert_str_equal if @a equal to @b, not assert, means passing. String type test.
|
||||
* @macro uassert_str_not_equal if @a not equal to @b, not assert, means passing. String type test.
|
||||
* @macro uassert_buf_equal if @a equal to @b, not assert, means passing. buf type test.
|
||||
* @macro uassert_buf_not_equal if @a not equal to @b, not assert, means passing. buf type test.
|
||||
* @macro uassert_in_range if @value is in range of min and max, not assert, means passing.
|
||||
* @macro uassert_not_in_range if @value is not in range of min and max, not assert, means passing.
|
||||
*
|
||||
*/
|
||||
#define uassert_true(value) __utest_assert(value, "(" #value ") is false")
|
||||
#define uassert_false(value) __utest_assert(!(value), "(" #value ") is true")
|
||||
#define uassert_null(value) __utest_assert((const char *)(value) == RT_NULL, "(" #value ") is not null")
|
||||
#define uassert_not_null(value) __utest_assert((const char *)(value) != RT_NULL, "(" #value ") is null")
|
||||
|
||||
#define uassert_int_equal(a, b) __utest_assert((a) == (b), "(" #a ") not equal to (" #b ")")
|
||||
#define uassert_int_not_equal(a, b) __utest_assert((a) != (b), "(" #a ") equal to (" #b ")")
|
||||
|
||||
#define uassert_str_equal(a, b) utest_assert_string((const char*)(a), (const char*)(b), RT_TRUE, __FILE__, __LINE__, __func__, "string not equal")
|
||||
#define uassert_str_not_equal(a, b) utest_assert_string((const char*)(a), (const char*)(b), RT_FALSE, __FILE__, __LINE__, __func__, "string equal")
|
||||
|
||||
#define uassert_buf_equal(a, b, sz) utest_assert_buf((const char*)(a), (const char*)(b), (sz), RT_TRUE, __FILE__, __LINE__, __func__, "buf not equal")
|
||||
#define uassert_buf_not_equal(a, b, sz) utest_assert_buf((const char*)(a), (const char*)(b), (sz), RT_FALSE, __FILE__, __LINE__, __func__, "buf equal")
|
||||
|
||||
#define uassert_in_range(value, min, max) __utest_assert(((value >= min) && (value <= max)), "(" #value ") not in range("#min","#max")")
|
||||
#define uassert_not_in_range(value, min, max) __utest_assert(!((value >= min) && (value <= max)), "(" #value ") in range("#min","#max")")
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* __UTEST_ASSERT_H__ */
|
34
rt-thread/components/utilities/utest/utest_log.h
Normal file
34
rt-thread/components/utilities/utest/utest_log.h
Normal file
@@ -0,0 +1,34 @@
|
||||
/*
|
||||
* Copyright (c) 2006-2021, RT-Thread Development Team
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*
|
||||
* Change Logs:
|
||||
* Date Author Notes
|
||||
* 2018-11-19 MurphyZhao the first version
|
||||
*/
|
||||
|
||||
#ifndef __UTEST_LOG_H__
|
||||
#define __UTEST_LOG_H__
|
||||
|
||||
#include <rtthread.h>
|
||||
|
||||
#define UTEST_DEBUG
|
||||
|
||||
#undef DBG_TAG
|
||||
#undef DBG_LVL
|
||||
|
||||
#define DBG_TAG "testcase"
|
||||
#ifdef UTEST_DEBUG
|
||||
#define DBG_LVL DBG_LOG
|
||||
#else
|
||||
#define DBG_LVL DBG_INFO
|
||||
#endif
|
||||
#include <rtdbg.h>
|
||||
|
||||
#define UTEST_LOG_ALL (1u)
|
||||
#define UTEST_LOG_ASSERT (2u)
|
||||
|
||||
void utest_log_lv_set(rt_uint8_t lv);
|
||||
|
||||
#endif /* __UTEST_LOG_H__ */
|
8
rt-thread/components/utilities/var_export/SConscript
Normal file
8
rt-thread/components/utilities/var_export/SConscript
Normal file
@@ -0,0 +1,8 @@
|
||||
from building import *
|
||||
|
||||
cwd = GetCurrentDir()
|
||||
src = Glob('*.c')
|
||||
CPPPATH = [cwd]
|
||||
group = DefineGroup('Utilities', src, depend = ['RT_USING_VAR_EXPORT'], CPPPATH = CPPPATH)
|
||||
|
||||
Return('group')
|
244
rt-thread/components/utilities/var_export/var_export.c
Normal file
244
rt-thread/components/utilities/var_export/var_export.c
Normal file
@@ -0,0 +1,244 @@
|
||||
/*
|
||||
* Copyright (c) 2006-2022, RT-Thread Development Team
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*
|
||||
* Change Logs:
|
||||
* Date Author Notes
|
||||
* 2021-06-04 WillianChan first version
|
||||
* 2021-06-08 WillianChan support to MS VC++ compiler
|
||||
*/
|
||||
|
||||
#include <var_export.h>
|
||||
|
||||
static const ve_exporter_t *ve_exporter_table = RT_NULL;
|
||||
static rt_size_t ve_exporter_num = 0;
|
||||
|
||||
/* for IAR compiler */
|
||||
#if defined(__ICCARM__) || defined(__ICCRX__)
|
||||
#pragma section="VarExpTab"
|
||||
#endif
|
||||
|
||||
/* for ARM C and IAR Compiler */
|
||||
#if defined(__ARMCC_VERSION) || defined (__ICCARM__) || defined(__ICCRX__)
|
||||
static rt_used const struct ve_exporter __ve_table_start
|
||||
rt_section("0.""VarExpTab") = {"ve_start", "ve_start", 0};
|
||||
|
||||
static rt_used const struct ve_exporter __ve_table_end
|
||||
rt_section("2.""VarExpTab") = {"ve_end", "ve_end", 2};
|
||||
#endif
|
||||
|
||||
/* for MS VC++ compiler */
|
||||
#if defined(_MSC_VER)
|
||||
#pragma section("VarExpTab$a", read)
|
||||
__declspec(allocate("VarExpTab$a"))
|
||||
rt_used const struct ve_exporter __ve_table_start = { "ve_start", "ve_start", 0};
|
||||
|
||||
#pragma section("VarExpTab$z", read)
|
||||
__declspec(allocate("VarExpTab$z"))
|
||||
rt_used const struct ve_exporter __ve_table_end = { "ve_end", "ve_end", 2};
|
||||
|
||||
/* Find var objects in VarExpTab segments */
|
||||
static int ve_init_find_obj(unsigned int *begin, unsigned int *end, ve_exporter_t *table)
|
||||
{
|
||||
int obj_count = 0;
|
||||
|
||||
while (begin < end)
|
||||
{
|
||||
if (*begin != RT_NULL)
|
||||
{
|
||||
*table++ = *((struct ve_exporter *)begin);
|
||||
begin += sizeof(struct ve_exporter) / sizeof(unsigned int);
|
||||
obj_count += 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
begin++;
|
||||
}
|
||||
}
|
||||
|
||||
return obj_count;
|
||||
}
|
||||
#endif /* _MSC_VER */
|
||||
|
||||
/* initialize var export */
|
||||
int var_export_init(void)
|
||||
{
|
||||
/* initialize the var export table.*/
|
||||
#if defined(__ARMCC_VERSION) /* for ARM C Compiler */
|
||||
ve_exporter_table = &__ve_table_start + 1;
|
||||
ve_exporter_num = &__ve_table_end - &__ve_table_start;
|
||||
#elif defined (__IAR_SYSTEMS_ICC__) /* for IAR Compiler */
|
||||
ve_exporter_table = &__ve_table_start + 1;
|
||||
ve_exporter_num = &__ve_table_end - &__ve_table_start - 1;
|
||||
#elif defined (__GNUC__) /* for GCC Compiler */
|
||||
extern const int __ve_table_start;
|
||||
extern const int __ve_table_end;
|
||||
ve_exporter_table = (const ve_exporter_t *)&__ve_table_start;
|
||||
ve_exporter_num = (const ve_exporter_t *)&__ve_table_end - ve_exporter_table;
|
||||
#elif defined (_MSC_VER) /* for MS VC++ compiler */
|
||||
unsigned int *ptr_begin = (unsigned int *)&__ve_table_start;
|
||||
unsigned int *ptr_end = (unsigned int *)&__ve_table_end;
|
||||
static ve_exporter_t ve_exporter_tab[2048];
|
||||
static char __vexp_strbuf1[1024];
|
||||
static char __vexp_strbuf2[1024];
|
||||
ve_exporter_t ve_exporter_temp;
|
||||
rt_size_t index_i, index_j;
|
||||
|
||||
/* past the three members in first ptr_begin */
|
||||
ptr_begin += (sizeof(struct ve_exporter) / sizeof(unsigned int));
|
||||
while (*ptr_begin == 0) ptr_begin++;
|
||||
do ptr_end--; while (*ptr_end == 0);
|
||||
|
||||
/* Find var objects in custom segments to solve the problem of holes in objects in different files */
|
||||
ve_exporter_num = ve_init_find_obj(ptr_begin, ptr_end, ve_exporter_tab);
|
||||
|
||||
/* check if the ve_exporter_num is out of bounds */
|
||||
RT_ASSERT(ve_exporter_num < (sizeof(ve_exporter_tab) / sizeof(ve_exporter_t)));
|
||||
|
||||
/* bubble sort algorithms */
|
||||
for (index_i = 0; index_i < (ve_exporter_num - 1); index_i++)
|
||||
{
|
||||
for (index_j = 0; index_j < ((ve_exporter_num - 1) - index_i); index_j++)
|
||||
{
|
||||
/* splice ve_exporter's module and ve_exporter's identifier into a complete string */
|
||||
rt_snprintf(__vexp_strbuf1,
|
||||
sizeof(__vexp_strbuf1),
|
||||
"%s%s",
|
||||
ve_exporter_tab[index_j].module,
|
||||
ve_exporter_tab[index_j].identifier);
|
||||
rt_snprintf(__vexp_strbuf2,
|
||||
sizeof(__vexp_strbuf2),
|
||||
"%s%s",
|
||||
ve_exporter_tab[index_j + 1].module,
|
||||
ve_exporter_tab[index_j + 1].identifier);
|
||||
if (rt_strcmp(__vexp_strbuf1, __vexp_strbuf2) > 0)
|
||||
{
|
||||
ve_exporter_temp = ve_exporter_tab[index_j];
|
||||
ve_exporter_tab[index_j] = ve_exporter_tab[index_j + 1];
|
||||
ve_exporter_tab[index_j + 1] = ve_exporter_temp;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ve_exporter_table = ve_exporter_tab;
|
||||
#endif /* __ARMCC_VERSION */
|
||||
|
||||
return ve_exporter_num;
|
||||
}
|
||||
INIT_BOARD_EXPORT(var_export_init);
|
||||
|
||||
/* initialize module */
|
||||
int ve_module_init(ve_module_t *mod, const char *module)
|
||||
{
|
||||
const ve_exporter_t *exporter = ve_exporter_table;
|
||||
rt_bool_t first_exist = RT_FALSE;
|
||||
rt_size_t found_index;
|
||||
|
||||
for (found_index = 0; found_index < ve_exporter_num; found_index++)
|
||||
{
|
||||
if (!rt_strcmp(exporter->module, module))
|
||||
{
|
||||
if (first_exist == RT_FALSE)
|
||||
{
|
||||
mod->begin = exporter;
|
||||
first_exist = RT_TRUE;
|
||||
}
|
||||
mod->end = exporter;
|
||||
}
|
||||
exporter++;
|
||||
}
|
||||
|
||||
if (first_exist == RT_FALSE)
|
||||
{
|
||||
return -RT_ERROR;
|
||||
}
|
||||
|
||||
return RT_EOK;
|
||||
}
|
||||
|
||||
/* initialize iterator */
|
||||
void ve_iter_init(ve_module_t *mod, ve_iterator_t *iter)
|
||||
{
|
||||
if (iter)
|
||||
{
|
||||
iter->exp_index = mod->begin;
|
||||
iter->exp_end = mod->end;
|
||||
}
|
||||
}
|
||||
|
||||
/* iterate backward */
|
||||
const ve_exporter_t *ve_iter_next(ve_iterator_t *iter)
|
||||
{
|
||||
if (iter->exp_index <= iter->exp_end)
|
||||
{
|
||||
return iter->exp_index++;
|
||||
}
|
||||
else
|
||||
{
|
||||
return RT_NULL;
|
||||
}
|
||||
}
|
||||
|
||||
/* binary search based on identifier */
|
||||
const ve_exporter_t *ve_binary_search(ve_module_t *mod, const char *identifier)
|
||||
{
|
||||
int ve_low_num = 0;
|
||||
int ve_high_num = mod->end - mod->begin;
|
||||
int ve_mid_num = 0;
|
||||
int strcmp_rst = 0;
|
||||
|
||||
while ((ve_low_num <= ve_high_num) && (ve_high_num >= 0) && (ve_low_num >= 0))
|
||||
{
|
||||
ve_mid_num = (ve_high_num + ve_low_num) / 2;
|
||||
strcmp_rst = rt_strcmp(mod->begin[ve_mid_num].identifier, identifier);
|
||||
|
||||
if (strcmp_rst == 0)
|
||||
{
|
||||
return &mod->begin[ve_mid_num];
|
||||
}
|
||||
else if (strcmp_rst > 0)
|
||||
{
|
||||
ve_high_num = ve_mid_num - 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
ve_low_num = ve_mid_num + 1;
|
||||
}
|
||||
}
|
||||
|
||||
return RT_NULL;
|
||||
}
|
||||
|
||||
/* get the value by identifier */
|
||||
rt_base_t ve_value_get(ve_module_t *mod, const char *identifier)
|
||||
{
|
||||
const ve_exporter_t *exporter = ve_binary_search(mod, identifier);
|
||||
|
||||
if (exporter)
|
||||
{
|
||||
return exporter->value;
|
||||
}
|
||||
else
|
||||
{
|
||||
return VE_NOT_FOUND;
|
||||
}
|
||||
}
|
||||
|
||||
/* check if this value exists in the module*/
|
||||
rt_bool_t ve_value_exist(ve_module_t *mod, const char *identifier)
|
||||
{
|
||||
if (ve_binary_search(mod, identifier))
|
||||
{
|
||||
return RT_TRUE;
|
||||
}
|
||||
else
|
||||
{
|
||||
return RT_FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
rt_size_t ve_value_count(ve_module_t *mod)
|
||||
{
|
||||
return mod->end - mod->begin + 1;
|
||||
}
|
96
rt-thread/components/utilities/var_export/var_export.h
Normal file
96
rt-thread/components/utilities/var_export/var_export.h
Normal file
@@ -0,0 +1,96 @@
|
||||
/*
|
||||
* Copyright (c) 2006-2022, RT-Thread Development Team
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*
|
||||
* Change Logs:
|
||||
* Date Author Notes
|
||||
* 2021-06-04 WillianChan first version
|
||||
* 2021-06-08 WillianChan support to MS VC++ compiler
|
||||
*/
|
||||
|
||||
#ifndef _VAR_EXPORT_H__
|
||||
#define _VAR_EXPORT_H__
|
||||
|
||||
#include <rtthread.h>
|
||||
|
||||
/* exported object */
|
||||
struct ve_exporter
|
||||
{
|
||||
const char *module; /* module name */
|
||||
const char *identifier; /* module identifier */
|
||||
rt_base_t value; /* module value */
|
||||
};
|
||||
typedef struct ve_exporter ve_exporter_t;
|
||||
|
||||
/* module object */
|
||||
struct ve_module
|
||||
{
|
||||
const ve_exporter_t *begin; /* the first module of the same name */
|
||||
const ve_exporter_t *end; /* the last module of the same */
|
||||
};
|
||||
typedef struct ve_module ve_module_t;
|
||||
|
||||
/* iterator object */
|
||||
struct ve_iterator
|
||||
{
|
||||
const ve_exporter_t *exp_index; /* iterator index */
|
||||
const ve_exporter_t *exp_end; /* iterate over exporter */
|
||||
};
|
||||
typedef struct ve_iterator ve_iterator_t;
|
||||
|
||||
#define VE_NOT_FOUND (0xFFFFFFFFu) /* not found */
|
||||
|
||||
/* exporter's export command */
|
||||
#if defined(__ARMCC_VERSION) || defined(__IAR_SYSTEMS_ICC__)
|
||||
#define VAR_EXPORT(module, identi, value) \
|
||||
const char _vexp_##identi##_module[] rt_section(".rodata.vexp") = #module; \
|
||||
const char _vexp_##identi##_identi[] rt_section(".rodata.vexp") = #identi; \
|
||||
rt_used const struct ve_exporter _vexp_##module##identi \
|
||||
rt_section("1."#module".VarExpTab."#identi) = \
|
||||
{ \
|
||||
_vexp_##identi##_module, \
|
||||
_vexp_##identi##_identi, \
|
||||
value, \
|
||||
}
|
||||
#elif defined(__GNUC__)
|
||||
#define VAR_EXPORT(module, identi, value) \
|
||||
const char _vexp_##identi##_module[] rt_section(".rodata.vexp") = #module; \
|
||||
const char _vexp_##identi##_identi[] rt_section(".rodata.vexp") = #identi; \
|
||||
rt_used const struct ve_exporter _vexp_##module##identi \
|
||||
rt_section(#module".VarExpTab."#identi) = \
|
||||
{ \
|
||||
_vexp_##identi##_module, \
|
||||
_vexp_##identi##_identi, \
|
||||
value, \
|
||||
}
|
||||
#elif defined(_MSC_VER)
|
||||
#pragma section("VarExpTab$f",read)
|
||||
#define VAR_EXPORT(module, identi, value) \
|
||||
const char _vexp_##identi##_module[] rt_section(".rodata.vexp") = #module; \
|
||||
const char _vexp_##identi##_identi[] rt_section(".rodata.vexp") = #identi; \
|
||||
__declspec(allocate("VarExpTab$f")) \
|
||||
rt_used const struct ve_exporter _vexp_##module##identi = \
|
||||
{ \
|
||||
_vexp_##identi##_module, \
|
||||
_vexp_##identi##_identi, \
|
||||
value, \
|
||||
}
|
||||
#endif
|
||||
|
||||
/* initialize var export */
|
||||
int ve_exporter_init(void);
|
||||
/* initialize module */
|
||||
int ve_module_init(ve_module_t *mod, const char *module);
|
||||
/* initialize iterator */
|
||||
void ve_iter_init(ve_module_t *mod, ve_iterator_t *iter);
|
||||
/* iterate backward */
|
||||
const ve_exporter_t *ve_iter_next(ve_iterator_t *iter);
|
||||
/* get the value by identifier */
|
||||
rt_base_t ve_value_get(ve_module_t *mod, const char *identifier);
|
||||
/* check if this value exists in the module*/
|
||||
rt_bool_t ve_value_exist(ve_module_t *mod, const char *identifier);
|
||||
rt_size_t ve_value_count(ve_module_t *mod);
|
||||
const ve_exporter_t *ve_binary_search(ve_module_t *mod, const char *identifier);
|
||||
|
||||
#endif /* _VAR_EXPORT_H__ */
|
166
rt-thread/components/utilities/var_export/var_export_cmd.c
Normal file
166
rt-thread/components/utilities/var_export/var_export_cmd.c
Normal file
@@ -0,0 +1,166 @@
|
||||
/*
|
||||
* Copyright (c) 2006-2022, RT-Thread Development Team
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*
|
||||
* Change Logs:
|
||||
* Date Author Notes
|
||||
* 2021-06-05 WillianChan first version
|
||||
*/
|
||||
|
||||
#include <var_export.h>
|
||||
|
||||
static int ve_cmd_help(int argc, char **argv);
|
||||
static int ve_find_module(int argc, char **argv);
|
||||
static int ve_find_value(int argc, char **argv);
|
||||
|
||||
|
||||
struct ve_cmd_des
|
||||
{
|
||||
const char *cmd;
|
||||
int (*fun)(int argc, char **argv);
|
||||
};
|
||||
|
||||
/* dcm cmd table */
|
||||
static const struct ve_cmd_des cmd_tab[] =
|
||||
{
|
||||
{"module", ve_find_module},
|
||||
{"value", ve_find_value},
|
||||
};
|
||||
|
||||
static int ve_cmd_help(int argc, char **argv)
|
||||
{
|
||||
rt_kprintf("Usage:\n");
|
||||
rt_kprintf("ve_find module <module> - Find by module name\n");
|
||||
rt_kprintf("ve_find value <module> <identifier> - Find accurately\n");
|
||||
|
||||
return RT_EOK;
|
||||
}
|
||||
|
||||
rt_inline void ve_object_split(int len)
|
||||
{
|
||||
while (len--) rt_kprintf("-");
|
||||
}
|
||||
|
||||
static int ve_find_module(int argc, char **argv)
|
||||
{
|
||||
ve_iterator_t iter;
|
||||
const ve_exporter_t *exporter;
|
||||
ve_module_t module;
|
||||
int maxlen = (RT_NAME_MAX * 2);
|
||||
const char *item_title = "ve_module";
|
||||
|
||||
rt_kprintf("%-*.s identifier value\n", maxlen, item_title); ve_object_split(maxlen);
|
||||
rt_kprintf(" ---------------- -----\n");
|
||||
|
||||
if (!ve_module_init(&module, argv[2]))
|
||||
{
|
||||
ve_iter_init(&module, &iter);
|
||||
}
|
||||
else
|
||||
{
|
||||
return -RT_ERROR;
|
||||
}
|
||||
|
||||
while (1)
|
||||
{
|
||||
exporter = ve_iter_next(&iter);
|
||||
if (exporter == RT_NULL)
|
||||
{
|
||||
return -RT_ERROR;
|
||||
}
|
||||
else
|
||||
{
|
||||
rt_kprintf("%-*.s %-*.s %d\n",
|
||||
maxlen, exporter->module,
|
||||
maxlen, exporter->identifier,
|
||||
exporter->value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static int ve_find_value(int argc, char **argv)
|
||||
{
|
||||
ve_iterator_t iter;
|
||||
const ve_exporter_t *exporter;
|
||||
ve_module_t module;
|
||||
int maxlen = (RT_NAME_MAX * 2);
|
||||
const char *item_title = "ve_module";
|
||||
|
||||
rt_kprintf("%-*.s identifier value\n", maxlen, item_title); ve_object_split(maxlen);
|
||||
rt_kprintf(" ---------------- -----\n");
|
||||
|
||||
if (!ve_module_init(&module, argv[2]))
|
||||
{
|
||||
ve_iter_init(&module, &iter);
|
||||
}
|
||||
else
|
||||
{
|
||||
return -RT_ERROR;
|
||||
}
|
||||
|
||||
while (1)
|
||||
{
|
||||
exporter = ve_iter_next(&iter);
|
||||
if (exporter == RT_NULL)
|
||||
{
|
||||
return -RT_ERROR;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!rt_strcmp(exporter->identifier, argv[3]))
|
||||
{
|
||||
rt_kprintf("%-*.s %-*.s %d\n",
|
||||
maxlen, exporter->module,
|
||||
maxlen, exporter->identifier,
|
||||
exporter->value);
|
||||
|
||||
return RT_EOK;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static int ve_find(int argc, char **argv)
|
||||
{
|
||||
int i, resule = RT_EOK;
|
||||
const struct ve_cmd_des *run_cmd = RT_NULL;
|
||||
|
||||
if (argc == 1)
|
||||
{
|
||||
ve_cmd_help(argc, argv);
|
||||
return RT_EOK;
|
||||
}
|
||||
|
||||
/* find command function */
|
||||
for (i = 0; i < sizeof(cmd_tab) / sizeof(cmd_tab[0]); i++)
|
||||
{
|
||||
if (rt_strcmp(cmd_tab[i].cmd, argv[1]) == 0)
|
||||
{
|
||||
run_cmd = &cmd_tab[i];
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* not find command function, print help information */
|
||||
if (run_cmd == RT_NULL)
|
||||
{
|
||||
rt_kprintf("There is no command option named %s.\n", argv[1]);
|
||||
ve_cmd_help(argc, argv);
|
||||
return RT_EOK;
|
||||
}
|
||||
|
||||
/* run command function */
|
||||
if (run_cmd->fun != RT_NULL)
|
||||
{
|
||||
resule = run_cmd->fun(argc, argv);
|
||||
}
|
||||
|
||||
if (resule)
|
||||
{
|
||||
ve_cmd_help(argc, argv);
|
||||
}
|
||||
|
||||
return RT_EOK;
|
||||
}
|
||||
MSH_CMD_EXPORT(ve_find, find the specified export variable);
|
15
rt-thread/components/utilities/ymodem/SConscript
Normal file
15
rt-thread/components/utilities/ymodem/SConscript
Normal file
@@ -0,0 +1,15 @@
|
||||
from building import *
|
||||
|
||||
cwd = GetCurrentDir()
|
||||
src = Split('''
|
||||
ymodem.c
|
||||
''')
|
||||
|
||||
CPPPATH = [cwd]
|
||||
|
||||
if GetDepend('YMODEM_USING_FILE_TRANSFER'):
|
||||
src += ['ry_sy.c']
|
||||
|
||||
group = DefineGroup('Utilities', src, depend = ['RT_USING_RYM'], CPPPATH = CPPPATH)
|
||||
|
||||
Return('group')
|
297
rt-thread/components/utilities/ymodem/ry_sy.c
Normal file
297
rt-thread/components/utilities/ymodem/ry_sy.c
Normal file
@@ -0,0 +1,297 @@
|
||||
/*
|
||||
* Copyright (c) 2019 Fuzhou Rockchip Electronics Co., Ltd
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*
|
||||
* Change Logs:
|
||||
* Date Author Notes
|
||||
* 2019-12-09 Steven Liu the first version
|
||||
* 2021-04-14 Meco Man Check the file path's legitimacy of 'sy' command
|
||||
*/
|
||||
|
||||
#include <rtthread.h>
|
||||
#include <ymodem.h>
|
||||
#include <dfs_file.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/statfs.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#ifndef DFS_USING_POSIX
|
||||
#error "Please enable DFS_USING_POSIX"
|
||||
#endif
|
||||
|
||||
struct custom_ctx
|
||||
{
|
||||
struct rym_ctx parent;
|
||||
int fd;
|
||||
int flen;
|
||||
char fpath[DFS_PATH_MAX];
|
||||
};
|
||||
|
||||
static const char *_get_path_lastname(const char *path)
|
||||
{
|
||||
char *ptr;
|
||||
if ((ptr = (char *)strrchr(path, '/')) == NULL)
|
||||
return path;
|
||||
|
||||
/* skip the '/' then return */
|
||||
return ++ptr;
|
||||
}
|
||||
|
||||
static enum rym_code _rym_recv_begin(
|
||||
struct rym_ctx *ctx,
|
||||
rt_uint8_t *buf,
|
||||
rt_size_t len)
|
||||
{
|
||||
struct custom_ctx *cctx = (struct custom_ctx *)ctx;
|
||||
struct stat file_buf;
|
||||
char insert_0 = '\0';
|
||||
char *ret;
|
||||
rt_err_t err;
|
||||
ret = strchr(cctx->fpath,insert_0);
|
||||
if(ret)
|
||||
{
|
||||
*ret = '/';
|
||||
}
|
||||
else
|
||||
{
|
||||
rt_kprintf("No end character\n");
|
||||
return RYM_ERR_ACK;
|
||||
}
|
||||
rt_strncpy(ret + 1, (const char *)buf, len - 1);
|
||||
cctx->fd = open(cctx->fpath, O_CREAT | O_WRONLY | O_TRUNC, 0);
|
||||
if (cctx->fd < 0)
|
||||
{
|
||||
rt_err_t err = rt_get_errno();
|
||||
rt_kprintf("error creating file: %d\n", err);
|
||||
return RYM_CODE_CAN;
|
||||
}
|
||||
cctx->flen = atoi(1 + (const char *)buf + rt_strnlen((const char *)buf, len - 1));
|
||||
if (cctx->flen == 0)
|
||||
cctx->flen = -1;
|
||||
|
||||
return RYM_CODE_ACK;
|
||||
}
|
||||
|
||||
static enum rym_code _rym_recv_data(
|
||||
struct rym_ctx *ctx,
|
||||
rt_uint8_t *buf,
|
||||
rt_size_t len)
|
||||
{
|
||||
struct custom_ctx *cctx = (struct custom_ctx *)ctx;
|
||||
|
||||
RT_ASSERT(cctx->fd >= 0);
|
||||
if (cctx->flen == -1)
|
||||
{
|
||||
write(cctx->fd, buf, len);
|
||||
}
|
||||
else
|
||||
{
|
||||
int wlen = len > cctx->flen ? cctx->flen : len;
|
||||
write(cctx->fd, buf, wlen);
|
||||
cctx->flen -= wlen;
|
||||
}
|
||||
|
||||
return RYM_CODE_ACK;
|
||||
}
|
||||
|
||||
static enum rym_code _rym_recv_end(
|
||||
struct rym_ctx *ctx,
|
||||
rt_uint8_t *buf,
|
||||
rt_size_t len)
|
||||
{
|
||||
struct custom_ctx *cctx = (struct custom_ctx *)ctx;
|
||||
|
||||
RT_ASSERT(cctx->fd >= 0);
|
||||
close(cctx->fd);
|
||||
cctx->fd = -1;
|
||||
|
||||
return RYM_CODE_ACK;
|
||||
}
|
||||
|
||||
static enum rym_code _rym_send_begin(
|
||||
struct rym_ctx *ctx,
|
||||
rt_uint8_t *buf,
|
||||
rt_size_t len)
|
||||
{
|
||||
struct custom_ctx *cctx = (struct custom_ctx *)ctx;
|
||||
struct stat file_buf;
|
||||
char insert_0 = '\0';
|
||||
rt_err_t err;
|
||||
|
||||
cctx->fd = open(cctx->fpath, O_RDONLY);
|
||||
if (cctx->fd < 0)
|
||||
{
|
||||
err = rt_get_errno();
|
||||
rt_kprintf("error open file: %d\n", err);
|
||||
return RYM_ERR_FILE;
|
||||
}
|
||||
rt_memset(buf, 0, len);
|
||||
err = stat(cctx->fpath, &file_buf);
|
||||
if (err != RT_EOK)
|
||||
{
|
||||
rt_kprintf("error open file.\n");
|
||||
return RYM_ERR_FILE;
|
||||
}
|
||||
|
||||
const char *fdst = _get_path_lastname(cctx->fpath);
|
||||
if(fdst != cctx->fpath)
|
||||
{
|
||||
fdst = dfs_normalize_path(RT_NULL, fdst);
|
||||
if (fdst == RT_NULL)
|
||||
{
|
||||
return RYM_ERR_FILE;
|
||||
}
|
||||
}
|
||||
|
||||
rt_sprintf((char *)buf, "%s%c%d", fdst, insert_0, file_buf.st_size);
|
||||
|
||||
return RYM_CODE_SOH;
|
||||
}
|
||||
|
||||
static enum rym_code _rym_send_data(
|
||||
struct rym_ctx *ctx,
|
||||
rt_uint8_t *buf,
|
||||
rt_size_t len)
|
||||
{
|
||||
struct custom_ctx *cctx = (struct custom_ctx *)ctx;
|
||||
rt_size_t read_size;
|
||||
int retry_read;
|
||||
|
||||
read_size = 0;
|
||||
for (retry_read = 0; retry_read < 10; retry_read++)
|
||||
{
|
||||
read_size += read(cctx->fd, buf + read_size, len - read_size);
|
||||
if (read_size == len)
|
||||
break;
|
||||
}
|
||||
|
||||
if (read_size < len)
|
||||
{
|
||||
rt_memset(buf + read_size, 0x1A, len - read_size);
|
||||
ctx->stage = RYM_STAGE_FINISHING;
|
||||
}
|
||||
|
||||
if (read_size > 128)
|
||||
{
|
||||
return RYM_CODE_STX;
|
||||
}
|
||||
return RYM_CODE_SOH;
|
||||
}
|
||||
|
||||
static enum rym_code _rym_send_end(
|
||||
struct rym_ctx *ctx,
|
||||
rt_uint8_t *buf,
|
||||
rt_size_t len)
|
||||
{
|
||||
struct custom_ctx *cctx = (struct custom_ctx *)ctx;
|
||||
rt_memset(buf, 0, len);
|
||||
close(cctx->fd);
|
||||
cctx->fd = -1;
|
||||
|
||||
return RYM_CODE_SOH;
|
||||
}
|
||||
|
||||
static rt_err_t rym_download_file(rt_device_t idev,const char *file_path)
|
||||
{
|
||||
rt_err_t res;
|
||||
struct custom_ctx *ctx = rt_calloc(1, sizeof(*ctx));
|
||||
|
||||
if (!ctx)
|
||||
{
|
||||
rt_kprintf("rt_malloc failed\n");
|
||||
return -RT_ENOMEM;
|
||||
}
|
||||
ctx->fd = -1;
|
||||
rt_strncpy(ctx->fpath, file_path, DFS_PATH_MAX);
|
||||
RT_ASSERT(idev);
|
||||
res = rym_recv_on_device(&ctx->parent, idev, RT_DEVICE_OFLAG_RDWR | RT_DEVICE_FLAG_INT_RX,
|
||||
_rym_recv_begin, _rym_recv_data, _rym_recv_end, 1000);
|
||||
rt_free(ctx);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
static rt_err_t rym_upload_file(rt_device_t idev, const char *file_path)
|
||||
{
|
||||
rt_err_t res = 0;
|
||||
|
||||
struct custom_ctx *ctx = rt_calloc(1, sizeof(*ctx));
|
||||
if (!ctx)
|
||||
{
|
||||
rt_kprintf("rt_malloc failed\n");
|
||||
return -RT_ENOMEM;
|
||||
}
|
||||
ctx->fd = -1;
|
||||
rt_strncpy(ctx->fpath, file_path, DFS_PATH_MAX);
|
||||
RT_ASSERT(idev);
|
||||
res = rym_send_on_device(&ctx->parent, idev,
|
||||
RT_DEVICE_OFLAG_RDWR | RT_DEVICE_FLAG_INT_RX,
|
||||
_rym_send_begin, _rym_send_data, _rym_send_end, 1000);
|
||||
rt_free(ctx);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
#ifdef RT_USING_FINSH
|
||||
#include <finsh.h>
|
||||
|
||||
static rt_err_t ry(uint8_t argc, char **argv)
|
||||
{
|
||||
rt_err_t res;
|
||||
rt_device_t dev;
|
||||
/* temporarily support 1 file*/
|
||||
const char *file_path;
|
||||
if (argc < 2)
|
||||
{
|
||||
rt_kprintf("invalid file path.\n");
|
||||
return -RT_ERROR;
|
||||
}
|
||||
if (argc > 2)
|
||||
dev = rt_device_find(argv[2]);
|
||||
else
|
||||
dev = rt_console_get_device();
|
||||
if (!dev)
|
||||
{
|
||||
rt_kprintf("could not find device.\n");
|
||||
return -RT_ERROR;
|
||||
}
|
||||
file_path = argv[1];
|
||||
res = rym_download_file(dev,file_path);
|
||||
|
||||
return res;
|
||||
}
|
||||
MSH_CMD_EXPORT(ry, YMODEM Receive e.g: ry file_path [uart0] default by console.);
|
||||
|
||||
static rt_err_t sy(uint8_t argc, char **argv)
|
||||
{
|
||||
rt_err_t res;
|
||||
/* temporarily support 1 file*/
|
||||
const char *file_path;
|
||||
rt_device_t dev;
|
||||
|
||||
if (argc < 2)
|
||||
{
|
||||
rt_kprintf("invalid file path.\n");
|
||||
return -RT_ERROR;
|
||||
}
|
||||
|
||||
if (argc > 2)
|
||||
dev = rt_device_find(argv[2]);
|
||||
else
|
||||
dev = rt_console_get_device();
|
||||
if (!dev)
|
||||
{
|
||||
rt_kprintf("could not find device.\n");
|
||||
return -RT_ERROR;
|
||||
}
|
||||
file_path = argv[1];
|
||||
res = rym_upload_file(dev, file_path);
|
||||
|
||||
return res;
|
||||
}
|
||||
MSH_CMD_EXPORT(sy, YMODEM Send e.g: sy file_path [uart0] default by console.);
|
||||
|
||||
#endif /* RT_USING_FINSH */
|
763
rt-thread/components/utilities/ymodem/ymodem.c
Normal file
763
rt-thread/components/utilities/ymodem/ymodem.c
Normal file
@@ -0,0 +1,763 @@
|
||||
/*
|
||||
* COPYRIGHT (C) 2011-2023, Real-Thread Information Technology Ltd
|
||||
* All rights reserved
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*
|
||||
* Change Logs:
|
||||
* Date Author Notes
|
||||
* 2013-04-14 Grissiom initial implementation
|
||||
* 2019-12-09 Steven Liu add YMODEM send protocol
|
||||
*/
|
||||
|
||||
#include <rthw.h>
|
||||
#include "ymodem.h"
|
||||
|
||||
#ifdef YMODEM_USING_CRC_TABLE
|
||||
static const rt_uint16_t ccitt_table[256] =
|
||||
{
|
||||
0x0000, 0x1021, 0x2042, 0x3063, 0x4084, 0x50A5, 0x60C6, 0x70E7,
|
||||
0x8108, 0x9129, 0xA14A, 0xB16B, 0xC18C, 0xD1AD, 0xE1CE, 0xF1EF,
|
||||
0x1231, 0x0210, 0x3273, 0x2252, 0x52B5, 0x4294, 0x72F7, 0x62D6,
|
||||
0x9339, 0x8318, 0xB37B, 0xA35A, 0xD3BD, 0xC39C, 0xF3FF, 0xE3DE,
|
||||
0x2462, 0x3443, 0x0420, 0x1401, 0x64E6, 0x74C7, 0x44A4, 0x5485,
|
||||
0xA56A, 0xB54B, 0x8528, 0x9509, 0xE5EE, 0xF5CF, 0xC5AC, 0xD58D,
|
||||
0x3653, 0x2672, 0x1611, 0x0630, 0x76D7, 0x66F6, 0x5695, 0x46B4,
|
||||
0xB75B, 0xA77A, 0x9719, 0x8738, 0xF7DF, 0xE7FE, 0xD79D, 0xC7BC,
|
||||
0x48C4, 0x58E5, 0x6886, 0x78A7, 0x0840, 0x1861, 0x2802, 0x3823,
|
||||
0xC9CC, 0xD9ED, 0xE98E, 0xF9AF, 0x8948, 0x9969, 0xA90A, 0xB92B,
|
||||
0x5AF5, 0x4AD4, 0x7AB7, 0x6A96, 0x1A71, 0x0A50, 0x3A33, 0x2A12,
|
||||
0xDBFD, 0xCBDC, 0xFBBF, 0xEB9E, 0x9B79, 0x8B58, 0xBB3B, 0xAB1A,
|
||||
0x6CA6, 0x7C87, 0x4CE4, 0x5CC5, 0x2C22, 0x3C03, 0x0C60, 0x1C41,
|
||||
0xEDAE, 0xFD8F, 0xCDEC, 0xDDCD, 0xAD2A, 0xBD0B, 0x8D68, 0x9D49,
|
||||
0x7E97, 0x6EB6, 0x5ED5, 0x4EF4, 0x3E13, 0x2E32, 0x1E51, 0x0E70,
|
||||
0xFF9F, 0xEFBE, 0xDFDD, 0xCFFC, 0xBF1B, 0xAF3A, 0x9F59, 0x8F78,
|
||||
0x9188, 0x81A9, 0xB1CA, 0xA1EB, 0xD10C, 0xC12D, 0xF14E, 0xE16F,
|
||||
0x1080, 0x00A1, 0x30C2, 0x20E3, 0x5004, 0x4025, 0x7046, 0x6067,
|
||||
0x83B9, 0x9398, 0xA3FB, 0xB3DA, 0xC33D, 0xD31C, 0xE37F, 0xF35E,
|
||||
0x02B1, 0x1290, 0x22F3, 0x32D2, 0x4235, 0x5214, 0x6277, 0x7256,
|
||||
0xB5EA, 0xA5CB, 0x95A8, 0x8589, 0xF56E, 0xE54F, 0xD52C, 0xC50D,
|
||||
0x34E2, 0x24C3, 0x14A0, 0x0481, 0x7466, 0x6447, 0x5424, 0x4405,
|
||||
0xA7DB, 0xB7FA, 0x8799, 0x97B8, 0xE75F, 0xF77E, 0xC71D, 0xD73C,
|
||||
0x26D3, 0x36F2, 0x0691, 0x16B0, 0x6657, 0x7676, 0x4615, 0x5634,
|
||||
0xD94C, 0xC96D, 0xF90E, 0xE92F, 0x99C8, 0x89E9, 0xB98A, 0xA9AB,
|
||||
0x5844, 0x4865, 0x7806, 0x6827, 0x18C0, 0x08E1, 0x3882, 0x28A3,
|
||||
0xCB7D, 0xDB5C, 0xEB3F, 0xFB1E, 0x8BF9, 0x9BD8, 0xABBB, 0xBB9A,
|
||||
0x4A75, 0x5A54, 0x6A37, 0x7A16, 0x0AF1, 0x1AD0, 0x2AB3, 0x3A92,
|
||||
0xFD2E, 0xED0F, 0xDD6C, 0xCD4D, 0xBDAA, 0xAD8B, 0x9DE8, 0x8DC9,
|
||||
0x7C26, 0x6C07, 0x5C64, 0x4C45, 0x3CA2, 0x2C83, 0x1CE0, 0x0CC1,
|
||||
0xEF1F, 0xFF3E, 0xCF5D, 0xDF7C, 0xAF9B, 0xBFBA, 0x8FD9, 0x9FF8,
|
||||
0x6E17, 0x7E36, 0x4E55, 0x5E74, 0x2E93, 0x3EB2, 0x0ED1, 0x1EF0
|
||||
};
|
||||
static rt_uint16_t CRC16(unsigned char *q, int len)
|
||||
{
|
||||
rt_uint16_t crc = 0;
|
||||
|
||||
while (len-- > 0)
|
||||
crc = (crc << 8) ^ ccitt_table[((crc >> 8) ^ *q++) & 0xff];
|
||||
return crc;
|
||||
}
|
||||
#else
|
||||
static rt_uint16_t CRC16(unsigned char *q, int len)
|
||||
{
|
||||
rt_uint16_t crc;
|
||||
char i;
|
||||
|
||||
crc = 0;
|
||||
while (--len >= 0)
|
||||
{
|
||||
crc = crc ^ (int) * q++ << 8;
|
||||
i = 8;
|
||||
do
|
||||
{
|
||||
if (crc & 0x8000)
|
||||
crc = crc << 1 ^ 0x1021;
|
||||
else
|
||||
crc = crc << 1;
|
||||
}
|
||||
while (--i);
|
||||
}
|
||||
|
||||
return (crc);
|
||||
}
|
||||
#endif
|
||||
|
||||
// we could only use global varible because we could not use
|
||||
// rt_device_t->user_data(it is used by the serial driver)...
|
||||
static struct rym_ctx *_rym_the_ctx;
|
||||
|
||||
static rt_err_t _rym_rx_ind(rt_device_t dev, rt_size_t size)
|
||||
{
|
||||
return rt_sem_release(&_rym_the_ctx->sem);
|
||||
}
|
||||
|
||||
/* SOH/STX + seq + payload + crc */
|
||||
#define _RYM_SOH_PKG_SZ (1+2+128+2)
|
||||
#define _RYM_STX_PKG_SZ (1+2+1024+2)
|
||||
|
||||
static enum rym_code _rym_read_code(
|
||||
struct rym_ctx *ctx,
|
||||
rt_tick_t timeout)
|
||||
{
|
||||
/* Fast path */
|
||||
if (rt_device_read(ctx->dev, 0, ctx->buf, 1) == 1)
|
||||
return (enum rym_code)(*ctx->buf);
|
||||
|
||||
/* Slow path */
|
||||
do
|
||||
{
|
||||
rt_size_t rsz;
|
||||
|
||||
/* No data yet, wait for one */
|
||||
if (rt_sem_take(&ctx->sem, timeout) != RT_EOK)
|
||||
return RYM_CODE_NONE;
|
||||
|
||||
/* Try to read one */
|
||||
rsz = rt_device_read(ctx->dev, 0, ctx->buf, 1);
|
||||
if (rsz == 1)
|
||||
return (enum rym_code)(*ctx->buf);
|
||||
}
|
||||
while (1);
|
||||
}
|
||||
|
||||
/* the caller should at least alloc _RYM_STX_PKG_SZ buffer */
|
||||
static rt_ssize_t _rym_read_data(
|
||||
struct rym_ctx *ctx,
|
||||
rt_size_t len)
|
||||
{
|
||||
/* we should already have had the code */
|
||||
rt_uint8_t *buf = ctx->buf + 1;
|
||||
rt_size_t readlen = 0;
|
||||
|
||||
do
|
||||
{
|
||||
readlen += rt_device_read(ctx->dev,
|
||||
0, buf + readlen, len - readlen);
|
||||
if (readlen >= len)
|
||||
return readlen;
|
||||
}
|
||||
while (rt_sem_take(&ctx->sem, RYM_WAIT_CHR_TICK) == RT_EOK);
|
||||
|
||||
return readlen;
|
||||
}
|
||||
|
||||
static rt_err_t _rym_send_packet(
|
||||
struct rym_ctx *ctx,
|
||||
enum rym_code code,
|
||||
rt_uint8_t index)
|
||||
{
|
||||
rt_uint16_t send_crc;
|
||||
rt_uint8_t index_inv = ~index;
|
||||
rt_size_t writelen = 0;
|
||||
rt_size_t packetlen = 0;
|
||||
|
||||
switch(code)
|
||||
{
|
||||
case RYM_CODE_SOH:
|
||||
packetlen = _RYM_SOH_PKG_SZ;
|
||||
break;
|
||||
case RYM_CODE_STX:
|
||||
packetlen = _RYM_STX_PKG_SZ;
|
||||
break;
|
||||
default:
|
||||
return -RT_ERROR;
|
||||
}
|
||||
|
||||
send_crc = CRC16(ctx->buf + 3, packetlen - 5);
|
||||
ctx->buf[0] = code;
|
||||
ctx->buf[1] = index;
|
||||
ctx->buf[2] = index_inv;
|
||||
ctx->buf[packetlen - 2] = (rt_uint8_t)(send_crc >> 8);
|
||||
ctx->buf[packetlen - 1] = (rt_uint8_t)send_crc & 0xff;
|
||||
|
||||
do
|
||||
{
|
||||
writelen += rt_device_write(ctx->dev, 0, ctx->buf + writelen,
|
||||
packetlen - writelen);
|
||||
}
|
||||
while (writelen < packetlen);
|
||||
|
||||
return RT_EOK;
|
||||
}
|
||||
|
||||
static rt_ssize_t _rym_putchar(struct rym_ctx *ctx, rt_uint8_t code)
|
||||
{
|
||||
rt_device_write(ctx->dev, 0, &code, sizeof(code));
|
||||
return 1;
|
||||
}
|
||||
|
||||
static rt_ssize_t _rym_getchar(struct rym_ctx *ctx)
|
||||
{
|
||||
rt_uint8_t getc_ack;
|
||||
|
||||
while (rt_device_read(ctx->dev, 0, &getc_ack, 1) != 1)
|
||||
{
|
||||
rt_sem_take(&ctx->sem, RT_WAITING_FOREVER);
|
||||
}
|
||||
return getc_ack;
|
||||
}
|
||||
|
||||
static rt_err_t _rym_do_handshake(
|
||||
struct rym_ctx *ctx,
|
||||
int tm_sec)
|
||||
{
|
||||
enum rym_code code;
|
||||
rt_size_t i;
|
||||
rt_uint16_t recv_crc, cal_crc;
|
||||
rt_size_t data_sz;
|
||||
rt_tick_t tick;
|
||||
|
||||
ctx->stage = RYM_STAGE_ESTABLISHING;
|
||||
/* send C every second, so the sender could know we are waiting for it. */
|
||||
for (i = 0; i < tm_sec; i++)
|
||||
{
|
||||
_rym_putchar(ctx, RYM_CODE_C);
|
||||
code = _rym_read_code(ctx,
|
||||
RYM_CHD_INTV_TICK);
|
||||
if (code == RYM_CODE_SOH)
|
||||
{
|
||||
data_sz = _RYM_SOH_PKG_SZ;
|
||||
break;
|
||||
}
|
||||
else if (code == RYM_CODE_STX)
|
||||
{
|
||||
data_sz = _RYM_STX_PKG_SZ;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (i == tm_sec)
|
||||
{
|
||||
return -RYM_ERR_TMO;
|
||||
}
|
||||
|
||||
/* receive all data */
|
||||
i = 0;
|
||||
/* automatic exit after receiving specified length data, timeout: 100ms */
|
||||
tick = rt_tick_get();
|
||||
while (rt_tick_get() <= (tick + rt_tick_from_millisecond(100)) && i < (data_sz - 1))
|
||||
{
|
||||
i += _rym_read_data(ctx, data_sz - 1);
|
||||
rt_thread_mdelay(5);
|
||||
}
|
||||
|
||||
if (i != (data_sz - 1))
|
||||
return -RYM_ERR_DSZ;
|
||||
|
||||
/* sanity check */
|
||||
if (ctx->buf[1] != 0 || ctx->buf[2] != 0xFF)
|
||||
return -RYM_ERR_SEQ;
|
||||
|
||||
recv_crc = (rt_uint16_t)(*(ctx->buf + data_sz - 2) << 8) | *(ctx->buf + data_sz - 1);
|
||||
cal_crc = CRC16(ctx->buf + 3, data_sz - 5);
|
||||
if (recv_crc != cal_crc)
|
||||
return -RYM_ERR_CRC;
|
||||
|
||||
/* congratulations, check passed. */
|
||||
if (ctx->on_begin && ctx->on_begin(ctx, ctx->buf + 3, data_sz - 5) != RYM_CODE_ACK)
|
||||
return -RYM_ERR_CAN;
|
||||
|
||||
return RT_EOK;
|
||||
}
|
||||
|
||||
static rt_err_t _rym_do_send_handshake(
|
||||
struct rym_ctx *ctx,
|
||||
int tm_sec)
|
||||
{
|
||||
enum rym_code code;
|
||||
rt_size_t i;
|
||||
rt_size_t data_sz;
|
||||
rt_uint8_t index = 0;
|
||||
rt_uint8_t getc_ack;
|
||||
|
||||
ctx->stage = RYM_STAGE_ESTABLISHING;
|
||||
data_sz = _RYM_SOH_PKG_SZ;
|
||||
|
||||
/* receive C every second */
|
||||
for (i = 0; i < tm_sec; i++)
|
||||
{
|
||||
code = _rym_read_code(ctx,
|
||||
RYM_CHD_INTV_TICK);
|
||||
if (code == RYM_CODE_C)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (i == tm_sec)
|
||||
{
|
||||
return -RYM_ERR_TMO;
|
||||
}
|
||||
|
||||
/* congratulations, check passed. */
|
||||
if (ctx->on_begin && ctx->on_begin(ctx, ctx->buf + 3, data_sz - 5) != RYM_CODE_SOH)
|
||||
return -RYM_ERR_CODE;
|
||||
|
||||
code = RYM_CODE_SOH;
|
||||
_rym_send_packet(ctx, code, index);
|
||||
|
||||
rt_device_set_rx_indicate(ctx->dev, _rym_rx_ind);
|
||||
getc_ack = _rym_getchar(ctx);
|
||||
|
||||
if (getc_ack != RYM_CODE_ACK)
|
||||
{
|
||||
return -RYM_ERR_ACK;
|
||||
}
|
||||
|
||||
getc_ack = _rym_getchar(ctx);
|
||||
|
||||
if (getc_ack != RYM_CODE_C)
|
||||
{
|
||||
return -RYM_ERR_ACK;
|
||||
}
|
||||
|
||||
ctx->stage = RYM_STAGE_ESTABLISHED;
|
||||
|
||||
return RT_EOK;
|
||||
}
|
||||
|
||||
static rt_err_t _rym_trans_data(
|
||||
struct rym_ctx *ctx,
|
||||
rt_size_t data_sz,
|
||||
enum rym_code *code)
|
||||
{
|
||||
const rt_size_t tsz = 2 + data_sz + 2;
|
||||
rt_uint16_t recv_crc;
|
||||
|
||||
/* seq + data + crc */
|
||||
rt_size_t i = _rym_read_data(ctx, tsz);
|
||||
if (i != tsz)
|
||||
return -RYM_ERR_DSZ;
|
||||
|
||||
if ((ctx->buf[1] + ctx->buf[2]) != 0xFF)
|
||||
{
|
||||
return -RYM_ERR_SEQ;
|
||||
}
|
||||
|
||||
/* As we are sending C continuously, there is a chance that the
|
||||
* sender(remote) receive an C after sending the first handshake package.
|
||||
* So the sender will interpret it as NAK and re-send the package. So we
|
||||
* just ignore it and proceed. */
|
||||
if (ctx->stage == RYM_STAGE_ESTABLISHED && ctx->buf[1] == 0x00)
|
||||
{
|
||||
*code = RYM_CODE_NONE;
|
||||
return RT_EOK;
|
||||
}
|
||||
|
||||
ctx->stage = RYM_STAGE_TRANSMITTING;
|
||||
|
||||
/* sanity check */
|
||||
recv_crc = (rt_uint16_t)(*(ctx->buf + tsz - 1) << 8) | *(ctx->buf + tsz);
|
||||
if (recv_crc != CRC16(ctx->buf + 3, data_sz))
|
||||
return -RYM_ERR_CRC;
|
||||
|
||||
/* congratulations, check passed. */
|
||||
if (ctx->on_data)
|
||||
*code = ctx->on_data(ctx, ctx->buf + 3, data_sz);
|
||||
else
|
||||
*code = RYM_CODE_ACK;
|
||||
return RT_EOK;
|
||||
}
|
||||
|
||||
static rt_err_t _rym_do_trans(struct rym_ctx *ctx)
|
||||
{
|
||||
_rym_putchar(ctx, RYM_CODE_ACK);
|
||||
_rym_putchar(ctx, RYM_CODE_C);
|
||||
ctx->stage = RYM_STAGE_ESTABLISHED;
|
||||
rt_size_t errors = 0;
|
||||
|
||||
while (1)
|
||||
{
|
||||
rt_err_t err;
|
||||
enum rym_code code;
|
||||
rt_size_t data_sz, i;
|
||||
|
||||
code = _rym_read_code(ctx,
|
||||
RYM_WAIT_PKG_TICK);
|
||||
switch (code)
|
||||
{
|
||||
case RYM_CODE_SOH:
|
||||
data_sz = 128;
|
||||
break;
|
||||
case RYM_CODE_STX:
|
||||
data_sz = 1024;
|
||||
break;
|
||||
case RYM_CODE_EOT:
|
||||
return RT_EOK;
|
||||
default:
|
||||
errors++;
|
||||
if(errors > RYM_MAX_ERRORS)
|
||||
{
|
||||
return -RYM_ERR_CODE;/* Abort communication */
|
||||
}
|
||||
else
|
||||
{
|
||||
_rym_putchar(ctx, RYM_CODE_NAK);/* Ask for a packet */
|
||||
continue;
|
||||
}
|
||||
};
|
||||
|
||||
err = _rym_trans_data(ctx, data_sz, &code);
|
||||
if (err != RT_EOK)
|
||||
{
|
||||
errors++;
|
||||
if(errors > RYM_MAX_ERRORS)
|
||||
{
|
||||
return err;/* Abort communication */
|
||||
}
|
||||
else
|
||||
{
|
||||
_rym_putchar(ctx, RYM_CODE_NAK);/* Ask for a packet */
|
||||
continue;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
errors = 0;
|
||||
}
|
||||
|
||||
switch (code)
|
||||
{
|
||||
case RYM_CODE_CAN:
|
||||
/* the spec require multiple CAN */
|
||||
for (i = 0; i < RYM_END_SESSION_SEND_CAN_NUM; i++)
|
||||
{
|
||||
_rym_putchar(ctx, RYM_CODE_CAN);
|
||||
}
|
||||
return -RYM_ERR_CAN;
|
||||
case RYM_CODE_ACK:
|
||||
_rym_putchar(ctx, RYM_CODE_ACK);
|
||||
break;
|
||||
default:
|
||||
// wrong code
|
||||
break;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
static rt_err_t _rym_do_send_trans(struct rym_ctx *ctx)
|
||||
{
|
||||
ctx->stage = RYM_STAGE_TRANSMITTING;
|
||||
enum rym_code code;
|
||||
rt_size_t data_sz;
|
||||
rt_uint32_t index = 1;
|
||||
rt_uint8_t getc_ack;
|
||||
|
||||
data_sz = _RYM_STX_PKG_SZ;
|
||||
while (1)
|
||||
{
|
||||
if (!ctx->on_data)
|
||||
{
|
||||
return -RYM_ERR_CODE;
|
||||
}
|
||||
code = ctx->on_data(ctx, ctx->buf + 3, data_sz - 5);
|
||||
|
||||
_rym_send_packet(ctx, code, index);
|
||||
index++;
|
||||
rt_device_set_rx_indicate(ctx->dev, _rym_rx_ind);
|
||||
|
||||
getc_ack = _rym_getchar(ctx);
|
||||
if (getc_ack != RYM_CODE_ACK)
|
||||
{
|
||||
return -RYM_ERR_ACK;
|
||||
}
|
||||
|
||||
if (ctx->stage == RYM_STAGE_FINISHING)
|
||||
break;
|
||||
}
|
||||
|
||||
return RT_EOK;
|
||||
}
|
||||
|
||||
static rt_err_t _rym_do_fin(struct rym_ctx *ctx)
|
||||
{
|
||||
enum rym_code code;
|
||||
rt_uint16_t recv_crc;
|
||||
rt_size_t i;
|
||||
rt_size_t data_sz;
|
||||
|
||||
ctx->stage = RYM_STAGE_FINISHING;
|
||||
/* we already got one EOT in the caller. invoke the callback if there is
|
||||
* one. */
|
||||
if (ctx->on_end)
|
||||
ctx->on_end(ctx, ctx->buf + 3, 128);
|
||||
|
||||
_rym_putchar(ctx, RYM_CODE_NAK);
|
||||
code = _rym_read_code(ctx, RYM_WAIT_PKG_TICK);
|
||||
if (code != RYM_CODE_EOT)
|
||||
return -RYM_ERR_CODE;
|
||||
|
||||
_rym_putchar(ctx, RYM_CODE_ACK);
|
||||
_rym_putchar(ctx, RYM_CODE_C);
|
||||
|
||||
code = _rym_read_code(ctx, RYM_WAIT_PKG_TICK);
|
||||
if (code == RYM_CODE_SOH)
|
||||
{
|
||||
data_sz = _RYM_SOH_PKG_SZ;
|
||||
}
|
||||
else if (code == RYM_CODE_STX)
|
||||
{
|
||||
data_sz = _RYM_STX_PKG_SZ;
|
||||
}
|
||||
else
|
||||
return -RYM_ERR_CODE;
|
||||
|
||||
i = _rym_read_data(ctx, _RYM_SOH_PKG_SZ - 1);
|
||||
if (i != (_RYM_SOH_PKG_SZ - 1))
|
||||
return -RYM_ERR_DSZ;
|
||||
|
||||
/* sanity check
|
||||
*/
|
||||
if (ctx->buf[1] != 0 || ctx->buf[2] != 0xFF)
|
||||
return -RYM_ERR_SEQ;
|
||||
|
||||
recv_crc = (rt_uint16_t)(*(ctx->buf + _RYM_SOH_PKG_SZ - 2) << 8) | *(ctx->buf + _RYM_SOH_PKG_SZ - 1);
|
||||
if (recv_crc != CRC16(ctx->buf + 3, _RYM_SOH_PKG_SZ - 5))
|
||||
return -RYM_ERR_CRC;
|
||||
|
||||
/*next file transmission*/
|
||||
if (ctx->buf[3] != 0)
|
||||
{
|
||||
if (ctx->on_begin && ctx->on_begin(ctx, ctx->buf + 3, data_sz - 5) != RYM_CODE_ACK)
|
||||
return -RYM_ERR_CAN;
|
||||
return RT_EOK;
|
||||
}
|
||||
|
||||
/* congratulations, check passed. */
|
||||
ctx->stage = RYM_STAGE_FINISHED;
|
||||
|
||||
/* put the last ACK */
|
||||
_rym_putchar(ctx, RYM_CODE_ACK);
|
||||
|
||||
return RT_EOK;
|
||||
}
|
||||
|
||||
static rt_err_t _rym_do_send_fin(struct rym_ctx *ctx)
|
||||
{
|
||||
enum rym_code code;
|
||||
rt_size_t data_sz;
|
||||
rt_uint8_t index = 0;
|
||||
rt_uint8_t getc_ack;
|
||||
|
||||
data_sz = _RYM_SOH_PKG_SZ;
|
||||
rt_device_set_rx_indicate(ctx->dev, _rym_rx_ind);
|
||||
|
||||
_rym_putchar(ctx, RYM_CODE_EOT);
|
||||
getc_ack = _rym_getchar(ctx);
|
||||
|
||||
if (getc_ack != RYM_CODE_NAK)
|
||||
{
|
||||
return -RYM_ERR_ACK;
|
||||
}
|
||||
|
||||
_rym_putchar(ctx, RYM_CODE_EOT);
|
||||
getc_ack = _rym_getchar(ctx);
|
||||
|
||||
if (getc_ack != RYM_CODE_ACK)
|
||||
{
|
||||
return -RYM_ERR_ACK;
|
||||
}
|
||||
|
||||
getc_ack = _rym_getchar(ctx);
|
||||
|
||||
if (getc_ack != RYM_CODE_C)
|
||||
{
|
||||
return -RYM_ERR_ACK;
|
||||
}
|
||||
|
||||
if (ctx->on_end && ctx->on_end(ctx, ctx->buf + 3, data_sz - 5) != RYM_CODE_SOH)
|
||||
return -RYM_ERR_CODE;
|
||||
|
||||
code = RYM_CODE_SOH;
|
||||
|
||||
_rym_send_packet(ctx, code, index);
|
||||
|
||||
ctx->stage = RYM_STAGE_FINISHED;
|
||||
|
||||
return RT_EOK;
|
||||
}
|
||||
|
||||
static rt_err_t _rym_do_recv(
|
||||
struct rym_ctx *ctx,
|
||||
int handshake_timeout)
|
||||
{
|
||||
rt_err_t err;
|
||||
|
||||
ctx->stage = RYM_STAGE_NONE;
|
||||
|
||||
ctx->buf = rt_malloc(_RYM_STX_PKG_SZ);
|
||||
if (ctx->buf == RT_NULL)
|
||||
return -RT_ENOMEM;
|
||||
|
||||
err = _rym_do_handshake(ctx, handshake_timeout);
|
||||
if (err != RT_EOK)
|
||||
{
|
||||
rt_free(ctx->buf);
|
||||
return err;
|
||||
}
|
||||
while (1)
|
||||
{
|
||||
err = _rym_do_trans(ctx);
|
||||
|
||||
err = _rym_do_fin(ctx);
|
||||
if (err != RT_EOK)
|
||||
{
|
||||
rt_free(ctx->buf);
|
||||
return err;
|
||||
}
|
||||
|
||||
if (ctx->stage == RYM_STAGE_FINISHED)
|
||||
break;
|
||||
}
|
||||
rt_free(ctx->buf);
|
||||
return err;
|
||||
}
|
||||
|
||||
static rt_err_t _rym_do_send(
|
||||
struct rym_ctx *ctx,
|
||||
int handshake_timeout)
|
||||
{
|
||||
rt_err_t err;
|
||||
|
||||
ctx->stage = RYM_STAGE_NONE;
|
||||
|
||||
ctx->buf = rt_malloc(_RYM_STX_PKG_SZ);
|
||||
if (ctx->buf == RT_NULL)
|
||||
return -RT_ENOMEM;
|
||||
|
||||
err = _rym_do_send_handshake(ctx, handshake_timeout);
|
||||
if (err != RT_EOK)
|
||||
{
|
||||
rt_free(ctx->buf);
|
||||
return err;
|
||||
}
|
||||
|
||||
err = _rym_do_send_trans(ctx);
|
||||
if (err != RT_EOK)
|
||||
{
|
||||
rt_free(ctx->buf);
|
||||
return err;
|
||||
}
|
||||
|
||||
err = _rym_do_send_fin(ctx);
|
||||
if (err != RT_EOK)
|
||||
{
|
||||
rt_free(ctx->buf);
|
||||
return err;
|
||||
}
|
||||
|
||||
rt_free(ctx->buf);
|
||||
return err;
|
||||
}
|
||||
|
||||
rt_err_t rym_recv_on_device(
|
||||
struct rym_ctx *ctx,
|
||||
rt_device_t dev,
|
||||
rt_uint16_t oflag,
|
||||
rym_callback on_begin,
|
||||
rym_callback on_data,
|
||||
rym_callback on_end,
|
||||
int handshake_timeout)
|
||||
{
|
||||
rt_err_t res;
|
||||
rt_err_t (*odev_rx_ind)(rt_device_t dev, rt_size_t size);
|
||||
rt_uint16_t odev_flag;
|
||||
rt_base_t level;
|
||||
|
||||
RT_ASSERT(_rym_the_ctx == 0);
|
||||
_rym_the_ctx = ctx;
|
||||
|
||||
ctx->on_begin = on_begin;
|
||||
ctx->on_data = on_data;
|
||||
ctx->on_end = on_end;
|
||||
ctx->dev = dev;
|
||||
rt_sem_init(&ctx->sem, "rymsem", 0, RT_IPC_FLAG_FIFO);
|
||||
|
||||
odev_rx_ind = dev->rx_indicate;
|
||||
/* no data should be received before the device has been fully setted up.
|
||||
*/
|
||||
level = rt_hw_interrupt_disable();
|
||||
rt_device_set_rx_indicate(dev, _rym_rx_ind);
|
||||
|
||||
odev_flag = dev->open_flag;
|
||||
/* make sure the device don't change the content. */
|
||||
dev->open_flag &= ~RT_DEVICE_FLAG_STREAM;
|
||||
rt_hw_interrupt_enable(level);
|
||||
|
||||
res = rt_device_open(dev, oflag);
|
||||
if (res != RT_EOK)
|
||||
goto __exit;
|
||||
|
||||
res = _rym_do_recv(ctx, handshake_timeout);
|
||||
|
||||
rt_device_close(dev);
|
||||
|
||||
__exit:
|
||||
/* no rx_ind should be called before the callback has been fully detached.
|
||||
*/
|
||||
level = rt_hw_interrupt_disable();
|
||||
rt_sem_detach(&ctx->sem);
|
||||
|
||||
dev->open_flag = odev_flag;
|
||||
rt_device_set_rx_indicate(dev, odev_rx_ind);
|
||||
rt_hw_interrupt_enable(level);
|
||||
|
||||
_rym_the_ctx = RT_NULL;
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
rt_err_t rym_send_on_device(
|
||||
struct rym_ctx *ctx,
|
||||
rt_device_t dev,
|
||||
rt_uint16_t oflag,
|
||||
rym_callback on_begin,
|
||||
rym_callback on_data,
|
||||
rym_callback on_end,
|
||||
int handshake_timeout)
|
||||
{
|
||||
rt_err_t res = 0;
|
||||
rt_err_t (*odev_rx_ind)(rt_device_t dev, rt_size_t size);
|
||||
rt_uint16_t odev_flag;
|
||||
rt_base_t level;
|
||||
|
||||
RT_ASSERT(_rym_the_ctx == 0);
|
||||
_rym_the_ctx = ctx;
|
||||
|
||||
ctx->on_begin = on_begin;
|
||||
ctx->on_data = on_data;
|
||||
ctx->on_end = on_end;
|
||||
ctx->dev = dev;
|
||||
rt_sem_init(&ctx->sem, "rymsem", 0, RT_IPC_FLAG_FIFO);
|
||||
|
||||
odev_rx_ind = dev->rx_indicate;
|
||||
/* no data should be received before the device has been fully setted up.
|
||||
*/
|
||||
level = rt_hw_interrupt_disable();
|
||||
rt_device_set_rx_indicate(dev, _rym_rx_ind);
|
||||
|
||||
odev_flag = dev->open_flag;
|
||||
/* make sure the device don't change the content. */
|
||||
dev->open_flag &= ~RT_DEVICE_FLAG_STREAM;
|
||||
rt_hw_interrupt_enable(level);
|
||||
|
||||
res = rt_device_open(dev, oflag);
|
||||
if (res != RT_EOK)
|
||||
goto __exit;
|
||||
|
||||
res = _rym_do_send(ctx, handshake_timeout);
|
||||
|
||||
rt_device_close(dev);
|
||||
|
||||
__exit:
|
||||
|
||||
level = rt_hw_interrupt_disable();
|
||||
rt_sem_detach(&ctx->sem);
|
||||
|
||||
dev->open_flag = odev_flag;
|
||||
rt_device_set_rx_indicate(dev, odev_rx_ind);
|
||||
rt_hw_interrupt_enable(level);
|
||||
|
||||
_rym_the_ctx = RT_NULL;
|
||||
|
||||
return res;
|
||||
}
|
||||
|
167
rt-thread/components/utilities/ymodem/ymodem.h
Normal file
167
rt-thread/components/utilities/ymodem/ymodem.h
Normal file
@@ -0,0 +1,167 @@
|
||||
/*
|
||||
* COPYRIGHT (C) 2011-2022, Real-Thread Information Technology Ltd
|
||||
* All rights reserved
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*
|
||||
* Change Logs:
|
||||
* Date Author Notes
|
||||
* 2013-04-14 Grissiom initial implementation
|
||||
* 2019-12-09 Steven Liu add YMODEM send protocol
|
||||
* 2022-08-04 Meco Man move error codes to rym_code to silence warnings
|
||||
*/
|
||||
|
||||
#ifndef __YMODEM_H__
|
||||
#define __YMODEM_H__
|
||||
|
||||
#include "rtthread.h"
|
||||
#include <string.h>
|
||||
|
||||
/* The word "RYM" is stand for "Real-YModem". */
|
||||
enum rym_code
|
||||
{
|
||||
RYM_CODE_NONE = 0x00,
|
||||
RYM_CODE_SOH = 0x01,
|
||||
RYM_CODE_STX = 0x02,
|
||||
RYM_CODE_EOT = 0x04,
|
||||
RYM_CODE_ACK = 0x06,
|
||||
RYM_CODE_NAK = 0x15,
|
||||
RYM_CODE_CAN = 0x18,
|
||||
RYM_CODE_C = 0x43,
|
||||
|
||||
/* RYM error code */
|
||||
RYM_ERR_TMO = 0x70, /* timeout on handshake */
|
||||
RYM_ERR_CODE = 0x71, /* wrong code, wrong SOH, STX etc */
|
||||
RYM_ERR_SEQ = 0x72, /* wrong sequence number */
|
||||
RYM_ERR_CRC = 0x73, /* wrong CRC checksum */
|
||||
RYM_ERR_DSZ = 0x74, /* not enough data received */
|
||||
RYM_ERR_CAN = 0x75, /* the transmission is aborted by user */
|
||||
RYM_ERR_ACK = 0x76, /* wrong answer, wrong ACK or C */
|
||||
RYM_ERR_FILE = 0x77, /* transmit file invalid */
|
||||
};
|
||||
|
||||
/* how many ticks wait for chars between packet. */
|
||||
#ifndef RYM_WAIT_CHR_TICK
|
||||
#define RYM_WAIT_CHR_TICK (RT_TICK_PER_SECOND * 3)
|
||||
#endif
|
||||
/* how many ticks wait for between packet. */
|
||||
#ifndef RYM_WAIT_PKG_TICK
|
||||
#define RYM_WAIT_PKG_TICK (RT_TICK_PER_SECOND * 3)
|
||||
#endif
|
||||
/* how many ticks between two handshake code. */
|
||||
#ifndef RYM_CHD_INTV_TICK
|
||||
#define RYM_CHD_INTV_TICK (RT_TICK_PER_SECOND * 3)
|
||||
#endif
|
||||
|
||||
/* how many CAN be sent when user active end the session. */
|
||||
#ifndef RYM_END_SESSION_SEND_CAN_NUM
|
||||
#define RYM_END_SESSION_SEND_CAN_NUM 0x07
|
||||
#endif
|
||||
|
||||
/* how many retries were made when the error occurred */
|
||||
#ifndef RYM_MAX_ERRORS
|
||||
#define RYM_MAX_ERRORS ((rt_size_t)5)
|
||||
#endif
|
||||
|
||||
enum rym_stage
|
||||
{
|
||||
RYM_STAGE_NONE = 0,
|
||||
/* set when C is send */
|
||||
RYM_STAGE_ESTABLISHING,
|
||||
/* set when we've got the packet 0 and sent ACK and second C */
|
||||
RYM_STAGE_ESTABLISHED,
|
||||
/* set when the sender respond to our second C and recviever got a real
|
||||
* data packet. */
|
||||
RYM_STAGE_TRANSMITTING,
|
||||
/* set when the sender send a EOT */
|
||||
RYM_STAGE_FINISHING,
|
||||
/* set when transmission is really finished, i.e., after the NAK, C, final
|
||||
* NULL packet stuff. */
|
||||
RYM_STAGE_FINISHED,
|
||||
};
|
||||
|
||||
struct rym_ctx;
|
||||
/* When receiving files, the buf will be the data received from ymodem protocol
|
||||
* and the len is the data size.
|
||||
*
|
||||
* When sending files, the len is the buf size in RYM. The callback need to
|
||||
* fill the buf with data to send. Returning RYM_CODE_EOT will terminate the
|
||||
* transfer and the buf will be discarded. Any other return values will cause
|
||||
* the transfer continue.
|
||||
*/
|
||||
typedef enum rym_code(*rym_callback)(struct rym_ctx *ctx, rt_uint8_t *buf, rt_size_t len);
|
||||
|
||||
/* Currently RYM only support one transfer session(ctx) for simplicity.
|
||||
*
|
||||
* In case we could support multiple sessions in The future, the first argument
|
||||
* of APIs are (struct rym_ctx*).
|
||||
*/
|
||||
struct rym_ctx
|
||||
{
|
||||
rym_callback on_data;
|
||||
rym_callback on_begin;
|
||||
rym_callback on_end;
|
||||
/* When error happened, user need to check this to get when the error has
|
||||
* happened. */
|
||||
enum rym_stage stage;
|
||||
/* user could get the error content through this */
|
||||
rt_uint8_t *buf;
|
||||
|
||||
struct rt_semaphore sem;
|
||||
|
||||
rt_device_t dev;
|
||||
};
|
||||
|
||||
/* recv a file on device dev with ymodem session ctx.
|
||||
*
|
||||
* If an error happens, you can get where it is failed from ctx->stage.
|
||||
*
|
||||
* @param on_begin The callback will be invoked when the first packet arrived.
|
||||
* This packet often contain file names and the size of the file, if the sender
|
||||
* support it. So if you want to save the data to a file, you may need to
|
||||
* create the file on need. It is the on_begin's responsibility to parse the
|
||||
* data content. The on_begin can be NULL, in which case the transmission will
|
||||
* continue without any side-effects.
|
||||
*
|
||||
* @param on_data The callback will be invoked on the packets received. The
|
||||
* callback should save the data to the destination. The return value will be
|
||||
* sent to the sender and in turn, only RYM_{ACK,CAN} is valid. When on_data is
|
||||
* NULL, RYM will barely send ACK on every packet and have no side-effects.
|
||||
*
|
||||
* @param on_end The callback will be invoked when one transmission is
|
||||
* finished. The data should be 128 bytes of NULL. You can do some cleaning job
|
||||
* in this callback such as closing the file. The return value of this callback
|
||||
* is ignored. As above, this parameter can be NULL if you don't need such
|
||||
* function.
|
||||
*
|
||||
* @param handshake_timeout the timeout when hand shaking. The unit is in
|
||||
* second.
|
||||
*/
|
||||
rt_err_t rym_recv_on_device(struct rym_ctx *ctx, rt_device_t dev, rt_uint16_t oflag,
|
||||
rym_callback on_begin, rym_callback on_data, rym_callback on_end,
|
||||
int handshake_timeout);
|
||||
|
||||
/* send a file on device dev with ymodem session ctx.
|
||||
*
|
||||
* If an error happens, you can get where it is failed from ctx->stage.
|
||||
*
|
||||
* @param on_begin The callback will be invoked when the first packet is sent.
|
||||
* This packet often contain file names and the size of the file. It is the
|
||||
* on_begin's responsibility to parse the basic information of the file. The
|
||||
* on_begin can not be NULL.
|
||||
*
|
||||
* @param on_data The callback will be invoked when the data packets is sent.
|
||||
* The callback should read file system and prepare the data packets. The
|
||||
* on_data can not be NULL.
|
||||
*
|
||||
* @param on_end The callback will be invoked when one transmission is
|
||||
* finished. The data should be 128 bytes of NULL. The on_end can not be NULL.
|
||||
*
|
||||
* @param handshake_timeout the timeout when hand shaking. The unit is in
|
||||
* second.
|
||||
*/
|
||||
rt_err_t rym_send_on_device(struct rym_ctx *ctx, rt_device_t dev, rt_uint16_t oflag,
|
||||
rym_callback on_begin, rym_callback on_data, rym_callback on_end,
|
||||
int handshake_timeout);
|
||||
|
||||
#endif
|
Reference in New Issue
Block a user