import RT-Thread@9217865c without bsp, libcpu and components/net
This commit is contained in:
commit
e2376a3709
1414 changed files with 390370 additions and 0 deletions
226
components/utilities/Kconfig
Normal file
226
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
|
||||
|
||||
config 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_ADT
|
||||
bool "Enable ADT(abstract data type), such as AVL tree"
|
||||
default y if ARCH_MM_MMU
|
||||
default n
|
||||
|
||||
source "$RTT_DIR/components/utilities/rt-link/Kconfig"
|
||||
|
||||
endmenu
|
15
components/utilities/SConscript
Normal file
15
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')
|
8
components/utilities/libadt/SConscript
Normal file
8
components/utilities/libadt/SConscript
Normal file
|
@ -0,0 +1,8 @@
|
|||
from building import *
|
||||
|
||||
cwd = GetCurrentDir()
|
||||
src = Glob('*.c')
|
||||
CPPPATH = [cwd]
|
||||
group = DefineGroup('Utilities', src, depend = ['RT_USING_ADT'], CPPPATH = CPPPATH)
|
||||
|
||||
Return('group')
|
242
components/utilities/libadt/avl.c
Normal file
242
components/utilities/libadt/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
components/utilities/libadt/avl.h
Normal file
116
components/utilities/libadt/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__ */
|
8
components/utilities/resource/SConscript
Normal file
8
components/utilities/resource/SConscript
Normal file
|
@ -0,0 +1,8 @@
|
|||
from building import *
|
||||
|
||||
cwd = GetCurrentDir()
|
||||
src = Glob('*.c')
|
||||
CPPPATH = [cwd]
|
||||
group = DefineGroup('Utilities', src, depend = ['RT_USING_POSIX_PIPE'], CPPPATH = CPPPATH)
|
||||
|
||||
Return('group')
|
62
components/utilities/resource/resource_id.c
Normal file
62
components/utilities/resource/resource_id.c
Normal file
|
@ -0,0 +1,62 @@
|
|||
/*
|
||||
* 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
|
||||
*/
|
||||
#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;
|
||||
}
|
||||
}
|
||||
|
||||
int resource_id_get(resource_id_t *mgr)
|
||||
{
|
||||
rt_base_t level;
|
||||
void **cur;
|
||||
|
||||
level = rt_hw_interrupt_disable();
|
||||
if (mgr->_free)
|
||||
{
|
||||
cur = mgr->_free;
|
||||
mgr->_free = (void **)*mgr->_free;
|
||||
rt_hw_interrupt_enable(level);
|
||||
return cur - mgr->_res;
|
||||
}
|
||||
else if (mgr->noused < mgr->size)
|
||||
{
|
||||
cur = &mgr->_res[mgr->noused++];
|
||||
rt_hw_interrupt_enable(level);
|
||||
return cur - mgr->_res;
|
||||
}
|
||||
rt_hw_interrupt_enable(level);
|
||||
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_hw_interrupt_disable();
|
||||
cur = &mgr->_res[no];
|
||||
*cur = (void *)mgr->_free;
|
||||
mgr->_free = cur;
|
||||
rt_hw_interrupt_enable(level);
|
||||
}
|
||||
}
|
||||
|
31
components/utilities/resource/resource_id.h
Normal file
31
components/utilities/resource/resource_id.h
Normal file
|
@ -0,0 +1,31 @@
|
|||
/*
|
||||
* 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
|
||||
*/
|
||||
|
||||
#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}
|
||||
|
||||
typedef struct
|
||||
{
|
||||
int size;
|
||||
void **_res;
|
||||
int noused;
|
||||
void **_free;
|
||||
} 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__*/
|
25
components/utilities/rt-link/Kconfig
Normal file
25
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
components/utilities/rt-link/SConscript
Normal file
15
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
components/utilities/rt-link/inc/rtlink.h
Normal file
210
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
components/utilities/rt-link/inc/rtlink_dev.h
Normal file
41
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
components/utilities/rt-link/inc/rtlink_hw.h
Normal file
26
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
components/utilities/rt-link/inc/rtlink_port.h
Normal file
33
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
components/utilities/rt-link/inc/rtlink_utils.h
Normal file
21
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
components/utilities/rt-link/src/SConscript
Normal file
13
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
components/utilities/rt-link/src/rtlink.c
Normal file
1320
components/utilities/rt-link/src/rtlink.c
Normal file
File diff suppressed because it is too large
Load diff
398
components/utilities/rt-link/src/rtlink_dev.c
Normal file
398
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
components/utilities/rt-link/src/rtlink_hw.c
Normal file
296
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
components/utilities/rt-link/src/rtlink_utils.c
Normal file
90
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
components/utilities/ulog/SConscript
Normal file
20
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')
|
53
components/utilities/ulog/backend/console_be.c
Normal file
53
components/utilities/ulog/backend/console_be.c
Normal file
|
@ -0,0 +1,53 @@
|
|||
/*
|
||||
* 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)
|
||||
{
|
||||
#ifdef RT_USING_DEVICE
|
||||
rt_device_t dev = rt_console_get_device();
|
||||
|
||||
if (dev == RT_NULL)
|
||||
{
|
||||
rt_hw_console_output(log);
|
||||
}
|
||||
else
|
||||
{
|
||||
rt_device_write(dev, 0, log, len);
|
||||
}
|
||||
#else
|
||||
rt_hw_console_output(log);
|
||||
#endif
|
||||
|
||||
}
|
||||
|
||||
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
components/utilities/ulog/backend/file_be.c
Normal file
226
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
components/utilities/ulog/backend/ulog_be.h
Normal file
44
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
components/utilities/ulog/syslog/syslog.c
Normal file
262
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, 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
components/utilities/ulog/syslog/syslog.h
Normal file
100
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_ */
|
1553
components/utilities/ulog/ulog.c
Normal file
1553
components/utilities/ulog/ulog.c
Normal file
File diff suppressed because it is too large
Load diff
102
components/utilities/ulog/ulog.h
Normal file
102
components/utilities/ulog/ulog.h
Normal file
|
@ -0,0 +1,102 @@
|
|||
/*
|
||||
* 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_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
components/utilities/ulog/ulog_def.h
Normal file
222
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
components/utilities/utest/SConscript
Normal file
8
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')
|
453
components/utilities/utest/utest.c
Normal file
453
components/utilities/utest/utest.c
Normal file
|
@ -0,0 +1,453 @@
|
|||
/*
|
||||
* 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;
|
||||
#elif defined (__GNUC__) /* for GCC Compiler */
|
||||
extern const int __rt_utest_tc_tab_start;
|
||||
extern const int __rt_utest_tc_tab_end;
|
||||
tc_table = (utest_tc_export_t)&__rt_utest_tc_tab_start;
|
||||
tc_num = (utest_tc_export_t) &__rt_utest_tc_tab_end - tc_table;
|
||||
#elif defined(_MSC_VER)
|
||||
unsigned int* ptr_begin, * ptr_end;
|
||||
|
||||
ptr_begin = (unsigned int*)&__tc_export_begin;
|
||||
ptr_begin += (sizeof(struct utest_tc_export) / sizeof(unsigned int));
|
||||
while (*ptr_begin == 0) ptr_begin++;
|
||||
|
||||
ptr_end = (unsigned int*)&__tc_export_end;
|
||||
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 void 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);
|
||||
}
|
||||
}
|
||||
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;
|
||||
|
||||
rt_thread_mdelay(1000);
|
||||
|
||||
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_testcase_run(int argc, char** argv)
|
||||
{
|
||||
void *thr_param = RT_NULL;
|
||||
|
||||
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;
|
||||
}
|
||||
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);
|
||||
thr_param = (void*)utest_name;
|
||||
|
||||
if (argc == 4) tc_loop = atoi(argv[3]);
|
||||
}
|
||||
tid = rt_thread_create("utest",
|
||||
(void (*)(void *))utest_run, thr_param,
|
||||
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();
|
||||
}
|
||||
}
|
||||
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
components/utilities/utest/utest.h
Normal file
184
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
components/utilities/utest/utest_assert.h
Normal file
72
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
components/utilities/utest/utest_log.h
Normal file
34
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
components/utilities/var_export/SConscript
Normal file
8
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
components/utilities/var_export/var_export.c
Normal file
244
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_PREV_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
components/utilities/var_export/var_export.h
Normal file
96
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
components/utilities/var_export/var_export_cmd.c
Normal file
166
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
components/utilities/ymodem/SConscript
Normal file
15
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')
|
294
components/utilities/ymodem/ry_sy.c
Normal file
294
components/utilities/ymodem/ry_sy.c
Normal file
|
@ -0,0 +1,294 @@
|
|||
/*
|
||||
* 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)
|
||||
{
|
||||
rt_memset(buf, 0, len);
|
||||
|
||||
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
components/utilities/ymodem/ymodem.c
Normal file
763
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
components/utilities/ymodem/ymodem.h
Normal file
167
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
|
129
components/utilities/zmodem/crc.h
Normal file
129
components/utilities/zmodem/crc.h
Normal file
|
@ -0,0 +1,129 @@
|
|||
/*
|
||||
* crc calculation stuff
|
||||
*/
|
||||
|
||||
/* crctab calculated by Mark G. Mendel, Network Systems Corporation */
|
||||
static unsigned short crctab[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
|
||||
};
|
||||
|
||||
/*
|
||||
* updcrc macro derived from article Copyright (C) 1986 Stephen Satchell.
|
||||
* NOTE: First srgument must be in range 0 to 255.
|
||||
* Second argument is referenced twice.
|
||||
*
|
||||
* Programmers may incorporate any or all code into their programs,
|
||||
* giving proper credit within the source. Publication of the
|
||||
* source routines is permitted so long as proper credit is given
|
||||
* to Stephen Satchell, Satchell Evaluations and Chuck Forsberg,
|
||||
* Omen Technology.
|
||||
*/
|
||||
|
||||
#define updcrc16(cp, crc) ( crctab[((crc >> 8) & 255)] ^ (crc << 8) ^ cp)
|
||||
|
||||
/*
|
||||
* Copyright (C) 1986 Gary S. Brown. You may use this program, or
|
||||
* code or tables extracted from it, as desired without restriction.
|
||||
*/
|
||||
|
||||
/* First, the polynomial itself and its table of feedback terms. The */
|
||||
/* polynomial is */
|
||||
/* X^32+X^26+X^23+X^22+X^16+X^12+X^11+X^10+X^8+X^7+X^5+X^4+X^2+X^1+X^0 */
|
||||
/* Note that we take it "backwards" and put the highest-order term in */
|
||||
/* the lowest-order bit. The X^32 term is "implied"; the LSB is the */
|
||||
/* X^31 term, etc. The X^0 term (usually shown as "+1") results in */
|
||||
/* the MSB being 1. */
|
||||
|
||||
/* Note that the usual hardware shift register implementation, which */
|
||||
/* is what we're using (we're merely optimizing it by doing eight-bit */
|
||||
/* chunks at a time) shifts bits into the lowest-order term. In our */
|
||||
/* implementation, that means shifting towards the right. Why do we */
|
||||
/* do it this way? Because the calculated CRC must be transmitted in */
|
||||
/* order from highest-order term to lowest-order term. UARTs transmit */
|
||||
/* characters in order from LSB to MSB. By storing the CRC this way, */
|
||||
/* we hand it to the UART in the order low-byte to high-byte; the UART */
|
||||
/* sends each low-bit to hight-bit; and the result is transmission bit */
|
||||
/* by bit from highest- to lowest-order term without requiring any bit */
|
||||
/* shuffling on our part. Reception works similarly. */
|
||||
|
||||
/* The feedback terms table consists of 256, 32-bit entries. Notes: */
|
||||
/* */
|
||||
/* The table can be generated at runtime if desired; code to do so */
|
||||
/* is shown later. It might not be obvious, but the feedback */
|
||||
/* terms simply represent the results of eight shift/xor opera- */
|
||||
/* tions for all combinations of data and CRC register values. */
|
||||
/* */
|
||||
/* The values must be right-shifted by eight bits by the "updcrc" */
|
||||
/* logic; the shift must be unsigned (bring in zeroes). On some */
|
||||
/* hardware you could probably optimize the shift in assembler by */
|
||||
/* using byte-swap instructions. */
|
||||
|
||||
static unsigned long cr3tab[] = { /* CRC polynomial 0xedb88320 */
|
||||
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
|
||||
};
|
||||
|
||||
#define updcrc32(b, c) (cr3tab[((int)c ^ b) & 0xff] ^ ((c >> 8) & 0x00FFFFFF))
|
||||
|
||||
/* End of crc.c */
|
402
components/utilities/zmodem/rz.c
Normal file
402
components/utilities/zmodem/rz.c
Normal file
|
@ -0,0 +1,402 @@
|
|||
/*
|
||||
* File : rz.c
|
||||
* the implemention of receiving files from the remote computers
|
||||
* through the zmodem protocol.
|
||||
* Change Logs:
|
||||
* Date Author Notes
|
||||
* 2011-03-29 itspy
|
||||
* 2011-12-12 aozima fixed syntax error.
|
||||
*/
|
||||
|
||||
#include <rtthread.h>
|
||||
#include <finsh.h>
|
||||
#include <shell.h>
|
||||
#include <rtdef.h>
|
||||
#include <dfs.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/statfs.h>
|
||||
#include <stdio.h>
|
||||
#include "zdef.h"
|
||||
|
||||
|
||||
void zr_start(char *path);
|
||||
static rt_err_t zrec_init(rt_uint8_t *rxbuf, struct zfile *zf);
|
||||
static rt_err_t zrec_files(struct zfile *zf);
|
||||
static rt_err_t zwrite_file(rt_uint8_t *buf, rt_uint16_t size, struct zfile *zf);
|
||||
static rt_err_t zrec_file_data(rt_uint8_t *buf, struct zfile *zf);;
|
||||
static rt_err_t zrec_file(rt_uint8_t *rxbuf, struct zfile *zf);
|
||||
static rt_err_t zget_file_info(char *name, struct zfile *zf);
|
||||
static rt_err_t zwrite_file(rt_uint8_t *buf, rt_uint16_t size, struct zfile *zf);
|
||||
static void zrec_ack_bibi(void);
|
||||
|
||||
|
||||
/* start zmodem receive proccess */
|
||||
void zr_start(char *path)
|
||||
{
|
||||
struct zfile *zf;
|
||||
rt_uint8_t n;
|
||||
char ch,*p,*q;
|
||||
rt_err_t res = -RT_ERROR;
|
||||
|
||||
zf = rt_malloc(sizeof(struct zfile));
|
||||
if (zf == RT_NULL)
|
||||
{
|
||||
rt_kprintf("zf: out of memory\r\n");
|
||||
return;
|
||||
}
|
||||
rt_memset(zf, 0, sizeof(struct zfile));
|
||||
zf->fname = path;
|
||||
zf->fd = -1;
|
||||
res = zrec_files(zf);
|
||||
p = zf->fname;
|
||||
for (;;)
|
||||
{
|
||||
q = strstr(p,"/");
|
||||
if (q == RT_NULL) break;
|
||||
p = q+1;
|
||||
}
|
||||
if (res == RT_EOK)
|
||||
{
|
||||
rt_kprintf("\b\b\bfile: %s \r\n",p);
|
||||
rt_kprintf("size: %ld bytes\r\n",zf->bytes_received);
|
||||
rt_kprintf("receive completed.\r\n");
|
||||
close(zf->fd);
|
||||
rt_free(zf->fname);
|
||||
}
|
||||
else
|
||||
{
|
||||
rt_kprintf("\b\b\bfile: %s \r\n",p);
|
||||
rt_kprintf("size: 0 bytes\r\n");
|
||||
rt_kprintf("receive failed.\r\n");
|
||||
if (zf->fd >= 0)
|
||||
{
|
||||
close(zf->fd);
|
||||
unlink(zf->fname); /* remove this file */
|
||||
rt_free(zf->fname);
|
||||
}
|
||||
}
|
||||
rt_free(zf);
|
||||
/* waiting,clear console buffer */
|
||||
rt_thread_delay(RT_TICK_PER_SECOND/2);
|
||||
while(1)
|
||||
{
|
||||
n=rt_device_read(shell->device, 0, &ch, 1);
|
||||
if (n == 0) break;
|
||||
}
|
||||
|
||||
return ;
|
||||
}
|
||||
|
||||
/* receiver init, wait for ack */
|
||||
static rt_err_t zrec_init(rt_uint8_t *rxbuf, struct zfile *zf)
|
||||
{
|
||||
rt_uint8_t err_cnt = 0;
|
||||
rt_err_t res = -RT_ERROR;
|
||||
|
||||
for (;;)
|
||||
{
|
||||
zput_pos(0L);
|
||||
tx_header[ZF0] = ZF0_CMD;
|
||||
tx_header[ZF1] = ZF1_CMD;
|
||||
tx_header[ZF2] = ZF2_CMD;
|
||||
zsend_hex_header(ZRINIT, tx_header);
|
||||
again:
|
||||
res = zget_header(rx_header);
|
||||
switch(res)
|
||||
{
|
||||
case ZFILE:
|
||||
ZF0_CMD = rx_header[ZF0];
|
||||
ZF1_CMD = rx_header[ZF1];
|
||||
ZF2_CMD = rx_header[ZF2];
|
||||
ZF3_CMD = rx_header[ZF3];
|
||||
res = zget_data(rxbuf, RX_BUFFER_SIZE);
|
||||
if (res == GOTCRCW)
|
||||
{
|
||||
if ((res =zget_file_info((char*)rxbuf,zf))!= RT_EOK)
|
||||
{
|
||||
zsend_hex_header(ZSKIP, tx_header);
|
||||
return (res);
|
||||
}
|
||||
return RT_EOK;;
|
||||
}
|
||||
zsend_hex_header(ZNAK, tx_header);
|
||||
goto again;
|
||||
case ZSINIT:
|
||||
if (zget_data((rt_uint8_t*)Attn, ZATTNLEN) == GOTCRCW) /* send zack */
|
||||
{
|
||||
zsend_hex_header(ZACK, tx_header);
|
||||
goto again;
|
||||
}
|
||||
zsend_hex_header(ZNAK, tx_header); /* send znak */
|
||||
goto again;
|
||||
case ZRQINIT:
|
||||
continue;
|
||||
case ZEOF:
|
||||
continue;
|
||||
case ZCOMPL:
|
||||
goto again;
|
||||
case ZFIN: /* end file session */
|
||||
zrec_ack_bibi();
|
||||
return res;
|
||||
default:
|
||||
if (++err_cnt >1000) return -RT_ERROR;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* receive files */
|
||||
static rt_err_t zrec_files(struct zfile *zf)
|
||||
{
|
||||
rt_uint8_t *rxbuf;
|
||||
rt_err_t res = -RT_ERROR;
|
||||
|
||||
zinit_parameter();
|
||||
rxbuf = rt_malloc(RX_BUFFER_SIZE*sizeof(rt_uint8_t));
|
||||
if (rxbuf == RT_NULL)
|
||||
{
|
||||
rt_kprintf("rxbuf: out of memory\r\n");
|
||||
return -RT_ERROR;
|
||||
}
|
||||
rt_kprintf("\r\nrz: ready...\r\n"); /* here ready to receive things */
|
||||
if ((res = zrec_init(rxbuf,zf))!= RT_EOK)
|
||||
{
|
||||
rt_kprintf("\b\b\breceive init failed\r\n");
|
||||
rt_free(rxbuf);
|
||||
return -RT_ERROR;
|
||||
}
|
||||
res = zrec_file(rxbuf,zf);
|
||||
if (res == ZFIN)
|
||||
{
|
||||
rt_free(rxbuf);
|
||||
return RT_EOK; /* if finish session */
|
||||
}
|
||||
else if (res == ZCAN)
|
||||
{
|
||||
rt_free(rxbuf);
|
||||
return ZCAN; /* cancel by sender */
|
||||
}
|
||||
else
|
||||
{
|
||||
zsend_can();
|
||||
rt_free(rxbuf);
|
||||
return res;
|
||||
}
|
||||
}
|
||||
/* receive file */
|
||||
static rt_err_t zrec_file(rt_uint8_t *rxbuf, struct zfile *zf)
|
||||
{
|
||||
rt_err_t res = -RT_ERROR;
|
||||
rt_uint16_t err_cnt = 0;
|
||||
|
||||
do
|
||||
{
|
||||
zput_pos(zf->bytes_received);
|
||||
zsend_hex_header(ZRPOS, tx_header);
|
||||
again:
|
||||
res = zget_header(rx_header);
|
||||
switch (res)
|
||||
{
|
||||
case ZDATA:
|
||||
zget_pos(Rxpos);
|
||||
if (Rxpos != zf->bytes_received)
|
||||
{
|
||||
zsend_break(Attn);
|
||||
continue;
|
||||
}
|
||||
err_cnt = 0;
|
||||
res = zrec_file_data(rxbuf,zf);
|
||||
if (res == -RT_ERROR)
|
||||
{
|
||||
zsend_break(Attn);
|
||||
continue;
|
||||
}
|
||||
else if (res == GOTCAN) return res;
|
||||
else goto again;
|
||||
case ZRPOS:
|
||||
zget_pos(Rxpos);
|
||||
continue;
|
||||
case ZEOF:
|
||||
err_cnt = 0;
|
||||
zget_pos(Rxpos);
|
||||
if (Rxpos != zf->bytes_received || Rxpos != zf->bytes_total)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
return (zrec_init(rxbuf,zf)); /* resend ZRINIT packet,ready to receive next file */
|
||||
case ZFIN:
|
||||
zrec_ack_bibi();
|
||||
return ZCOMPL;
|
||||
case ZCAN:
|
||||
#ifdef ZDEBUG
|
||||
rt_kprintf("error code: sender cancelled \r\n");
|
||||
#endif
|
||||
zf->bytes_received = 0L; /* throw the received data */
|
||||
return res;
|
||||
case ZSKIP:
|
||||
return res;
|
||||
case -RT_ERROR:
|
||||
zsend_break(Attn);
|
||||
continue;
|
||||
case ZNAK:
|
||||
case TIMEOUT:
|
||||
default:
|
||||
continue;
|
||||
}
|
||||
} while(++err_cnt < 100);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
/* proccess file infomation */
|
||||
static rt_err_t zget_file_info(char *name, struct zfile *zf)
|
||||
{
|
||||
char *p;
|
||||
char *full_path,*ptr;
|
||||
rt_uint16_t i,len;
|
||||
rt_err_t res = -RT_ERROR;
|
||||
struct statfs buf;
|
||||
struct stat finfo;
|
||||
|
||||
if (zf->fname == RT_NULL) /* extract file path */
|
||||
{
|
||||
len = strlen(name)+2;
|
||||
}
|
||||
else
|
||||
len = strlen(zf->fname)+strlen(name)+2;
|
||||
full_path = rt_malloc(len);
|
||||
if (full_path == RT_NULL)
|
||||
{
|
||||
zsend_can();
|
||||
rt_kprintf("\b\b\bfull_path: out of memory\n");
|
||||
rt_free(full_path);
|
||||
return -RT_ERROR;
|
||||
}
|
||||
rt_memset(full_path,0,len);
|
||||
|
||||
for (i=0,ptr=zf->fname;i<len-strlen(name)-2;i++)
|
||||
full_path[i] = *ptr++;
|
||||
full_path[len-strlen(name)-2] = '/';
|
||||
/* check if is a directory */
|
||||
if ((zf->fd=open(full_path, DFS_O_DIRECTORY,0)) < 0)
|
||||
{
|
||||
zsend_can();
|
||||
rt_kprintf("\b\b\bcan not open file:%s\r\n",zf->fname+1);
|
||||
close(zf->fd);
|
||||
zf->fd = -1;
|
||||
rt_free(full_path);
|
||||
return res;
|
||||
}
|
||||
fstat(zf->fd, &finfo);
|
||||
if ((finfo.st_mode&S_IFDIR) != S_IFDIR)
|
||||
{
|
||||
close(zf->fd);
|
||||
zf->fd = -1;
|
||||
return res;
|
||||
}
|
||||
close(zf->fd);
|
||||
/* get fullpath && file attributes */
|
||||
strcat(full_path,name);
|
||||
zf->fname = full_path;
|
||||
p = strlen(name)+name+1;
|
||||
sscanf((const char *)p, "%ld%lo%o", &zf->bytes_total,&zf->ctime,&zf->mode);
|
||||
#if defined(RT_USING_DFS) && defined(DFS_USING_WORKDIR)
|
||||
dfs_statfs(working_directory,&buf);
|
||||
if (zf->bytes_total > (buf.f_blocks * buf.f_bfree))
|
||||
{
|
||||
zsend_can();
|
||||
rt_kprintf("\b\b\bnot enough disk space\r\n");
|
||||
zf->fd = -1;
|
||||
rt_free(full_path);
|
||||
return -RT_ERROR;
|
||||
}
|
||||
#else
|
||||
buf = buf;
|
||||
#endif
|
||||
zf->bytes_received = 0L;
|
||||
if ((zf->fd = open(zf->fname,DFS_O_CREAT|DFS_O_WRONLY,0)) < 0) /* create or replace exist file */
|
||||
{
|
||||
zsend_can();
|
||||
rt_kprintf("\b\b\bcan not create file:%s \r\n",zf->fname);
|
||||
return -RT_ERROR;
|
||||
}
|
||||
|
||||
return RT_EOK;
|
||||
}
|
||||
|
||||
/* receive file data,continously, no ack */
|
||||
static rt_err_t zrec_file_data(rt_uint8_t *buf, struct zfile *zf)
|
||||
{
|
||||
rt_err_t res = -RT_ERROR;
|
||||
|
||||
more_data:
|
||||
res = zget_data(buf,RX_BUFFER_SIZE);
|
||||
switch(res)
|
||||
{
|
||||
case GOTCRCW: /* zack received */
|
||||
zwrite_file(buf,Rxcount,zf);
|
||||
zf->bytes_received += Rxcount;
|
||||
zput_pos(zf->bytes_received);
|
||||
zsend_line(XON);
|
||||
zsend_hex_header(ZACK, tx_header);
|
||||
return RT_EOK;
|
||||
case GOTCRCQ:
|
||||
zwrite_file(buf,Rxcount,zf);
|
||||
zf->bytes_received += Rxcount;
|
||||
zput_pos(zf->bytes_received);
|
||||
zsend_hex_header(ZACK, tx_header);
|
||||
goto more_data;
|
||||
case GOTCRCG:
|
||||
zwrite_file(buf,Rxcount,zf);
|
||||
zf->bytes_received += Rxcount;
|
||||
goto more_data;
|
||||
case GOTCRCE:
|
||||
zwrite_file(buf,Rxcount,zf);
|
||||
zf->bytes_received += Rxcount;
|
||||
return RT_EOK;
|
||||
case GOTCAN:
|
||||
#ifdef ZDEBUG
|
||||
rt_kprintf("error code : ZCAN \r\n");
|
||||
#endif
|
||||
return res;
|
||||
case TIMEOUT:
|
||||
return res;
|
||||
case -RT_ERROR:
|
||||
zsend_break(Attn);
|
||||
return res;
|
||||
default:
|
||||
return res;
|
||||
}
|
||||
}
|
||||
|
||||
/* write file */
|
||||
static rt_err_t zwrite_file(rt_uint8_t *buf,rt_uint16_t size, struct zfile *zf)
|
||||
{
|
||||
return (write(zf->fd,buf,size));
|
||||
}
|
||||
|
||||
/* ack bibi */
|
||||
static void zrec_ack_bibi(void)
|
||||
{
|
||||
rt_uint8_t i;
|
||||
|
||||
zput_pos(0L);
|
||||
for (i=0;i<3;i++)
|
||||
{
|
||||
zsend_hex_header(ZFIN, tx_header);
|
||||
switch (zread_line(100))
|
||||
{
|
||||
case 'O':
|
||||
zread_line(1);
|
||||
return;
|
||||
case RCDO:
|
||||
return;
|
||||
case TIMEOUT:
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* end of rz.c */
|
322
components/utilities/zmodem/sz.c
Normal file
322
components/utilities/zmodem/sz.c
Normal file
|
@ -0,0 +1,322 @@
|
|||
/*
|
||||
* File : sz.c
|
||||
* the implemention of sending files to the remote computers
|
||||
* through the zmodem protocol.
|
||||
* Change Logs:
|
||||
* Date Author Notes
|
||||
* 2011-03-29 itspy
|
||||
*/
|
||||
|
||||
#include <rtthread.h>
|
||||
#include <finsh.h>
|
||||
#include <shell.h>
|
||||
#include <rtdef.h>
|
||||
#include <dfs.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/statfs.h>
|
||||
#include "zdef.h"
|
||||
|
||||
|
||||
static rt_uint8_t TX_BUFFER[TX_BUFFER_SIZE]; /* sender buffer */
|
||||
static rt_uint8_t file_cnt = 0; /* count of number of files opened */
|
||||
static rt_uint8_t Rxflags = 0; /* rx parameter flags */
|
||||
static rt_uint8_t ZF2_OP; /* file transfer option */
|
||||
|
||||
void zs_start(char *path);
|
||||
static void zsend_init(void);
|
||||
static rt_err_t zsend_files(struct zfile *zf);
|
||||
static rt_err_t zsend_file(struct zfile *zf, rt_uint8_t *buf, rt_uint16_t len);
|
||||
static rt_err_t zsend_file_data(struct zfile *zf);
|
||||
static rt_uint16_t zfill_buffer(struct zfile *zf, rt_uint8_t *buf, rt_uint16_t size);
|
||||
static rt_err_t zget_sync(void);
|
||||
static void zsay_bibi(void);
|
||||
|
||||
|
||||
|
||||
|
||||
/* start zmodem send process */
|
||||
void zs_start(char *path)
|
||||
{
|
||||
struct zfile *zf;
|
||||
rt_err_t res = -RT_ERROR;
|
||||
char *p,*q;
|
||||
zf = rt_malloc(sizeof(struct zfile));
|
||||
if (zf == RT_NULL)
|
||||
{
|
||||
rt_kprintf("zf: out of memory\r\n");
|
||||
return;
|
||||
}
|
||||
rt_kprintf("\r\nsz: ready...\r\n"); /* here ready to send things */
|
||||
rt_memset(zf, 0, sizeof(struct zfile));
|
||||
zf->fname = path;
|
||||
zf->fd = -1;
|
||||
res = zsend_files(zf);
|
||||
p = zf->fname;
|
||||
for (;;)
|
||||
{
|
||||
q = strstr(p,"/");
|
||||
if (q == RT_NULL) break;
|
||||
p = q+1;
|
||||
}
|
||||
if (res == RT_EOK)
|
||||
{
|
||||
rt_kprintf("\r\nfile: %s \r\nsize: %ld bytes\r\nsend completed.\r\n",
|
||||
p,zf->bytes_received);
|
||||
}
|
||||
else
|
||||
{
|
||||
rt_kprintf("\r\nfile: %s \r\nsize: 0 bytes\r\nsend failed.\r\n",p);
|
||||
}
|
||||
rt_free(zf);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
/* init the parameters */
|
||||
static void zsend_init(void)
|
||||
{
|
||||
rt_err_t res = -RT_ERROR;
|
||||
|
||||
zinit_parameter();
|
||||
for(;;) /* wait ZPAD */
|
||||
{
|
||||
res = zread_line(800);
|
||||
if (res == ZPAD) break;
|
||||
}
|
||||
for (;;)
|
||||
{
|
||||
res = zget_header(rx_header);
|
||||
if (res == ZRINIT) break;
|
||||
}
|
||||
if ((rx_header[ZF1] & ZRQNVH))
|
||||
{
|
||||
zput_pos(0x80L); /* Show we can var header */
|
||||
zsend_hex_header(ZRQINIT, tx_header);
|
||||
}
|
||||
Rxflags = rx_header[ZF0] & 0377;
|
||||
if (Rxflags & CANFC32) Txfcs32 = 1; /* used 32bits CRC check */
|
||||
|
||||
if (ZF2_OP == ZTRLE && (Rxflags & CANRLE)) /* for RLE packet */
|
||||
Txfcs32 = 2;
|
||||
else
|
||||
ZF2_OP = 0;
|
||||
/* send SINIT cmd */
|
||||
return;
|
||||
}
|
||||
|
||||
/* send files */
|
||||
static rt_err_t zsend_files(struct zfile *zf)
|
||||
{
|
||||
char *p,*q;
|
||||
char *str = "/";
|
||||
struct stat finfo;
|
||||
rt_err_t res = -RT_ERROR;
|
||||
|
||||
if (zf->fname == RT_NULL)
|
||||
{
|
||||
rt_kprintf("\r\nerror: no file to be send.\r\n");
|
||||
return res;
|
||||
}
|
||||
if ((zf->fd=open(zf->fname, DFS_O_RDONLY,0)) <0)
|
||||
{
|
||||
rt_kprintf("\r\ncan not open file:%s\r\n",zf->fname+1);
|
||||
return res;
|
||||
}
|
||||
|
||||
zf->file_end = 0;
|
||||
++file_cnt;
|
||||
/* extract file name */
|
||||
p = zf->fname;
|
||||
for (;;)
|
||||
{
|
||||
q = strstr(p,str);
|
||||
if (q == RT_NULL) break;
|
||||
p = q+1;
|
||||
}
|
||||
q = (char*)TX_BUFFER;
|
||||
for (;;)
|
||||
{
|
||||
*q++ = *p++;
|
||||
if (*p == 0) break;
|
||||
}
|
||||
*q++ = 0;
|
||||
p=q;
|
||||
while (q < (char*)(TX_BUFFER + 1024))
|
||||
*q++ = 0;
|
||||
/* get file attributes */
|
||||
fstat(zf->fd,&finfo);
|
||||
Left_sizes += finfo.st_size;
|
||||
rt_sprintf(p, "%lu %lo %o 3 %d %ld", (long)finfo.st_size, finfo.st_mtime,
|
||||
finfo.st_mode, file_cnt, Left_sizes);
|
||||
Left_sizes -= finfo.st_size;
|
||||
TX_BUFFER[127] = (finfo.st_size + 127) >>7;
|
||||
TX_BUFFER[126] = (finfo.st_size + 127) >>15;
|
||||
|
||||
zsend_init();
|
||||
/* start sending files */
|
||||
res = zsend_file(zf,TX_BUFFER, (p-(char*)TX_BUFFER)+strlen(p)+1);
|
||||
zsay_bibi();
|
||||
close(zf->fd);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
/* send file name and related info */
|
||||
static rt_err_t zsend_file(struct zfile *zf, rt_uint8_t *buf, rt_uint16_t len)
|
||||
{
|
||||
rt_uint8_t cnt;
|
||||
rt_err_t res = -RT_ERROR;
|
||||
|
||||
for (cnt=0;cnt<5;cnt++)
|
||||
{
|
||||
tx_header[ZF0] = ZF0_CMD; /* file conversion option */
|
||||
tx_header[ZF1] = ZF1_CMD; /* file management option */
|
||||
tx_header[ZF2] = (ZF3_CMD|ZF2_OP); /* file transfer option */
|
||||
tx_header[ZF3] = ZF3_CMD;
|
||||
zsend_bin_header(ZFILE, tx_header);
|
||||
zsend_bin_data(buf, len, ZCRCW);
|
||||
loop:
|
||||
res = zget_header(rx_header);
|
||||
switch (res)
|
||||
{
|
||||
case ZRINIT:
|
||||
while ((res = zread_line(50)) > 0)
|
||||
{
|
||||
if (res == ZPAD)
|
||||
{
|
||||
goto loop;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case ZCAN:
|
||||
case TIMEOUT:
|
||||
case ZABORT:
|
||||
case ZFIN:
|
||||
break;
|
||||
case -RT_ERROR:
|
||||
case ZNAK:
|
||||
break;
|
||||
case ZCRC: /* no CRC request */
|
||||
goto loop;
|
||||
case ZFERR:
|
||||
case ZSKIP:
|
||||
break;
|
||||
case ZRPOS: /* here we want */
|
||||
zget_pos(Rxpos);
|
||||
Txpos = Rxpos;
|
||||
return(zsend_file_data(zf));
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
/* send the file data */
|
||||
static rt_err_t zsend_file_data(struct zfile *zf)
|
||||
{
|
||||
rt_int16_t cnt;
|
||||
rt_uint8_t cmd;
|
||||
rt_err_t res = -RT_ERROR;
|
||||
/* send ZDATA packet, start to send data */
|
||||
start_send:
|
||||
zput_pos(Txpos);
|
||||
zsend_bin_header(ZDATA, tx_header);
|
||||
do
|
||||
{
|
||||
cnt = zfill_buffer(zf,TX_BUFFER,RX_BUFFER_SIZE);
|
||||
if (cnt < RX_BUFFER_SIZE )
|
||||
cmd = ZCRCE;
|
||||
else
|
||||
cmd = ZCRCG;
|
||||
zsend_bin_data(TX_BUFFER, cnt, cmd);
|
||||
zf->bytes_received= Txpos += cnt;
|
||||
if (cmd == ZCRCW)
|
||||
goto get_syn1;
|
||||
} while (cnt == RX_BUFFER_SIZE);
|
||||
for (;;) /* get ack and check if send finish */
|
||||
{
|
||||
zput_pos(Txpos);
|
||||
zsend_bin_header(ZEOF, tx_header);
|
||||
get_syn1:
|
||||
res = zget_sync();
|
||||
switch (res)
|
||||
{
|
||||
case ZACK:
|
||||
goto get_syn1;
|
||||
case ZNAK:
|
||||
continue;
|
||||
case ZRPOS: /* resend here */
|
||||
lseek(zf->fd,Txpos,0);
|
||||
goto start_send;
|
||||
case ZRINIT: /* send finish,then begin to send next file */
|
||||
return RT_EOK;
|
||||
case ZSKIP:
|
||||
case -RT_ERROR:
|
||||
return res;
|
||||
default:
|
||||
return res;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* fill file data to buffer*/
|
||||
static rt_uint16_t zfill_buffer(struct zfile *zf, rt_uint8_t *buf, rt_uint16_t size)
|
||||
{
|
||||
return (read(zf->fd,buf,size));
|
||||
}
|
||||
|
||||
/* wait sync(ack) from the receiver */
|
||||
static rt_err_t zget_sync(void)
|
||||
{
|
||||
rt_err_t res = -RT_ERROR;
|
||||
|
||||
for (;;)
|
||||
{
|
||||
res = zget_header(rx_header);
|
||||
switch (res)
|
||||
{
|
||||
case ZCAN:
|
||||
case ZABORT:
|
||||
case ZFIN:
|
||||
case TIMEOUT:
|
||||
return -RT_ERROR;
|
||||
case ZRPOS: /* get pos, need to resend */
|
||||
zget_pos(Rxpos);
|
||||
Txpos = Rxpos;
|
||||
return res;
|
||||
case ZACK:
|
||||
return res;
|
||||
case ZRINIT: /* get ZRINIT indicate that the prev file send completed */
|
||||
return res;
|
||||
case ZSKIP:
|
||||
return res;
|
||||
case -RT_ERROR:
|
||||
default:
|
||||
zsend_bin_header(ZNAK, tx_header);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* say "bibi" to the receiver */
|
||||
static void zsay_bibi(void)
|
||||
{
|
||||
for (;;)
|
||||
{
|
||||
zput_pos(0L); /* reninit position of next file*/
|
||||
zsend_hex_header(ZFIN, tx_header); /* send finished session cmd */
|
||||
switch (zget_header(rx_header))
|
||||
{
|
||||
case ZFIN:
|
||||
zsend_line('O');
|
||||
zsend_line('O');
|
||||
case ZCAN:
|
||||
case TIMEOUT:
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
/* end of sz.c */
|
886
components/utilities/zmodem/zcore.c
Normal file
886
components/utilities/zmodem/zcore.c
Normal file
|
@ -0,0 +1,886 @@
|
|||
/*
|
||||
* File : rz.c
|
||||
* the core functions of implementing zmodem protocol
|
||||
* Change Logs:
|
||||
* Date Author Notes
|
||||
* 2011-03-29 itspy
|
||||
*/
|
||||
|
||||
#include <rtthread.h>
|
||||
#include <finsh.h>
|
||||
#include <shell.h>
|
||||
#include <rtdef.h>
|
||||
#include <dfs.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/statfs.h>
|
||||
#include <stdio.h>
|
||||
#include "zdef.h"
|
||||
|
||||
char ZF0_CMD; /* file conversion request */
|
||||
char ZF1_CMD; /* file management request */
|
||||
char ZF2_CMD; /* file transport request */
|
||||
char ZF3_CMD;
|
||||
rt_uint8_t Rxframeind; /* ZBIN ZBIN32, or ZHEX type of frame */
|
||||
rt_uint16_t Rxcount; /* received count*/
|
||||
char header_type; /* header type */
|
||||
rt_uint8_t rx_header[4]; /* received header */
|
||||
rt_uint8_t tx_header[4]; /* transmitted header */
|
||||
rt_uint32_t Rxpos; /* received file position */
|
||||
rt_uint32_t Txpos; /* transmitted file position */
|
||||
rt_uint8_t Txfcs32; /* TURE means send binary frames with 32 bit FCS */
|
||||
rt_uint8_t TxCRC; /* controls 32 bit CRC being sent */
|
||||
rt_uint8_t RxCRC; /* indicates/controls 32 bit CRC being received */
|
||||
/* 0 == CRC16, 1 == CRC32, 2 == CRC32 + RLE */
|
||||
char Attn[ZATTNLEN+1]; /* attention string rx sends to tx on err */
|
||||
|
||||
void zinit_parameter(void);
|
||||
void zsend_bin_header(rt_uint8_t type, rt_uint8_t *hdr);
|
||||
void zsend_hex_header(rt_uint8_t type, rt_uint8_t *hdr);
|
||||
void zsend_bin_data(rt_uint8_t *buf, rt_int16_t len, rt_uint8_t frameend);
|
||||
static rt_int16_t zrec_data16(rt_uint8_t *buf, rt_uint16_t len);
|
||||
static rt_int16_t zrec_data32(rt_uint8_t *buf, rt_int16_t len);
|
||||
static rt_int16_t zrec_data32r(rt_uint8_t *buf, rt_int16_t len);
|
||||
rt_int16_t zget_data(rt_uint8_t *buf, rt_uint16_t len);
|
||||
rt_int16_t zget_header(rt_uint8_t *hdr);
|
||||
static rt_int16_t zget_bin_header(rt_uint8_t *hdr);
|
||||
static rt_int16_t zget_bin_fcs(rt_uint8_t *hdr);
|
||||
rt_int16_t zget_hex_header(rt_uint8_t *hdr);
|
||||
static void zsend_ascii(rt_uint8_t c);
|
||||
void zsend_zdle_char(rt_uint16_t ch);
|
||||
static rt_int16_t zget_hex(void);
|
||||
rt_int16_t zread_byte(void);
|
||||
rt_int16_t zxor_read(void);
|
||||
void zput_pos(rt_uint32_t pos);
|
||||
void zget_pos(rt_uint32_t pos);
|
||||
|
||||
|
||||
|
||||
|
||||
void zinit_parameter(void)
|
||||
{
|
||||
rt_uint8_t i;
|
||||
|
||||
ZF0_CMD = CANFC32|CANFDX|CANOVIO; /* not chose CANFC32,CANRLE,although it have been supported */
|
||||
ZF1_CMD = 0; /* fix header length,not support CANVHDR */
|
||||
ZF2_CMD = 0;
|
||||
ZF3_CMD = 0;
|
||||
Rxframeind =0;
|
||||
header_type = 0;
|
||||
Rxcount = 0;
|
||||
for (i=0;i<4;i++) rx_header[i] = tx_header[i] = 0;
|
||||
Rxpos = Txpos = 0;
|
||||
RxCRC = 0;
|
||||
Txfcs32 = 0;
|
||||
|
||||
return ;
|
||||
}
|
||||
|
||||
/* send binary header */
|
||||
void zsend_bin_header(rt_uint8_t type, rt_uint8_t *hdr)
|
||||
{
|
||||
rt_uint8_t i;
|
||||
rt_uint32_t crc;
|
||||
|
||||
zsend_byte(ZPAD);
|
||||
zsend_byte(ZDLE);
|
||||
TxCRC = Txfcs32;
|
||||
if (TxCRC == 0)
|
||||
{
|
||||
zsend_byte(ZBIN);
|
||||
zsend_zdle_char(type);
|
||||
/* add 16bits crc */
|
||||
crc = 0L;
|
||||
crc = updcrc16(type, 0);
|
||||
for (i=0;i<4;i++)
|
||||
{
|
||||
zsend_zdle_char(*hdr);
|
||||
crc = updcrc16((0377 & *hdr++),crc);
|
||||
}
|
||||
crc = updcrc16(0,updcrc16(0,crc));
|
||||
zsend_zdle_char(((int)(crc>>8)));
|
||||
zsend_zdle_char(crc);
|
||||
}
|
||||
else if(TxCRC == 1)
|
||||
{
|
||||
zsend_byte(ZBIN32);
|
||||
zsend_zdle_char(type);
|
||||
/* add 32bits crc */
|
||||
crc = 0xffffffffL;
|
||||
crc = updcrc32(type, crc);
|
||||
for (i=0;i<4;i++)
|
||||
{
|
||||
zsend_zdle_char(*hdr);
|
||||
crc = updcrc32((0377 & *hdr++), crc);
|
||||
}
|
||||
crc = ~crc;
|
||||
for (i=0; i<4;i++)
|
||||
{
|
||||
zsend_zdle_char(crc);
|
||||
crc >>= 8;
|
||||
}
|
||||
}
|
||||
else if (TxCRC == 2)
|
||||
{
|
||||
zsend_byte(ZBINR32);
|
||||
zsend_zdle_char(type);
|
||||
/* add 32bits crc */
|
||||
crc = 0xffffffffL;
|
||||
crc = updcrc32(type, crc);
|
||||
for (i=0;i<4;i++)
|
||||
{
|
||||
zsend_zdle_char(*hdr);
|
||||
crc = updcrc32((0377 & *hdr++), crc);
|
||||
}
|
||||
crc = ~crc;
|
||||
for (i=0; i<4;i++)
|
||||
{
|
||||
zsend_zdle_char(crc);
|
||||
crc >>= 8;
|
||||
}
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
/* send hex header */
|
||||
void zsend_hex_header(rt_uint8_t type, rt_uint8_t *hdr)
|
||||
{
|
||||
rt_uint8_t i;
|
||||
rt_uint16_t crc;
|
||||
|
||||
zsend_line(ZPAD); zsend_line(ZPAD); zsend_line(ZDLE);
|
||||
zsend_line(ZHEX);
|
||||
zsend_ascii(type);
|
||||
crc = updcrc16(type, 0);
|
||||
for (i=0; i<4; i++)
|
||||
{
|
||||
zsend_ascii(*hdr);
|
||||
crc = updcrc16((0377 & *hdr++), crc);
|
||||
}
|
||||
crc = updcrc16(0,updcrc16(0,crc));
|
||||
zsend_ascii(crc>>8);
|
||||
zsend_ascii(crc);
|
||||
/* send display control cmd */
|
||||
zsend_line(015); zsend_line(0212);
|
||||
if (type != ZFIN && type != ZACK)
|
||||
zsend_line(021);
|
||||
TxCRC = 0; /* clear tx crc type */
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
/* send binary data,with frameend */
|
||||
void zsend_bin_data(rt_uint8_t *buf, rt_int16_t len, rt_uint8_t frameend)
|
||||
{
|
||||
rt_int16_t i,c,tmp;
|
||||
rt_uint32_t crc;
|
||||
|
||||
if (TxCRC == 0) /* send binary data with 16bits crc check */
|
||||
{
|
||||
crc = 0x0L;
|
||||
for (i=0;i<len;i++)
|
||||
{
|
||||
zsend_zdle_char(*buf);
|
||||
crc = updcrc16((0377 & *buf++), crc);
|
||||
}
|
||||
zsend_byte(ZDLE); zsend_byte(frameend);
|
||||
crc = updcrc16(frameend, crc);
|
||||
crc = updcrc16(0,updcrc16(0,crc));
|
||||
zsend_zdle_char(crc>>8);
|
||||
zsend_zdle_char(crc);
|
||||
}
|
||||
else if (TxCRC == 1) /* send binary data with 32 bits crc check */
|
||||
{
|
||||
crc = 0xffffffffL;
|
||||
for (i=0;i<len;i++)
|
||||
{
|
||||
c = *buf++ & 0377;
|
||||
zsend_zdle_char(c);
|
||||
crc = updcrc32(c, crc);
|
||||
}
|
||||
zsend_byte(ZDLE); zsend_byte(frameend);
|
||||
crc = updcrc32(frameend, crc);
|
||||
crc = ~crc;
|
||||
for (i=0;i<4;i++)
|
||||
{
|
||||
zsend_zdle_char((int)crc); crc >>= 8;
|
||||
}
|
||||
}
|
||||
else if (TxCRC == 2) /* send binary data with 32bits crc check,RLE encode */
|
||||
{
|
||||
crc = 0xffffffffL;
|
||||
tmp = *buf++ & 0377;
|
||||
for (i = 0; --len >= 0; ++buf)
|
||||
{
|
||||
if ((c = *buf & 0377) == tmp && i < 126 && len>0)
|
||||
{
|
||||
++i; continue;
|
||||
}
|
||||
if (i==0)
|
||||
{
|
||||
zsend_zdle_char(tmp);
|
||||
crc = updcrc32(tmp, crc);
|
||||
if (tmp == ZRESC)
|
||||
{
|
||||
zsend_zdle_char(0100); crc = updcrc32(0100, crc);
|
||||
}
|
||||
tmp = c;
|
||||
}
|
||||
else if (i == 1)
|
||||
{
|
||||
if (tmp != ZRESC)
|
||||
{
|
||||
zsend_zdle_char(tmp); zsend_zdle_char(tmp);
|
||||
crc = updcrc32(tmp, crc);
|
||||
crc = updcrc32(tmp, crc);
|
||||
i = 0; tmp = c;
|
||||
}
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
zsend_zdle_char(ZRESC); crc = updcrc32(ZRESC, crc);
|
||||
if (tmp == 040 && i < 34)
|
||||
{
|
||||
i += 036;
|
||||
zsend_zdle_char(i);
|
||||
crc = updcrc32(i, crc);
|
||||
}
|
||||
else
|
||||
{
|
||||
i += 0101;
|
||||
zsend_zdle_char(i); crc = updcrc32(i, crc);
|
||||
zsend_zdle_char(tmp); crc = updcrc32(tmp, crc);
|
||||
}
|
||||
i = 0; tmp = c;
|
||||
}
|
||||
}
|
||||
zsend_byte(ZDLE); zsend_byte(frameend);
|
||||
crc = updcrc32(frameend, crc);
|
||||
crc = ~crc;
|
||||
for (i=0;i<4;i++)
|
||||
{
|
||||
zsend_zdle_char(crc);
|
||||
crc >>= 8;
|
||||
}
|
||||
}
|
||||
if (frameend == ZCRCW)
|
||||
zsend_byte(XON);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
/* receive data,with 16bits CRC check */
|
||||
static rt_int16_t zrec_data16(rt_uint8_t *buf, rt_uint16_t len)
|
||||
{
|
||||
rt_int16_t c,crc_cnt;
|
||||
rt_uint16_t crc;
|
||||
rt_err_t res = -RT_ERROR;
|
||||
rt_uint8_t *p,flag = 0;
|
||||
|
||||
p = buf;
|
||||
crc_cnt = 0; crc = 0L;
|
||||
Rxcount = 0;
|
||||
while(buf <= p+len)
|
||||
{
|
||||
if ((res = zread_byte()) & ~0377)
|
||||
{
|
||||
if (res == GOTCRCE || res == GOTCRCG ||
|
||||
res == GOTCRCQ || res == GOTCRCW)
|
||||
{
|
||||
c = res;
|
||||
c = res;
|
||||
crc = updcrc16(res&0377, crc);
|
||||
flag = 1;
|
||||
continue;
|
||||
}
|
||||
else if (res == GOTCAN) return ZCAN;
|
||||
else if (res == TIMEOUT) return TIMEOUT;
|
||||
else return res;
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
if (flag)
|
||||
{
|
||||
crc = updcrc16(res, crc);
|
||||
crc_cnt++;
|
||||
if (crc_cnt < 2) continue;
|
||||
if ((crc & 0xffff))
|
||||
{
|
||||
#ifdef ZDEBUG
|
||||
rt_kprintf("error code: CRC16 error \r\n");
|
||||
#endif
|
||||
return -RT_ERROR;
|
||||
}
|
||||
return c;
|
||||
}
|
||||
else
|
||||
{
|
||||
*buf++ = res;
|
||||
Rxcount++;
|
||||
crc = updcrc16(res, crc);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return -RT_ERROR;
|
||||
}
|
||||
|
||||
/* receive data,with 32bits CRC check */
|
||||
static rt_int16_t zrec_data32(rt_uint8_t *buf, rt_int16_t len)
|
||||
{
|
||||
rt_int16_t c,crc_cnt;
|
||||
rt_uint32_t crc;
|
||||
rt_err_t res = -RT_ERROR;
|
||||
rt_uint8_t *p,flag = 0;
|
||||
p = buf;
|
||||
crc_cnt = 0; crc = 0xffffffffL;
|
||||
Rxcount = 0;
|
||||
while (buf <= p+len)
|
||||
{
|
||||
if ((res = zread_byte()) & ~0377)
|
||||
{
|
||||
if (res == GOTCRCE || res == GOTCRCG ||
|
||||
res == GOTCRCQ || res == GOTCRCW)
|
||||
{
|
||||
c = res;
|
||||
crc = updcrc32(res&0377, crc);
|
||||
flag = 1;
|
||||
continue;
|
||||
}
|
||||
else if (res == GOTCAN) return ZCAN;
|
||||
else if (res == TIMEOUT) return TIMEOUT;
|
||||
else return res;
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
if (flag)
|
||||
{
|
||||
crc = updcrc32(res, crc);
|
||||
crc_cnt++;
|
||||
if (crc_cnt < 4) continue;
|
||||
if ((crc & 0xDEBB20E3))
|
||||
{
|
||||
#ifdef ZDEBUG
|
||||
rt_kprintf("error code: CRC32 error \r\n");
|
||||
#endif
|
||||
return -RT_ERROR;
|
||||
}
|
||||
return c;
|
||||
}
|
||||
else
|
||||
{
|
||||
*buf++ = res;
|
||||
Rxcount++;
|
||||
crc = updcrc32(res, crc);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return -RT_ERROR;
|
||||
}
|
||||
/* receive data,with RLE encoded,32bits CRC check */
|
||||
static rt_int16_t zrec_data32r(rt_uint8_t *buf, rt_int16_t len)
|
||||
{
|
||||
rt_int16_t c,crc_cnt;
|
||||
rt_uint32_t crc;
|
||||
rt_err_t res = -RT_ERROR;
|
||||
rt_uint8_t *p,flag = 0;
|
||||
|
||||
crc_cnt = 0; crc = 0xffffffffL;
|
||||
Rxcount = 0;
|
||||
p = buf;
|
||||
while (buf <= p+len)
|
||||
{
|
||||
if ((res = zread_byte()) & ~0377)
|
||||
{
|
||||
if (res == GOTCRCE || res == GOTCRCG ||
|
||||
res == GOTCRCQ || res == GOTCRCW)
|
||||
{
|
||||
c = res;
|
||||
crc = updcrc32(res&0377, crc);
|
||||
flag = 1;
|
||||
continue;
|
||||
}
|
||||
else if (res == GOTCAN) return ZCAN;
|
||||
else if (res == TIMEOUT) return TIMEOUT;
|
||||
else return res;
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
if (flag)
|
||||
{
|
||||
crc = updcrc32(res, crc);
|
||||
crc_cnt++;
|
||||
if (crc_cnt < 4) continue;
|
||||
if ((crc & 0xDEBB20E3))
|
||||
{
|
||||
#ifdef ZDEBUG
|
||||
rt_kprintf("error code: CRC32 error \r\n");
|
||||
#endif
|
||||
return -RT_ERROR;
|
||||
}
|
||||
return c;
|
||||
}
|
||||
else
|
||||
{
|
||||
crc = updcrc32(res, crc);
|
||||
switch (c)
|
||||
{
|
||||
case 0:
|
||||
if (res == ZRESC)
|
||||
{
|
||||
c = -1; continue;
|
||||
}
|
||||
*buf++ = res;
|
||||
Rxcount++;
|
||||
continue;
|
||||
case -1:
|
||||
if (res >= 040 && res < 0100)
|
||||
{
|
||||
c = res - 035; res = 040;
|
||||
goto spaces;
|
||||
}
|
||||
if (res == 0100)
|
||||
{
|
||||
c = 0;
|
||||
*buf++ = ZRESC;
|
||||
Rxcount++;
|
||||
continue;
|
||||
}
|
||||
c = res; continue;
|
||||
default:
|
||||
c -= 0100;
|
||||
if (c < 1)
|
||||
goto end;
|
||||
spaces:
|
||||
if ((buf + c) > p+len)
|
||||
goto end;
|
||||
while ( --res >= 0)
|
||||
{
|
||||
*buf++ = res;
|
||||
Rxcount++;
|
||||
}
|
||||
c = 0; continue;
|
||||
}
|
||||
}
|
||||
} // if -else
|
||||
|
||||
}
|
||||
end:
|
||||
return -RT_ERROR;
|
||||
}
|
||||
rt_int16_t zget_data(rt_uint8_t *buf, rt_uint16_t len)
|
||||
{
|
||||
rt_int16_t res = -RT_ERROR;
|
||||
|
||||
if (RxCRC == 0)
|
||||
{
|
||||
res = zrec_data16(buf,len);
|
||||
}
|
||||
else if (RxCRC == 1)
|
||||
{
|
||||
res = zrec_data32(buf, len);
|
||||
}
|
||||
else if (RxCRC == 2)
|
||||
{
|
||||
res = zrec_data32r(buf, len);
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
/* get type and cmd of header, fix lenght */
|
||||
rt_int16_t zget_header(rt_uint8_t *hdr)
|
||||
{
|
||||
rt_int16_t c,prev_char;
|
||||
rt_uint32_t bit;
|
||||
rt_uint16_t get_can,step_out;
|
||||
|
||||
bit = get_device_baud(); /* get console baud rate */
|
||||
Rxframeind = header_type = 0;
|
||||
step_out = 0;
|
||||
prev_char = 0xff;
|
||||
for (;;)
|
||||
{
|
||||
c = zread_line(100);
|
||||
switch(c)
|
||||
{
|
||||
case 021:
|
||||
case 0221:
|
||||
if (prev_char == CAN) break;
|
||||
if (prev_char == ZCRCW) goto start_again;
|
||||
break;
|
||||
case RCDO:
|
||||
goto end;
|
||||
case TIMEOUT:
|
||||
if (prev_char == CAN) break;
|
||||
if (prev_char == ZCRCW)
|
||||
{
|
||||
c = -RT_ERROR; goto end;
|
||||
}
|
||||
goto end;
|
||||
case ZCRCW:
|
||||
if (prev_char == CAN) goto start_again;
|
||||
break;
|
||||
case CAN:
|
||||
get_can:
|
||||
if (++get_can > 5)
|
||||
{
|
||||
c = ZCAN; goto end;
|
||||
}
|
||||
break;
|
||||
case ZPAD:
|
||||
if (prev_char == CAN) break;
|
||||
if (prev_char == ZCRCW) goto start_again;
|
||||
step_out = 1;
|
||||
break;
|
||||
default:
|
||||
if (prev_char == CAN) break;
|
||||
if (prev_char == ZCRCW) goto start_again;
|
||||
start_again:
|
||||
if (--bit == 0)
|
||||
{
|
||||
c = GCOUNT; goto end;
|
||||
}
|
||||
get_can = 0;
|
||||
break;
|
||||
}
|
||||
prev_char = c;
|
||||
if (step_out) break; /* exit loop */
|
||||
}
|
||||
step_out = get_can = 0;
|
||||
for (;;)
|
||||
{
|
||||
c = zxor_read();
|
||||
switch(c)
|
||||
{
|
||||
case ZPAD:
|
||||
break;
|
||||
case RCDO:
|
||||
case TIMEOUT:
|
||||
goto end;
|
||||
case ZDLE:
|
||||
step_out = 1;
|
||||
break;
|
||||
default:
|
||||
goto start_again;
|
||||
}
|
||||
if (step_out) break;
|
||||
}
|
||||
|
||||
Rxframeind = c = zxor_read();
|
||||
switch (c)
|
||||
{
|
||||
case ZBIN32:
|
||||
RxCRC = 1; c = zget_bin_fcs(hdr); break;
|
||||
case ZBINR32:
|
||||
RxCRC = 2; c = zget_bin_fcs(hdr); break;
|
||||
case ZBIN:
|
||||
RxCRC = 0; c = zget_bin_header(hdr); break;
|
||||
case ZHEX:
|
||||
RxCRC = 0; c = zget_hex_header(hdr); break;
|
||||
case CAN:
|
||||
goto get_can;
|
||||
case RCDO:
|
||||
case TIMEOUT:
|
||||
goto end;
|
||||
default:
|
||||
goto start_again;
|
||||
}
|
||||
end:
|
||||
return c;
|
||||
}
|
||||
|
||||
/* receive a binary header */
|
||||
static rt_int16_t zget_bin_header(rt_uint8_t *hdr)
|
||||
{
|
||||
rt_int16_t res, i;
|
||||
rt_uint16_t crc;
|
||||
|
||||
if ((res = zread_byte()) & ~0377)
|
||||
return res;
|
||||
header_type = res;
|
||||
crc = updcrc16(res, 0);
|
||||
|
||||
for (i=0;i<4;i++)
|
||||
{
|
||||
if ((res = zread_byte()) & ~0377)
|
||||
return res;
|
||||
crc = updcrc16(res, crc);
|
||||
*hdr++ = res;
|
||||
}
|
||||
if ((res = zread_byte()) & ~0377)
|
||||
return res;
|
||||
crc = updcrc16(res, crc);
|
||||
if ((res = zread_byte()) & ~0377)
|
||||
return res;
|
||||
crc = updcrc16(res, crc);
|
||||
if (crc & 0xFFFF)
|
||||
{
|
||||
rt_kprintf("CRC error\n");
|
||||
return -RT_ERROR;
|
||||
}
|
||||
|
||||
return header_type;
|
||||
}
|
||||
|
||||
/* receive a binary header,with 32bits FCS */
|
||||
static rt_int16_t zget_bin_fcs(rt_uint8_t *hdr)
|
||||
{
|
||||
rt_int16_t res, i;
|
||||
rt_uint32_t crc;
|
||||
|
||||
if ((res = zread_byte()) & ~0377)
|
||||
return res;
|
||||
header_type = res;
|
||||
crc = 0xFFFFFFFFL;
|
||||
crc = updcrc32(res, crc);
|
||||
|
||||
for (i=0;i<4;i++) /* 4headers */
|
||||
{
|
||||
if ((res = zread_byte()) & ~0377)
|
||||
return res;
|
||||
crc = updcrc32(res, crc);
|
||||
*hdr++ = res;
|
||||
|
||||
}
|
||||
for (i=0;i<4;i++) /* 4bytes crc */
|
||||
{
|
||||
if ((res = zread_byte()) & ~0377)
|
||||
return res;
|
||||
crc = updcrc32(res, crc);
|
||||
|
||||
}
|
||||
if (crc != 0xDEBB20E3)
|
||||
{
|
||||
#ifdef ZDEBUG
|
||||
rt_kprintf("CRC error\n");
|
||||
#endif
|
||||
return -RT_ERROR;
|
||||
}
|
||||
|
||||
return header_type;
|
||||
}
|
||||
|
||||
|
||||
/* receive a hex style header (type and position) */
|
||||
rt_int16_t zget_hex_header(rt_uint8_t *hdr)
|
||||
{
|
||||
rt_int16_t res,i;
|
||||
rt_uint16_t crc;
|
||||
|
||||
if ((res = zget_hex()) < 0)
|
||||
return res;
|
||||
header_type = res;
|
||||
crc = updcrc16(res, 0);
|
||||
|
||||
for (i=0;i<4;i++)
|
||||
{
|
||||
if ((res = zget_hex()) < 0)
|
||||
return res;
|
||||
crc = updcrc16(res, crc);
|
||||
*hdr++ = res;
|
||||
}
|
||||
if ((res = zget_hex()) < 0)
|
||||
return res;
|
||||
crc = updcrc16(res, crc);
|
||||
if ((res = zget_hex()) < 0)
|
||||
return res;
|
||||
crc = updcrc16(res, crc);
|
||||
if (crc & 0xFFFF)
|
||||
{
|
||||
#ifdef ZDEBUG
|
||||
rt_kprintf("error code : CRC error\r\n");
|
||||
#endif
|
||||
return -RT_ERROR;
|
||||
}
|
||||
res = zread_line(100);
|
||||
if (res < 0)
|
||||
return res;
|
||||
res = zread_line(100);
|
||||
if (res < 0)
|
||||
return res;
|
||||
|
||||
return header_type;
|
||||
}
|
||||
|
||||
/* convert to ascii */
|
||||
static void zsend_ascii(rt_uint8_t c)
|
||||
{
|
||||
const char hex[] = "0123456789abcdef";
|
||||
|
||||
zsend_line(hex[(c&0xF0)>>4]);
|
||||
zsend_line(hex[(c)&0xF]);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* aend character c with ZMODEM escape sequence encoding.
|
||||
*/
|
||||
void zsend_zdle_char(rt_uint16_t ch)
|
||||
{
|
||||
rt_uint16_t res;
|
||||
|
||||
res = ch & 0377;
|
||||
switch (res)
|
||||
{
|
||||
case 0377:
|
||||
zsend_byte(res);
|
||||
break;
|
||||
case ZDLE:
|
||||
zsend_byte(ZDLE);
|
||||
res ^= 0100;
|
||||
zsend_byte(res);
|
||||
break;
|
||||
case 021:
|
||||
case 023:
|
||||
case 0221:
|
||||
case 0223:
|
||||
zsend_byte(ZDLE);
|
||||
res ^= 0100;
|
||||
zsend_byte(res);
|
||||
break;
|
||||
default:
|
||||
zsend_byte(res);
|
||||
}
|
||||
}
|
||||
|
||||
/* decode two lower case hex digits into an 8 bit byte value */
|
||||
static rt_int16_t zget_hex(void)
|
||||
{
|
||||
rt_int16_t res,n;
|
||||
|
||||
if ((res = zxor_read()) < 0)
|
||||
return res;
|
||||
n = res - '0';
|
||||
if (n > 9)
|
||||
n -= ('a' - ':');
|
||||
if (n & ~0x0f)
|
||||
return -RT_ERROR;
|
||||
if ((res = zxor_read()) < 0)
|
||||
return res;
|
||||
res -= '0';
|
||||
if (res > 9)
|
||||
res -= ('a' - ':');
|
||||
if (res & ~0x0f)
|
||||
return -RT_ERROR;
|
||||
res += (n<<4);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* read a byte, checking for ZMODEM escape encoding
|
||||
* including CAN*5 which represents a quick abort
|
||||
*/
|
||||
rt_int16_t zread_byte(void)
|
||||
{
|
||||
register int res;
|
||||
|
||||
again:
|
||||
/* Quick check for non control characters */
|
||||
if ((res = zread_line(100)) & 0140)
|
||||
return res;
|
||||
switch (res)
|
||||
{
|
||||
case ZDLE:
|
||||
break;
|
||||
case 023:
|
||||
case 0223:
|
||||
case 021:
|
||||
case 0221:
|
||||
goto again;
|
||||
default:
|
||||
return res;
|
||||
}
|
||||
again2:
|
||||
if ((res = zread_line(100)) < 0)
|
||||
return res;
|
||||
if (res == CAN && (res = zread_line(100)) < 0)
|
||||
return res;
|
||||
if (res == CAN && (res = zread_line(100)) < 0)
|
||||
return res;
|
||||
if (res == CAN && (res = zread_line(100)) < 0)
|
||||
return res;
|
||||
switch (res)
|
||||
{
|
||||
case CAN:
|
||||
return GOTCAN;
|
||||
case ZCRCE:
|
||||
case ZCRCG:
|
||||
case ZCRCQ:
|
||||
case ZCRCW:
|
||||
return (res | GOTOR);
|
||||
case ZRUB0:
|
||||
return 0177;
|
||||
case ZRUB1:
|
||||
return 0377;
|
||||
case 023:
|
||||
case 0223:
|
||||
case 021:
|
||||
case 0221:
|
||||
goto again2;
|
||||
default:
|
||||
if ((res & 0140) == 0100)
|
||||
return (res ^ 0100);
|
||||
break;
|
||||
}
|
||||
|
||||
return -RT_ERROR;
|
||||
}
|
||||
|
||||
/*
|
||||
* @read a character from the modem line with timeout.
|
||||
* @eat parity, XON and XOFF characters.
|
||||
*/
|
||||
rt_int16_t zxor_read(void)
|
||||
{
|
||||
rt_int16_t res;
|
||||
|
||||
for (;;)
|
||||
{
|
||||
if ((res = zread_line(100)) < 0)
|
||||
return res;
|
||||
switch (res &= 0177) {
|
||||
case XON:
|
||||
case XOFF:
|
||||
continue;
|
||||
case '\r':
|
||||
case '\n':
|
||||
case ZDLE:
|
||||
default:
|
||||
return res;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/* put file posistion into the header*/
|
||||
void zput_pos(rt_uint32_t pos)
|
||||
{
|
||||
tx_header[ZP0] = pos;
|
||||
tx_header[ZP1] = pos>>8;
|
||||
tx_header[ZP2] = pos>>16;
|
||||
tx_header[ZP3] = pos>>24;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
/* Recover a long integer from a header */
|
||||
void zget_pos(rt_uint32_t pos)
|
||||
{
|
||||
Rxpos = (rx_header[ZP3] & 0377);
|
||||
Rxpos = (Rxpos << 8) | (rx_header[ZP2] & 0377);
|
||||
Rxpos = (Rxpos << 8) | (rx_header[ZP1] & 0377);
|
||||
Rxpos = (Rxpos << 8) | (rx_header[ZP0] & 0377);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
/* end of zcore.c */
|
217
components/utilities/zmodem/zdef.h
Normal file
217
components/utilities/zmodem/zdef.h
Normal file
|
@ -0,0 +1,217 @@
|
|||
#ifndef __ZDEF_H__
|
||||
#define __ZDEF_H__
|
||||
|
||||
#include <rtthread.h>
|
||||
#include "crc.h"
|
||||
#define ZPAD '*' /* 052 padding character begins frames */
|
||||
#define ZDLE 030 /* ctrl-X ZMODEM escape - `ala BISYNC DLE */
|
||||
#define ZDLEE (ZDLE^0100) /* escaped ZDLE as transmitted */
|
||||
#define ZBIN 'A' /* binary frame indicator (CRC-16) */
|
||||
#define ZHEX 'B' /* hex frame indicator */
|
||||
#define ZBIN32 'C' /* binary frame with 32 bit FCS */
|
||||
#define ZBINR32 'D' /* RLE packed Binary frame with 32 bit FCS */
|
||||
#define ZVBIN 'a' /* binary frame indicator (CRC-16) */
|
||||
#define ZVHEX 'b' /* hex frame indicator */
|
||||
#define ZVBIN32 'c' /* binary frame with 32 bit FCS */
|
||||
#define ZVBINR32 'd' /* RLE packed Binary frame with 32 bit FCS */
|
||||
#define ZRESC 0176 /* RLE flag/escape character */
|
||||
|
||||
|
||||
/* Frame types */
|
||||
#define ZRQINIT 0 /* request receive init */
|
||||
#define ZRINIT 1 /* receive init */
|
||||
#define ZSINIT 2 /* send init sequence (optional) */
|
||||
#define ZACK 3 /* ACK to above */
|
||||
#define ZFILE 4 /* file name from sender */
|
||||
#define ZSKIP 5 /* ro sender: skip this file */
|
||||
#define ZNAK 6 /* last packet was garbled */
|
||||
#define ZABORT 7 /* abort batch transfers */
|
||||
#define ZFIN 8 /* finish session */
|
||||
#define ZRPOS 9 /* resume data trans at this position */
|
||||
#define ZDATA 10 /* data packet(s) follow */
|
||||
#define ZEOF 11 /* end of file */
|
||||
#define ZFERR 12 /* fatal Read or Write error Detected */
|
||||
#define ZCRC 13 /* request for file CRC and response */
|
||||
#define ZCHALLENGE 14 /* receiver's Challenge */
|
||||
#define ZCOMPL 15 /* request is complete */
|
||||
#define ZCAN 16 /* other end canned session with CAN*5 */
|
||||
#define ZFREECNT 17 /* request for free bytes on filesystem */
|
||||
#define ZCOMMAND 18 /* command from sending program */
|
||||
|
||||
/* ZDLE sequfences */
|
||||
#define ZCRCE 'h' /* CRC next, frame ends, header packet follows */
|
||||
#define ZCRCG 'i' /* CRC next, frame continues nonstop */
|
||||
#define ZCRCQ 'j' /* CRC next, frame continues, ZACK expected */
|
||||
#define ZCRCW 'k' /* CRC next, ZACK expected, end of frame */
|
||||
#define ZRUB0 'l' /* translate to rubout 0177 */
|
||||
#define ZRUB1 'm' /* translate to rubout 0377 */
|
||||
|
||||
/* zdlread return values (internal) */
|
||||
/* -1 is general error, -2 is timeout */
|
||||
#define GOTOR 0400
|
||||
#define GOTCRCE (ZCRCE|GOTOR) /* ZDLE-ZCRCE received */
|
||||
#define GOTCRCG (ZCRCG|GOTOR) /* ZDLE-ZCRCG received */
|
||||
#define GOTCRCQ (ZCRCQ|GOTOR) /* ZDLE-ZCRCQ received */
|
||||
#define GOTCRCW (ZCRCW|GOTOR) /* ZDLE-ZCRCW received */
|
||||
#define GOTCAN (GOTOR|030) /* CAN*5 seen */
|
||||
|
||||
/* Byte positions within header array */
|
||||
#define ZF0 3 /* first flags byte */
|
||||
#define ZF1 2
|
||||
#define ZF2 1
|
||||
#define ZF3 0
|
||||
#define ZP0 0 /* low order 8 bits of position */
|
||||
#define ZP1 1
|
||||
#define ZP2 2
|
||||
#define ZP3 3 /* high order 8 bits of file position */
|
||||
|
||||
/* parameters for ZRINIT header */
|
||||
#define ZRPXWN 8 /* 9th byte in header contains window size/256 */
|
||||
#define ZRPXQQ 9 /* 10th to 14th bytes contain quote mask */
|
||||
/* bit Masks for ZRINIT flags byte ZF0 */
|
||||
#define CANFDX 0x01 /* rx can send and receive true FDX */
|
||||
#define CANOVIO 0x02 /* rx can receive data during disk I/O */
|
||||
#define CANBRK 0x04 /* rx can send a break signal */
|
||||
#define CANRLE 0x10 /* receiver can decode RLE */
|
||||
#define CANLZW 0x20 /* receiver can uncompress */
|
||||
#define CANFC32 0x28 /* receiver can use 32 bit Frame Check */
|
||||
#define ESCCTL 0x64 /* receiver expects ctl chars to be escaped */
|
||||
#define ESC8 0xc8 /* receiver expects 8th bit to be escaped */
|
||||
|
||||
/* bit Masks for ZRINIT flags byte ZF1 */
|
||||
#define CANVHDR 01 /* variable headers OK */
|
||||
#define ZRRQWN 8 /* receiver specified window size in ZRPXWN */
|
||||
#define ZRRQQQ 16 /* additional control chars to quote in ZRPXQQ */
|
||||
#define ZRQNVH (ZRRQWN|ZRRQQQ) /* variable len hdr reqd to access info */
|
||||
|
||||
/* Parameters for ZSINIT frame */
|
||||
#define ZATTNLEN 32 /* max length of attention string */
|
||||
#define ALTCOFF ZF1 /* offset to alternate canit string, 0 if not used */
|
||||
|
||||
/* Parameters for ZFILE frame */
|
||||
/* Conversion options one of these in ZF0 */
|
||||
#define ZCBIN 1 /* binary transfer - inhibit conversion */
|
||||
#define ZCNL 2 /* convert NL to local end of line convention */
|
||||
#define ZCRESUM 3 /* resume interrupted file transfer */
|
||||
/* management include options, one of these ored in ZF1 */
|
||||
#define ZMSKNOLOC 0200 /* skip file if not present at rx */
|
||||
/* management options, one of these ored in ZF1 */
|
||||
#define ZMMASK 037 /* mask for the choices below */
|
||||
#define ZMNEWL 1 /* transfer if source newer or longer */
|
||||
#define ZMCRC 2 /* transfer if different file CRC or length */
|
||||
#define ZMAPND 3 /* append contents to existing file (if any) */
|
||||
#define ZMCLOB 4 /* replace existing file */
|
||||
#define ZMNEW 5 /* transfer if source newer */
|
||||
/* number 5 is alive ... */
|
||||
#define ZMDIFF 6 /* transfer if dates or lengths different */
|
||||
#define ZMPROT 7 /* protect destination file */
|
||||
#define ZMCHNG 8 /* change filename if destination exists */
|
||||
/* transport options, one of these in ZF2 */
|
||||
#define ZTLZW 1 /* lempel-Ziv compression */
|
||||
#define ZTRLE 3 /* run Length encoding */
|
||||
/* extended options for ZF3, bit encoded */
|
||||
#define ZXSPARS 64 /* encoding for sparse file operations */
|
||||
#define ZCANVHDR 01 /* variable headers OK */
|
||||
/* receiver window size override */
|
||||
#define ZRWOVR 4 /* byte position for receive window override/256 */
|
||||
|
||||
/* parameters for ZCOMMAND frame ZF0 (otherwise 0) */
|
||||
#define ZCACK1 1 /* acknowledge, then do command */
|
||||
extern char Attn[ZATTNLEN+1]; /* Attention string rx sends to tx on err */
|
||||
|
||||
/* globals used by ZMODEM functions */
|
||||
extern rt_uint8_t Rxframeind; /* ZBIN ZBIN32, or ZHEX type of frame */
|
||||
extern char header_type; /* type of header received */
|
||||
extern rt_uint8_t rx_header[4]; /* received header */
|
||||
extern rt_uint8_t tx_header[4]; /* transmitted header */
|
||||
extern rt_uint8_t Txfcs32; /* TRUE means send binary frames with 32 bit FCS */
|
||||
extern rt_uint16_t Rxcount; /* count of data bytes received */
|
||||
extern rt_uint16_t Rxtimeout; /* tenths of seconds to wait for something */
|
||||
extern rt_uint32_t Rxpos; /* received file position */
|
||||
extern rt_uint32_t Txpos; /* transmitted file position */
|
||||
extern rt_uint8_t Txfcs32; /* TURE means send binary frames with 32 bit FCS */
|
||||
|
||||
/* ward Christensen / CP/M parameters - Don't change these! */
|
||||
#define ENQ 005
|
||||
#define CAN ('X'&037)
|
||||
#define XOFF ('s'&037)
|
||||
#define XON ('q'&037)
|
||||
#define SOH 1
|
||||
#define STX 2
|
||||
#define ETX 3
|
||||
#define SYN 026
|
||||
#define ESC 033
|
||||
#define WANTG 0107 /* send G not NAK to get nonstop batch xmsn */
|
||||
#define EOT 4
|
||||
#define ACK 6
|
||||
#define NAK 025
|
||||
#define CPMEOF 032
|
||||
#define WANTCRC 0103 /* send C not NAK to get crc not checksum */
|
||||
#define TIMEOUT (-2)
|
||||
#define RCDO (-3)
|
||||
#define GCOUNT (-4)
|
||||
#define ERRORMAX 5
|
||||
#define RETRYMAX 5
|
||||
#define WCEOT (-10)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
#define BITRATE 115200
|
||||
#define TX_BUFFER_SIZE 1024
|
||||
#define RX_BUFFER_SIZE 1024 /* sender or receiver's max buffer size */
|
||||
extern char ZF0_CMD; /* local ZMODEM file conversion request */
|
||||
extern char ZF1_CMD; /* local ZMODEM file management request */
|
||||
extern char ZF2_CMD; /* local ZMODEM file management request */
|
||||
extern char ZF3_CMD; /* local ZMODEM file management request */
|
||||
extern rt_uint32_t Baudrate ;
|
||||
extern rt_uint32_t Left_bytes;
|
||||
extern rt_uint32_t Left_sizes;
|
||||
|
||||
|
||||
struct zmodemf
|
||||
{
|
||||
struct rt_semaphore zsem;
|
||||
rt_device_t device;
|
||||
};
|
||||
extern struct zmodemf zmodem;
|
||||
|
||||
struct zfile
|
||||
{
|
||||
char *fname;
|
||||
rt_int32_t fd;
|
||||
rt_uint32_t ctime;
|
||||
rt_uint32_t mode;
|
||||
rt_uint32_t bytes_total;
|
||||
rt_uint32_t bytes_sent;
|
||||
rt_uint32_t bytes_received;
|
||||
rt_uint32_t file_end;
|
||||
|
||||
};
|
||||
extern struct finsh_shell* shell;
|
||||
|
||||
#define ZDEBUG 0
|
||||
/* sz.c */
|
||||
extern void zs_start(char *path);
|
||||
/* rz.c */
|
||||
extern void zr_start(char *path);
|
||||
|
||||
/* zcore.c */
|
||||
extern void zinit_parameter(void);
|
||||
extern rt_int16_t zget_header(rt_uint8_t *hdr);
|
||||
extern void zsend_bin_header(rt_uint8_t type, rt_uint8_t *hdr);
|
||||
extern void zsend_hex_header(rt_uint8_t type, rt_uint8_t *hdr);
|
||||
extern rt_int16_t zget_data(rt_uint8_t *buf, rt_uint16_t len);
|
||||
extern void zsend_bin_data(rt_uint8_t *buf, rt_int16_t len, rt_uint8_t frameend);
|
||||
extern void zput_pos(rt_uint32_t pos);
|
||||
extern void zget_pos(rt_uint32_t pos);
|
||||
/* zdevice.c */
|
||||
extern rt_uint32_t get_device_baud(void);
|
||||
extern void zsend_byte(rt_uint16_t c);
|
||||
extern void zsend_line(rt_uint16_t c);
|
||||
extern rt_int16_t zread_line(rt_uint16_t timeout);
|
||||
extern void zsend_break(char *cmd);
|
||||
extern void zsend_can(void);
|
||||
|
||||
#endif /* __ZDEF_H__ */
|
115
components/utilities/zmodem/zdevice.c
Normal file
115
components/utilities/zmodem/zdevice.c
Normal file
|
@ -0,0 +1,115 @@
|
|||
/*
|
||||
* File : zdevice.c
|
||||
* the implemention of zmodem protocol.
|
||||
* Change Logs:
|
||||
* Date Author Notes
|
||||
* 2011-03-29 itspy
|
||||
*/
|
||||
|
||||
#include <rtthread.h>
|
||||
#include <finsh.h>
|
||||
#include <shell.h>
|
||||
#include <rtdef.h>
|
||||
#include <dfs.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/statfs.h>
|
||||
#include "zdef.h"
|
||||
|
||||
|
||||
rt_uint32_t Line_left = 0; /* left number of data in the read line buffer*/
|
||||
rt_uint32_t Left_sizes = 0; /* left file sizes */
|
||||
rt_uint32_t Baudrate = BITRATE; /* console baudrate */
|
||||
|
||||
|
||||
|
||||
rt_uint32_t get_device_baud(void)
|
||||
{
|
||||
return(Baudrate);
|
||||
}
|
||||
|
||||
rt_uint32_t get_sys_time(void)
|
||||
{
|
||||
return(0L);
|
||||
}
|
||||
|
||||
void zsend_byte(rt_uint16_t ch)
|
||||
{
|
||||
rt_device_write(zmodem.device, 0, &ch,1);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
void zsend_line(rt_uint16_t c)
|
||||
{
|
||||
rt_uint16_t ch;
|
||||
|
||||
ch = (c & 0377);
|
||||
rt_device_write(zmodem.device, 0, &ch, 1);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
rt_int16_t zread_line(rt_uint16_t timeout)
|
||||
{
|
||||
char *str;
|
||||
static char buf[10];
|
||||
|
||||
if (Line_left > 0)
|
||||
{
|
||||
Line_left -= 1;
|
||||
return (*str++ & 0377);
|
||||
}
|
||||
Line_left = 0;
|
||||
timeout/=5;
|
||||
while (1)
|
||||
{
|
||||
Line_left = rt_device_read(shell->device, 0, buf, 1);
|
||||
if (Line_left)
|
||||
{
|
||||
Line_left = Line_left;
|
||||
str = buf;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (Line_left < 1) return TIMEOUT;
|
||||
Line_left -=1;
|
||||
|
||||
return (*str++ & 0377);
|
||||
}
|
||||
|
||||
/*
|
||||
* send a string to the modem, processing for \336 (sleep 1 sec)
|
||||
* and \335 (break signal)
|
||||
*/
|
||||
void zsend_break(char *cmd)
|
||||
{
|
||||
|
||||
while (*cmd++)
|
||||
{
|
||||
switch (*cmd)
|
||||
{
|
||||
case '\336':
|
||||
continue;
|
||||
case '\335':
|
||||
rt_thread_delay(RT_TICK_PER_SECOND);
|
||||
continue;
|
||||
default:
|
||||
zsend_line(*cmd);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
/* send cancel string to get the other end to shut up */
|
||||
void zsend_can(void)
|
||||
{
|
||||
static char cmd[] = {24,24,24,24,24,24,24,24,24,24,0};
|
||||
|
||||
zsend_break(cmd);
|
||||
rt_kprintf("\x0d");
|
||||
Line_left=0; /* clear Line_left */
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
/* end of zdevice.c */
|
120
components/utilities/zmodem/zstart.c
Normal file
120
components/utilities/zmodem/zstart.c
Normal file
|
@ -0,0 +1,120 @@
|
|||
/*
|
||||
* File : zstart.c
|
||||
* the implemention of zmodem protocol.
|
||||
* Change Logs:
|
||||
* Date Author Notes
|
||||
* 2011-03-29 itspy
|
||||
*/
|
||||
|
||||
#include <rtthread.h>
|
||||
#include <finsh.h>
|
||||
#include <shell.h>
|
||||
#include <dfs.h>
|
||||
#include <dfs_file.h>
|
||||
#include "zdef.h"
|
||||
|
||||
|
||||
|
||||
struct zmodemf zmodem;
|
||||
|
||||
rt_err_t zmodem_rx_ind(rt_device_t dev, rt_size_t size)
|
||||
{
|
||||
/* release semaphore */
|
||||
rt_sem_release(&zmodem.zsem);
|
||||
|
||||
return RT_EOK;
|
||||
}
|
||||
|
||||
void finsh_rz(void *parameter)
|
||||
{
|
||||
char *path;
|
||||
rt_err_t (*rx_indicate)(rt_device_t dev, rt_size_t size);
|
||||
rt_uint8_t flag;
|
||||
|
||||
flag = RT_DEVICE_FLAG_STREAM;
|
||||
zmodem.device->flag &=(~flag);
|
||||
rt_sem_init(&(zmodem.zsem), "zsem", 0, 0);
|
||||
path = rt_thread_self()->parameter;
|
||||
/* save old rx_indicate */
|
||||
rx_indicate = zmodem.device->rx_indicate;
|
||||
/* set new rx_indicate */
|
||||
rt_device_set_rx_indicate(zmodem.device, RT_NULL);
|
||||
/* start receive remote files */
|
||||
zr_start(path);
|
||||
zmodem.device->flag |=flag;
|
||||
/* recovery old rx_indicate */
|
||||
rt_device_set_rx_indicate(zmodem.device, rx_indicate);
|
||||
/* finsh>> */
|
||||
rt_kprintf(FINSH_PROMPT);
|
||||
}
|
||||
void finsh_sz(void *parameter)
|
||||
{
|
||||
char *path;
|
||||
rt_err_t (*rx_indicate)(rt_device_t dev, rt_size_t size);
|
||||
rt_uint8_t flag;
|
||||
|
||||
flag = RT_DEVICE_FLAG_STREAM;
|
||||
zmodem.device->flag &=(~flag);
|
||||
rt_sem_init(&(zmodem.zsem), "zsem", 0, 0);
|
||||
path = rt_thread_self()->parameter;
|
||||
/* save old rx_indicate */
|
||||
rx_indicate = zmodem.device->rx_indicate;
|
||||
/* set new rx_indicate */
|
||||
rt_device_set_rx_indicate(zmodem.device, zmodem_rx_ind);
|
||||
zs_start(path);
|
||||
zmodem.device->flag |=flag;
|
||||
/* recovery old rx_indicate */
|
||||
rt_device_set_rx_indicate(zmodem.device, rx_indicate);
|
||||
/* finsh>> */
|
||||
rt_kprintf(FINSH_PROMPT);
|
||||
}
|
||||
|
||||
#ifdef RT_USING_FINSH
|
||||
#include <finsh.h>
|
||||
#include <shell.h>
|
||||
|
||||
static void rz(char *para)
|
||||
{
|
||||
rt_thread_t init_thread;
|
||||
rt_device_t device;
|
||||
const char* device_name = finsh_get_device();
|
||||
|
||||
device = rt_device_find(device_name);
|
||||
if( device == RT_NULL )
|
||||
{
|
||||
rt_kprintf("%s not find\r\n",device_name);
|
||||
}
|
||||
zmodem.device = device;
|
||||
init_thread = rt_thread_create("rz",
|
||||
finsh_rz,
|
||||
(void*)para,
|
||||
2048,
|
||||
rt_thread_self()->current_priority+1,
|
||||
20);
|
||||
|
||||
if (init_thread != RT_NULL) rt_thread_startup(init_thread);
|
||||
}
|
||||
FINSH_FUNCTION_EXPORT(rz, receive files by zmodem protocol)
|
||||
static void sz(char *para)
|
||||
{
|
||||
rt_thread_t init_thread;
|
||||
rt_device_t device;
|
||||
const char* device_name = finsh_get_device();
|
||||
|
||||
device = rt_device_find(device_name);
|
||||
if( device == RT_NULL )
|
||||
{
|
||||
rt_kprintf("%s not find\r\n",device_name);
|
||||
}
|
||||
zmodem.device = device;
|
||||
init_thread = rt_thread_create("sz",
|
||||
finsh_sz,
|
||||
(void*)para,
|
||||
2048,
|
||||
rt_thread_self()->current_priority+1,
|
||||
20);
|
||||
|
||||
if (init_thread != RT_NULL) rt_thread_startup(init_thread);
|
||||
}
|
||||
FINSH_FUNCTION_EXPORT(sz, send files by zmodem protocol)
|
||||
#endif
|
Loading…
Add table
Add a link
Reference in a new issue