18a7e8ff8SDavid S. Miller // SPDX-License-Identifier: GPL-2.0-or-later
299c2aa15SArnd Bergmann /*
399c2aa15SArnd Bergmann * isdnhdlc.c -- General purpose ISDN HDLC decoder.
499c2aa15SArnd Bergmann *
599c2aa15SArnd Bergmann * Copyright (C)
699c2aa15SArnd Bergmann * 2009 Karsten Keil <keil@b1-systems.de>
799c2aa15SArnd Bergmann * 2002 Wolfgang Mües <wolfgang@iksw-muees.de>
899c2aa15SArnd Bergmann * 2001 Frode Isaksen <fisaksen@bewan.com>
999c2aa15SArnd Bergmann * 2001 Kai Germaschewski <kai.germaschewski@gmx.de>
1099c2aa15SArnd Bergmann */
1199c2aa15SArnd Bergmann
1299c2aa15SArnd Bergmann #include <linux/module.h>
1399c2aa15SArnd Bergmann #include <linux/init.h>
1499c2aa15SArnd Bergmann #include <linux/crc-ccitt.h>
1599c2aa15SArnd Bergmann #include <linux/bitrev.h>
1699c2aa15SArnd Bergmann #include "isdnhdlc.h"
1799c2aa15SArnd Bergmann
1899c2aa15SArnd Bergmann /*-------------------------------------------------------------------*/
1999c2aa15SArnd Bergmann
2099c2aa15SArnd Bergmann MODULE_AUTHOR("Wolfgang Mües <wolfgang@iksw-muees.de>, "
2199c2aa15SArnd Bergmann "Frode Isaksen <fisaksen@bewan.com>, "
2299c2aa15SArnd Bergmann "Kai Germaschewski <kai.germaschewski@gmx.de>");
2399c2aa15SArnd Bergmann MODULE_DESCRIPTION("General purpose ISDN HDLC decoder");
2499c2aa15SArnd Bergmann MODULE_LICENSE("GPL");
2599c2aa15SArnd Bergmann
2699c2aa15SArnd Bergmann /*-------------------------------------------------------------------*/
2799c2aa15SArnd Bergmann
2899c2aa15SArnd Bergmann enum {
2999c2aa15SArnd Bergmann HDLC_FAST_IDLE, HDLC_GET_FLAG_B0, HDLC_GETFLAG_B1A6, HDLC_GETFLAG_B7,
3099c2aa15SArnd Bergmann HDLC_GET_DATA, HDLC_FAST_FLAG
3199c2aa15SArnd Bergmann };
3299c2aa15SArnd Bergmann
3399c2aa15SArnd Bergmann enum {
3499c2aa15SArnd Bergmann HDLC_SEND_DATA, HDLC_SEND_CRC1, HDLC_SEND_FAST_FLAG,
3599c2aa15SArnd Bergmann HDLC_SEND_FIRST_FLAG, HDLC_SEND_CRC2, HDLC_SEND_CLOSING_FLAG,
3699c2aa15SArnd Bergmann HDLC_SEND_IDLE1, HDLC_SEND_FAST_IDLE, HDLC_SENDFLAG_B0,
3799c2aa15SArnd Bergmann HDLC_SENDFLAG_B1A6, HDLC_SENDFLAG_B7, STOPPED, HDLC_SENDFLAG_ONE
3899c2aa15SArnd Bergmann };
3999c2aa15SArnd Bergmann
isdnhdlc_rcv_init(struct isdnhdlc_vars * hdlc,u32 features)4099c2aa15SArnd Bergmann void isdnhdlc_rcv_init(struct isdnhdlc_vars *hdlc, u32 features)
4199c2aa15SArnd Bergmann {
4299c2aa15SArnd Bergmann memset(hdlc, 0, sizeof(struct isdnhdlc_vars));
4399c2aa15SArnd Bergmann hdlc->state = HDLC_GET_DATA;
4499c2aa15SArnd Bergmann if (features & HDLC_56KBIT)
4599c2aa15SArnd Bergmann hdlc->do_adapt56 = 1;
4699c2aa15SArnd Bergmann if (features & HDLC_BITREVERSE)
4799c2aa15SArnd Bergmann hdlc->do_bitreverse = 1;
4899c2aa15SArnd Bergmann }
4999c2aa15SArnd Bergmann EXPORT_SYMBOL(isdnhdlc_out_init);
5099c2aa15SArnd Bergmann
isdnhdlc_out_init(struct isdnhdlc_vars * hdlc,u32 features)5199c2aa15SArnd Bergmann void isdnhdlc_out_init(struct isdnhdlc_vars *hdlc, u32 features)
5299c2aa15SArnd Bergmann {
5399c2aa15SArnd Bergmann memset(hdlc, 0, sizeof(struct isdnhdlc_vars));
5499c2aa15SArnd Bergmann if (features & HDLC_DCHANNEL) {
5599c2aa15SArnd Bergmann hdlc->dchannel = 1;
5699c2aa15SArnd Bergmann hdlc->state = HDLC_SEND_FIRST_FLAG;
5799c2aa15SArnd Bergmann } else {
5899c2aa15SArnd Bergmann hdlc->dchannel = 0;
5999c2aa15SArnd Bergmann hdlc->state = HDLC_SEND_FAST_FLAG;
6099c2aa15SArnd Bergmann hdlc->ffvalue = 0x7e;
6199c2aa15SArnd Bergmann }
6299c2aa15SArnd Bergmann hdlc->cbin = 0x7e;
6399c2aa15SArnd Bergmann if (features & HDLC_56KBIT) {
6499c2aa15SArnd Bergmann hdlc->do_adapt56 = 1;
6599c2aa15SArnd Bergmann hdlc->state = HDLC_SENDFLAG_B0;
6699c2aa15SArnd Bergmann } else
6799c2aa15SArnd Bergmann hdlc->data_bits = 8;
6899c2aa15SArnd Bergmann if (features & HDLC_BITREVERSE)
6999c2aa15SArnd Bergmann hdlc->do_bitreverse = 1;
7099c2aa15SArnd Bergmann }
7199c2aa15SArnd Bergmann EXPORT_SYMBOL(isdnhdlc_rcv_init);
7299c2aa15SArnd Bergmann
7399c2aa15SArnd Bergmann static int
check_frame(struct isdnhdlc_vars * hdlc)7499c2aa15SArnd Bergmann check_frame(struct isdnhdlc_vars *hdlc)
7599c2aa15SArnd Bergmann {
7699c2aa15SArnd Bergmann int status;
7799c2aa15SArnd Bergmann
7899c2aa15SArnd Bergmann if (hdlc->dstpos < 2) /* too small - framing error */
7999c2aa15SArnd Bergmann status = -HDLC_FRAMING_ERROR;
8099c2aa15SArnd Bergmann else if (hdlc->crc != 0xf0b8) /* crc error */
8199c2aa15SArnd Bergmann status = -HDLC_CRC_ERROR;
8299c2aa15SArnd Bergmann else {
8399c2aa15SArnd Bergmann /* remove CRC */
8499c2aa15SArnd Bergmann hdlc->dstpos -= 2;
8599c2aa15SArnd Bergmann /* good frame */
8699c2aa15SArnd Bergmann status = hdlc->dstpos;
8799c2aa15SArnd Bergmann }
8899c2aa15SArnd Bergmann return status;
8999c2aa15SArnd Bergmann }
9099c2aa15SArnd Bergmann
9199c2aa15SArnd Bergmann /*
9299c2aa15SArnd Bergmann isdnhdlc_decode - decodes HDLC frames from a transparent bit stream.
9399c2aa15SArnd Bergmann
9499c2aa15SArnd Bergmann The source buffer is scanned for valid HDLC frames looking for
9599c2aa15SArnd Bergmann flags (01111110) to indicate the start of a frame. If the start of
9699c2aa15SArnd Bergmann the frame is found, the bit stuffing is removed (0 after 5 1's).
9799c2aa15SArnd Bergmann When a new flag is found, the complete frame has been received
9899c2aa15SArnd Bergmann and the CRC is checked.
9999c2aa15SArnd Bergmann If a valid frame is found, the function returns the frame length
10099c2aa15SArnd Bergmann excluding the CRC with the bit HDLC_END_OF_FRAME set.
10199c2aa15SArnd Bergmann If the beginning of a valid frame is found, the function returns
10299c2aa15SArnd Bergmann the length.
10399c2aa15SArnd Bergmann If a framing error is found (too many 1s and not a flag) the function
10499c2aa15SArnd Bergmann returns the length with the bit HDLC_FRAMING_ERROR set.
10599c2aa15SArnd Bergmann If a CRC error is found the function returns the length with the
10699c2aa15SArnd Bergmann bit HDLC_CRC_ERROR set.
10799c2aa15SArnd Bergmann If the frame length exceeds the destination buffer size, the function
10899c2aa15SArnd Bergmann returns the length with the bit HDLC_LENGTH_ERROR set.
10999c2aa15SArnd Bergmann
11099c2aa15SArnd Bergmann src - source buffer
11199c2aa15SArnd Bergmann slen - source buffer length
11299c2aa15SArnd Bergmann count - number of bytes removed (decoded) from the source buffer
11399c2aa15SArnd Bergmann dst _ destination buffer
11499c2aa15SArnd Bergmann dsize - destination buffer size
11599c2aa15SArnd Bergmann returns - number of decoded bytes in the destination buffer and status
11699c2aa15SArnd Bergmann flag.
11799c2aa15SArnd Bergmann */
isdnhdlc_decode(struct isdnhdlc_vars * hdlc,const u8 * src,int slen,int * count,u8 * dst,int dsize)11899c2aa15SArnd Bergmann int isdnhdlc_decode(struct isdnhdlc_vars *hdlc, const u8 *src, int slen,
11999c2aa15SArnd Bergmann int *count, u8 *dst, int dsize)
12099c2aa15SArnd Bergmann {
12199c2aa15SArnd Bergmann int status = 0;
12299c2aa15SArnd Bergmann
12399c2aa15SArnd Bergmann static const unsigned char fast_flag[] = {
12499c2aa15SArnd Bergmann 0x00, 0x00, 0x00, 0x20, 0x30, 0x38, 0x3c, 0x3e, 0x3f
12599c2aa15SArnd Bergmann };
12699c2aa15SArnd Bergmann
12799c2aa15SArnd Bergmann static const unsigned char fast_flag_value[] = {
12899c2aa15SArnd Bergmann 0x00, 0x7e, 0xfc, 0xf9, 0xf3, 0xe7, 0xcf, 0x9f, 0x3f
12999c2aa15SArnd Bergmann };
13099c2aa15SArnd Bergmann
13199c2aa15SArnd Bergmann static const unsigned char fast_abort[] = {
13299c2aa15SArnd Bergmann 0x00, 0x00, 0x80, 0xc0, 0xe0, 0xf0, 0xf8, 0xfc, 0xfe, 0xff
13399c2aa15SArnd Bergmann };
13499c2aa15SArnd Bergmann
13599c2aa15SArnd Bergmann #define handle_fast_flag(h) \
13699c2aa15SArnd Bergmann do { \
13799c2aa15SArnd Bergmann if (h->cbin == fast_flag[h->bit_shift]) { \
13899c2aa15SArnd Bergmann h->ffvalue = fast_flag_value[h->bit_shift]; \
13999c2aa15SArnd Bergmann h->state = HDLC_FAST_FLAG; \
14099c2aa15SArnd Bergmann h->ffbit_shift = h->bit_shift; \
14199c2aa15SArnd Bergmann h->bit_shift = 1; \
14299c2aa15SArnd Bergmann } else { \
14399c2aa15SArnd Bergmann h->state = HDLC_GET_DATA; \
14499c2aa15SArnd Bergmann h->data_received = 0; \
14599c2aa15SArnd Bergmann } \
14699c2aa15SArnd Bergmann } while (0)
14799c2aa15SArnd Bergmann
14899c2aa15SArnd Bergmann #define handle_abort(h) \
14999c2aa15SArnd Bergmann do { \
15099c2aa15SArnd Bergmann h->shift_reg = fast_abort[h->ffbit_shift - 1]; \
15199c2aa15SArnd Bergmann h->hdlc_bits1 = h->ffbit_shift - 2; \
15299c2aa15SArnd Bergmann if (h->hdlc_bits1 < 0) \
15399c2aa15SArnd Bergmann h->hdlc_bits1 = 0; \
15499c2aa15SArnd Bergmann h->data_bits = h->ffbit_shift - 1; \
15599c2aa15SArnd Bergmann h->state = HDLC_GET_DATA; \
15699c2aa15SArnd Bergmann h->data_received = 0; \
15799c2aa15SArnd Bergmann } while (0)
15899c2aa15SArnd Bergmann
15999c2aa15SArnd Bergmann *count = slen;
16099c2aa15SArnd Bergmann
16199c2aa15SArnd Bergmann while (slen > 0) {
16299c2aa15SArnd Bergmann if (hdlc->bit_shift == 0) {
16399c2aa15SArnd Bergmann /* the code is for bitreverse streams */
16499c2aa15SArnd Bergmann if (hdlc->do_bitreverse == 0)
16599c2aa15SArnd Bergmann hdlc->cbin = bitrev8(*src++);
16699c2aa15SArnd Bergmann else
16799c2aa15SArnd Bergmann hdlc->cbin = *src++;
16899c2aa15SArnd Bergmann slen--;
16999c2aa15SArnd Bergmann hdlc->bit_shift = 8;
17099c2aa15SArnd Bergmann if (hdlc->do_adapt56)
17199c2aa15SArnd Bergmann hdlc->bit_shift--;
17299c2aa15SArnd Bergmann }
17399c2aa15SArnd Bergmann
17499c2aa15SArnd Bergmann switch (hdlc->state) {
17599c2aa15SArnd Bergmann case STOPPED:
17699c2aa15SArnd Bergmann return 0;
17799c2aa15SArnd Bergmann case HDLC_FAST_IDLE:
17899c2aa15SArnd Bergmann if (hdlc->cbin == 0xff) {
17999c2aa15SArnd Bergmann hdlc->bit_shift = 0;
18099c2aa15SArnd Bergmann break;
18199c2aa15SArnd Bergmann }
18299c2aa15SArnd Bergmann hdlc->state = HDLC_GET_FLAG_B0;
18399c2aa15SArnd Bergmann hdlc->hdlc_bits1 = 0;
18499c2aa15SArnd Bergmann hdlc->bit_shift = 8;
18599c2aa15SArnd Bergmann break;
18699c2aa15SArnd Bergmann case HDLC_GET_FLAG_B0:
18799c2aa15SArnd Bergmann if (!(hdlc->cbin & 0x80)) {
18899c2aa15SArnd Bergmann hdlc->state = HDLC_GETFLAG_B1A6;
18999c2aa15SArnd Bergmann hdlc->hdlc_bits1 = 0;
19099c2aa15SArnd Bergmann } else {
19199c2aa15SArnd Bergmann if ((!hdlc->do_adapt56) &&
19299c2aa15SArnd Bergmann (++hdlc->hdlc_bits1 >= 8) &&
19399c2aa15SArnd Bergmann (hdlc->bit_shift == 1))
19499c2aa15SArnd Bergmann hdlc->state = HDLC_FAST_IDLE;
19599c2aa15SArnd Bergmann }
19699c2aa15SArnd Bergmann hdlc->cbin <<= 1;
19799c2aa15SArnd Bergmann hdlc->bit_shift--;
19899c2aa15SArnd Bergmann break;
19999c2aa15SArnd Bergmann case HDLC_GETFLAG_B1A6:
20099c2aa15SArnd Bergmann if (hdlc->cbin & 0x80) {
20199c2aa15SArnd Bergmann hdlc->hdlc_bits1++;
20299c2aa15SArnd Bergmann if (hdlc->hdlc_bits1 == 6)
20399c2aa15SArnd Bergmann hdlc->state = HDLC_GETFLAG_B7;
20499c2aa15SArnd Bergmann } else
20599c2aa15SArnd Bergmann hdlc->hdlc_bits1 = 0;
20699c2aa15SArnd Bergmann hdlc->cbin <<= 1;
20799c2aa15SArnd Bergmann hdlc->bit_shift--;
20899c2aa15SArnd Bergmann break;
20999c2aa15SArnd Bergmann case HDLC_GETFLAG_B7:
21099c2aa15SArnd Bergmann if (hdlc->cbin & 0x80) {
21199c2aa15SArnd Bergmann hdlc->state = HDLC_GET_FLAG_B0;
21299c2aa15SArnd Bergmann } else {
21399c2aa15SArnd Bergmann hdlc->state = HDLC_GET_DATA;
21499c2aa15SArnd Bergmann hdlc->crc = 0xffff;
21599c2aa15SArnd Bergmann hdlc->shift_reg = 0;
21699c2aa15SArnd Bergmann hdlc->hdlc_bits1 = 0;
21799c2aa15SArnd Bergmann hdlc->data_bits = 0;
21899c2aa15SArnd Bergmann hdlc->data_received = 0;
21999c2aa15SArnd Bergmann }
22099c2aa15SArnd Bergmann hdlc->cbin <<= 1;
22199c2aa15SArnd Bergmann hdlc->bit_shift--;
22299c2aa15SArnd Bergmann break;
22399c2aa15SArnd Bergmann case HDLC_GET_DATA:
22499c2aa15SArnd Bergmann if (hdlc->cbin & 0x80) {
22599c2aa15SArnd Bergmann hdlc->hdlc_bits1++;
22699c2aa15SArnd Bergmann switch (hdlc->hdlc_bits1) {
22799c2aa15SArnd Bergmann case 6:
22899c2aa15SArnd Bergmann break;
22999c2aa15SArnd Bergmann case 7:
23099c2aa15SArnd Bergmann if (hdlc->data_received)
23199c2aa15SArnd Bergmann /* bad frame */
23299c2aa15SArnd Bergmann status = -HDLC_FRAMING_ERROR;
23399c2aa15SArnd Bergmann if (!hdlc->do_adapt56) {
23499c2aa15SArnd Bergmann if (hdlc->cbin == fast_abort
23599c2aa15SArnd Bergmann [hdlc->bit_shift + 1]) {
23699c2aa15SArnd Bergmann hdlc->state =
23799c2aa15SArnd Bergmann HDLC_FAST_IDLE;
23899c2aa15SArnd Bergmann hdlc->bit_shift = 1;
23999c2aa15SArnd Bergmann break;
24099c2aa15SArnd Bergmann }
24199c2aa15SArnd Bergmann } else
24299c2aa15SArnd Bergmann hdlc->state = HDLC_GET_FLAG_B0;
24399c2aa15SArnd Bergmann break;
24499c2aa15SArnd Bergmann default:
24599c2aa15SArnd Bergmann hdlc->shift_reg >>= 1;
24699c2aa15SArnd Bergmann hdlc->shift_reg |= 0x80;
24799c2aa15SArnd Bergmann hdlc->data_bits++;
24899c2aa15SArnd Bergmann break;
24999c2aa15SArnd Bergmann }
25099c2aa15SArnd Bergmann } else {
25199c2aa15SArnd Bergmann switch (hdlc->hdlc_bits1) {
25299c2aa15SArnd Bergmann case 5:
25399c2aa15SArnd Bergmann break;
25499c2aa15SArnd Bergmann case 6:
25599c2aa15SArnd Bergmann if (hdlc->data_received)
25699c2aa15SArnd Bergmann status = check_frame(hdlc);
25799c2aa15SArnd Bergmann hdlc->crc = 0xffff;
25899c2aa15SArnd Bergmann hdlc->shift_reg = 0;
25999c2aa15SArnd Bergmann hdlc->data_bits = 0;
26099c2aa15SArnd Bergmann if (!hdlc->do_adapt56)
26199c2aa15SArnd Bergmann handle_fast_flag(hdlc);
26299c2aa15SArnd Bergmann else {
26399c2aa15SArnd Bergmann hdlc->state = HDLC_GET_DATA;
26499c2aa15SArnd Bergmann hdlc->data_received = 0;
26599c2aa15SArnd Bergmann }
26699c2aa15SArnd Bergmann break;
26799c2aa15SArnd Bergmann default:
26899c2aa15SArnd Bergmann hdlc->shift_reg >>= 1;
26999c2aa15SArnd Bergmann hdlc->data_bits++;
27099c2aa15SArnd Bergmann break;
27199c2aa15SArnd Bergmann }
27299c2aa15SArnd Bergmann hdlc->hdlc_bits1 = 0;
27399c2aa15SArnd Bergmann }
27499c2aa15SArnd Bergmann if (status) {
27599c2aa15SArnd Bergmann hdlc->dstpos = 0;
27699c2aa15SArnd Bergmann *count -= slen;
27799c2aa15SArnd Bergmann hdlc->cbin <<= 1;
27899c2aa15SArnd Bergmann hdlc->bit_shift--;
27999c2aa15SArnd Bergmann return status;
28099c2aa15SArnd Bergmann }
28199c2aa15SArnd Bergmann if (hdlc->data_bits == 8) {
28299c2aa15SArnd Bergmann hdlc->data_bits = 0;
28399c2aa15SArnd Bergmann hdlc->data_received = 1;
28499c2aa15SArnd Bergmann hdlc->crc = crc_ccitt_byte(hdlc->crc,
28599c2aa15SArnd Bergmann hdlc->shift_reg);
28699c2aa15SArnd Bergmann
28799c2aa15SArnd Bergmann /* good byte received */
28899c2aa15SArnd Bergmann if (hdlc->dstpos < dsize)
28999c2aa15SArnd Bergmann dst[hdlc->dstpos++] = hdlc->shift_reg;
29099c2aa15SArnd Bergmann else {
29199c2aa15SArnd Bergmann /* frame too long */
29299c2aa15SArnd Bergmann status = -HDLC_LENGTH_ERROR;
29399c2aa15SArnd Bergmann hdlc->dstpos = 0;
29499c2aa15SArnd Bergmann }
29599c2aa15SArnd Bergmann }
29699c2aa15SArnd Bergmann hdlc->cbin <<= 1;
29799c2aa15SArnd Bergmann hdlc->bit_shift--;
29899c2aa15SArnd Bergmann break;
29999c2aa15SArnd Bergmann case HDLC_FAST_FLAG:
30099c2aa15SArnd Bergmann if (hdlc->cbin == hdlc->ffvalue) {
30199c2aa15SArnd Bergmann hdlc->bit_shift = 0;
30299c2aa15SArnd Bergmann break;
30399c2aa15SArnd Bergmann } else {
30499c2aa15SArnd Bergmann if (hdlc->cbin == 0xff) {
30599c2aa15SArnd Bergmann hdlc->state = HDLC_FAST_IDLE;
30699c2aa15SArnd Bergmann hdlc->bit_shift = 0;
30799c2aa15SArnd Bergmann } else if (hdlc->ffbit_shift == 8) {
30899c2aa15SArnd Bergmann hdlc->state = HDLC_GETFLAG_B7;
30999c2aa15SArnd Bergmann break;
31099c2aa15SArnd Bergmann } else
31199c2aa15SArnd Bergmann handle_abort(hdlc);
31299c2aa15SArnd Bergmann }
31399c2aa15SArnd Bergmann break;
31499c2aa15SArnd Bergmann default:
31599c2aa15SArnd Bergmann break;
31699c2aa15SArnd Bergmann }
31799c2aa15SArnd Bergmann }
31899c2aa15SArnd Bergmann *count -= slen;
31999c2aa15SArnd Bergmann return 0;
32099c2aa15SArnd Bergmann }
32199c2aa15SArnd Bergmann EXPORT_SYMBOL(isdnhdlc_decode);
32299c2aa15SArnd Bergmann /*
32399c2aa15SArnd Bergmann isdnhdlc_encode - encodes HDLC frames to a transparent bit stream.
32499c2aa15SArnd Bergmann
32599c2aa15SArnd Bergmann The bit stream starts with a beginning flag (01111110). After
32699c2aa15SArnd Bergmann that each byte is added to the bit stream with bit stuffing added
32799c2aa15SArnd Bergmann (0 after 5 1's).
32899c2aa15SArnd Bergmann When the last byte has been removed from the source buffer, the
32999c2aa15SArnd Bergmann CRC (2 bytes is added) and the frame terminates with the ending flag.
33099c2aa15SArnd Bergmann For the dchannel, the idle character (all 1's) is also added at the end.
33199c2aa15SArnd Bergmann If this function is called with empty source buffer (slen=0), flags or
33299c2aa15SArnd Bergmann idle character will be generated.
33399c2aa15SArnd Bergmann
33499c2aa15SArnd Bergmann src - source buffer
33599c2aa15SArnd Bergmann slen - source buffer length
33699c2aa15SArnd Bergmann count - number of bytes removed (encoded) from source buffer
33799c2aa15SArnd Bergmann dst _ destination buffer
33899c2aa15SArnd Bergmann dsize - destination buffer size
33999c2aa15SArnd Bergmann returns - number of encoded bytes in the destination buffer
34099c2aa15SArnd Bergmann */
isdnhdlc_encode(struct isdnhdlc_vars * hdlc,const u8 * src,u16 slen,int * count,u8 * dst,int dsize)34199c2aa15SArnd Bergmann int isdnhdlc_encode(struct isdnhdlc_vars *hdlc, const u8 *src, u16 slen,
34299c2aa15SArnd Bergmann int *count, u8 *dst, int dsize)
34399c2aa15SArnd Bergmann {
34499c2aa15SArnd Bergmann static const unsigned char xfast_flag_value[] = {
34599c2aa15SArnd Bergmann 0x7e, 0x3f, 0x9f, 0xcf, 0xe7, 0xf3, 0xf9, 0xfc, 0x7e
34699c2aa15SArnd Bergmann };
34799c2aa15SArnd Bergmann
34899c2aa15SArnd Bergmann int len = 0;
34999c2aa15SArnd Bergmann
35099c2aa15SArnd Bergmann *count = slen;
35199c2aa15SArnd Bergmann
35299c2aa15SArnd Bergmann /* special handling for one byte frames */
35399c2aa15SArnd Bergmann if ((slen == 1) && (hdlc->state == HDLC_SEND_FAST_FLAG))
35499c2aa15SArnd Bergmann hdlc->state = HDLC_SENDFLAG_ONE;
35599c2aa15SArnd Bergmann while (dsize > 0) {
35699c2aa15SArnd Bergmann if (hdlc->bit_shift == 0) {
35799c2aa15SArnd Bergmann if (slen && !hdlc->do_closing) {
35899c2aa15SArnd Bergmann hdlc->shift_reg = *src++;
35999c2aa15SArnd Bergmann slen--;
36099c2aa15SArnd Bergmann if (slen == 0)
36199c2aa15SArnd Bergmann /* closing sequence, CRC + flag(s) */
36299c2aa15SArnd Bergmann hdlc->do_closing = 1;
36399c2aa15SArnd Bergmann hdlc->bit_shift = 8;
36499c2aa15SArnd Bergmann } else {
36599c2aa15SArnd Bergmann if (hdlc->state == HDLC_SEND_DATA) {
36699c2aa15SArnd Bergmann if (hdlc->data_received) {
36799c2aa15SArnd Bergmann hdlc->state = HDLC_SEND_CRC1;
36899c2aa15SArnd Bergmann hdlc->crc ^= 0xffff;
36999c2aa15SArnd Bergmann hdlc->bit_shift = 8;
37099c2aa15SArnd Bergmann hdlc->shift_reg =
37199c2aa15SArnd Bergmann hdlc->crc & 0xff;
37299c2aa15SArnd Bergmann } else if (!hdlc->do_adapt56)
37399c2aa15SArnd Bergmann hdlc->state =
37499c2aa15SArnd Bergmann HDLC_SEND_FAST_FLAG;
37599c2aa15SArnd Bergmann else
37699c2aa15SArnd Bergmann hdlc->state =
37799c2aa15SArnd Bergmann HDLC_SENDFLAG_B0;
37899c2aa15SArnd Bergmann }
37999c2aa15SArnd Bergmann
38099c2aa15SArnd Bergmann }
38199c2aa15SArnd Bergmann }
38299c2aa15SArnd Bergmann
38399c2aa15SArnd Bergmann switch (hdlc->state) {
38499c2aa15SArnd Bergmann case STOPPED:
38599c2aa15SArnd Bergmann while (dsize--)
38699c2aa15SArnd Bergmann *dst++ = 0xff;
38799c2aa15SArnd Bergmann return dsize;
38899c2aa15SArnd Bergmann case HDLC_SEND_FAST_FLAG:
38999c2aa15SArnd Bergmann hdlc->do_closing = 0;
39099c2aa15SArnd Bergmann if (slen == 0) {
39199c2aa15SArnd Bergmann /* the code is for bitreverse streams */
39299c2aa15SArnd Bergmann if (hdlc->do_bitreverse == 0)
39399c2aa15SArnd Bergmann *dst++ = bitrev8(hdlc->ffvalue);
39499c2aa15SArnd Bergmann else
39599c2aa15SArnd Bergmann *dst++ = hdlc->ffvalue;
39699c2aa15SArnd Bergmann len++;
39799c2aa15SArnd Bergmann dsize--;
39899c2aa15SArnd Bergmann break;
39999c2aa15SArnd Bergmann }
400*df561f66SGustavo A. R. Silva fallthrough;
40199c2aa15SArnd Bergmann case HDLC_SENDFLAG_ONE:
40299c2aa15SArnd Bergmann if (hdlc->bit_shift == 8) {
40399c2aa15SArnd Bergmann hdlc->cbin = hdlc->ffvalue >>
40499c2aa15SArnd Bergmann (8 - hdlc->data_bits);
40599c2aa15SArnd Bergmann hdlc->state = HDLC_SEND_DATA;
40699c2aa15SArnd Bergmann hdlc->crc = 0xffff;
40799c2aa15SArnd Bergmann hdlc->hdlc_bits1 = 0;
40899c2aa15SArnd Bergmann hdlc->data_received = 1;
40999c2aa15SArnd Bergmann }
41099c2aa15SArnd Bergmann break;
41199c2aa15SArnd Bergmann case HDLC_SENDFLAG_B0:
41299c2aa15SArnd Bergmann hdlc->do_closing = 0;
41399c2aa15SArnd Bergmann hdlc->cbin <<= 1;
41499c2aa15SArnd Bergmann hdlc->data_bits++;
41599c2aa15SArnd Bergmann hdlc->hdlc_bits1 = 0;
41699c2aa15SArnd Bergmann hdlc->state = HDLC_SENDFLAG_B1A6;
41799c2aa15SArnd Bergmann break;
41899c2aa15SArnd Bergmann case HDLC_SENDFLAG_B1A6:
41999c2aa15SArnd Bergmann hdlc->cbin <<= 1;
42099c2aa15SArnd Bergmann hdlc->data_bits++;
42199c2aa15SArnd Bergmann hdlc->cbin++;
42299c2aa15SArnd Bergmann if (++hdlc->hdlc_bits1 == 6)
42399c2aa15SArnd Bergmann hdlc->state = HDLC_SENDFLAG_B7;
42499c2aa15SArnd Bergmann break;
42599c2aa15SArnd Bergmann case HDLC_SENDFLAG_B7:
42699c2aa15SArnd Bergmann hdlc->cbin <<= 1;
42799c2aa15SArnd Bergmann hdlc->data_bits++;
42899c2aa15SArnd Bergmann if (slen == 0) {
42999c2aa15SArnd Bergmann hdlc->state = HDLC_SENDFLAG_B0;
43099c2aa15SArnd Bergmann break;
43199c2aa15SArnd Bergmann }
43299c2aa15SArnd Bergmann if (hdlc->bit_shift == 8) {
43399c2aa15SArnd Bergmann hdlc->state = HDLC_SEND_DATA;
43499c2aa15SArnd Bergmann hdlc->crc = 0xffff;
43599c2aa15SArnd Bergmann hdlc->hdlc_bits1 = 0;
43699c2aa15SArnd Bergmann hdlc->data_received = 1;
43799c2aa15SArnd Bergmann }
43899c2aa15SArnd Bergmann break;
43999c2aa15SArnd Bergmann case HDLC_SEND_FIRST_FLAG:
44099c2aa15SArnd Bergmann hdlc->data_received = 1;
44199c2aa15SArnd Bergmann if (hdlc->data_bits == 8) {
44299c2aa15SArnd Bergmann hdlc->state = HDLC_SEND_DATA;
44399c2aa15SArnd Bergmann hdlc->crc = 0xffff;
44499c2aa15SArnd Bergmann hdlc->hdlc_bits1 = 0;
44599c2aa15SArnd Bergmann break;
44699c2aa15SArnd Bergmann }
44799c2aa15SArnd Bergmann hdlc->cbin <<= 1;
44899c2aa15SArnd Bergmann hdlc->data_bits++;
44999c2aa15SArnd Bergmann if (hdlc->shift_reg & 0x01)
45099c2aa15SArnd Bergmann hdlc->cbin++;
45199c2aa15SArnd Bergmann hdlc->shift_reg >>= 1;
45299c2aa15SArnd Bergmann hdlc->bit_shift--;
45399c2aa15SArnd Bergmann if (hdlc->bit_shift == 0) {
45499c2aa15SArnd Bergmann hdlc->state = HDLC_SEND_DATA;
45599c2aa15SArnd Bergmann hdlc->crc = 0xffff;
45699c2aa15SArnd Bergmann hdlc->hdlc_bits1 = 0;
45799c2aa15SArnd Bergmann }
45899c2aa15SArnd Bergmann break;
45999c2aa15SArnd Bergmann case HDLC_SEND_DATA:
46099c2aa15SArnd Bergmann hdlc->cbin <<= 1;
46199c2aa15SArnd Bergmann hdlc->data_bits++;
46299c2aa15SArnd Bergmann if (hdlc->hdlc_bits1 == 5) {
46399c2aa15SArnd Bergmann hdlc->hdlc_bits1 = 0;
46499c2aa15SArnd Bergmann break;
46599c2aa15SArnd Bergmann }
46699c2aa15SArnd Bergmann if (hdlc->bit_shift == 8)
46799c2aa15SArnd Bergmann hdlc->crc = crc_ccitt_byte(hdlc->crc,
46899c2aa15SArnd Bergmann hdlc->shift_reg);
46999c2aa15SArnd Bergmann if (hdlc->shift_reg & 0x01) {
47099c2aa15SArnd Bergmann hdlc->hdlc_bits1++;
47199c2aa15SArnd Bergmann hdlc->cbin++;
47299c2aa15SArnd Bergmann hdlc->shift_reg >>= 1;
47399c2aa15SArnd Bergmann hdlc->bit_shift--;
47499c2aa15SArnd Bergmann } else {
47599c2aa15SArnd Bergmann hdlc->hdlc_bits1 = 0;
47699c2aa15SArnd Bergmann hdlc->shift_reg >>= 1;
47799c2aa15SArnd Bergmann hdlc->bit_shift--;
47899c2aa15SArnd Bergmann }
47999c2aa15SArnd Bergmann break;
48099c2aa15SArnd Bergmann case HDLC_SEND_CRC1:
48199c2aa15SArnd Bergmann hdlc->cbin <<= 1;
48299c2aa15SArnd Bergmann hdlc->data_bits++;
48399c2aa15SArnd Bergmann if (hdlc->hdlc_bits1 == 5) {
48499c2aa15SArnd Bergmann hdlc->hdlc_bits1 = 0;
48599c2aa15SArnd Bergmann break;
48699c2aa15SArnd Bergmann }
48799c2aa15SArnd Bergmann if (hdlc->shift_reg & 0x01) {
48899c2aa15SArnd Bergmann hdlc->hdlc_bits1++;
48999c2aa15SArnd Bergmann hdlc->cbin++;
49099c2aa15SArnd Bergmann hdlc->shift_reg >>= 1;
49199c2aa15SArnd Bergmann hdlc->bit_shift--;
49299c2aa15SArnd Bergmann } else {
49399c2aa15SArnd Bergmann hdlc->hdlc_bits1 = 0;
49499c2aa15SArnd Bergmann hdlc->shift_reg >>= 1;
49599c2aa15SArnd Bergmann hdlc->bit_shift--;
49699c2aa15SArnd Bergmann }
49799c2aa15SArnd Bergmann if (hdlc->bit_shift == 0) {
49899c2aa15SArnd Bergmann hdlc->shift_reg = (hdlc->crc >> 8);
49999c2aa15SArnd Bergmann hdlc->state = HDLC_SEND_CRC2;
50099c2aa15SArnd Bergmann hdlc->bit_shift = 8;
50199c2aa15SArnd Bergmann }
50299c2aa15SArnd Bergmann break;
50399c2aa15SArnd Bergmann case HDLC_SEND_CRC2:
50499c2aa15SArnd Bergmann hdlc->cbin <<= 1;
50599c2aa15SArnd Bergmann hdlc->data_bits++;
50699c2aa15SArnd Bergmann if (hdlc->hdlc_bits1 == 5) {
50799c2aa15SArnd Bergmann hdlc->hdlc_bits1 = 0;
50899c2aa15SArnd Bergmann break;
50999c2aa15SArnd Bergmann }
51099c2aa15SArnd Bergmann if (hdlc->shift_reg & 0x01) {
51199c2aa15SArnd Bergmann hdlc->hdlc_bits1++;
51299c2aa15SArnd Bergmann hdlc->cbin++;
51399c2aa15SArnd Bergmann hdlc->shift_reg >>= 1;
51499c2aa15SArnd Bergmann hdlc->bit_shift--;
51599c2aa15SArnd Bergmann } else {
51699c2aa15SArnd Bergmann hdlc->hdlc_bits1 = 0;
51799c2aa15SArnd Bergmann hdlc->shift_reg >>= 1;
51899c2aa15SArnd Bergmann hdlc->bit_shift--;
51999c2aa15SArnd Bergmann }
52099c2aa15SArnd Bergmann if (hdlc->bit_shift == 0) {
52199c2aa15SArnd Bergmann hdlc->shift_reg = 0x7e;
52299c2aa15SArnd Bergmann hdlc->state = HDLC_SEND_CLOSING_FLAG;
52399c2aa15SArnd Bergmann hdlc->bit_shift = 8;
52499c2aa15SArnd Bergmann }
52599c2aa15SArnd Bergmann break;
52699c2aa15SArnd Bergmann case HDLC_SEND_CLOSING_FLAG:
52799c2aa15SArnd Bergmann hdlc->cbin <<= 1;
52899c2aa15SArnd Bergmann hdlc->data_bits++;
52999c2aa15SArnd Bergmann if (hdlc->hdlc_bits1 == 5) {
53099c2aa15SArnd Bergmann hdlc->hdlc_bits1 = 0;
53199c2aa15SArnd Bergmann break;
53299c2aa15SArnd Bergmann }
53399c2aa15SArnd Bergmann if (hdlc->shift_reg & 0x01)
53499c2aa15SArnd Bergmann hdlc->cbin++;
53599c2aa15SArnd Bergmann hdlc->shift_reg >>= 1;
53699c2aa15SArnd Bergmann hdlc->bit_shift--;
53799c2aa15SArnd Bergmann if (hdlc->bit_shift == 0) {
53899c2aa15SArnd Bergmann hdlc->ffvalue =
53999c2aa15SArnd Bergmann xfast_flag_value[hdlc->data_bits];
54099c2aa15SArnd Bergmann if (hdlc->dchannel) {
54199c2aa15SArnd Bergmann hdlc->ffvalue = 0x7e;
54299c2aa15SArnd Bergmann hdlc->state = HDLC_SEND_IDLE1;
54399c2aa15SArnd Bergmann hdlc->bit_shift = 8-hdlc->data_bits;
54499c2aa15SArnd Bergmann if (hdlc->bit_shift == 0)
54599c2aa15SArnd Bergmann hdlc->state =
54699c2aa15SArnd Bergmann HDLC_SEND_FAST_IDLE;
54799c2aa15SArnd Bergmann } else {
54899c2aa15SArnd Bergmann if (!hdlc->do_adapt56) {
54999c2aa15SArnd Bergmann hdlc->state =
55099c2aa15SArnd Bergmann HDLC_SEND_FAST_FLAG;
55199c2aa15SArnd Bergmann hdlc->data_received = 0;
55299c2aa15SArnd Bergmann } else {
55399c2aa15SArnd Bergmann hdlc->state = HDLC_SENDFLAG_B0;
55499c2aa15SArnd Bergmann hdlc->data_received = 0;
55599c2aa15SArnd Bergmann }
55699c2aa15SArnd Bergmann /* Finished this frame, send flags */
55799c2aa15SArnd Bergmann if (dsize > 1)
55899c2aa15SArnd Bergmann dsize = 1;
55999c2aa15SArnd Bergmann }
56099c2aa15SArnd Bergmann }
56199c2aa15SArnd Bergmann break;
56299c2aa15SArnd Bergmann case HDLC_SEND_IDLE1:
56399c2aa15SArnd Bergmann hdlc->do_closing = 0;
56499c2aa15SArnd Bergmann hdlc->cbin <<= 1;
56599c2aa15SArnd Bergmann hdlc->cbin++;
56699c2aa15SArnd Bergmann hdlc->data_bits++;
56799c2aa15SArnd Bergmann hdlc->bit_shift--;
56899c2aa15SArnd Bergmann if (hdlc->bit_shift == 0) {
56999c2aa15SArnd Bergmann hdlc->state = HDLC_SEND_FAST_IDLE;
57099c2aa15SArnd Bergmann hdlc->bit_shift = 0;
57199c2aa15SArnd Bergmann }
57299c2aa15SArnd Bergmann break;
57399c2aa15SArnd Bergmann case HDLC_SEND_FAST_IDLE:
57499c2aa15SArnd Bergmann hdlc->do_closing = 0;
57599c2aa15SArnd Bergmann hdlc->cbin = 0xff;
57699c2aa15SArnd Bergmann hdlc->data_bits = 8;
57799c2aa15SArnd Bergmann if (hdlc->bit_shift == 8) {
57899c2aa15SArnd Bergmann hdlc->cbin = 0x7e;
57999c2aa15SArnd Bergmann hdlc->state = HDLC_SEND_FIRST_FLAG;
58099c2aa15SArnd Bergmann } else {
58199c2aa15SArnd Bergmann /* the code is for bitreverse streams */
58299c2aa15SArnd Bergmann if (hdlc->do_bitreverse == 0)
58399c2aa15SArnd Bergmann *dst++ = bitrev8(hdlc->cbin);
58499c2aa15SArnd Bergmann else
58599c2aa15SArnd Bergmann *dst++ = hdlc->cbin;
58699c2aa15SArnd Bergmann hdlc->bit_shift = 0;
58799c2aa15SArnd Bergmann hdlc->data_bits = 0;
58899c2aa15SArnd Bergmann len++;
58999c2aa15SArnd Bergmann dsize = 0;
59099c2aa15SArnd Bergmann }
59199c2aa15SArnd Bergmann break;
59299c2aa15SArnd Bergmann default:
59399c2aa15SArnd Bergmann break;
59499c2aa15SArnd Bergmann }
59599c2aa15SArnd Bergmann if (hdlc->do_adapt56) {
59699c2aa15SArnd Bergmann if (hdlc->data_bits == 7) {
59799c2aa15SArnd Bergmann hdlc->cbin <<= 1;
59899c2aa15SArnd Bergmann hdlc->cbin++;
59999c2aa15SArnd Bergmann hdlc->data_bits++;
60099c2aa15SArnd Bergmann }
60199c2aa15SArnd Bergmann }
60299c2aa15SArnd Bergmann if (hdlc->data_bits == 8) {
60399c2aa15SArnd Bergmann /* the code is for bitreverse streams */
60499c2aa15SArnd Bergmann if (hdlc->do_bitreverse == 0)
60599c2aa15SArnd Bergmann *dst++ = bitrev8(hdlc->cbin);
60699c2aa15SArnd Bergmann else
60799c2aa15SArnd Bergmann *dst++ = hdlc->cbin;
60899c2aa15SArnd Bergmann hdlc->data_bits = 0;
60999c2aa15SArnd Bergmann len++;
61099c2aa15SArnd Bergmann dsize--;
61199c2aa15SArnd Bergmann }
61299c2aa15SArnd Bergmann }
61399c2aa15SArnd Bergmann *count -= slen;
61499c2aa15SArnd Bergmann
61599c2aa15SArnd Bergmann return len;
61699c2aa15SArnd Bergmann }
61799c2aa15SArnd Bergmann EXPORT_SYMBOL(isdnhdlc_encode);
618