Add support for preamble/trailer before starting real data transmission: this give...
[bertos.git] / bertos / net / ax25.c
index c698edc697e808ab2f9994b250b2f62375f5962a..d2c2639b105805a79385722277cdf4081cf6618e 100644 (file)
 #include "ax25.h"
 #include "cfg/cfg_ax25.h"
 
-#include <drv/afsk.h>
 #include <algo/crc_ccitt.h>
 
 #define LOG_LEVEL  AX25_LOG_LEVEL
 #define LOG_FORMAT AX25_LOG_FORMAT
 #include <cfg/log.h>
 
-#include <string.h> //memset
+#include <string.h> //memset, memcmp
+#include <ctype.h>  //isalnum, toupper
 
 #define DECODE_CALL(buf, addr) \
        for (unsigned i = 0; i < sizeof((addr)); i++) \
        { \
-               ASSERT(!(*(buf) & 0x01)); \
                (addr)[i] = *(buf)++ >> 1; \
        }
 
@@ -64,7 +63,6 @@ static void ax25_decode(AX25Ctx *ctx)
        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);
@@ -94,25 +92,36 @@ static void ax25_decode(AX25Ctx *ctx)
        msg.ctrl = *buf++;
        if (msg.ctrl != AX25_CTRL_UI)
        {
-               LOG_INFO("Only UI frames are handled, got [%02X]\n", msg.ctrl);
+               LOG_WARN("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);
+               LOG_WARN("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);
+       LOG_INFO("DATA: %.*s\n", msg.len, msg.info);
 
        if (ctx->hook)
                ctx->hook(&msg);
 }
 
+
+/**
+ * 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)
 {
        int c;
@@ -121,7 +130,6 @@ void ax25_poll(AX25Ctx *ctx)
        {
                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)
@@ -176,6 +184,83 @@ void ax25_poll(AX25Ctx *ctx)
        }
 }
 
+static void ax25_putchar(AX25Ctx *ctx, uint8_t c)
+{
+       if (c == HDLC_FLAG || c == HDLC_RESET
+               || c == AX25_ESC)
+               kfile_putc(AX25_ESC, ctx->ch);
+       ctx->crc_out = updcrc_ccitt(c, ctx->crc_out);
+       kfile_putc(c, ctx->ch);
+}
+
+static void ax25_sendCall(AX25Ctx *ctx, const AX25Call *addr)
+{
+       unsigned len = MIN(sizeof(addr->call), strlen(addr->call));
+
+       for (unsigned i = 0; i < len; i++)
+       {
+               uint8_t c = addr->call[i];
+               ASSERT(isalnum(c) || c == ' ');
+               c = toupper(c);
+               ax25_putchar(ctx, c << 1);
+       }
+
+       /* Fill with spaces the rest of the CALL if it's shorter */
+       if (len < sizeof(addr->call))
+               for (unsigned i = 0; i < sizeof(addr->call) - len; i++)
+                       ax25_putchar(ctx, ' ' << 1);
+}
+
+/**
+ * Send an AX25 frame on the channel.
+ * \param ctx AX25 context to operate on.
+ * \param dst the destination callsign for the frame, \see AX25_CALL
+ *        for a handy way to create a callsign on the fly.
+ * \param src the source callsign for the frame, \see AX25_CALL
+ *        for a handy way to create a callsign on the fly.
+ * \param _buf payload buffer.
+ * \param len length of the payload.
+ */
+void ax25_send(AX25Ctx *ctx, const AX25Call *dst, const AX25Call *src, const void *_buf, size_t len)
+{
+       const uint8_t *buf = (const uint8_t *)_buf;
+
+       ctx->crc_out = CRC_CCITT_INIT_VAL;
+       kfile_putc(HDLC_FLAG, ctx->ch);
+
+       ax25_sendCall(ctx, dst);
+       ax25_putchar(ctx, dst->ssid << 1);
+
+       ax25_sendCall(ctx, src);
+       ax25_putchar(ctx, (src->ssid << 1) | 0x01);
+       ax25_putchar(ctx, AX25_CTRL_UI);
+       ax25_putchar(ctx, AX25_PID_NOLAYER3);
+
+       while (len--)
+               ax25_putchar(ctx, *buf++);
+
+       /*
+        * According to AX25 protocol,
+        * CRC is sent in reverse order!
+        */
+       uint8_t crcl = (ctx->crc_out & 0xff) ^ 0xff;
+       uint8_t crch = (ctx->crc_out >> 8) ^ 0xff;
+       ax25_putchar(ctx, crcl);
+       ax25_putchar(ctx, crch);
+
+       ASSERT(ctx->crc_out == AX25_CRC_CORRECT);
+
+       kfile_putc(HDLC_FLAG, ctx->ch);
+}
+
+
+/**
+ * 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)
 {
        ASSERT(ctx);