forked from arti/vt1200
112 lines
3.2 KiB
C
112 lines
3.2 KiB
C
/*
|
|
* frame_parse.c -- receive-side frame check for the VT1200 video stream.
|
|
*
|
|
* fp_parse() answers one question: is the block of bytes the transport just
|
|
* delivered a complete, uncorrupted frame of the format frame.py writes? It
|
|
* checks, in this order, the length bound, the magic and version, and finally
|
|
* the CRC -- the CRC is the expensive part, so a truncated or foreign frame is
|
|
* rejected before it is computed.
|
|
*
|
|
* The CRC is CCITT-FALSE (polynomial 0x1021, initial value 0xFFFF, no input or
|
|
* output reflection, no final xor), the same one frame.py implements with a
|
|
* 256-entry table. It covers header bytes 0..11 followed by the payload; the
|
|
* stored crc16 at offset 12 is never part of its own input.
|
|
*
|
|
* This file is compiled twice: by the 1987 TI C compiler inside the payload
|
|
* build (build-stream.sh stages it as FRMPSR.C) and by the host cc for
|
|
* tests/test_frame_parse.c. So it is K&R C -- no prototypes in definitions,
|
|
* no declarations after statements, no '//', no 'const' -- and it must not
|
|
* assume a 32-bit int beyond the fields it masks.
|
|
*/
|
|
#include "frmpsr.h"
|
|
|
|
/*
|
|
* One byte through the CRC, bit by bit. frame.py builds a table from the
|
|
* identical loop; that is the whole reason this is written the long way, so
|
|
* the two can be compared line for line.
|
|
*/
|
|
static unsigned int fp_crc_byte(crc, byte)
|
|
unsigned int crc;
|
|
unsigned int byte;
|
|
{
|
|
int i;
|
|
|
|
crc = (crc ^ ((byte & 0xFF) << 8)) & 0xFFFF;
|
|
for (i = 0; i < 8; i++) {
|
|
if (crc & 0x8000) {
|
|
crc = ((crc << 1) ^ 0x1021) & 0xFFFF;
|
|
} else {
|
|
crc = (crc << 1) & 0xFFFF;
|
|
}
|
|
}
|
|
return crc;
|
|
}
|
|
|
|
/* Fold len bytes at buf into crc. */
|
|
static unsigned int fp_crc_add(crc, buf, len)
|
|
unsigned int crc;
|
|
unsigned char *buf;
|
|
unsigned int len;
|
|
{
|
|
unsigned int i;
|
|
|
|
for (i = 0; i < len; i++) {
|
|
crc = fp_crc_byte(crc, buf[i]);
|
|
}
|
|
return crc;
|
|
}
|
|
|
|
/*
|
|
* int fp_parse(unsigned char *buf, unsigned int len, fp_header *out)
|
|
*
|
|
* See frame_parse.h for the contract. A NULL buffer or header is a rejection
|
|
* rather than a crash: the payload calls this with a pointer it published
|
|
* itself, and a zero there means "no slot yet".
|
|
*/
|
|
int fp_parse(buf, len, out)
|
|
unsigned char *buf;
|
|
unsigned int len;
|
|
fp_header *out;
|
|
{
|
|
unsigned int plen, stored, crc;
|
|
|
|
if (buf == 0 || out == 0) {
|
|
return 0;
|
|
}
|
|
if (len < FP_HEADER_SIZE) {
|
|
return 0;
|
|
}
|
|
if (buf[0] != FP_MAGIC0 || buf[1] != FP_MAGIC1) {
|
|
return 0;
|
|
}
|
|
if (buf[2] != FP_VERSION) {
|
|
return 0;
|
|
}
|
|
|
|
plen = (unsigned int) buf[6] | ((unsigned int) buf[7] << 8);
|
|
/*
|
|
* Subtracting rather than adding: plen can be 0xFFFF, and
|
|
* FP_HEADER_SIZE + plen would still fit a 32-bit unsigned int but not a
|
|
* 16-bit one. len is known to be at least FP_HEADER_SIZE here.
|
|
*/
|
|
if (len - FP_HEADER_SIZE < plen) {
|
|
return 0;
|
|
}
|
|
|
|
stored = ((unsigned int) buf[FP_CRC_OFF + 1] << 8)
|
|
| (unsigned int) buf[FP_CRC_OFF];
|
|
crc = fp_crc_add(0xFFFF, buf, FP_PREFIX_SIZE);
|
|
crc = fp_crc_add(crc, buf + FP_HEADER_SIZE, plen);
|
|
if (crc != stored) {
|
|
return 0;
|
|
}
|
|
|
|
out->kind = (unsigned int) buf[3];
|
|
out->frame_no = (unsigned int) buf[4] | ((unsigned int) buf[5] << 8);
|
|
out->payload_len = plen;
|
|
out->rows = (unsigned int) buf[8];
|
|
out->stretch = (unsigned int) buf[9];
|
|
out->payload_off = FP_HEADER_SIZE;
|
|
return 1;
|
|
}
|