162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * isdnhdlc.c  --  General purpose ISDN HDLC decoder.
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * Copyright (C)
662306a36Sopenharmony_ci *	2009	Karsten Keil		<keil@b1-systems.de>
762306a36Sopenharmony_ci *	2002	Wolfgang Mües		<wolfgang@iksw-muees.de>
862306a36Sopenharmony_ci *	2001	Frode Isaksen		<fisaksen@bewan.com>
962306a36Sopenharmony_ci *      2001	Kai Germaschewski	<kai.germaschewski@gmx.de>
1062306a36Sopenharmony_ci */
1162306a36Sopenharmony_ci
1262306a36Sopenharmony_ci#include <linux/module.h>
1362306a36Sopenharmony_ci#include <linux/init.h>
1462306a36Sopenharmony_ci#include <linux/crc-ccitt.h>
1562306a36Sopenharmony_ci#include <linux/bitrev.h>
1662306a36Sopenharmony_ci#include "isdnhdlc.h"
1762306a36Sopenharmony_ci
1862306a36Sopenharmony_ci/*-------------------------------------------------------------------*/
1962306a36Sopenharmony_ci
2062306a36Sopenharmony_ciMODULE_AUTHOR("Wolfgang Mües <wolfgang@iksw-muees.de>, "
2162306a36Sopenharmony_ci	      "Frode Isaksen <fisaksen@bewan.com>, "
2262306a36Sopenharmony_ci	      "Kai Germaschewski <kai.germaschewski@gmx.de>");
2362306a36Sopenharmony_ciMODULE_DESCRIPTION("General purpose ISDN HDLC decoder");
2462306a36Sopenharmony_ciMODULE_LICENSE("GPL");
2562306a36Sopenharmony_ci
2662306a36Sopenharmony_ci/*-------------------------------------------------------------------*/
2762306a36Sopenharmony_ci
2862306a36Sopenharmony_cienum {
2962306a36Sopenharmony_ci	HDLC_FAST_IDLE, HDLC_GET_FLAG_B0, HDLC_GETFLAG_B1A6, HDLC_GETFLAG_B7,
3062306a36Sopenharmony_ci	HDLC_GET_DATA, HDLC_FAST_FLAG
3162306a36Sopenharmony_ci};
3262306a36Sopenharmony_ci
3362306a36Sopenharmony_cienum {
3462306a36Sopenharmony_ci	HDLC_SEND_DATA, HDLC_SEND_CRC1, HDLC_SEND_FAST_FLAG,
3562306a36Sopenharmony_ci	HDLC_SEND_FIRST_FLAG, HDLC_SEND_CRC2, HDLC_SEND_CLOSING_FLAG,
3662306a36Sopenharmony_ci	HDLC_SEND_IDLE1, HDLC_SEND_FAST_IDLE, HDLC_SENDFLAG_B0,
3762306a36Sopenharmony_ci	HDLC_SENDFLAG_B1A6, HDLC_SENDFLAG_B7, STOPPED, HDLC_SENDFLAG_ONE
3862306a36Sopenharmony_ci};
3962306a36Sopenharmony_ci
4062306a36Sopenharmony_civoid isdnhdlc_rcv_init(struct isdnhdlc_vars *hdlc, u32 features)
4162306a36Sopenharmony_ci{
4262306a36Sopenharmony_ci	memset(hdlc, 0, sizeof(struct isdnhdlc_vars));
4362306a36Sopenharmony_ci	hdlc->state = HDLC_GET_DATA;
4462306a36Sopenharmony_ci	if (features & HDLC_56KBIT)
4562306a36Sopenharmony_ci		hdlc->do_adapt56 = 1;
4662306a36Sopenharmony_ci	if (features & HDLC_BITREVERSE)
4762306a36Sopenharmony_ci		hdlc->do_bitreverse = 1;
4862306a36Sopenharmony_ci}
4962306a36Sopenharmony_ciEXPORT_SYMBOL(isdnhdlc_out_init);
5062306a36Sopenharmony_ci
5162306a36Sopenharmony_civoid isdnhdlc_out_init(struct isdnhdlc_vars *hdlc, u32 features)
5262306a36Sopenharmony_ci{
5362306a36Sopenharmony_ci	memset(hdlc, 0, sizeof(struct isdnhdlc_vars));
5462306a36Sopenharmony_ci	if (features & HDLC_DCHANNEL) {
5562306a36Sopenharmony_ci		hdlc->dchannel = 1;
5662306a36Sopenharmony_ci		hdlc->state = HDLC_SEND_FIRST_FLAG;
5762306a36Sopenharmony_ci	} else {
5862306a36Sopenharmony_ci		hdlc->dchannel = 0;
5962306a36Sopenharmony_ci		hdlc->state = HDLC_SEND_FAST_FLAG;
6062306a36Sopenharmony_ci		hdlc->ffvalue = 0x7e;
6162306a36Sopenharmony_ci	}
6262306a36Sopenharmony_ci	hdlc->cbin = 0x7e;
6362306a36Sopenharmony_ci	if (features & HDLC_56KBIT) {
6462306a36Sopenharmony_ci		hdlc->do_adapt56 = 1;
6562306a36Sopenharmony_ci		hdlc->state = HDLC_SENDFLAG_B0;
6662306a36Sopenharmony_ci	} else
6762306a36Sopenharmony_ci		hdlc->data_bits = 8;
6862306a36Sopenharmony_ci	if (features & HDLC_BITREVERSE)
6962306a36Sopenharmony_ci		hdlc->do_bitreverse = 1;
7062306a36Sopenharmony_ci}
7162306a36Sopenharmony_ciEXPORT_SYMBOL(isdnhdlc_rcv_init);
7262306a36Sopenharmony_ci
7362306a36Sopenharmony_cistatic int
7462306a36Sopenharmony_cicheck_frame(struct isdnhdlc_vars *hdlc)
7562306a36Sopenharmony_ci{
7662306a36Sopenharmony_ci	int status;
7762306a36Sopenharmony_ci
7862306a36Sopenharmony_ci	if (hdlc->dstpos < 2)	/* too small - framing error */
7962306a36Sopenharmony_ci		status = -HDLC_FRAMING_ERROR;
8062306a36Sopenharmony_ci	else if (hdlc->crc != 0xf0b8)	/* crc error */
8162306a36Sopenharmony_ci		status = -HDLC_CRC_ERROR;
8262306a36Sopenharmony_ci	else {
8362306a36Sopenharmony_ci		/* remove CRC */
8462306a36Sopenharmony_ci		hdlc->dstpos -= 2;
8562306a36Sopenharmony_ci		/* good frame */
8662306a36Sopenharmony_ci		status = hdlc->dstpos;
8762306a36Sopenharmony_ci	}
8862306a36Sopenharmony_ci	return status;
8962306a36Sopenharmony_ci}
9062306a36Sopenharmony_ci
9162306a36Sopenharmony_ci/*
9262306a36Sopenharmony_ci  isdnhdlc_decode - decodes HDLC frames from a transparent bit stream.
9362306a36Sopenharmony_ci
9462306a36Sopenharmony_ci  The source buffer is scanned for valid HDLC frames looking for
9562306a36Sopenharmony_ci  flags (01111110) to indicate the start of a frame. If the start of
9662306a36Sopenharmony_ci  the frame is found, the bit stuffing is removed (0 after 5 1's).
9762306a36Sopenharmony_ci  When a new flag is found, the complete frame has been received
9862306a36Sopenharmony_ci  and the CRC is checked.
9962306a36Sopenharmony_ci  If a valid frame is found, the function returns the frame length
10062306a36Sopenharmony_ci  excluding the CRC with the bit HDLC_END_OF_FRAME set.
10162306a36Sopenharmony_ci  If the beginning of a valid frame is found, the function returns
10262306a36Sopenharmony_ci  the length.
10362306a36Sopenharmony_ci  If a framing error is found (too many 1s and not a flag) the function
10462306a36Sopenharmony_ci  returns the length with the bit HDLC_FRAMING_ERROR set.
10562306a36Sopenharmony_ci  If a CRC error is found the function returns the length with the
10662306a36Sopenharmony_ci  bit HDLC_CRC_ERROR set.
10762306a36Sopenharmony_ci  If the frame length exceeds the destination buffer size, the function
10862306a36Sopenharmony_ci  returns the length with the bit HDLC_LENGTH_ERROR set.
10962306a36Sopenharmony_ci
11062306a36Sopenharmony_ci  src - source buffer
11162306a36Sopenharmony_ci  slen - source buffer length
11262306a36Sopenharmony_ci  count - number of bytes removed (decoded) from the source buffer
11362306a36Sopenharmony_ci  dst _ destination buffer
11462306a36Sopenharmony_ci  dsize - destination buffer size
11562306a36Sopenharmony_ci  returns - number of decoded bytes in the destination buffer and status
11662306a36Sopenharmony_ci  flag.
11762306a36Sopenharmony_ci*/
11862306a36Sopenharmony_ciint isdnhdlc_decode(struct isdnhdlc_vars *hdlc, const u8 *src, int slen,
11962306a36Sopenharmony_ci		    int *count, u8 *dst, int dsize)
12062306a36Sopenharmony_ci{
12162306a36Sopenharmony_ci	int status = 0;
12262306a36Sopenharmony_ci
12362306a36Sopenharmony_ci	static const unsigned char fast_flag[] = {
12462306a36Sopenharmony_ci		0x00, 0x00, 0x00, 0x20, 0x30, 0x38, 0x3c, 0x3e, 0x3f
12562306a36Sopenharmony_ci	};
12662306a36Sopenharmony_ci
12762306a36Sopenharmony_ci	static const unsigned char fast_flag_value[] = {
12862306a36Sopenharmony_ci		0x00, 0x7e, 0xfc, 0xf9, 0xf3, 0xe7, 0xcf, 0x9f, 0x3f
12962306a36Sopenharmony_ci	};
13062306a36Sopenharmony_ci
13162306a36Sopenharmony_ci	static const unsigned char fast_abort[] = {
13262306a36Sopenharmony_ci		0x00, 0x00, 0x80, 0xc0, 0xe0, 0xf0, 0xf8, 0xfc, 0xfe, 0xff
13362306a36Sopenharmony_ci	};
13462306a36Sopenharmony_ci
13562306a36Sopenharmony_ci#define handle_fast_flag(h)						\
13662306a36Sopenharmony_ci	do {								\
13762306a36Sopenharmony_ci		if (h->cbin == fast_flag[h->bit_shift]) {		\
13862306a36Sopenharmony_ci			h->ffvalue = fast_flag_value[h->bit_shift];	\
13962306a36Sopenharmony_ci			h->state = HDLC_FAST_FLAG;			\
14062306a36Sopenharmony_ci			h->ffbit_shift = h->bit_shift;			\
14162306a36Sopenharmony_ci			h->bit_shift = 1;				\
14262306a36Sopenharmony_ci		} else {						\
14362306a36Sopenharmony_ci			h->state = HDLC_GET_DATA;			\
14462306a36Sopenharmony_ci			h->data_received = 0;				\
14562306a36Sopenharmony_ci		}							\
14662306a36Sopenharmony_ci	} while (0)
14762306a36Sopenharmony_ci
14862306a36Sopenharmony_ci#define handle_abort(h)						\
14962306a36Sopenharmony_ci	do {							\
15062306a36Sopenharmony_ci		h->shift_reg = fast_abort[h->ffbit_shift - 1];	\
15162306a36Sopenharmony_ci		h->hdlc_bits1 = h->ffbit_shift - 2;		\
15262306a36Sopenharmony_ci		if (h->hdlc_bits1 < 0)				\
15362306a36Sopenharmony_ci			h->hdlc_bits1 = 0;			\
15462306a36Sopenharmony_ci		h->data_bits = h->ffbit_shift - 1;		\
15562306a36Sopenharmony_ci		h->state = HDLC_GET_DATA;			\
15662306a36Sopenharmony_ci		h->data_received = 0;				\
15762306a36Sopenharmony_ci	} while (0)
15862306a36Sopenharmony_ci
15962306a36Sopenharmony_ci	*count = slen;
16062306a36Sopenharmony_ci
16162306a36Sopenharmony_ci	while (slen > 0) {
16262306a36Sopenharmony_ci		if (hdlc->bit_shift == 0) {
16362306a36Sopenharmony_ci			/* the code is for bitreverse streams */
16462306a36Sopenharmony_ci			if (hdlc->do_bitreverse == 0)
16562306a36Sopenharmony_ci				hdlc->cbin = bitrev8(*src++);
16662306a36Sopenharmony_ci			else
16762306a36Sopenharmony_ci				hdlc->cbin = *src++;
16862306a36Sopenharmony_ci			slen--;
16962306a36Sopenharmony_ci			hdlc->bit_shift = 8;
17062306a36Sopenharmony_ci			if (hdlc->do_adapt56)
17162306a36Sopenharmony_ci				hdlc->bit_shift--;
17262306a36Sopenharmony_ci		}
17362306a36Sopenharmony_ci
17462306a36Sopenharmony_ci		switch (hdlc->state) {
17562306a36Sopenharmony_ci		case STOPPED:
17662306a36Sopenharmony_ci			return 0;
17762306a36Sopenharmony_ci		case HDLC_FAST_IDLE:
17862306a36Sopenharmony_ci			if (hdlc->cbin == 0xff) {
17962306a36Sopenharmony_ci				hdlc->bit_shift = 0;
18062306a36Sopenharmony_ci				break;
18162306a36Sopenharmony_ci			}
18262306a36Sopenharmony_ci			hdlc->state = HDLC_GET_FLAG_B0;
18362306a36Sopenharmony_ci			hdlc->hdlc_bits1 = 0;
18462306a36Sopenharmony_ci			hdlc->bit_shift = 8;
18562306a36Sopenharmony_ci			break;
18662306a36Sopenharmony_ci		case HDLC_GET_FLAG_B0:
18762306a36Sopenharmony_ci			if (!(hdlc->cbin & 0x80)) {
18862306a36Sopenharmony_ci				hdlc->state = HDLC_GETFLAG_B1A6;
18962306a36Sopenharmony_ci				hdlc->hdlc_bits1 = 0;
19062306a36Sopenharmony_ci			} else {
19162306a36Sopenharmony_ci				if ((!hdlc->do_adapt56) &&
19262306a36Sopenharmony_ci				    (++hdlc->hdlc_bits1 >= 8) &&
19362306a36Sopenharmony_ci				    (hdlc->bit_shift == 1))
19462306a36Sopenharmony_ci					hdlc->state = HDLC_FAST_IDLE;
19562306a36Sopenharmony_ci			}
19662306a36Sopenharmony_ci			hdlc->cbin <<= 1;
19762306a36Sopenharmony_ci			hdlc->bit_shift--;
19862306a36Sopenharmony_ci			break;
19962306a36Sopenharmony_ci		case HDLC_GETFLAG_B1A6:
20062306a36Sopenharmony_ci			if (hdlc->cbin & 0x80) {
20162306a36Sopenharmony_ci				hdlc->hdlc_bits1++;
20262306a36Sopenharmony_ci				if (hdlc->hdlc_bits1 == 6)
20362306a36Sopenharmony_ci					hdlc->state = HDLC_GETFLAG_B7;
20462306a36Sopenharmony_ci			} else
20562306a36Sopenharmony_ci				hdlc->hdlc_bits1 = 0;
20662306a36Sopenharmony_ci			hdlc->cbin <<= 1;
20762306a36Sopenharmony_ci			hdlc->bit_shift--;
20862306a36Sopenharmony_ci			break;
20962306a36Sopenharmony_ci		case HDLC_GETFLAG_B7:
21062306a36Sopenharmony_ci			if (hdlc->cbin & 0x80) {
21162306a36Sopenharmony_ci				hdlc->state = HDLC_GET_FLAG_B0;
21262306a36Sopenharmony_ci			} else {
21362306a36Sopenharmony_ci				hdlc->state = HDLC_GET_DATA;
21462306a36Sopenharmony_ci				hdlc->crc = 0xffff;
21562306a36Sopenharmony_ci				hdlc->shift_reg = 0;
21662306a36Sopenharmony_ci				hdlc->hdlc_bits1 = 0;
21762306a36Sopenharmony_ci				hdlc->data_bits = 0;
21862306a36Sopenharmony_ci				hdlc->data_received = 0;
21962306a36Sopenharmony_ci			}
22062306a36Sopenharmony_ci			hdlc->cbin <<= 1;
22162306a36Sopenharmony_ci			hdlc->bit_shift--;
22262306a36Sopenharmony_ci			break;
22362306a36Sopenharmony_ci		case HDLC_GET_DATA:
22462306a36Sopenharmony_ci			if (hdlc->cbin & 0x80) {
22562306a36Sopenharmony_ci				hdlc->hdlc_bits1++;
22662306a36Sopenharmony_ci				switch (hdlc->hdlc_bits1) {
22762306a36Sopenharmony_ci				case 6:
22862306a36Sopenharmony_ci					break;
22962306a36Sopenharmony_ci				case 7:
23062306a36Sopenharmony_ci					if (hdlc->data_received)
23162306a36Sopenharmony_ci						/* bad frame */
23262306a36Sopenharmony_ci						status = -HDLC_FRAMING_ERROR;
23362306a36Sopenharmony_ci					if (!hdlc->do_adapt56) {
23462306a36Sopenharmony_ci						if (hdlc->cbin == fast_abort
23562306a36Sopenharmony_ci						    [hdlc->bit_shift + 1]) {
23662306a36Sopenharmony_ci							hdlc->state =
23762306a36Sopenharmony_ci								HDLC_FAST_IDLE;
23862306a36Sopenharmony_ci							hdlc->bit_shift = 1;
23962306a36Sopenharmony_ci							break;
24062306a36Sopenharmony_ci						}
24162306a36Sopenharmony_ci					} else
24262306a36Sopenharmony_ci						hdlc->state = HDLC_GET_FLAG_B0;
24362306a36Sopenharmony_ci					break;
24462306a36Sopenharmony_ci				default:
24562306a36Sopenharmony_ci					hdlc->shift_reg >>= 1;
24662306a36Sopenharmony_ci					hdlc->shift_reg |= 0x80;
24762306a36Sopenharmony_ci					hdlc->data_bits++;
24862306a36Sopenharmony_ci					break;
24962306a36Sopenharmony_ci				}
25062306a36Sopenharmony_ci			} else {
25162306a36Sopenharmony_ci				switch (hdlc->hdlc_bits1) {
25262306a36Sopenharmony_ci				case 5:
25362306a36Sopenharmony_ci					break;
25462306a36Sopenharmony_ci				case 6:
25562306a36Sopenharmony_ci					if (hdlc->data_received)
25662306a36Sopenharmony_ci						status = check_frame(hdlc);
25762306a36Sopenharmony_ci					hdlc->crc = 0xffff;
25862306a36Sopenharmony_ci					hdlc->shift_reg = 0;
25962306a36Sopenharmony_ci					hdlc->data_bits = 0;
26062306a36Sopenharmony_ci					if (!hdlc->do_adapt56)
26162306a36Sopenharmony_ci						handle_fast_flag(hdlc);
26262306a36Sopenharmony_ci					else {
26362306a36Sopenharmony_ci						hdlc->state = HDLC_GET_DATA;
26462306a36Sopenharmony_ci						hdlc->data_received = 0;
26562306a36Sopenharmony_ci					}
26662306a36Sopenharmony_ci					break;
26762306a36Sopenharmony_ci				default:
26862306a36Sopenharmony_ci					hdlc->shift_reg >>= 1;
26962306a36Sopenharmony_ci					hdlc->data_bits++;
27062306a36Sopenharmony_ci					break;
27162306a36Sopenharmony_ci				}
27262306a36Sopenharmony_ci				hdlc->hdlc_bits1 = 0;
27362306a36Sopenharmony_ci			}
27462306a36Sopenharmony_ci			if (status) {
27562306a36Sopenharmony_ci				hdlc->dstpos = 0;
27662306a36Sopenharmony_ci				*count -= slen;
27762306a36Sopenharmony_ci				hdlc->cbin <<= 1;
27862306a36Sopenharmony_ci				hdlc->bit_shift--;
27962306a36Sopenharmony_ci				return status;
28062306a36Sopenharmony_ci			}
28162306a36Sopenharmony_ci			if (hdlc->data_bits == 8) {
28262306a36Sopenharmony_ci				hdlc->data_bits = 0;
28362306a36Sopenharmony_ci				hdlc->data_received = 1;
28462306a36Sopenharmony_ci				hdlc->crc = crc_ccitt_byte(hdlc->crc,
28562306a36Sopenharmony_ci							   hdlc->shift_reg);
28662306a36Sopenharmony_ci
28762306a36Sopenharmony_ci				/* good byte received */
28862306a36Sopenharmony_ci				if (hdlc->dstpos < dsize)
28962306a36Sopenharmony_ci					dst[hdlc->dstpos++] = hdlc->shift_reg;
29062306a36Sopenharmony_ci				else {
29162306a36Sopenharmony_ci					/* frame too long */
29262306a36Sopenharmony_ci					status = -HDLC_LENGTH_ERROR;
29362306a36Sopenharmony_ci					hdlc->dstpos = 0;
29462306a36Sopenharmony_ci				}
29562306a36Sopenharmony_ci			}
29662306a36Sopenharmony_ci			hdlc->cbin <<= 1;
29762306a36Sopenharmony_ci			hdlc->bit_shift--;
29862306a36Sopenharmony_ci			break;
29962306a36Sopenharmony_ci		case HDLC_FAST_FLAG:
30062306a36Sopenharmony_ci			if (hdlc->cbin == hdlc->ffvalue) {
30162306a36Sopenharmony_ci				hdlc->bit_shift = 0;
30262306a36Sopenharmony_ci				break;
30362306a36Sopenharmony_ci			} else {
30462306a36Sopenharmony_ci				if (hdlc->cbin == 0xff) {
30562306a36Sopenharmony_ci					hdlc->state = HDLC_FAST_IDLE;
30662306a36Sopenharmony_ci					hdlc->bit_shift = 0;
30762306a36Sopenharmony_ci				} else if (hdlc->ffbit_shift == 8) {
30862306a36Sopenharmony_ci					hdlc->state = HDLC_GETFLAG_B7;
30962306a36Sopenharmony_ci					break;
31062306a36Sopenharmony_ci				} else
31162306a36Sopenharmony_ci					handle_abort(hdlc);
31262306a36Sopenharmony_ci			}
31362306a36Sopenharmony_ci			break;
31462306a36Sopenharmony_ci		default:
31562306a36Sopenharmony_ci			break;
31662306a36Sopenharmony_ci		}
31762306a36Sopenharmony_ci	}
31862306a36Sopenharmony_ci	*count -= slen;
31962306a36Sopenharmony_ci	return 0;
32062306a36Sopenharmony_ci}
32162306a36Sopenharmony_ciEXPORT_SYMBOL(isdnhdlc_decode);
32262306a36Sopenharmony_ci/*
32362306a36Sopenharmony_ci  isdnhdlc_encode - encodes HDLC frames to a transparent bit stream.
32462306a36Sopenharmony_ci
32562306a36Sopenharmony_ci  The bit stream starts with a beginning flag (01111110). After
32662306a36Sopenharmony_ci  that each byte is added to the bit stream with bit stuffing added
32762306a36Sopenharmony_ci  (0 after 5 1's).
32862306a36Sopenharmony_ci  When the last byte has been removed from the source buffer, the
32962306a36Sopenharmony_ci  CRC (2 bytes is added) and the frame terminates with the ending flag.
33062306a36Sopenharmony_ci  For the dchannel, the idle character (all 1's) is also added at the end.
33162306a36Sopenharmony_ci  If this function is called with empty source buffer (slen=0), flags or
33262306a36Sopenharmony_ci  idle character will be generated.
33362306a36Sopenharmony_ci
33462306a36Sopenharmony_ci  src - source buffer
33562306a36Sopenharmony_ci  slen - source buffer length
33662306a36Sopenharmony_ci  count - number of bytes removed (encoded) from source buffer
33762306a36Sopenharmony_ci  dst _ destination buffer
33862306a36Sopenharmony_ci  dsize - destination buffer size
33962306a36Sopenharmony_ci  returns - number of encoded bytes in the destination buffer
34062306a36Sopenharmony_ci*/
34162306a36Sopenharmony_ciint isdnhdlc_encode(struct isdnhdlc_vars *hdlc, const u8 *src, u16 slen,
34262306a36Sopenharmony_ci		    int *count, u8 *dst, int dsize)
34362306a36Sopenharmony_ci{
34462306a36Sopenharmony_ci	static const unsigned char xfast_flag_value[] = {
34562306a36Sopenharmony_ci		0x7e, 0x3f, 0x9f, 0xcf, 0xe7, 0xf3, 0xf9, 0xfc, 0x7e
34662306a36Sopenharmony_ci	};
34762306a36Sopenharmony_ci
34862306a36Sopenharmony_ci	int len = 0;
34962306a36Sopenharmony_ci
35062306a36Sopenharmony_ci	*count = slen;
35162306a36Sopenharmony_ci
35262306a36Sopenharmony_ci	/* special handling for one byte frames */
35362306a36Sopenharmony_ci	if ((slen == 1) && (hdlc->state == HDLC_SEND_FAST_FLAG))
35462306a36Sopenharmony_ci		hdlc->state = HDLC_SENDFLAG_ONE;
35562306a36Sopenharmony_ci	while (dsize > 0) {
35662306a36Sopenharmony_ci		if (hdlc->bit_shift == 0) {
35762306a36Sopenharmony_ci			if (slen && !hdlc->do_closing) {
35862306a36Sopenharmony_ci				hdlc->shift_reg = *src++;
35962306a36Sopenharmony_ci				slen--;
36062306a36Sopenharmony_ci				if (slen == 0)
36162306a36Sopenharmony_ci					/* closing sequence, CRC + flag(s) */
36262306a36Sopenharmony_ci					hdlc->do_closing = 1;
36362306a36Sopenharmony_ci				hdlc->bit_shift = 8;
36462306a36Sopenharmony_ci			} else {
36562306a36Sopenharmony_ci				if (hdlc->state == HDLC_SEND_DATA) {
36662306a36Sopenharmony_ci					if (hdlc->data_received) {
36762306a36Sopenharmony_ci						hdlc->state = HDLC_SEND_CRC1;
36862306a36Sopenharmony_ci						hdlc->crc ^= 0xffff;
36962306a36Sopenharmony_ci						hdlc->bit_shift = 8;
37062306a36Sopenharmony_ci						hdlc->shift_reg =
37162306a36Sopenharmony_ci							hdlc->crc & 0xff;
37262306a36Sopenharmony_ci					} else if (!hdlc->do_adapt56)
37362306a36Sopenharmony_ci						hdlc->state =
37462306a36Sopenharmony_ci							HDLC_SEND_FAST_FLAG;
37562306a36Sopenharmony_ci					else
37662306a36Sopenharmony_ci						hdlc->state =
37762306a36Sopenharmony_ci							HDLC_SENDFLAG_B0;
37862306a36Sopenharmony_ci				}
37962306a36Sopenharmony_ci
38062306a36Sopenharmony_ci			}
38162306a36Sopenharmony_ci		}
38262306a36Sopenharmony_ci
38362306a36Sopenharmony_ci		switch (hdlc->state) {
38462306a36Sopenharmony_ci		case STOPPED:
38562306a36Sopenharmony_ci			while (dsize--)
38662306a36Sopenharmony_ci				*dst++ = 0xff;
38762306a36Sopenharmony_ci			return dsize;
38862306a36Sopenharmony_ci		case HDLC_SEND_FAST_FLAG:
38962306a36Sopenharmony_ci			hdlc->do_closing = 0;
39062306a36Sopenharmony_ci			if (slen == 0) {
39162306a36Sopenharmony_ci				/* the code is for bitreverse streams */
39262306a36Sopenharmony_ci				if (hdlc->do_bitreverse == 0)
39362306a36Sopenharmony_ci					*dst++ = bitrev8(hdlc->ffvalue);
39462306a36Sopenharmony_ci				else
39562306a36Sopenharmony_ci					*dst++ = hdlc->ffvalue;
39662306a36Sopenharmony_ci				len++;
39762306a36Sopenharmony_ci				dsize--;
39862306a36Sopenharmony_ci				break;
39962306a36Sopenharmony_ci			}
40062306a36Sopenharmony_ci			fallthrough;
40162306a36Sopenharmony_ci		case HDLC_SENDFLAG_ONE:
40262306a36Sopenharmony_ci			if (hdlc->bit_shift == 8) {
40362306a36Sopenharmony_ci				hdlc->cbin = hdlc->ffvalue >>
40462306a36Sopenharmony_ci					(8 - hdlc->data_bits);
40562306a36Sopenharmony_ci				hdlc->state = HDLC_SEND_DATA;
40662306a36Sopenharmony_ci				hdlc->crc = 0xffff;
40762306a36Sopenharmony_ci				hdlc->hdlc_bits1 = 0;
40862306a36Sopenharmony_ci				hdlc->data_received = 1;
40962306a36Sopenharmony_ci			}
41062306a36Sopenharmony_ci			break;
41162306a36Sopenharmony_ci		case HDLC_SENDFLAG_B0:
41262306a36Sopenharmony_ci			hdlc->do_closing = 0;
41362306a36Sopenharmony_ci			hdlc->cbin <<= 1;
41462306a36Sopenharmony_ci			hdlc->data_bits++;
41562306a36Sopenharmony_ci			hdlc->hdlc_bits1 = 0;
41662306a36Sopenharmony_ci			hdlc->state = HDLC_SENDFLAG_B1A6;
41762306a36Sopenharmony_ci			break;
41862306a36Sopenharmony_ci		case HDLC_SENDFLAG_B1A6:
41962306a36Sopenharmony_ci			hdlc->cbin <<= 1;
42062306a36Sopenharmony_ci			hdlc->data_bits++;
42162306a36Sopenharmony_ci			hdlc->cbin++;
42262306a36Sopenharmony_ci			if (++hdlc->hdlc_bits1 == 6)
42362306a36Sopenharmony_ci				hdlc->state = HDLC_SENDFLAG_B7;
42462306a36Sopenharmony_ci			break;
42562306a36Sopenharmony_ci		case HDLC_SENDFLAG_B7:
42662306a36Sopenharmony_ci			hdlc->cbin <<= 1;
42762306a36Sopenharmony_ci			hdlc->data_bits++;
42862306a36Sopenharmony_ci			if (slen == 0) {
42962306a36Sopenharmony_ci				hdlc->state = HDLC_SENDFLAG_B0;
43062306a36Sopenharmony_ci				break;
43162306a36Sopenharmony_ci			}
43262306a36Sopenharmony_ci			if (hdlc->bit_shift == 8) {
43362306a36Sopenharmony_ci				hdlc->state = HDLC_SEND_DATA;
43462306a36Sopenharmony_ci				hdlc->crc = 0xffff;
43562306a36Sopenharmony_ci				hdlc->hdlc_bits1 = 0;
43662306a36Sopenharmony_ci				hdlc->data_received = 1;
43762306a36Sopenharmony_ci			}
43862306a36Sopenharmony_ci			break;
43962306a36Sopenharmony_ci		case HDLC_SEND_FIRST_FLAG:
44062306a36Sopenharmony_ci			hdlc->data_received = 1;
44162306a36Sopenharmony_ci			if (hdlc->data_bits == 8) {
44262306a36Sopenharmony_ci				hdlc->state = HDLC_SEND_DATA;
44362306a36Sopenharmony_ci				hdlc->crc = 0xffff;
44462306a36Sopenharmony_ci				hdlc->hdlc_bits1 = 0;
44562306a36Sopenharmony_ci				break;
44662306a36Sopenharmony_ci			}
44762306a36Sopenharmony_ci			hdlc->cbin <<= 1;
44862306a36Sopenharmony_ci			hdlc->data_bits++;
44962306a36Sopenharmony_ci			if (hdlc->shift_reg & 0x01)
45062306a36Sopenharmony_ci				hdlc->cbin++;
45162306a36Sopenharmony_ci			hdlc->shift_reg >>= 1;
45262306a36Sopenharmony_ci			hdlc->bit_shift--;
45362306a36Sopenharmony_ci			if (hdlc->bit_shift == 0) {
45462306a36Sopenharmony_ci				hdlc->state = HDLC_SEND_DATA;
45562306a36Sopenharmony_ci				hdlc->crc = 0xffff;
45662306a36Sopenharmony_ci				hdlc->hdlc_bits1 = 0;
45762306a36Sopenharmony_ci			}
45862306a36Sopenharmony_ci			break;
45962306a36Sopenharmony_ci		case HDLC_SEND_DATA:
46062306a36Sopenharmony_ci			hdlc->cbin <<= 1;
46162306a36Sopenharmony_ci			hdlc->data_bits++;
46262306a36Sopenharmony_ci			if (hdlc->hdlc_bits1 == 5) {
46362306a36Sopenharmony_ci				hdlc->hdlc_bits1 = 0;
46462306a36Sopenharmony_ci				break;
46562306a36Sopenharmony_ci			}
46662306a36Sopenharmony_ci			if (hdlc->bit_shift == 8)
46762306a36Sopenharmony_ci				hdlc->crc = crc_ccitt_byte(hdlc->crc,
46862306a36Sopenharmony_ci							   hdlc->shift_reg);
46962306a36Sopenharmony_ci			if (hdlc->shift_reg & 0x01) {
47062306a36Sopenharmony_ci				hdlc->hdlc_bits1++;
47162306a36Sopenharmony_ci				hdlc->cbin++;
47262306a36Sopenharmony_ci				hdlc->shift_reg >>= 1;
47362306a36Sopenharmony_ci				hdlc->bit_shift--;
47462306a36Sopenharmony_ci			} else {
47562306a36Sopenharmony_ci				hdlc->hdlc_bits1 = 0;
47662306a36Sopenharmony_ci				hdlc->shift_reg >>= 1;
47762306a36Sopenharmony_ci				hdlc->bit_shift--;
47862306a36Sopenharmony_ci			}
47962306a36Sopenharmony_ci			break;
48062306a36Sopenharmony_ci		case HDLC_SEND_CRC1:
48162306a36Sopenharmony_ci			hdlc->cbin <<= 1;
48262306a36Sopenharmony_ci			hdlc->data_bits++;
48362306a36Sopenharmony_ci			if (hdlc->hdlc_bits1 == 5) {
48462306a36Sopenharmony_ci				hdlc->hdlc_bits1 = 0;
48562306a36Sopenharmony_ci				break;
48662306a36Sopenharmony_ci			}
48762306a36Sopenharmony_ci			if (hdlc->shift_reg & 0x01) {
48862306a36Sopenharmony_ci				hdlc->hdlc_bits1++;
48962306a36Sopenharmony_ci				hdlc->cbin++;
49062306a36Sopenharmony_ci				hdlc->shift_reg >>= 1;
49162306a36Sopenharmony_ci				hdlc->bit_shift--;
49262306a36Sopenharmony_ci			} else {
49362306a36Sopenharmony_ci				hdlc->hdlc_bits1 = 0;
49462306a36Sopenharmony_ci				hdlc->shift_reg >>= 1;
49562306a36Sopenharmony_ci				hdlc->bit_shift--;
49662306a36Sopenharmony_ci			}
49762306a36Sopenharmony_ci			if (hdlc->bit_shift == 0) {
49862306a36Sopenharmony_ci				hdlc->shift_reg = (hdlc->crc >> 8);
49962306a36Sopenharmony_ci				hdlc->state = HDLC_SEND_CRC2;
50062306a36Sopenharmony_ci				hdlc->bit_shift = 8;
50162306a36Sopenharmony_ci			}
50262306a36Sopenharmony_ci			break;
50362306a36Sopenharmony_ci		case HDLC_SEND_CRC2:
50462306a36Sopenharmony_ci			hdlc->cbin <<= 1;
50562306a36Sopenharmony_ci			hdlc->data_bits++;
50662306a36Sopenharmony_ci			if (hdlc->hdlc_bits1 == 5) {
50762306a36Sopenharmony_ci				hdlc->hdlc_bits1 = 0;
50862306a36Sopenharmony_ci				break;
50962306a36Sopenharmony_ci			}
51062306a36Sopenharmony_ci			if (hdlc->shift_reg & 0x01) {
51162306a36Sopenharmony_ci				hdlc->hdlc_bits1++;
51262306a36Sopenharmony_ci				hdlc->cbin++;
51362306a36Sopenharmony_ci				hdlc->shift_reg >>= 1;
51462306a36Sopenharmony_ci				hdlc->bit_shift--;
51562306a36Sopenharmony_ci			} else {
51662306a36Sopenharmony_ci				hdlc->hdlc_bits1 = 0;
51762306a36Sopenharmony_ci				hdlc->shift_reg >>= 1;
51862306a36Sopenharmony_ci				hdlc->bit_shift--;
51962306a36Sopenharmony_ci			}
52062306a36Sopenharmony_ci			if (hdlc->bit_shift == 0) {
52162306a36Sopenharmony_ci				hdlc->shift_reg = 0x7e;
52262306a36Sopenharmony_ci				hdlc->state = HDLC_SEND_CLOSING_FLAG;
52362306a36Sopenharmony_ci				hdlc->bit_shift = 8;
52462306a36Sopenharmony_ci			}
52562306a36Sopenharmony_ci			break;
52662306a36Sopenharmony_ci		case HDLC_SEND_CLOSING_FLAG:
52762306a36Sopenharmony_ci			hdlc->cbin <<= 1;
52862306a36Sopenharmony_ci			hdlc->data_bits++;
52962306a36Sopenharmony_ci			if (hdlc->hdlc_bits1 == 5) {
53062306a36Sopenharmony_ci				hdlc->hdlc_bits1 = 0;
53162306a36Sopenharmony_ci				break;
53262306a36Sopenharmony_ci			}
53362306a36Sopenharmony_ci			if (hdlc->shift_reg & 0x01)
53462306a36Sopenharmony_ci				hdlc->cbin++;
53562306a36Sopenharmony_ci			hdlc->shift_reg >>= 1;
53662306a36Sopenharmony_ci			hdlc->bit_shift--;
53762306a36Sopenharmony_ci			if (hdlc->bit_shift == 0) {
53862306a36Sopenharmony_ci				hdlc->ffvalue =
53962306a36Sopenharmony_ci					xfast_flag_value[hdlc->data_bits];
54062306a36Sopenharmony_ci				if (hdlc->dchannel) {
54162306a36Sopenharmony_ci					hdlc->ffvalue = 0x7e;
54262306a36Sopenharmony_ci					hdlc->state = HDLC_SEND_IDLE1;
54362306a36Sopenharmony_ci					hdlc->bit_shift = 8-hdlc->data_bits;
54462306a36Sopenharmony_ci					if (hdlc->bit_shift == 0)
54562306a36Sopenharmony_ci						hdlc->state =
54662306a36Sopenharmony_ci							HDLC_SEND_FAST_IDLE;
54762306a36Sopenharmony_ci				} else {
54862306a36Sopenharmony_ci					if (!hdlc->do_adapt56) {
54962306a36Sopenharmony_ci						hdlc->state =
55062306a36Sopenharmony_ci							HDLC_SEND_FAST_FLAG;
55162306a36Sopenharmony_ci						hdlc->data_received = 0;
55262306a36Sopenharmony_ci					} else {
55362306a36Sopenharmony_ci						hdlc->state = HDLC_SENDFLAG_B0;
55462306a36Sopenharmony_ci						hdlc->data_received = 0;
55562306a36Sopenharmony_ci					}
55662306a36Sopenharmony_ci					/* Finished this frame, send flags */
55762306a36Sopenharmony_ci					if (dsize > 1)
55862306a36Sopenharmony_ci						dsize = 1;
55962306a36Sopenharmony_ci				}
56062306a36Sopenharmony_ci			}
56162306a36Sopenharmony_ci			break;
56262306a36Sopenharmony_ci		case HDLC_SEND_IDLE1:
56362306a36Sopenharmony_ci			hdlc->do_closing = 0;
56462306a36Sopenharmony_ci			hdlc->cbin <<= 1;
56562306a36Sopenharmony_ci			hdlc->cbin++;
56662306a36Sopenharmony_ci			hdlc->data_bits++;
56762306a36Sopenharmony_ci			hdlc->bit_shift--;
56862306a36Sopenharmony_ci			if (hdlc->bit_shift == 0) {
56962306a36Sopenharmony_ci				hdlc->state = HDLC_SEND_FAST_IDLE;
57062306a36Sopenharmony_ci				hdlc->bit_shift = 0;
57162306a36Sopenharmony_ci			}
57262306a36Sopenharmony_ci			break;
57362306a36Sopenharmony_ci		case HDLC_SEND_FAST_IDLE:
57462306a36Sopenharmony_ci			hdlc->do_closing = 0;
57562306a36Sopenharmony_ci			hdlc->cbin = 0xff;
57662306a36Sopenharmony_ci			hdlc->data_bits = 8;
57762306a36Sopenharmony_ci			if (hdlc->bit_shift == 8) {
57862306a36Sopenharmony_ci				hdlc->cbin = 0x7e;
57962306a36Sopenharmony_ci				hdlc->state = HDLC_SEND_FIRST_FLAG;
58062306a36Sopenharmony_ci			} else {
58162306a36Sopenharmony_ci				/* the code is for bitreverse streams */
58262306a36Sopenharmony_ci				if (hdlc->do_bitreverse == 0)
58362306a36Sopenharmony_ci					*dst++ = bitrev8(hdlc->cbin);
58462306a36Sopenharmony_ci				else
58562306a36Sopenharmony_ci					*dst++ = hdlc->cbin;
58662306a36Sopenharmony_ci				hdlc->bit_shift = 0;
58762306a36Sopenharmony_ci				hdlc->data_bits = 0;
58862306a36Sopenharmony_ci				len++;
58962306a36Sopenharmony_ci				dsize = 0;
59062306a36Sopenharmony_ci			}
59162306a36Sopenharmony_ci			break;
59262306a36Sopenharmony_ci		default:
59362306a36Sopenharmony_ci			break;
59462306a36Sopenharmony_ci		}
59562306a36Sopenharmony_ci		if (hdlc->do_adapt56) {
59662306a36Sopenharmony_ci			if (hdlc->data_bits == 7) {
59762306a36Sopenharmony_ci				hdlc->cbin <<= 1;
59862306a36Sopenharmony_ci				hdlc->cbin++;
59962306a36Sopenharmony_ci				hdlc->data_bits++;
60062306a36Sopenharmony_ci			}
60162306a36Sopenharmony_ci		}
60262306a36Sopenharmony_ci		if (hdlc->data_bits == 8) {
60362306a36Sopenharmony_ci			/* the code is for bitreverse streams */
60462306a36Sopenharmony_ci			if (hdlc->do_bitreverse == 0)
60562306a36Sopenharmony_ci				*dst++ = bitrev8(hdlc->cbin);
60662306a36Sopenharmony_ci			else
60762306a36Sopenharmony_ci				*dst++ = hdlc->cbin;
60862306a36Sopenharmony_ci			hdlc->data_bits = 0;
60962306a36Sopenharmony_ci			len++;
61062306a36Sopenharmony_ci			dsize--;
61162306a36Sopenharmony_ci		}
61262306a36Sopenharmony_ci	}
61362306a36Sopenharmony_ci	*count -= slen;
61462306a36Sopenharmony_ci
61562306a36Sopenharmony_ci	return len;
61662306a36Sopenharmony_ci}
61762306a36Sopenharmony_ciEXPORT_SYMBOL(isdnhdlc_encode);
618