From: batt Date: Mon, 28 Sep 2009 21:54:19 +0000 (+0000) Subject: Add AX25 protocol decoder and test (preliminary). X-Git-Tag: 2.3.0~99 X-Git-Url: https://codewiz.org/gitweb?p=bertos.git;a=commitdiff_plain;h=6ea89e0647ea241ed2393a53cccb0d8ba01be48e Add AX25 protocol decoder and test (preliminary). git-svn-id: https://src.develer.com/svnoss/bertos/trunk@2981 38d2e660-2303-0410-9eaa-f027e97ec537 --- diff --git a/bertos/cfg/cfg_ax25.h b/bertos/cfg/cfg_ax25.h new file mode 100644 index 00000000..64f862f1 --- /dev/null +++ b/bertos/cfg/cfg_ax25.h @@ -0,0 +1,76 @@ +/** + * \file + * + * + * \brief Configuration file for the AX25 protocol module. + * + * \version $Id$ + * \author Francesco Sacchi + */ + +#ifndef CFG_AX25_H +#define CFG_AX25_H + +/** + * Module logging level. + * + * $WIZ$ type = "enum" + * $WIZ$ value_list = "log_level" + */ +#define AX25_LOG_LEVEL LOG_LVL_WARN + +/** + * Module logging format. + * + * $WIZ$ type = "enum" + * $WIZ$ value_list = "log_format" + */ +#define AX25_LOG_FORMAT LOG_FMT_TERSE + +/** + * AX25 frame buffer lenght. + * + * $WIZ$ type = "int" + * $WIZ$ min = 18 + */ +#define CONFIG_AX25_FRAME_BUF_LEN 330 + + +/** + * Enable repeaters listing in AX25 frames. + * If enabled use 56 addtional bytes of RAM + * for each message received. + * + * $WIZ$ type = "boolean" + */ +#define CONFIG_AX25_RPT_LST 1 + +#endif /* CFG_AX25_H */ diff --git a/bertos/net/ax25.c b/bertos/net/ax25.c new file mode 100644 index 00000000..c698edc6 --- /dev/null +++ b/bertos/net/ax25.c @@ -0,0 +1,188 @@ +/** + * \file + * + * \brief Simple AX25 data link layer implementation. + * + * For now, only UI frames without any Layer 3 protocol are handled. + * This however is enough to send/receive APRS packets. + * + * \version $Id$ + * \author Francesco Sacchi + * + */ + +#include "ax25.h" +#include "cfg/cfg_ax25.h" + +#include +#include + +#define LOG_LEVEL AX25_LOG_LEVEL +#define LOG_FORMAT AX25_LOG_FORMAT +#include + +#include //memset + +#define DECODE_CALL(buf, addr) \ + for (unsigned i = 0; i < sizeof((addr)); i++) \ + { \ + ASSERT(!(*(buf) & 0x01)); \ + (addr)[i] = *(buf)++ >> 1; \ + } + +static void ax25_decode(AX25Ctx *ctx) +{ + AX25Msg msg; + uint8_t *buf = ctx->buf; + + DECODE_CALL(buf, msg.dst.call); + ASSERT(!(*buf & 0x01)); + msg.dst.ssid = (*buf++ >> 1) & 0x0F; + + DECODE_CALL(buf, msg.src.call); + msg.src.ssid = (*buf >> 1) & 0x0F; + + LOG_INFO("SRC[%.6s-%d], DST[%.6s-%d]\n", msg.src.call, msg.src.ssid, msg.dst.call, msg.dst.ssid); + + /* Repeater addresses */ + #if CONFIG_AX25_RPT_LST + for (msg.rpt_cnt = 0; !(*buf++ & 0x01) && (msg.rpt_cnt < countof(msg.rpt_lst)); msg.rpt_cnt++) + { + DECODE_CALL(buf, msg.rpt_lst[msg.rpt_cnt].call); + msg.rpt_lst[msg.rpt_cnt].ssid = (*buf >> 1) & 0x0F; + LOG_INFO("RPT%d[%.6s-%d]\n", msg.rpt_cnt, msg.rpt_lst[msg.rpt_cnt].call, msg.rpt_lst[msg.rpt_cnt].ssid); + } + #else + while (!(*buf++ & 0x01)) + { + char rpt[6]; + uint8_t ssid; + DECODE_CALL(buf, rpt); + ssid = (*buf >> 1) & 0x0F; + LOG_INFO("RPT[%.6s-%d]\n", rpt, ssid); + } + #endif + + msg.ctrl = *buf++; + if (msg.ctrl != AX25_CTRL_UI) + { + LOG_INFO("Only UI frames are handled, got [%02X]\n", msg.ctrl); + return; + } + + msg.pid = *buf++; + if (msg.pid != AX25_PID_NOLAYER3) + { + LOG_INFO("Only frames without layer3 protocol are handled, got [%02X]\n", msg.pid); + return; + } + + msg.len = ctx->frm_len - 2 - (buf - ctx->buf); + msg.info = buf; + LOG_INFO("DATA[%.*s]\n", msg.len, msg.info); + + if (ctx->hook) + ctx->hook(&msg); +} + +void ax25_poll(AX25Ctx *ctx) +{ + int c; + + while ((c = kfile_getc(ctx->ch)) != EOF) + { + if (!ctx->escape && c == HDLC_FLAG) + { + LOG_INFO("Frame start\n"); + if (ctx->frm_len >= AX25_MIN_FRAME_LEN) + { + if (ctx->crc_in == AX25_CRC_CORRECT) + { + LOG_INFO("Frame found!\n"); + ax25_decode(ctx); + } + else + { + LOG_INFO("CRC error, computed [%04X]\n", ctx->crc_in); + } + } + ctx->sync = true; + ctx->crc_in = CRC_CCITT_INIT_VAL; + ctx->frm_len = 0; + continue; + } + + if (!ctx->escape && c == HDLC_RESET) + { + LOG_INFO("HDLC reset\n"); + ctx->sync = false; + continue; + } + + if (!ctx->escape && c == AX25_ESC) + { + ctx->escape = true; + continue; + } + + if (ctx->sync) + { + if (ctx->frm_len < CONFIG_AX25_FRAME_BUF_LEN) + { + ctx->buf[ctx->frm_len++] = c; + ctx->crc_in = updcrc_ccitt(c, ctx->crc_in); + } + else + { + LOG_INFO("Buffer overrun"); + ctx->sync = false; + } + } + ctx->escape = false; + } + + if (kfile_error(ctx->ch)) + { + LOG_ERR("Channel error [%04x]\n", kfile_error(ctx->ch)); + kfile_clearerr(ctx->ch); + } +} + +void ax25_init(AX25Ctx *ctx, KFile *channel, ax25_callback_t hook) +{ + ASSERT(ctx); + ASSERT(channel); + + memset(ctx, 0, sizeof(*ctx)); + ctx->ch = channel; + ctx->hook = hook; + ctx->crc_in = ctx->crc_out = CRC_CCITT_INIT_VAL; +} diff --git a/bertos/net/ax25.h b/bertos/net/ax25.h new file mode 100644 index 00000000..0aa8513d --- /dev/null +++ b/bertos/net/ax25.h @@ -0,0 +1,158 @@ +/** + * \file + * + * \brief Simple AX25 data link layer implementation. + * + * For now, only UI frames without any Layer 3 protocol are handled. + * This however is enough to send/receive APRS packets. + * + * \version $Id$ + * \author Francesco Sacchi + * + * $WIZ$ module_name = "ax25" + * $WIZ$ module_configuration = "bertos/cfg/cfg_ax25.h" + * $WIZ$ module_depends = "kfile" + */ + + +#ifndef NET_AX25_H +#define NET_AX25_H + +#include "cfg/cfg_ax25.h" + +#include +#include + +/** + * Maximum size of a AX25 frame. + */ +#define AX25_MIN_FRAME_LEN 18 + +/** + * CRC computation on correct AX25 packets should + * give this result (don't ask why). + */ +#define AX25_CRC_CORRECT 0xF0B8 + +struct AX25Msg; // fwd declaration + +/** + * Type for AX25 messages callback. + */ +typedef void (*ax25_callback_t)(struct AX25Msg *msg); + + +/** + * AX25 Protocol context. + */ +typedef struct AX25Ctx +{ + uint8_t buf[CONFIG_AX25_FRAME_BUF_LEN]; ///< buffer for received chars + KFile *ch; ///< KFile used to access the physical medium + size_t frm_len; ///< received frame length. + uint16_t crc_in; ///< CRC for current received frame + uint16_t crc_out; ///< CRC of current sent frame + ax25_callback_t hook; ///< Hook function to be called when a message is received + bool sync; ///< True if we have received a HDLC flag. + bool escape; ///< True when we have to escape the following char. +} AX25Ctx; + + +/** + * AX25 Call sign. + */ +typedef struct AX25Call +{ + char call[6]; ///< Call string, max 6 character + uint8_t ssid; ///< SSID (secondary station ID) for the call +} AX25Call; + +/** + * Maximum number of Repeaters in a AX25 message. + */ +#define AX25_MAX_RPT 8 + + +/** + * AX25 Message. + * Used to handle AX25 sent/received messages. + */ +typedef struct AX25Msg +{ + AX25Call src; ///< Source adress + AX25Call dst; ///< Destination address + #if CONFIG_AX25_RPT_LST + AX25Call rpt_lst[AX25_MAX_RPT]; ///< List of repeaters + uint8_t rpt_cnt; ///< Number of repeaters in this message + #endif + uint16_t ctrl; ///< AX25 control field + uint8_t pid; ///< AX25 PID field + uint8_t *info; ///< Pointer to the info field (payload) of the message + size_t len; ///< Payload length +} AX25Msg; + +#define AX25_CTRL_UI 0x03 +#define AX25_PID_NOLAYER3 0xF0 + +/** + * HDLC flags + * These should be moved in + * a separated HDLC related file one day... + * \{ + */ +#define HDLC_FLAG 0x7E +#define HDLC_RESET 0x7F +#define AX25_ESC 0x1B +/* \} */ + + +/** + * Check if there are any AX25 messages to be processed. + * This function read available characters from the medium and search for + * any AX25 messages. + * If a message is found it is decoded and the linked callback executed. + * This function may be blocking if there are no available chars and the KFile + * used in \a ctx to access the medium is configured in blocking mode. + * + * \param ctx AX25 context to operate on. + */ +void ax25_poll(AX25Ctx *ctx); + +/** + * Init the AX25 protocol decoder. + * + * \param ctx AX25 context to init. + * \param channel Used to gain access to the physical medium + * \param hook Callback function called when a message is received + */ +void ax25_init(AX25Ctx *ctx, KFile *channel, ax25_callback_t hook); + +#endif /* NET_AX25_H */ diff --git a/bertos/net/ax25_test.c b/bertos/net/ax25_test.c new file mode 100644 index 00000000..b4e0e73e --- /dev/null +++ b/bertos/net/ax25_test.c @@ -0,0 +1,92 @@ +/** + * \file + * + * + * \brief AX25 test. + * + * \version $Id$ + * \author Francesco Sacchi + */ + +#include "ax25.h" + +#include +#include + +#include +#include + +#include //strncmp + +static AX25Ctx ax25; +static KFileMem mem; + +uint8_t aprs_packet[] = +{ + HDLC_FLAG, + 0x82, 0xA0, 0xA4, 0xA6, 0x40, 0x40, 0xE0, 0xA6, 0x6A, 0x6E, 0x98, 0x9C, 0x40, 0x61, 0x03, 0xF0, + 0x3D, 0x34, 0x36, 0x30, 0x33, 0x2E, 0x36, 0x33, 0x4E, 0x2F, 0x30, 0x31, 0x34, 0x33, 0x31, 0x2E, + 0x32, 0x36, 0x45, 0x2D, 0x4F, 0x70, 0x2E, 0x20, 0x41, 0x6E, 0x64, 0x72, 0x65, 0x6A, 0x40, 0x65, + HDLC_FLAG, +}; + +static void msg_callback(AX25Msg *msg) +{ + ASSERT(strncmp(msg->dst.call, "APRS ", 6) == 0); + ASSERT(strncmp(msg->src.call, "S57LN ", 6) == 0); + ASSERT(msg->src.ssid == 0); + ASSERT(msg->dst.ssid == 0); + ASSERT(msg->ctrl == AX25_CTRL_UI); + ASSERT(msg->pid == AX25_PID_NOLAYER3); + ASSERT(msg->len == 30); + ASSERT(strncmp((char *)msg->info, "=4603.63N/01431.26E-Op. Andrej", 30) == 0); +} + +int ax25_testSetup(void) +{ + kdbg_init(); + kfilemem_init(&mem, aprs_packet, sizeof(aprs_packet)); + ax25_init(&ax25, &mem.fd, msg_callback); + return 0; +} + +int ax25_testTearDown(void) +{ + return 0; +} + +int ax25_testRun(void) +{ + ax25_poll(&ax25); + return 0; +} + +TEST_MAIN(ax25); diff --git a/test/run_tests.sh b/test/run_tests.sh index b9325d58..0662cc3f 100755 --- a/test/run_tests.sh +++ b/test/run_tests.sh @@ -28,7 +28,7 @@ TESTS=${TESTS:-`find . \ -o -name "*_test.c" -print` } TESTOUT="testout" -SRC_LIST="bertos/algo/ramp.c bertos/drv/kdebug.c bertos/drv/timer.c bertos/fs/battfs.c bertos/kern/coop.c bertos/kern/idle.c bertos/kern/kfile.c bertos/kern/monitor.c bertos/kern/proc.c bertos/kern/signal.c bertos/kern/sem.c bertos/mware/event.c bertos/mware/formatwr.c bertos/mware/hex.c bertos/mware/sprintf.c bertos/os/hptime.c bertos/struct/kfile_fifo.c bertos/fs/fatfs/ff.c bertos/emul/diskio_emul.c bertos/fs/fat.c bertos/emul/switch_ctx_emul.S bertos/mware/ini_reader.c bertos/emul/kfile_posix.c bertos/algo/crc_ccitt.c bertos/algo/crc.c bertos/struct/kfile_mem.c" +SRC_LIST="bertos/algo/ramp.c bertos/drv/kdebug.c bertos/drv/timer.c bertos/fs/battfs.c bertos/kern/coop.c bertos/kern/idle.c bertos/kern/kfile.c bertos/kern/monitor.c bertos/kern/proc.c bertos/kern/signal.c bertos/kern/sem.c bertos/mware/event.c bertos/mware/formatwr.c bertos/mware/hex.c bertos/mware/sprintf.c bertos/os/hptime.c bertos/struct/kfile_fifo.c bertos/fs/fatfs/ff.c bertos/emul/diskio_emul.c bertos/fs/fat.c bertos/emul/switch_ctx_emul.S bertos/mware/ini_reader.c bertos/emul/kfile_posix.c bertos/algo/crc_ccitt.c bertos/algo/crc.c bertos/struct/kfile_mem.c bertos/net/ax25.c" buildout='/dev/null' runout='/dev/null'