First commit

This commit is contained in:
marcel
2024-02-14 16:29:31 +01:00
parent 54da6fbfac
commit 5b646d27ae
221 changed files with 186799 additions and 2 deletions

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

View 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

View 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

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

View 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

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

View 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