import RT-Thread@9217865c without bsp, libcpu and components/net

This commit is contained in:
Zihao Yu 2023-05-20 16:23:33 +08:00
commit e2376a3709
1414 changed files with 390370 additions and 0 deletions

View 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

View 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')

View 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')

View 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;
}

View 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__ */

View 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')

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

View 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__*/

View 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

View 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')

View 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__ */

View 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);

View 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_ */

View 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__ */

View 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__ */

View 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')

File diff suppressed because it is too large Load diff

View 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;
}

View 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;
}

View 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 */

View 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')

View 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 */

View 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 */

View 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_ */

View 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 */

View 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_ */

File diff suppressed because it is too large Load diff

View 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_ */

View 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_ */

View 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')

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

View 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__ */

View 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__ */

View 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__ */

View 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')

View 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;
}

View 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__ */

View 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);

View 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')

View 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 */

View 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;
}

View 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

View 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 */

View 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 */

View 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 */

View 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 */

View 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__ */

View 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 */

View 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