First commit
This commit is contained in:
88
hardware/packetmodem_nano2/MicroModemGP/protocol/AX25.c
Executable file
88
hardware/packetmodem_nano2/MicroModemGP/protocol/AX25.c
Executable file
@@ -0,0 +1,88 @@
|
||||
// Based on work by Francesco Sacchi
|
||||
|
||||
#include <string.h>
|
||||
#include <ctype.h>
|
||||
#include "AX25.h"
|
||||
#include "protocol/HDLC.h"
|
||||
#include "util/CRC-CCIT.h"
|
||||
#include "../hardware/AFSK.h"
|
||||
|
||||
#define countof(a) sizeof(a)/sizeof(a[0])
|
||||
#define MIN(a,b) ({ typeof(a) _a = (a); typeof(b) _b = (b); ((typeof(_a))((_a < _b) ? _a : _b)); })
|
||||
#define DECODE_CALL(buf, addr) for (unsigned i = 0; i < sizeof((addr)); i++) { char c = (*(buf)++ >> 1); (addr)[i] = (c == ' ') ? '\x0' : c; }
|
||||
#define AX25_SET_REPEATED(msg, idx, val) do { if (val) { (msg)->rpt_flags |= _BV(idx); } else { (msg)->rpt_flags &= ~_BV(idx) ; } } while(0)
|
||||
|
||||
void ax25_init(AX25Ctx *ctx, FILE *channel, ax25_callback_t hook) {
|
||||
memset(ctx, 0, sizeof(*ctx));
|
||||
ctx->ch = channel;
|
||||
ctx->hook = hook;
|
||||
ctx->crc_in = ctx->crc_out = CRC_CCIT_INIT_VAL;
|
||||
}
|
||||
|
||||
static void ax25_decode(AX25Ctx *ctx) {
|
||||
#if SERIAL_PROTOCOL == PROTOCOL_KISS
|
||||
if (ctx->hook) ctx->hook(ctx);
|
||||
#endif
|
||||
}
|
||||
|
||||
void ax25_poll(AX25Ctx *ctx) {
|
||||
int c;
|
||||
|
||||
while ((c = fgetc(ctx->ch)) != EOF) {
|
||||
if (!ctx->escape && c == HDLC_FLAG) {
|
||||
if (ctx->frame_len >= AX25_MIN_FRAME_LEN) {
|
||||
if (ctx->crc_in == AX25_CRC_CORRECT) {
|
||||
#if OPEN_SQUELCH == true
|
||||
LED_RX_ON();
|
||||
#endif
|
||||
ax25_decode(ctx);
|
||||
}
|
||||
}
|
||||
ctx->sync = true;
|
||||
ctx->crc_in = CRC_CCIT_INIT_VAL;
|
||||
ctx->frame_len = 0;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!ctx->escape && c == HDLC_RESET) {
|
||||
ctx->sync = false;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!ctx->escape && c == AX25_ESC) {
|
||||
ctx->escape = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (ctx->sync) {
|
||||
if (ctx->frame_len < AX25_MAX_FRAME_LEN) {
|
||||
ctx->buf[ctx->frame_len++] = c;
|
||||
ctx->crc_in = update_crc_ccit(c, ctx->crc_in);
|
||||
} else {
|
||||
ctx->sync = false;
|
||||
}
|
||||
}
|
||||
ctx->escape = false;
|
||||
}
|
||||
}
|
||||
|
||||
static void ax25_putchar(AX25Ctx *ctx, uint8_t c)
|
||||
{
|
||||
if (c == HDLC_FLAG || c == HDLC_RESET || c == AX25_ESC) fputc(AX25_ESC, ctx->ch);
|
||||
ctx->crc_out = update_crc_ccit(c, ctx->crc_out);
|
||||
fputc(c, ctx->ch);
|
||||
}
|
||||
|
||||
void ax25_sendRaw(AX25Ctx *ctx, void *_buf, size_t len) {
|
||||
ctx->crc_out = CRC_CCIT_INIT_VAL;
|
||||
fputc(HDLC_FLAG, ctx->ch);
|
||||
const uint8_t *buf = (const uint8_t *)_buf;
|
||||
while (len--) ax25_putchar(ctx, *buf++);
|
||||
|
||||
uint8_t crcl = (ctx->crc_out & 0xff) ^ 0xff;
|
||||
uint8_t crch = (ctx->crc_out >> 8) ^ 0xff;
|
||||
ax25_putchar(ctx, crcl);
|
||||
ax25_putchar(ctx, crch);
|
||||
|
||||
fputc(HDLC_FLAG, ctx->ch);
|
||||
}
|
40
hardware/packetmodem_nano2/MicroModemGP/protocol/AX25.h
Executable file
40
hardware/packetmodem_nano2/MicroModemGP/protocol/AX25.h
Executable file
@@ -0,0 +1,40 @@
|
||||
#ifndef PROTOCOL_AX25_H
|
||||
#define PROTOCOL_AX25_H
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdbool.h>
|
||||
#include "device.h"
|
||||
|
||||
#define AX25_MIN_FRAME_LEN 18
|
||||
#ifndef CUSTOM_FRAME_SIZE
|
||||
#define AX25_MAX_FRAME_LEN 620
|
||||
#else
|
||||
#define AX25_MAX_FRAME_LEN CUSTOM_FRAME_SIZE
|
||||
#endif
|
||||
|
||||
#define AX25_CRC_CORRECT 0xF0B8
|
||||
|
||||
#define AX25_CTRL_UI 0x03
|
||||
#define AX25_PID_NOLAYER3 0xF0
|
||||
|
||||
struct AX25Ctx; // Forward declarations
|
||||
struct AX25Msg;
|
||||
|
||||
typedef void (*ax25_callback_t)(struct AX25Ctx *ctx);
|
||||
|
||||
typedef struct AX25Ctx {
|
||||
uint8_t buf[AX25_MAX_FRAME_LEN];
|
||||
FILE *ch;
|
||||
size_t frame_len;
|
||||
uint16_t crc_in;
|
||||
uint16_t crc_out;
|
||||
ax25_callback_t hook;
|
||||
bool sync;
|
||||
bool escape;
|
||||
} AX25Ctx;
|
||||
|
||||
void ax25_poll(AX25Ctx *ctx);
|
||||
void ax25_sendRaw(AX25Ctx *ctx, void *_buf, size_t len);
|
||||
void ax25_init(AX25Ctx *ctx, FILE *channel, ax25_callback_t hook);
|
||||
|
||||
#endif
|
8
hardware/packetmodem_nano2/MicroModemGP/protocol/HDLC.h
Executable file
8
hardware/packetmodem_nano2/MicroModemGP/protocol/HDLC.h
Executable file
@@ -0,0 +1,8 @@
|
||||
#ifndef PROTOCOL_HDLC_H
|
||||
#define PROTOCOL_HDLC_H
|
||||
|
||||
#define HDLC_FLAG 0x7E
|
||||
#define HDLC_RESET 0x7F
|
||||
#define LLP_ESC 0x1B
|
||||
|
||||
#endif
|
159
hardware/packetmodem_nano2/MicroModemGP/protocol/KISS.c
Executable file
159
hardware/packetmodem_nano2/MicroModemGP/protocol/KISS.c
Executable file
@@ -0,0 +1,159 @@
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "device.h"
|
||||
#include "KISS.h"
|
||||
|
||||
static uint8_t serialBuffer[LLP_MAX_DATA_SIZE]; // Buffer for holding incoming serial data
|
||||
LLPCtx *llpCtx;
|
||||
Afsk *channel;
|
||||
Serial *serial;
|
||||
size_t frame_len;
|
||||
bool IN_FRAME;
|
||||
bool ESCAPE;
|
||||
bool FLOWCONTROL;
|
||||
|
||||
uint8_t command = CMD_UNKNOWN;
|
||||
unsigned long custom_preamble = CONFIG_AFSK_PREAMBLE_LEN;
|
||||
unsigned long custom_tail = CONFIG_AFSK_TRAILER_LEN;
|
||||
|
||||
unsigned long slotTime = 200;
|
||||
uint8_t p = 255;
|
||||
ticks_t timeout_ticks;
|
||||
|
||||
void kiss_init(LLPCtx *ctx, Afsk *afsk, Serial *ser) {
|
||||
llpCtx = ctx;
|
||||
serial = ser;
|
||||
channel = afsk;
|
||||
FLOWCONTROL = false;
|
||||
}
|
||||
|
||||
void kiss_messageCallback(LLPCtx *ctx) {
|
||||
#if (SERIAL_FRAMING == SERIAL_FRAMING_DIRECT)
|
||||
for (unsigned i = 0; i < ctx->frame_len; i++) {
|
||||
uint8_t b = ctx->buf[i];
|
||||
fputc(b, &serial->uart0);
|
||||
}
|
||||
#else
|
||||
fputc(FEND, &serial->uart0);
|
||||
fputc(0x00, &serial->uart0);
|
||||
for (unsigned i = 0; i < ctx->frame_len; i++) {
|
||||
uint8_t b = ctx->buf[i];
|
||||
if (b == FEND) {
|
||||
fputc(FESC, &serial->uart0);
|
||||
fputc(TFEND, &serial->uart0);
|
||||
} else if (b == FESC) {
|
||||
fputc(FESC, &serial->uart0);
|
||||
fputc(TFESC, &serial->uart0);
|
||||
} else {
|
||||
fputc(b, &serial->uart0);
|
||||
}
|
||||
}
|
||||
fputc(FEND, &serial->uart0);
|
||||
#endif
|
||||
}
|
||||
|
||||
void kiss_csma(LLPCtx *ctx, uint8_t *buf, size_t len) {
|
||||
bool sent = false;
|
||||
while (!sent) {
|
||||
//puts("Waiting in CSMA");
|
||||
if(!channel->hdlc.receiving) {
|
||||
uint8_t tp = rand() & 0xFF;
|
||||
if (tp < p) {
|
||||
//llp_sendRaw(ctx, buf, len);
|
||||
llp_broadcast(ctx, buf, len);
|
||||
sent = true;
|
||||
} else {
|
||||
ticks_t start = timer_clock();
|
||||
long slot_ticks = ms_to_ticks(slotTime);
|
||||
while (timer_clock() - start < slot_ticks) {
|
||||
cpu_relax();
|
||||
}
|
||||
}
|
||||
} else {
|
||||
while (!sent && channel->hdlc.receiving) {
|
||||
// Continously poll the modem for data
|
||||
// while waiting, so we don't overrun
|
||||
// receive buffers
|
||||
llp_poll(llpCtx);
|
||||
|
||||
if (channel->status != 0) {
|
||||
// If an overflow or other error
|
||||
// occurs, we'll back off and drop
|
||||
// this packet silently.
|
||||
channel->status = 0;
|
||||
sent = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (FLOWCONTROL) {
|
||||
while (!ctx->ready_for_data) { /* Wait */ }
|
||||
fputc(FEND, &serial->uart0);
|
||||
fputc(CMD_READY, &serial->uart0);
|
||||
fputc(0x01, &serial->uart0);
|
||||
fputc(FEND, &serial->uart0);
|
||||
}
|
||||
}
|
||||
|
||||
void kiss_checkTimeout(bool force) {
|
||||
if (force || (IN_FRAME && timer_clock() - timeout_ticks > ms_to_ticks(TX_MAXWAIT))) {
|
||||
kiss_csma(llpCtx, serialBuffer, frame_len);
|
||||
IN_FRAME = false;
|
||||
frame_len = 0;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void kiss_serialCallback(uint8_t sbyte) {
|
||||
#if SERIAL_FRAMING == SERIAL_FRAMING_DIRECT
|
||||
timeout_ticks = timer_clock();
|
||||
IN_FRAME = true;
|
||||
serialBuffer[frame_len++] = sbyte;
|
||||
if (frame_len >= LLP_MAX_DATA_SIZE) kiss_checkTimeout(true);
|
||||
#else
|
||||
if (IN_FRAME && sbyte == FEND && command == CMD_DATA) {
|
||||
IN_FRAME = false;
|
||||
kiss_csma(llpCtx, serialBuffer, frame_len);
|
||||
} else if (sbyte == FEND) {
|
||||
IN_FRAME = true;
|
||||
command = CMD_UNKNOWN;
|
||||
frame_len = 0;
|
||||
} else if (IN_FRAME && frame_len < LLP_MAX_DATA_SIZE) {
|
||||
// Have a look at the command byte first
|
||||
if (frame_len == 0 && command == CMD_UNKNOWN) {
|
||||
// MicroModem supports only one HDLC port, so we
|
||||
// strip off the port nibble of the command byte
|
||||
sbyte = sbyte & 0x0F;
|
||||
command = sbyte;
|
||||
} else if (command == CMD_DATA) {
|
||||
if (sbyte == FESC) {
|
||||
ESCAPE = true;
|
||||
} else {
|
||||
if (ESCAPE) {
|
||||
if (sbyte == TFEND) sbyte = FEND;
|
||||
if (sbyte == TFESC) sbyte = FESC;
|
||||
ESCAPE = false;
|
||||
}
|
||||
serialBuffer[frame_len++] = sbyte;
|
||||
}
|
||||
} else if (command == CMD_TXDELAY) {
|
||||
custom_preamble = sbyte * 10UL;
|
||||
} else if (command == CMD_TXTAIL) {
|
||||
custom_tail = sbyte * 10;
|
||||
} else if (command == CMD_SLOTTIME) {
|
||||
slotTime = sbyte * 10;
|
||||
} else if (command == CMD_P) {
|
||||
p = sbyte;
|
||||
} else if (command == CMD_READY) {
|
||||
if (sbyte == 0x00) {
|
||||
FLOWCONTROL = false;
|
||||
} else {
|
||||
FLOWCONTROL = true;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
#endif
|
||||
}
|
32
hardware/packetmodem_nano2/MicroModemGP/protocol/KISS.h
Executable file
32
hardware/packetmodem_nano2/MicroModemGP/protocol/KISS.h
Executable file
@@ -0,0 +1,32 @@
|
||||
#ifndef _PROTOCOL_KISS
|
||||
#define _PROTOCOL_KISS 0x02
|
||||
|
||||
#include "../hardware/AFSK.h"
|
||||
#include "../hardware/Serial.h"
|
||||
#include "../util/time.h"
|
||||
#include "LLP.h"
|
||||
#include "config.h"
|
||||
|
||||
#define FEND 0xC0
|
||||
#define FESC 0xDB
|
||||
#define TFEND 0xDC
|
||||
#define TFESC 0xDD
|
||||
|
||||
#define CMD_UNKNOWN 0xFE
|
||||
#define CMD_DATA 0x00
|
||||
#define CMD_TXDELAY 0x01
|
||||
#define CMD_P 0x02
|
||||
#define CMD_SLOTTIME 0x03
|
||||
#define CMD_TXTAIL 0x04
|
||||
#define CMD_FULLDUPLEX 0x05
|
||||
#define CMD_SETHARDWARE 0x06
|
||||
#define CMD_READY 0x0F
|
||||
#define CMD_RETURN 0xFF
|
||||
|
||||
void kiss_init(LLPCtx *ctx, Afsk *afsk, Serial *ser);
|
||||
void kiss_csma(LLPCtx *ctx, uint8_t *buf, size_t len);
|
||||
void kiss_messageCallback(LLPCtx *ctx);
|
||||
void kiss_serialCallback(uint8_t sbyte);
|
||||
void kiss_checkTimeout(bool force);
|
||||
|
||||
#endif
|
696
hardware/packetmodem_nano2/MicroModemGP/protocol/LLP.c
Executable file
696
hardware/packetmodem_nano2/MicroModemGP/protocol/LLP.c
Executable file
@@ -0,0 +1,696 @@
|
||||
#include <string.h>
|
||||
#include <ctype.h>
|
||||
#include "LLP.h"
|
||||
#include "protocol/HDLC.h"
|
||||
#include "util/CRC-CCIT.h"
|
||||
#include "../hardware/AFSK.h"
|
||||
|
||||
#define DISABLE_INTERLEAVE false
|
||||
#define PASSALL false
|
||||
#define STRIP_HEADERS true
|
||||
|
||||
// The GET_BIT macro is used in the interleaver
|
||||
// and deinterleaver to access single bits of a
|
||||
// byte.
|
||||
inline bool GET_BIT(uint8_t byte, int n) { return (byte & (1 << (8-n))) == (1 << (8-n)); }
|
||||
|
||||
// We need an indicator to tell us whether we
|
||||
// should send a parity byte. This happens
|
||||
// whenever two normal bytes of data has been
|
||||
// sent. We also keep the last sent byte in
|
||||
// memory because we need it to calculate the
|
||||
// parity byte.
|
||||
static bool sendParityBlock = false;
|
||||
static uint8_t lastByte = 0x00;
|
||||
|
||||
LLPAddress broadcast_address;
|
||||
|
||||
void llp_decode(LLPCtx *ctx) {
|
||||
if (ctx->hook) {
|
||||
size_t length = ctx->frame_len;
|
||||
uint8_t *buffer = (uint8_t*)&ctx->buf;
|
||||
size_t padding = buffer[LLP_HEADER_SIZE-1];
|
||||
size_t address_size = 2*sizeof(LLPAddress);
|
||||
#if STRIP_HEADERS
|
||||
uint8_t strip_headers = 1;
|
||||
#else
|
||||
uint8_t strip_headers = 0;
|
||||
#endif
|
||||
size_t subtraction = (address_size + (LLP_HEADER_SIZE - address_size))*strip_headers + padding;
|
||||
ctx->frame_len = length - subtraction - LLP_CHECKSUM_SIZE;
|
||||
|
||||
for (int i = 0; i < ctx->frame_len; i++) {
|
||||
#if STRIP_HEADERS
|
||||
buffer[i] = buffer[i+subtraction];
|
||||
#else
|
||||
if ( i >= LLP_HEADER_SIZE ) {
|
||||
buffer[i] = buffer[i+padding];
|
||||
} else {
|
||||
buffer[i] = buffer[i];
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
ctx->hook(ctx);
|
||||
}
|
||||
}
|
||||
|
||||
void llp_poll(LLPCtx *ctx) {
|
||||
int c;
|
||||
|
||||
#if DISABLE_INTERLEAVE
|
||||
while ((c = fgetc(ctx->ch)) != EOF) {
|
||||
if (!ctx->escape && c == HDLC_FLAG) {
|
||||
if (ctx->frame_len >= LLP_MIN_FRAME_LENGTH) {
|
||||
if (PASSALL || ctx->crc_in == LLP_CRC_CORRECT) {
|
||||
#if OPEN_SQUELCH == true
|
||||
LED_RX_ON();
|
||||
#endif
|
||||
llp_decode(ctx);
|
||||
}
|
||||
}
|
||||
ctx->sync = true;
|
||||
ctx->crc_in = CRC_CCIT_INIT_VAL;
|
||||
ctx->frame_len = 0;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!ctx->escape && c == HDLC_RESET) {
|
||||
ctx->sync = false;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!ctx->escape && c == LLP_ESC) {
|
||||
ctx->escape = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (ctx->sync) {
|
||||
if (ctx->frame_len < LLP_MAX_FRAME_LENGTH) {
|
||||
ctx->buf[ctx->frame_len++] = c;
|
||||
ctx->crc_in = update_crc_ccit(c, ctx->crc_in);
|
||||
} else {
|
||||
ctx->sync = false;
|
||||
}
|
||||
}
|
||||
ctx->escape = false;
|
||||
}
|
||||
#else
|
||||
while ((c = fgetc(ctx->ch)) != EOF) {
|
||||
|
||||
/////////////////////////////////////////////
|
||||
// Start of forward error correction block //
|
||||
/////////////////////////////////////////////
|
||||
if ((ctx->sync && (c != LLP_ESC )) || (ctx->sync && (ctx->escape && (c == LLP_ESC || c == HDLC_FLAG || c == HDLC_RESET)))) {
|
||||
// We have a byte, increment our read counter
|
||||
ctx->readLength++;
|
||||
|
||||
// Check if we have read 12 bytes. If we
|
||||
// have, we should now have a block of two
|
||||
// data bytes and a parity byte. This block
|
||||
if (ctx->readLength % LLP_INTERLEAVE_SIZE == 0) {
|
||||
// If the last character in the block
|
||||
// looks like a control character, we
|
||||
// need to set the escape indicator to
|
||||
// false, since the next byte will be
|
||||
// read immediately after the FEC
|
||||
// routine, and thus, the normal reading
|
||||
// code will not reset the indicator.
|
||||
if (c == LLP_ESC || c == HDLC_FLAG || c == HDLC_RESET) ctx->escape = false;
|
||||
|
||||
// The block is interleaved, so we will
|
||||
// first put the received bytes in the
|
||||
// deinterleaving buffer
|
||||
for (int i = 1; i < LLP_INTERLEAVE_SIZE; i++) {
|
||||
ctx->interleaveIn[i-1] = ctx->buf[ctx->frame_len-(LLP_INTERLEAVE_SIZE-i)];
|
||||
}
|
||||
ctx->interleaveIn[LLP_INTERLEAVE_SIZE-1] = c;
|
||||
|
||||
// We then deinterleave the block
|
||||
llpDeinterleave(ctx);
|
||||
|
||||
// Adjust the packet length, since we will get
|
||||
// parity bytes in the data buffer with block
|
||||
// sizes larger than 3
|
||||
ctx->frame_len -= LLP_INTERLEAVE_SIZE/3 - 1;
|
||||
|
||||
// For each 3-byte block in the deinterleaved
|
||||
// bytes, we apply forward error correction
|
||||
for (int i = 0; i < LLP_INTERLEAVE_SIZE; i+=3) {
|
||||
// We now calculate a parity byte on the
|
||||
// received data.
|
||||
|
||||
// Deinterleaved data bytes
|
||||
uint8_t a = ctx->interleaveIn[i];
|
||||
uint8_t b = ctx->interleaveIn[i+1];
|
||||
|
||||
// Deinterleaved parity byte
|
||||
uint8_t p = ctx->interleaveIn[i+2];
|
||||
|
||||
ctx->calculatedParity = llpParityBlock(a, b);
|
||||
|
||||
// By XORing the calculated parity byte
|
||||
// with the received parity byte, we get
|
||||
// what is called the "syndrome". This
|
||||
// number will tell us if we had any
|
||||
// errors during transmission, and if so
|
||||
// where they are. Using Hamming code, we
|
||||
// can only detect single bit errors in a
|
||||
// byte though, which is why we interleave
|
||||
// the data, since most errors will usually
|
||||
// occur in bursts of more than one bit.
|
||||
// With 2 data byte interleaving we can
|
||||
// correct 2 consecutive bit errors.
|
||||
uint8_t syndrome = ctx->calculatedParity ^ p;
|
||||
if (syndrome == 0x00) {
|
||||
// If the syndrome equals 0, we either
|
||||
// don't have any errors, or the error
|
||||
// is unrecoverable, so we don't do
|
||||
// anything
|
||||
} else {
|
||||
// If the syndrome is not equal to 0,
|
||||
// there is a problem, and we will try
|
||||
// to correct it. We first need to split
|
||||
// the syndrome byte up into the two
|
||||
// actual syndrome numbers, one for
|
||||
// each data byte.
|
||||
uint8_t syndromes[2];
|
||||
syndromes[0] = syndrome & 0x0f;
|
||||
syndromes[1] = (syndrome & 0xf0) >> 4;
|
||||
|
||||
// Then we look at each syndrome number
|
||||
// to determine what bit in the data
|
||||
// bytes to correct.
|
||||
for (int i = 0; i < 2; i++) {
|
||||
uint8_t s = syndromes[i];
|
||||
uint8_t correction = 0x00;
|
||||
if (s == 1 || s == 2 || s == 4 || s == 8) {
|
||||
// This signifies an error in the
|
||||
// parity block, so we actually
|
||||
// don't need any correction
|
||||
continue;
|
||||
}
|
||||
|
||||
// The following determines what
|
||||
// bit to correct according to
|
||||
// the syndrome value.
|
||||
if (s == 3) correction = 0x01;
|
||||
if (s == 5) correction = 0x02;
|
||||
if (s == 6) correction = 0x04;
|
||||
if (s == 7) correction = 0x08;
|
||||
if (s == 9) correction = 0x10;
|
||||
if (s == 10) correction = 0x20;
|
||||
if (s == 11) correction = 0x40;
|
||||
if (s == 12) correction = 0x80;
|
||||
|
||||
// And finally we apply the correction
|
||||
if (i == 1) a ^= correction;
|
||||
if (i == 0) b ^= correction;
|
||||
|
||||
// This is just for testing purposes.
|
||||
// Nice to know when corrections were
|
||||
// actually made.
|
||||
if (s != 0) ctx->correctionsMade += 1;
|
||||
}
|
||||
}
|
||||
|
||||
// We now update the checksum of the packet
|
||||
// with the deinterleaved and possibly
|
||||
// corrected bytes.
|
||||
|
||||
ctx->crc_in = update_crc_ccit(a, ctx->crc_in);
|
||||
ctx->crc_in = update_crc_ccit(b, ctx->crc_in);
|
||||
|
||||
ctx->buf[ctx->frame_len-(LLP_DATA_BLOCK_SIZE)+((i/3)*2)] = a;
|
||||
ctx->buf[ctx->frame_len-(LLP_DATA_BLOCK_SIZE-1)+((i/3)*2)] = b;
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
}
|
||||
/////////////////////////////////////////////
|
||||
// End of forward error correction block //
|
||||
/////////////////////////////////////////////
|
||||
|
||||
if (!ctx->escape && c == HDLC_FLAG) {
|
||||
if (ctx->frame_len >= LLP_MIN_FRAME_LENGTH) {
|
||||
if (PASSALL || ctx->crc_in == LLP_CRC_CORRECT) {
|
||||
#if OPEN_SQUELCH == true
|
||||
LED_RX_ON();
|
||||
#endif
|
||||
llp_decode(ctx);
|
||||
}
|
||||
}
|
||||
ctx->sync = true;
|
||||
ctx->crc_in = CRC_CCIT_INIT_VAL;
|
||||
ctx->frame_len = 0;
|
||||
ctx->readLength = 0;
|
||||
ctx->correctionsMade = 0;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!ctx->escape && c == HDLC_RESET) {
|
||||
ctx->sync = false;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!ctx->escape && c == LLP_ESC) {
|
||||
ctx->escape = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (ctx->sync) {
|
||||
if (ctx->frame_len < LLP_MAX_FRAME_LENGTH) {
|
||||
ctx->buf[ctx->frame_len++] = c;
|
||||
} else {
|
||||
ctx->sync = false;
|
||||
}
|
||||
}
|
||||
ctx->escape = false;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
static void llp_putchar(LLPCtx *ctx, uint8_t c) {
|
||||
if (c == HDLC_FLAG || c == HDLC_RESET || c == LLP_ESC) fputc(LLP_ESC, ctx->ch);
|
||||
fputc(c, ctx->ch);
|
||||
}
|
||||
|
||||
static void llp_sendchar(LLPCtx *ctx, uint8_t c) {
|
||||
llpInterleave(ctx, c);
|
||||
ctx->crc_out = update_crc_ccit(c, ctx->crc_out);
|
||||
|
||||
if (sendParityBlock) {
|
||||
uint8_t p = llpParityBlock(lastByte, c);
|
||||
llpInterleave(ctx, p);
|
||||
}
|
||||
|
||||
lastByte = c;
|
||||
sendParityBlock ^= true;
|
||||
}
|
||||
|
||||
void llp_sendaddress(LLPCtx *ctx, LLPAddress *address) {
|
||||
llp_sendchar(ctx, address->network >> 8);
|
||||
llp_sendchar(ctx, address->network & 0xff);
|
||||
llp_sendchar(ctx, address->host >> 8);
|
||||
llp_sendchar(ctx, address->host & 0xff);
|
||||
}
|
||||
|
||||
void llp_broadcast(LLPCtx *ctx, const void *_buf, size_t len) {
|
||||
llp_send(ctx, &broadcast_address, _buf, len);
|
||||
}
|
||||
|
||||
void llp_send(LLPCtx *ctx, LLPAddress *dst, const void *_buf, size_t len) {
|
||||
ctx->ready_for_data = false;
|
||||
ctx->interleaveCounter = 0;
|
||||
ctx->crc_out = CRC_CCIT_INIT_VAL;
|
||||
uint8_t *buffer = (uint8_t*)_buf;
|
||||
|
||||
LLPHeader header;
|
||||
memset(&header, 0, sizeof(header));
|
||||
|
||||
LLPAddress *localAddress = ctx->address;
|
||||
header.src.network = localAddress->network;
|
||||
header.src.host = localAddress->host;
|
||||
header.dst.network = dst->network;
|
||||
header.dst.host = dst->host;
|
||||
header.flags = 0x00;
|
||||
header.padding = (len + LLP_HEADER_SIZE + LLP_CRC_SIZE) % LLP_DATA_BLOCK_SIZE;
|
||||
if (header.padding != 0) {
|
||||
header.padding = LLP_DATA_BLOCK_SIZE - header.padding;
|
||||
}
|
||||
|
||||
// Transmit the HDLC_FLAG to signify start of TX
|
||||
fputc(HDLC_FLAG, ctx->ch);
|
||||
|
||||
// Transmit source & destination addresses
|
||||
llp_sendaddress(ctx, &header.src);
|
||||
llp_sendaddress(ctx, &header.dst);
|
||||
|
||||
// Transmit header flags & padding count
|
||||
llp_sendchar(ctx, header.flags);
|
||||
llp_sendchar(ctx, header.padding);
|
||||
|
||||
// Transmit padding
|
||||
while (header.padding--) {
|
||||
llp_sendchar(ctx, 0x00);
|
||||
}
|
||||
|
||||
// Transmit payload
|
||||
while (len--) {
|
||||
llp_sendchar(ctx, *buffer++);
|
||||
}
|
||||
|
||||
// Send CRC checksum
|
||||
uint8_t crcl = (ctx->crc_out & 0xff) ^ 0xff;
|
||||
uint8_t crch = (ctx->crc_out >> 8) ^ 0xff;
|
||||
llp_sendchar(ctx, crcl);
|
||||
llp_sendchar(ctx, crch);
|
||||
|
||||
// And transmit a HDLC_FLAG to signify
|
||||
// end of the transmission.
|
||||
fputc(HDLC_FLAG, ctx->ch);
|
||||
ctx->ready_for_data = true;
|
||||
}
|
||||
|
||||
void llp_sendRaw(LLPCtx *ctx, const void *_buf, size_t len) {
|
||||
ctx->ready_for_data = false;
|
||||
ctx->crc_out = CRC_CCIT_INIT_VAL;
|
||||
fputc(HDLC_FLAG, ctx->ch);
|
||||
const uint8_t *buf = (const uint8_t *)_buf;
|
||||
while (len--) llp_putchar(ctx, *buf++);
|
||||
|
||||
uint8_t crcl = (ctx->crc_out & 0xff) ^ 0xff;
|
||||
uint8_t crch = (ctx->crc_out >> 8) ^ 0xff;
|
||||
llp_putchar(ctx, crcl);
|
||||
llp_putchar(ctx, crch);
|
||||
|
||||
fputc(HDLC_FLAG, ctx->ch);
|
||||
|
||||
ctx->ready_for_data = true;
|
||||
}
|
||||
|
||||
void llp_init(LLPCtx *ctx, LLPAddress *address, FILE *channel, llp_callback_t hook) {
|
||||
memset(ctx, 0, sizeof(*ctx));
|
||||
ctx->ch = channel;
|
||||
ctx->hook = hook;
|
||||
ctx->address = address;
|
||||
ctx->crc_in = ctx->crc_out = CRC_CCIT_INIT_VAL;
|
||||
ctx->ready_for_data = true;
|
||||
|
||||
memset(&broadcast_address, 0, sizeof(broadcast_address));
|
||||
broadcast_address.network = LLP_ADDR_BROADCAST;
|
||||
broadcast_address.host = LLP_ADDR_BROADCAST;
|
||||
}
|
||||
|
||||
// This function calculates and returns a parity
|
||||
// byte for two input bytes. The parity byte is
|
||||
// used for correcting errors in the transmission.
|
||||
// The error correction algorithm is a standard
|
||||
// (12,8) Hamming code.
|
||||
inline bool BIT(uint8_t byte, int n) { return ((byte & _BV(n-1))>>(n-1)); }
|
||||
uint8_t llpParityBlock(uint8_t first, uint8_t other) {
|
||||
uint8_t parity = 0x00;
|
||||
|
||||
parity = ((BIT(first, 1) ^ BIT(first, 2) ^ BIT(first, 4) ^ BIT(first, 5) ^ BIT(first, 7))) +
|
||||
((BIT(first, 1) ^ BIT(first, 3) ^ BIT(first, 4) ^ BIT(first, 6) ^ BIT(first, 7))<<1) +
|
||||
((BIT(first, 2) ^ BIT(first, 3) ^ BIT(first, 4) ^ BIT(first, 8))<<2) +
|
||||
((BIT(first, 5) ^ BIT(first, 6) ^ BIT(first, 7) ^ BIT(first, 8))<<3) +
|
||||
|
||||
((BIT(other, 1) ^ BIT(other, 2) ^ BIT(other, 4) ^ BIT(other, 5) ^ BIT(other, 7))<<4) +
|
||||
((BIT(other, 1) ^ BIT(other, 3) ^ BIT(other, 4) ^ BIT(other, 6) ^ BIT(other, 7))<<5) +
|
||||
((BIT(other, 2) ^ BIT(other, 3) ^ BIT(other, 4) ^ BIT(other, 8))<<6) +
|
||||
((BIT(other, 5) ^ BIT(other, 6) ^ BIT(other, 7) ^ BIT(other, 8))<<7);
|
||||
|
||||
return parity;
|
||||
}
|
||||
|
||||
// Following is the functions responsible
|
||||
// for interleaving and deinterleaving
|
||||
// blocks of data. The interleaving table
|
||||
// for 3-byte interleaving is also included.
|
||||
// The table for 12-byte is much simpler,
|
||||
// and should be inferable from looking
|
||||
// at the function.
|
||||
|
||||
///////////////////////////////
|
||||
// Interleave-table (3-byte) //
|
||||
///////////////////////////////
|
||||
//
|
||||
// Non-interleaved:
|
||||
// aaaaaaaa bbbbbbbb cccccccc
|
||||
// 12345678 12345678 12345678
|
||||
// M L
|
||||
// S S
|
||||
// B B
|
||||
//
|
||||
// Interleaved:
|
||||
// abcabcab cabcabca bcabcabc
|
||||
// 11144477 22255578 63336688
|
||||
//
|
||||
///////////////////////////////
|
||||
|
||||
void llpInterleave(LLPCtx *ctx, uint8_t byte) {
|
||||
ctx->interleaveOut[ctx->interleaveCounter] = byte;
|
||||
ctx->interleaveCounter++;
|
||||
if (!DISABLE_INTERLEAVE) {
|
||||
if (ctx->interleaveCounter == LLP_INTERLEAVE_SIZE) {
|
||||
// We have the bytes we need for interleaving
|
||||
// in the buffer and are ready to interleave them.
|
||||
|
||||
uint8_t a = (GET_BIT(ctx->interleaveOut[0], 1) << 7) +
|
||||
(GET_BIT(ctx->interleaveOut[1], 1) << 6) +
|
||||
(GET_BIT(ctx->interleaveOut[3], 1) << 5) +
|
||||
(GET_BIT(ctx->interleaveOut[4], 1) << 4) +
|
||||
(GET_BIT(ctx->interleaveOut[6], 1) << 3) +
|
||||
(GET_BIT(ctx->interleaveOut[7], 1) << 2) +
|
||||
(GET_BIT(ctx->interleaveOut[9], 1) << 1) +
|
||||
(GET_BIT(ctx->interleaveOut[10],1));
|
||||
llp_putchar(ctx, a);
|
||||
|
||||
uint8_t b = (GET_BIT(ctx->interleaveOut[0], 2) << 7) +
|
||||
(GET_BIT(ctx->interleaveOut[1], 2) << 6) +
|
||||
(GET_BIT(ctx->interleaveOut[3], 2) << 5) +
|
||||
(GET_BIT(ctx->interleaveOut[4], 2) << 4) +
|
||||
(GET_BIT(ctx->interleaveOut[6], 2) << 3) +
|
||||
(GET_BIT(ctx->interleaveOut[7], 2) << 2) +
|
||||
(GET_BIT(ctx->interleaveOut[9], 2) << 1) +
|
||||
(GET_BIT(ctx->interleaveOut[10],2));
|
||||
llp_putchar(ctx, b);
|
||||
|
||||
uint8_t c = (GET_BIT(ctx->interleaveOut[0], 3) << 7) +
|
||||
(GET_BIT(ctx->interleaveOut[1], 3) << 6) +
|
||||
(GET_BIT(ctx->interleaveOut[3], 3) << 5) +
|
||||
(GET_BIT(ctx->interleaveOut[4], 3) << 4) +
|
||||
(GET_BIT(ctx->interleaveOut[6], 3) << 3) +
|
||||
(GET_BIT(ctx->interleaveOut[7], 3) << 2) +
|
||||
(GET_BIT(ctx->interleaveOut[9], 3) << 1) +
|
||||
(GET_BIT(ctx->interleaveOut[10],3));
|
||||
llp_putchar(ctx, c);
|
||||
|
||||
uint8_t d = (GET_BIT(ctx->interleaveOut[0], 4) << 7) +
|
||||
(GET_BIT(ctx->interleaveOut[1], 4) << 6) +
|
||||
(GET_BIT(ctx->interleaveOut[3], 4) << 5) +
|
||||
(GET_BIT(ctx->interleaveOut[4], 4) << 4) +
|
||||
(GET_BIT(ctx->interleaveOut[6], 4) << 3) +
|
||||
(GET_BIT(ctx->interleaveOut[7], 4) << 2) +
|
||||
(GET_BIT(ctx->interleaveOut[9], 4) << 1) +
|
||||
(GET_BIT(ctx->interleaveOut[10],4));
|
||||
llp_putchar(ctx, d);
|
||||
|
||||
uint8_t e = (GET_BIT(ctx->interleaveOut[0], 5) << 7) +
|
||||
(GET_BIT(ctx->interleaveOut[1], 5) << 6) +
|
||||
(GET_BIT(ctx->interleaveOut[3], 5) << 5) +
|
||||
(GET_BIT(ctx->interleaveOut[4], 5) << 4) +
|
||||
(GET_BIT(ctx->interleaveOut[6], 5) << 3) +
|
||||
(GET_BIT(ctx->interleaveOut[7], 5) << 2) +
|
||||
(GET_BIT(ctx->interleaveOut[9], 5) << 1) +
|
||||
(GET_BIT(ctx->interleaveOut[10],5));
|
||||
llp_putchar(ctx, e);
|
||||
|
||||
uint8_t f = (GET_BIT(ctx->interleaveOut[0], 6) << 7) +
|
||||
(GET_BIT(ctx->interleaveOut[1], 6) << 6) +
|
||||
(GET_BIT(ctx->interleaveOut[3], 6) << 5) +
|
||||
(GET_BIT(ctx->interleaveOut[4], 6) << 4) +
|
||||
(GET_BIT(ctx->interleaveOut[6], 6) << 3) +
|
||||
(GET_BIT(ctx->interleaveOut[7], 6) << 2) +
|
||||
(GET_BIT(ctx->interleaveOut[9], 6) << 1) +
|
||||
(GET_BIT(ctx->interleaveOut[10],6));
|
||||
llp_putchar(ctx, f);
|
||||
|
||||
uint8_t g = (GET_BIT(ctx->interleaveOut[0], 7) << 7) +
|
||||
(GET_BIT(ctx->interleaveOut[1], 7) << 6) +
|
||||
(GET_BIT(ctx->interleaveOut[3], 7) << 5) +
|
||||
(GET_BIT(ctx->interleaveOut[4], 7) << 4) +
|
||||
(GET_BIT(ctx->interleaveOut[6], 7) << 3) +
|
||||
(GET_BIT(ctx->interleaveOut[7], 7) << 2) +
|
||||
(GET_BIT(ctx->interleaveOut[9], 7) << 1) +
|
||||
(GET_BIT(ctx->interleaveOut[10],7));
|
||||
llp_putchar(ctx, g);
|
||||
|
||||
uint8_t h = (GET_BIT(ctx->interleaveOut[0], 8) << 7) +
|
||||
(GET_BIT(ctx->interleaveOut[1], 8) << 6) +
|
||||
(GET_BIT(ctx->interleaveOut[3], 8) << 5) +
|
||||
(GET_BIT(ctx->interleaveOut[4], 8) << 4) +
|
||||
(GET_BIT(ctx->interleaveOut[6], 8) << 3) +
|
||||
(GET_BIT(ctx->interleaveOut[7], 8) << 2) +
|
||||
(GET_BIT(ctx->interleaveOut[9], 8) << 1) +
|
||||
(GET_BIT(ctx->interleaveOut[10],8));
|
||||
llp_putchar(ctx, h);
|
||||
|
||||
uint8_t p = (GET_BIT(ctx->interleaveOut[2], 1) << 7) +
|
||||
(GET_BIT(ctx->interleaveOut[2], 5) << 6) +
|
||||
(GET_BIT(ctx->interleaveOut[5], 1) << 5) +
|
||||
(GET_BIT(ctx->interleaveOut[5], 5) << 4) +
|
||||
(GET_BIT(ctx->interleaveOut[8], 1) << 3) +
|
||||
(GET_BIT(ctx->interleaveOut[8], 5) << 2) +
|
||||
(GET_BIT(ctx->interleaveOut[11],1) << 1) +
|
||||
(GET_BIT(ctx->interleaveOut[11],5));
|
||||
llp_putchar(ctx, p);
|
||||
|
||||
uint8_t q = (GET_BIT(ctx->interleaveOut[2], 2) << 7) +
|
||||
(GET_BIT(ctx->interleaveOut[2], 6) << 6) +
|
||||
(GET_BIT(ctx->interleaveOut[5], 2) << 5) +
|
||||
(GET_BIT(ctx->interleaveOut[5], 6) << 4) +
|
||||
(GET_BIT(ctx->interleaveOut[8], 2) << 3) +
|
||||
(GET_BIT(ctx->interleaveOut[8], 6) << 2) +
|
||||
(GET_BIT(ctx->interleaveOut[11],2) << 1) +
|
||||
(GET_BIT(ctx->interleaveOut[11],6));
|
||||
llp_putchar(ctx, q);
|
||||
|
||||
uint8_t s = (GET_BIT(ctx->interleaveOut[2], 3) << 7) +
|
||||
(GET_BIT(ctx->interleaveOut[2], 7) << 6) +
|
||||
(GET_BIT(ctx->interleaveOut[5], 3) << 5) +
|
||||
(GET_BIT(ctx->interleaveOut[5], 7) << 4) +
|
||||
(GET_BIT(ctx->interleaveOut[8], 3) << 3) +
|
||||
(GET_BIT(ctx->interleaveOut[8], 7) << 2) +
|
||||
(GET_BIT(ctx->interleaveOut[11],3) << 1) +
|
||||
(GET_BIT(ctx->interleaveOut[11],7));
|
||||
llp_putchar(ctx, s);
|
||||
|
||||
uint8_t t = (GET_BIT(ctx->interleaveOut[2], 4) << 7) +
|
||||
(GET_BIT(ctx->interleaveOut[2], 8) << 6) +
|
||||
(GET_BIT(ctx->interleaveOut[5], 4) << 5) +
|
||||
(GET_BIT(ctx->interleaveOut[5], 8) << 4) +
|
||||
(GET_BIT(ctx->interleaveOut[8], 4) << 3) +
|
||||
(GET_BIT(ctx->interleaveOut[8], 8) << 2) +
|
||||
(GET_BIT(ctx->interleaveOut[11],4) << 1) +
|
||||
(GET_BIT(ctx->interleaveOut[11],8));
|
||||
llp_putchar(ctx, t);
|
||||
|
||||
ctx->interleaveCounter = 0;
|
||||
}
|
||||
} else {
|
||||
if (ctx->interleaveCounter == LLP_INTERLEAVE_SIZE) {
|
||||
for (int i = 0; i < LLP_INTERLEAVE_SIZE; i++) {
|
||||
llp_putchar(ctx, ctx->interleaveOut[i]);
|
||||
}
|
||||
ctx->interleaveCounter = 0;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void llpDeinterleave(LLPCtx *ctx) {
|
||||
uint8_t a = (GET_BIT(ctx->interleaveIn[0], 1) << 7) +
|
||||
(GET_BIT(ctx->interleaveIn[1], 1) << 6) +
|
||||
(GET_BIT(ctx->interleaveIn[2], 1) << 5) +
|
||||
(GET_BIT(ctx->interleaveIn[3], 1) << 4) +
|
||||
(GET_BIT(ctx->interleaveIn[4], 1) << 3) +
|
||||
(GET_BIT(ctx->interleaveIn[5], 1) << 2) +
|
||||
(GET_BIT(ctx->interleaveIn[6], 1) << 1) +
|
||||
(GET_BIT(ctx->interleaveIn[7], 1));
|
||||
|
||||
uint8_t b = (GET_BIT(ctx->interleaveIn[0], 2) << 7) +
|
||||
(GET_BIT(ctx->interleaveIn[1], 2) << 6) +
|
||||
(GET_BIT(ctx->interleaveIn[2], 2) << 5) +
|
||||
(GET_BIT(ctx->interleaveIn[3], 2) << 4) +
|
||||
(GET_BIT(ctx->interleaveIn[4], 2) << 3) +
|
||||
(GET_BIT(ctx->interleaveIn[5], 2) << 2) +
|
||||
(GET_BIT(ctx->interleaveIn[6], 2) << 1) +
|
||||
(GET_BIT(ctx->interleaveIn[7], 2));
|
||||
|
||||
uint8_t p = (GET_BIT(ctx->interleaveIn[8], 1) << 7) +
|
||||
(GET_BIT(ctx->interleaveIn[9], 1) << 6) +
|
||||
(GET_BIT(ctx->interleaveIn[10],1) << 5) +
|
||||
(GET_BIT(ctx->interleaveIn[11],1) << 4) +
|
||||
(GET_BIT(ctx->interleaveIn[8], 2) << 3) +
|
||||
(GET_BIT(ctx->interleaveIn[9], 2) << 2) +
|
||||
(GET_BIT(ctx->interleaveIn[10],2) << 1) +
|
||||
(GET_BIT(ctx->interleaveIn[11],2));
|
||||
|
||||
uint8_t c = (GET_BIT(ctx->interleaveIn[0], 3) << 7) +
|
||||
(GET_BIT(ctx->interleaveIn[1], 3) << 6) +
|
||||
(GET_BIT(ctx->interleaveIn[2], 3) << 5) +
|
||||
(GET_BIT(ctx->interleaveIn[3], 3) << 4) +
|
||||
(GET_BIT(ctx->interleaveIn[4], 3) << 3) +
|
||||
(GET_BIT(ctx->interleaveIn[5], 3) << 2) +
|
||||
(GET_BIT(ctx->interleaveIn[6], 3) << 1) +
|
||||
(GET_BIT(ctx->interleaveIn[7], 3));
|
||||
|
||||
uint8_t d = (GET_BIT(ctx->interleaveIn[0], 4) << 7) +
|
||||
(GET_BIT(ctx->interleaveIn[1], 4) << 6) +
|
||||
(GET_BIT(ctx->interleaveIn[2], 4) << 5) +
|
||||
(GET_BIT(ctx->interleaveIn[3], 4) << 4) +
|
||||
(GET_BIT(ctx->interleaveIn[4], 4) << 3) +
|
||||
(GET_BIT(ctx->interleaveIn[5], 4) << 2) +
|
||||
(GET_BIT(ctx->interleaveIn[6], 4) << 1) +
|
||||
(GET_BIT(ctx->interleaveIn[7], 4));
|
||||
|
||||
uint8_t q = (GET_BIT(ctx->interleaveIn[8], 3) << 7) +
|
||||
(GET_BIT(ctx->interleaveIn[9], 3) << 6) +
|
||||
(GET_BIT(ctx->interleaveIn[10],3) << 5) +
|
||||
(GET_BIT(ctx->interleaveIn[11],3) << 4) +
|
||||
(GET_BIT(ctx->interleaveIn[8], 4) << 3) +
|
||||
(GET_BIT(ctx->interleaveIn[9], 4) << 2) +
|
||||
(GET_BIT(ctx->interleaveIn[10],4) << 1) +
|
||||
(GET_BIT(ctx->interleaveIn[11],4));
|
||||
|
||||
uint8_t e = (GET_BIT(ctx->interleaveIn[0], 5) << 7) +
|
||||
(GET_BIT(ctx->interleaveIn[1], 5) << 6) +
|
||||
(GET_BIT(ctx->interleaveIn[2], 5) << 5) +
|
||||
(GET_BIT(ctx->interleaveIn[3], 5) << 4) +
|
||||
(GET_BIT(ctx->interleaveIn[4], 5) << 3) +
|
||||
(GET_BIT(ctx->interleaveIn[5], 5) << 2) +
|
||||
(GET_BIT(ctx->interleaveIn[6], 5) << 1) +
|
||||
(GET_BIT(ctx->interleaveIn[7], 5));
|
||||
|
||||
uint8_t f = (GET_BIT(ctx->interleaveIn[0], 6) << 7) +
|
||||
(GET_BIT(ctx->interleaveIn[1], 6) << 6) +
|
||||
(GET_BIT(ctx->interleaveIn[2], 6) << 5) +
|
||||
(GET_BIT(ctx->interleaveIn[3], 6) << 4) +
|
||||
(GET_BIT(ctx->interleaveIn[4], 6) << 3) +
|
||||
(GET_BIT(ctx->interleaveIn[5], 6) << 2) +
|
||||
(GET_BIT(ctx->interleaveIn[6], 6) << 1) +
|
||||
(GET_BIT(ctx->interleaveIn[7], 6));
|
||||
|
||||
uint8_t s = (GET_BIT(ctx->interleaveIn[8], 5) << 7) +
|
||||
(GET_BIT(ctx->interleaveIn[9], 5) << 6) +
|
||||
(GET_BIT(ctx->interleaveIn[10],5) << 5) +
|
||||
(GET_BIT(ctx->interleaveIn[11],5) << 4) +
|
||||
(GET_BIT(ctx->interleaveIn[8], 6) << 3) +
|
||||
(GET_BIT(ctx->interleaveIn[9], 6) << 2) +
|
||||
(GET_BIT(ctx->interleaveIn[10],6) << 1) +
|
||||
(GET_BIT(ctx->interleaveIn[11],6));
|
||||
|
||||
uint8_t g = (GET_BIT(ctx->interleaveIn[0], 7) << 7) +
|
||||
(GET_BIT(ctx->interleaveIn[1], 7) << 6) +
|
||||
(GET_BIT(ctx->interleaveIn[2], 7) << 5) +
|
||||
(GET_BIT(ctx->interleaveIn[3], 7) << 4) +
|
||||
(GET_BIT(ctx->interleaveIn[4], 7) << 3) +
|
||||
(GET_BIT(ctx->interleaveIn[5], 7) << 2) +
|
||||
(GET_BIT(ctx->interleaveIn[6], 7) << 1) +
|
||||
(GET_BIT(ctx->interleaveIn[7], 7));
|
||||
|
||||
uint8_t h = (GET_BIT(ctx->interleaveIn[0], 8) << 7) +
|
||||
(GET_BIT(ctx->interleaveIn[1], 8) << 6) +
|
||||
(GET_BIT(ctx->interleaveIn[2], 8) << 5) +
|
||||
(GET_BIT(ctx->interleaveIn[3], 8) << 4) +
|
||||
(GET_BIT(ctx->interleaveIn[4], 8) << 3) +
|
||||
(GET_BIT(ctx->interleaveIn[5], 8) << 2) +
|
||||
(GET_BIT(ctx->interleaveIn[6], 8) << 1) +
|
||||
(GET_BIT(ctx->interleaveIn[7], 8));
|
||||
|
||||
uint8_t t = (GET_BIT(ctx->interleaveIn[8], 7) << 7) +
|
||||
(GET_BIT(ctx->interleaveIn[9], 7) << 6) +
|
||||
(GET_BIT(ctx->interleaveIn[10],7) << 5) +
|
||||
(GET_BIT(ctx->interleaveIn[11],7) << 4) +
|
||||
(GET_BIT(ctx->interleaveIn[8], 8) << 3) +
|
||||
(GET_BIT(ctx->interleaveIn[9], 8) << 2) +
|
||||
(GET_BIT(ctx->interleaveIn[10],8) << 1) +
|
||||
(GET_BIT(ctx->interleaveIn[11],8));
|
||||
|
||||
ctx->interleaveIn[0] = a;
|
||||
ctx->interleaveIn[1] = b;
|
||||
ctx->interleaveIn[2] = p;
|
||||
ctx->interleaveIn[3] = c;
|
||||
ctx->interleaveIn[4] = d;
|
||||
ctx->interleaveIn[5] = q;
|
||||
ctx->interleaveIn[6] = e;
|
||||
ctx->interleaveIn[7] = f;
|
||||
ctx->interleaveIn[8] = s;
|
||||
ctx->interleaveIn[9] = g;
|
||||
ctx->interleaveIn[10] = h;
|
||||
ctx->interleaveIn[11] = t;
|
||||
}
|
72
hardware/packetmodem_nano2/MicroModemGP/protocol/LLP.h
Executable file
72
hardware/packetmodem_nano2/MicroModemGP/protocol/LLP.h
Executable file
@@ -0,0 +1,72 @@
|
||||
#ifndef PROTOCOL_LLP_H
|
||||
#define PROTOCOL_LLP_H
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdbool.h>
|
||||
#include "device.h"
|
||||
|
||||
#define LLP_ADDR_BROADCAST 0xFFFF
|
||||
|
||||
#define LLP_INTERLEAVE_SIZE 12
|
||||
#define LLP_MIN_FRAME_LENGTH LLP_INTERLEAVE_SIZE
|
||||
#define LLP_MAX_FRAME_LENGTH 48 * LLP_INTERLEAVE_SIZE
|
||||
#define LLP_HEADER_SIZE 10
|
||||
#define LLP_CHECKSUM_SIZE 2
|
||||
#define LLP_MAX_DATA_SIZE LLP_MAX_FRAME_LENGTH - LLP_HEADER_SIZE - LLP_CHECKSUM_SIZE
|
||||
#define LLP_DATA_BLOCK_SIZE ((LLP_INTERLEAVE_SIZE/3)*2)
|
||||
|
||||
#define LLP_CRC_SIZE 2
|
||||
#define LLP_CRC_CORRECT 0xF0B8
|
||||
|
||||
struct LLPCtx; // Forward declarations
|
||||
|
||||
typedef void (*llp_callback_t)(struct LLPCtx *ctx);
|
||||
|
||||
typedef struct LLPAddress {
|
||||
uint16_t network;
|
||||
uint16_t host;
|
||||
} LLPAddress;
|
||||
|
||||
typedef struct LLPHeader {
|
||||
LLPAddress src;
|
||||
LLPAddress dst;
|
||||
uint8_t flags;
|
||||
uint8_t padding;
|
||||
} LLPHeader;
|
||||
|
||||
typedef struct LLPMsg {
|
||||
LLPHeader header;
|
||||
const uint8_t *data;
|
||||
size_t len;
|
||||
} LLPMsg;
|
||||
|
||||
typedef struct LLPCtx {
|
||||
uint8_t buf[LLP_MAX_FRAME_LENGTH];
|
||||
FILE *ch;
|
||||
LLPAddress *address;
|
||||
size_t frame_len;
|
||||
size_t readLength;
|
||||
uint16_t crc_in;
|
||||
uint16_t crc_out;
|
||||
uint8_t calculatedParity;
|
||||
long correctionsMade;
|
||||
llp_callback_t hook;
|
||||
bool sync;
|
||||
bool escape;
|
||||
bool ready_for_data;
|
||||
uint8_t interleaveCounter; // Keeps track of when we have received an entire interleaved block
|
||||
uint8_t interleaveOut[LLP_INTERLEAVE_SIZE]; // A buffer for interleaving bytes before they are sent
|
||||
uint8_t interleaveIn[LLP_INTERLEAVE_SIZE]; // A buffer for storing interleaved bytes before they are deinterleaved
|
||||
} LLPCtx;
|
||||
|
||||
void llp_broadcast(LLPCtx *ctx, const void *_buf, size_t len);
|
||||
void llp_send(LLPCtx *ctx, LLPAddress *dst, const void *_buf, size_t len);
|
||||
void llp_sendRaw(LLPCtx *ctx, const void *_buf, size_t len);
|
||||
void llp_poll(LLPCtx *ctx);
|
||||
void llp_init(LLPCtx *ctx, LLPAddress *address, FILE *channel, llp_callback_t hook);
|
||||
|
||||
void llpInterleave(LLPCtx *ctx, uint8_t byte);
|
||||
void llpDeinterleave(LLPCtx *ctx);
|
||||
uint8_t llpParityBlock(uint8_t first, uint8_t other);
|
||||
|
||||
#endif
|
Reference in New Issue
Block a user