Files

136 lines
4.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 over the 12 header bytes.
*
* 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 ONLY (ruling R14): the bytes
* at offset 12 are the CRC itself and the payload is never walked. A
* payload-wide CRC cost ~139 instructions per byte on this CPU and made the
* parser the frame-rate ceiling; every datagram already carries an Ethernet
* FCS the LANCE validates, and the length bound below catches truncation.
*
* So a corrupted PAYLOAD byte does not make fp_parse() reject a frame -- the
* chip's FCS is the check for that -- while a corrupted HEADER byte does.
*
* 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".
*
* The CRC covers header bytes 0..11 only, so the body is never touched: a
* 20 KB frame costs the same as a 1 KB one (ruling R14).
*/
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);
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;
}
/*
* unsigned int fp_crc(unsigned char *buf, unsigned int len)
*
* The same CRC over an arbitrary span, for the transmit side. The terminal
* builds the status frame's header itself (vidstream.c, send_status()) and
* seals it with this, so there is ONE CRC implementation in the payload rather
* than two that could drift apart. It is the function fp_parse() already uses
* over the 12 header bytes -- the initial value, the polynomial and the shift
* direction are stated once, in fp_crc_byte() above.
*/
unsigned int fp_crc(buf, len)
unsigned char *buf;
unsigned int len;
{
return fp_crc_add(0xFFFF, buf, len);
}