/* * 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); }