X-Git-Url: https://git.gag.com/?p=fw%2Faltos;a=blobdiff_plain;f=src%2Fkernel%2Fao_fec_tx.c;fp=src%2Fkernel%2Fao_fec_tx.c;h=4941d74581254ec26402218b9ed952cdbc03138b;hp=0000000000000000000000000000000000000000;hb=24167015705ae831692b95735968b04a876f935e;hpb=bb9fdef607728cc326a82aa632e59724f272e53b diff --git a/src/kernel/ao_fec_tx.c b/src/kernel/ao_fec_tx.c new file mode 100644 index 00000000..4941d745 --- /dev/null +++ b/src/kernel/ao_fec_tx.c @@ -0,0 +1,134 @@ +/* + * Copyright © 2012 Keith Packard + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + */ + +#include +#include + +#if AO_FEC_DEBUG +void +ao_fec_dump_bytes(const uint8_t *bytes, uint16_t len, const char *name) +{ + uint16_t i; + + printf ("%s (%d):", name, len); + for (i = 0; i < len; i++) { + if ((i & 7) == 0) + printf ("\n\t%02x:", i); + printf(" %02x", bytes[i]); + } + printf ("\n"); +} +#endif + +uint16_t +ao_fec_crc(const uint8_t *bytes, uint8_t len) +{ + uint16_t crc = AO_FEC_CRC_INIT; + + while (len--) + crc = ao_fec_crc_byte(*bytes++, crc); + return crc; +} + +/* + * len is the length of the data; the crc will be + * the fist two bytes after that + */ + +uint8_t +ao_fec_check_crc(const uint8_t *bytes, uint8_t len) +{ + uint16_t computed_crc = ao_fec_crc(bytes, len); + uint16_t received_crc = (bytes[len] << 8) | (bytes[len+1]); + + return computed_crc == received_crc; +} + +/* + * Compute CRC and trellis-terminator/interleave-pad bytes + */ +static uint8_t +ao_fec_prepare(const uint8_t *in, uint8_t len, uint8_t *extra) +{ + uint16_t crc = ao_fec_crc (in, len); + uint8_t i = 0; + uint8_t num_fec; + + /* Append CRC */ + extra[i++] = crc >> 8; + extra[i++] = crc; + + /* Append FEC -- 1 byte if odd, two bytes if even */ + num_fec = 2 - (i & 1); + while (num_fec--) + extra[i++] = AO_FEC_TRELLIS_TERMINATOR; + return i; +} + +const uint8_t ao_fec_whiten_table[] = { +#include "ao_whiten.h" +}; + +static const uint8_t ao_fec_encode_table[16] = { +/* next 0 1 state */ + 0, 3, /* 000 */ + 1, 2, /* 001 */ + 3, 0, /* 010 */ + 2, 1, /* 011 */ + 3, 0, /* 100 */ + 2, 1, /* 101 */ + 0, 3, /* 110 */ + 1, 2 /* 111 */ +}; + +uint8_t +ao_fec_encode(const uint8_t *in, uint8_t len, uint8_t *out) +{ + uint8_t extra[AO_FEC_PREPARE_EXTRA]; + uint8_t extra_len; + uint32_t encode, interleave; + uint8_t pair, byte, bit; + uint16_t fec = 0; + const uint8_t *whiten = ao_fec_whiten_table; + + extra_len = ao_fec_prepare(in, len, extra); + for (pair = 0; pair < len + extra_len; pair += 2) { + encode = 0; + for (byte = 0; byte < 2; byte++) { + if (pair + byte == len) + in = extra; + fec |= *in++ ^ *whiten++; + for (bit = 0; bit < 8; bit++) { + encode = encode << 2 | ao_fec_encode_table[fec >> 7]; + fec = (fec << 1) & 0x7ff; + } + } + + interleave = 0; + for (bit = 0; bit < 4 * 4; bit++) { + uint8_t byte_shift = (bit & 0x3) << 3; + uint8_t bit_shift = (bit & 0xc) >> 1; + + interleave = (interleave << 2) | ((encode >> (byte_shift + bit_shift)) & 0x3); + } + *out++ = interleave >> 24; + *out++ = interleave >> 16; + *out++ = interleave >> 8; + *out++ = interleave >> 0; + } + return (len + extra_len) * 2; +}