123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534 |
- /*
- * Mark's Mythical Table-based raw HDLC implementation
- *
- * This is designed to be a very fast, but memory efficient
- * implementation of standard HDLC protocol.
- *
- * This table based HDLC technology is PATENT PENDING, but will always be
- * remain freely distributable under the terms of the GPL version 2.
- *
- * For non-GPL licensing, please contact Mark Spencer at
- * the below e-mail address.
- *
- * Copyright (C) 2001-2008, Digium, Inc.
- *
- * Written by Mark Spencer <markster@digium.com>
- *
- */
- /*
- * See http://www.asterisk.org for more information about
- * the Asterisk project. Please do not directly contact
- * any of the maintainers of this project for assistance;
- * the project provides a web site, mailing lists and IRC
- * channels for your use.
- *
- * This program is free software, distributed under the terms of
- * the GNU General Public License Version 2 as published by the
- * Free Software Foundation. See the LICENSE file included with
- * this program for more details.
- */
- #ifndef _FASTHDLC_H
- #define _FASTHDLC_H
- enum fasthdlc_mode {
- FASTHDLC_MODE_64 = 0,
- FASTHDLC_MODE_56,
- FASTHDLC_MODE_16,
- };
- struct fasthdlc_state {
- int state; /* What state we are in */
- unsigned int data; /* Our current data queue */
- int bits; /* Number of bits in our data queue */
- int ones; /* Number of ones */
- enum fasthdlc_mode mode;
- unsigned int minbits;
- };
- #ifdef FAST_HDLC_NEED_TABLES
- #define RETURN_COMPLETE_FLAG (0x1000)
- #define RETURN_DISCARD_FLAG (0x2000)
- #define RETURN_EMPTY_FLAG (0x4000)
- /* Unlike most HDLC implementations, we define only two states,
- when we are in a valid frame, and when we are searching for
- a frame header */
- #define FRAME_SEARCH 0
- #define PROCESS_FRAME 1
- /*
- HDLC Search State table -- Look for a frame header. The return value
- of this table is as follows:
- |---8---|---7---|---6---|---5---|---4---|---3---|---2---|---1---|
- | Z E R O E S | Next | Bits Consumed |
- |-------|-------|-------|-------|-------|-------|-------|-------|
- The indexes for this table are the state (0 or 1) and the next 8
- bits of the stream.
- Note that this table is only used for state 0 and 1.
- The user should discard the top "bits consumed" bits of data before
- the next call. "Next state" represents the actual next state for
- decoding.
- */
- static unsigned char hdlc_search[256];
- /*
- HDLC Data Table
- The indexes to this table are the number of one's we've seen so far (0-5) and
- the next 10 bits of input (which is enough to guarantee us that we
- will retrieve at least one byte of data (or frame or whatever).
- The format for the return value is:
- Bits 15: Status (1=Valid Data, 0=Control Frame (see bits 7-0 for type))
- Bits 14-12: Number of ones in a row, so far
- Bits 11-8: The number of bits consumed (0-10)
- Bits 7-0: The return data (if appropriate)
-
- The next state is simply bit #15
- */
- #define CONTROL_COMPLETE 1
- #define CONTROL_ABORT 2
- #define STATUS_MASK (1 << 15)
- #define STATUS_VALID (1 << 15)
- #define STATUS_CONTROL (0 << 15)
- #define STATE_MASK (1 << 15)
- #define ONES_MASK (7 << 12)
- #define DATA_MASK (0xff)
- static unsigned short hdlc_frame[6][1024];
- static unsigned int minbits[2] = { 8, 10 };
- /*
- Last, but not least, we have the encoder table. It takes
- as its indices the number of ones so far and a byte of data
- and returns an int composed of the following fields:
- Bots 31-22: Actual Data
- Bits 21-16: Unused
- Bits 15-8: Number of ones
- Bits 3-0: Number of bits of output (13-4) to use
- Of course we could optimize by reducing to two tables, but I don't
- really think it's worth the trouble at this point.
- */
- static unsigned int hdlc_encode[6][256];
- static inline char hdlc_search_precalc(unsigned char c)
- {
- int x, p=0;
- /* Look for a flag. If this isn't a flag,
- line us up for the next possible shot at
- a flag */
- /* If it's a flag, we go to state 1, and have
- consumed 8 bits */
- if (c == 0x7e)
- return 0x10 | 8;
- /* If it's an abort, we stay in the same state
- and have consumed 8 bits */
- if (c == 0x7f)
- return 0x00 | 8;
- /* If it's all 1's, we state in the same state and
- have consumed 8 bits */
- if (c == 0xff)
- return 0x00 | 8;
- /* If we get here, we must have at least one zero in us
- but we're not the flag. So, start at the end (LSB) and
- work our way to the top (MSB) looking for a zero. The
- position of that 0 is most optimistic start of a real
- frame header */
- x=1;
- p=7;
- while(p && (c & x)) {
- x <<= 1;
- p--;
- }
- return p;
- }
- #ifdef DEBUG_PRECALC
- static inline void hdlc_search_print(char c, char r)
- {
- int x=0x80;
- while(x) {
- printf("%s", c & x ? "1" : "0");
- x >>= 1;
- }
- printf(" => State %d, Consume %d\n", (r & 0x10) >> 4, r & 0xf);
- }
- #endif
- #define HFP(status, ones, bits, data) \
- ((status) | ((ones) << 12) | ((bits) << 8) | (data))
- static inline unsigned int hdlc_frame_precalc(unsigned char x, unsigned short c)
- {
- /* Assume we have seen 'x' one's so far, and have read the
- bottom 10 bytes of c (MSB first). Now, we HAVE to have
- a byte of data or a frame or something. We are assumed
- to be at the beginning of a byte of data or something */
- unsigned char ones = x;
- unsigned char data=0;
- int bits=0;
- int consumed=0;
- while(bits < 8) {
- data >>=1;
- consumed++;
- if (ones == 5) {
- /* We've seen five ones */
- if (c & 0x0200) {
- /* Another one -- Some sort of signal frame */
- if ((!(c & 0x0100)) && (bits == 6)) {
- /* This is a frame terminator (10) */
- return HFP(0,
- 0, 8, CONTROL_COMPLETE);
- } else {
- /* Yuck! It's something else...
- Abort this entire frame, and
- start looking for a good frame */
- return HFP(0,
- 0, consumed+1, CONTROL_ABORT);
- }
- } else {
- /* It's an inserted zero, just skip it */
- ones = 0;
- data <<= 1;
- }
- } else {
- /* Add it to our bit list, LSB to
- MSB */
- if (c & 0x0200) {
- data |= 0x80;
- ones++;
- } else
- ones=0;
- bits++;
- }
- c <<= 1;
- }
- /* Consume the extra 0 now rather than later. */
- if (ones == 5) {
- ones = 0;
- consumed++;
- }
- return HFP(STATUS_VALID, ones, consumed, data);
- }
- #ifdef DEBUG_PRECALC
- static inline void hdlc_frame_print(unsigned char x, unsigned short c, unsigned int res)
- {
- int z=0x0200;
- char *status[] = {
- "Control",
- "Valid",
- };
- printf("%d one's then ", x);
- while(z) {
- printf("%s", c & z ? "1" : "0");
- z >>= 1;
- }
- printf(" => Status %s, ", res & STATUS_MASK ? "1" : "0");
- printf("Consumed: %d, ", (res & 0x0f00) >> 8);
- printf("Status: %s, ", status[(res & STATUS_MASK) >> 15]);
- printf("Ones: %d, ", (res & ONES_MASK) >> 12);
- printf("Data: %02x\n", res & 0xff);
-
- }
- #endif
- static inline unsigned int hdlc_encode_precalc(int x, unsigned char y)
- {
- int bits=0;
- int ones=x;
- unsigned short data=0;
- int z;
- for (z=0;z<8;z++) {
- /* Zero-stuff if needed */
- if (ones == 5) {
- /* Stuff a zero */
- data <<= 1;
- ones=0;
- bits++;
- }
- if (y & 0x01) {
- /* There's a one */
- data <<= 1;
- data |= 0x1;
- ones++;
- bits++;
- } else {
- data <<= 1;
- ones = 0;
- bits++;
- }
- y >>= 1;
- }
- /* Special case -- Stuff the zero at the end if appropriate */
- if (ones == 5) {
- /* Stuff a zero */
- data <<= 1;
- ones=0;
- bits++;
- }
- data <<= (10-bits);
- return (data << 22) | (ones << 8) | (bits);
- }
- #ifdef DEBUG_PRECALC
- static inline void hdlc_encode_print(int x, unsigned char y, unsigned int val)
- {
- unsigned int z;
- unsigned short c;
- printf("%d ones, %02x (", x, y);
- z = 0x80;
- while(z) {
- printf("%s", y & z ? "1" : "0");
- z >>= 1;
- }
- printf(") encoded as ");
- z = 1 << 31;
- for (x=0;x<(val & 0xf);x++) {
- printf("%s", val & z ? "1" : "0");
- z >>= 1;
- }
- printf(" with %d ones now, %d bits in len\n", (val & 0xf00) >> 8, val & 0xf);
-
- }
- #endif
- static inline void fasthdlc_precalc(void)
- {
- int x;
- int y;
- /* First the easy part -- the searching */
- for (x=0;x<256;x++) {
- hdlc_search[x] = hdlc_search_precalc(x);
- #ifdef DEBUG_PRECALC
- hdlc_search_print(x, hdlc_search[x]);
- #endif
- }
- /* Now the hard part -- the frame tables */
- for (x=0;x<6;x++) {
- /* Given the # of preceeding ones, process the next
- byte of input (up to 10 actual bits) */
- for (y=0;y<1024;y++) {
- hdlc_frame[x][y] = hdlc_frame_precalc(x, y);
- #ifdef DEBUG_PRECALC
- hdlc_frame_print(x, y, hdlc_frame[x][y]);
- #endif
- }
- }
- /* Now another not-so-hard part, the encoding table */
- for (x=0;x<6;x++) {
- for (y=0;y<256;y++) {
- hdlc_encode[x][y] = hdlc_encode_precalc(x,y);
- #ifdef DEBUG_PRECALC
- hdlc_encode_print(x,y,hdlc_encode[x][y]);
- #endif
- }
- }
- }
- static inline void fasthdlc_init(struct fasthdlc_state *h, enum fasthdlc_mode mode)
- {
- /* Initializes all states appropriately */
- h->mode = mode;
- h->state = 0;
- h->bits = 0;
- h->data = 0;
- h->ones = 0;
- switch (mode) {
- case FASTHDLC_MODE_64:
- h->minbits = 8;
- break;
- case FASTHDLC_MODE_56:
- h->minbits = 7;
- break;
- case FASTHDLC_MODE_16:
- h->minbits = 2;
- break;
- }
- }
- static inline int fasthdlc_tx_load_nocheck(struct fasthdlc_state *h, unsigned char c)
- {
- unsigned int res;
- res = hdlc_encode[h->ones][c];
- h->ones = (res & 0xf00) >> 8;
- h->data |= (res & 0xffc00000) >> h->bits;
- h->bits += (res & 0xf);
- return 0;
- }
- static inline int fasthdlc_tx_load(struct fasthdlc_state *h, unsigned char c)
- {
- /* Gotta have at least 10 bits left */
- if (h->bits > 22)
- return -1;
- return fasthdlc_tx_load_nocheck(h, c);
- }
- static inline int fasthdlc_tx_frame_nocheck(struct fasthdlc_state *h)
- {
- h->ones = 0;
- h->data |= ( 0x7e000000 >> h->bits);
- h->bits += 8;
- return 0;
- }
- static inline int fasthdlc_tx_frame(struct fasthdlc_state *h)
- {
- if (h->bits > 24)
- return -1;
- return fasthdlc_tx_frame_nocheck(h);
- }
- static inline int fasthdlc_tx_need_data(struct fasthdlc_state *h)
- {
- if (h->mode == FASTHDLC_MODE_56) {
- if (h->bits < 7)
- return 1;
- } else if (h->mode == FASTHDLC_MODE_16) {
- if (h->bits < 2)
- return 1;
- } else {
- if (h->bits < 8)
- return 1;
- }
- return 0;
- }
- static inline int fasthdlc_tx_run_nocheck(struct fasthdlc_state *h)
- {
- unsigned char b;
- if (h->mode == FASTHDLC_MODE_16) {
- b = h->data >> 30;
- h->bits -= 2;
- h->data <<= 2;
- return (b & 3) << 6;
- } else if (h->mode == FASTHDLC_MODE_56) {
- b = h->data >> 25;
- h->bits -= 7;
- h->data <<= 7;
- return ((b & 0x7f) << 1) | 1;
- } else {
- b = h->data >> 24;
- h->bits -= 8;
- h->data <<= 8;
- return b;
- }
- }
- static inline int fasthdlc_tx_run(struct fasthdlc_state *h)
- {
- if (h->bits < h->minbits)
- return -1;
- return fasthdlc_tx_run_nocheck(h);
- }
- static inline int fasthdlc_rx_load_nocheck(struct fasthdlc_state *h, unsigned char b)
- {
- if (h->mode == FASTHDLC_MODE_16) {
- h->data |= (b >> 6) << (30-h->bits);
- h->bits += 2;
- } else if (h->mode == FASTHDLC_MODE_56) {
- h->data |= (b >> 1) << (25-h->bits);
- h->bits += 7;
- } else {
- /* Put the new byte in the data stream */
- h->data |= b << (24-h->bits);
- h->bits += 8;
- }
- return 0;
- }
- static inline int fasthdlc_rx_load(struct fasthdlc_state *h, unsigned char b)
- {
- /* Make sure we have enough space */
- if (h->bits > 24)
- return -1;
- return fasthdlc_rx_load_nocheck(h, b);
- }
- /*
- Returns a data character if available, logical OR'd with
- zero or more of RETURN_COMPLETE_FLAG, RETURN_DISCARD_FLAG,
- and RETURN_EMPTY_FLAG, signifying a complete frame, a
- discarded frame, or there is nothing to return.
- */
- static inline int fasthdlc_rx_run(struct fasthdlc_state *h)
- {
- unsigned short next;
- int retval=RETURN_EMPTY_FLAG;
- while ((h->bits >= minbits[h->state]) && (retval == RETURN_EMPTY_FLAG)) {
- /* Run until we can no longer be assured that we will
- have enough bits to continue */
- switch(h->state) {
- case FRAME_SEARCH:
- /* Look for an HDLC frame, keying from
- the top byte. */
- next = hdlc_search[h->data >> 24];
- h->bits -= next & 0x0f;
- h->data <<= next & 0x0f;
- h->state = next >> 4;
- h->ones = 0;
- break;
- case PROCESS_FRAME:
- /* Process as much as the next ten bits */
- next = hdlc_frame[h->ones][h->data >> 22];
- h->bits -= ((next & 0x0f00) >> 8);
- h->data <<= ((next & 0x0f00) >> 8);
- h->state = (next & STATE_MASK) >> 15;
- h->ones = (next & ONES_MASK) >> 12;
- switch(next & STATUS_MASK) {
- case STATUS_CONTROL:
- if (next & CONTROL_COMPLETE) {
- /* A complete, valid frame received */
- retval = (RETURN_COMPLETE_FLAG);
- /* Stay in this state */
- h->state = 1;
- } else {
- /* An abort (either out of sync of explicit) */
- retval = (RETURN_DISCARD_FLAG);
- }
- break;
- case STATUS_VALID:
- retval = (next & DATA_MASK);
- }
- }
- }
- return retval;
- }
- #endif /* FAST_HDLC_NEED_TABLES */
- #endif
|