blob: c989aa35dc2fc56ede0c29eafb8e34cd378bf1ae [file] [log] [blame]
Linus Torvalds1da177e2005-04-16 15:20:36 -07001/*
2 * isdnhdlc.c -- General purpose ISDN HDLC decoder.
3 *
Karsten Keil6bd4bcd2009-07-08 19:11:09 +02004 * Copyright (C)
Karsten Keilc38fc3b2009-07-08 20:31:42 +02005 * 2009 Karsten Keil <keil@b1-systems.de>
Karsten Keil6bd4bcd2009-07-08 19:11:09 +02006 * 2002 Wolfgang Mües <wolfgang@iksw-muees.de>
7 * 2001 Frode Isaksen <fisaksen@bewan.com>
8 * 2001 Kai Germaschewski <kai.germaschewski@gmx.de>
Linus Torvalds1da177e2005-04-16 15:20:36 -07009 *
Karsten Keil6bd4bcd2009-07-08 19:11:09 +020010 * This program is free software; you can redistribute it and/or modify
11 * it under the terms of the GNU General Public License as published by
12 * the Free Software Foundation; either version 2 of the License, or
13 * (at your option) any later version.
Linus Torvalds1da177e2005-04-16 15:20:36 -070014 *
Karsten Keil6bd4bcd2009-07-08 19:11:09 +020015 * This program is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU General Public License for more details.
Linus Torvalds1da177e2005-04-16 15:20:36 -070019 *
Karsten Keil6bd4bcd2009-07-08 19:11:09 +020020 * You should have received a copy of the GNU General Public License
21 * along with this program; if not, write to the Free Software
22 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
Linus Torvalds1da177e2005-04-16 15:20:36 -070023 */
24
25#include <linux/module.h>
26#include <linux/init.h>
27#include <linux/crc-ccitt.h>
Karsten Keilcb3824b2009-07-08 14:21:12 +020028#include <linux/isdn/hdlc.h>
Karsten Keilc38fc3b2009-07-08 20:31:42 +020029#include <linux/bitrev.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070030
31/*-------------------------------------------------------------------*/
32
Jan Engelhardt96de0e22007-10-19 23:21:04 +020033MODULE_AUTHOR("Wolfgang Mües <wolfgang@iksw-muees.de>, "
Linus Torvalds1da177e2005-04-16 15:20:36 -070034 "Frode Isaksen <fisaksen@bewan.com>, "
35 "Kai Germaschewski <kai.germaschewski@gmx.de>");
36MODULE_DESCRIPTION("General purpose ISDN HDLC decoder");
37MODULE_LICENSE("GPL");
38
39/*-------------------------------------------------------------------*/
40
Linus Torvalds1da177e2005-04-16 15:20:36 -070041enum {
Karsten Keil6bd4bcd2009-07-08 19:11:09 +020042 HDLC_FAST_IDLE, HDLC_GET_FLAG_B0, HDLC_GETFLAG_B1A6, HDLC_GETFLAG_B7,
43 HDLC_GET_DATA, HDLC_FAST_FLAG
Linus Torvalds1da177e2005-04-16 15:20:36 -070044};
45
46enum {
Karsten Keil6bd4bcd2009-07-08 19:11:09 +020047 HDLC_SEND_DATA, HDLC_SEND_CRC1, HDLC_SEND_FAST_FLAG,
48 HDLC_SEND_FIRST_FLAG, HDLC_SEND_CRC2, HDLC_SEND_CLOSING_FLAG,
49 HDLC_SEND_IDLE1, HDLC_SEND_FAST_IDLE, HDLC_SENDFLAG_B0,
Karsten Keilf3fad222009-07-08 20:58:33 +020050 HDLC_SENDFLAG_B1A6, HDLC_SENDFLAG_B7, STOPPED, HDLC_SENDFLAG_ONE
Linus Torvalds1da177e2005-04-16 15:20:36 -070051};
52
Karsten Keilc38fc3b2009-07-08 20:31:42 +020053void isdnhdlc_rcv_init(struct isdnhdlc_vars *hdlc, u32 features)
Linus Torvalds1da177e2005-04-16 15:20:36 -070054{
Karsten Keilc38fc3b2009-07-08 20:31:42 +020055 memset(hdlc, 0, sizeof(struct isdnhdlc_vars));
Linus Torvalds1da177e2005-04-16 15:20:36 -070056 hdlc->state = HDLC_GET_DATA;
Karsten Keilc38fc3b2009-07-08 20:31:42 +020057 if (features & HDLC_56KBIT)
58 hdlc->do_adapt56 = 1;
59 if (features & HDLC_BITREVERSE)
60 hdlc->do_bitreverse = 1;
Linus Torvalds1da177e2005-04-16 15:20:36 -070061}
Karsten Keil6bd4bcd2009-07-08 19:11:09 +020062EXPORT_SYMBOL(isdnhdlc_out_init);
Linus Torvalds1da177e2005-04-16 15:20:36 -070063
Karsten Keilc38fc3b2009-07-08 20:31:42 +020064void isdnhdlc_out_init(struct isdnhdlc_vars *hdlc, u32 features)
Linus Torvalds1da177e2005-04-16 15:20:36 -070065{
Karsten Keilc38fc3b2009-07-08 20:31:42 +020066 memset(hdlc, 0, sizeof(struct isdnhdlc_vars));
67 if (features & HDLC_DCHANNEL) {
Linus Torvalds1da177e2005-04-16 15:20:36 -070068 hdlc->dchannel = 1;
69 hdlc->state = HDLC_SEND_FIRST_FLAG;
70 } else {
71 hdlc->dchannel = 0;
72 hdlc->state = HDLC_SEND_FAST_FLAG;
73 hdlc->ffvalue = 0x7e;
74 }
75 hdlc->cbin = 0x7e;
Karsten Keilc38fc3b2009-07-08 20:31:42 +020076 if (features & HDLC_56KBIT) {
Linus Torvalds1da177e2005-04-16 15:20:36 -070077 hdlc->do_adapt56 = 1;
Linus Torvalds1da177e2005-04-16 15:20:36 -070078 hdlc->state = HDLC_SENDFLAG_B0;
Karsten Keilc38fc3b2009-07-08 20:31:42 +020079 } else
Linus Torvalds1da177e2005-04-16 15:20:36 -070080 hdlc->data_bits = 8;
Karsten Keilc38fc3b2009-07-08 20:31:42 +020081 if (features & HDLC_BITREVERSE)
82 hdlc->do_bitreverse = 1;
Linus Torvalds1da177e2005-04-16 15:20:36 -070083}
Karsten Keil6bd4bcd2009-07-08 19:11:09 +020084EXPORT_SYMBOL(isdnhdlc_rcv_init);
85
86static int
87check_frame(struct isdnhdlc_vars *hdlc)
88{
89 int status;
90
91 if (hdlc->dstpos < 2) /* too small - framing error */
92 status = -HDLC_FRAMING_ERROR;
93 else if (hdlc->crc != 0xf0b8) /* crc error */
94 status = -HDLC_CRC_ERROR;
95 else {
96 /* remove CRC */
97 hdlc->dstpos -= 2;
98 /* good frame */
99 status = hdlc->dstpos;
100 }
101 return status;
102}
Linus Torvalds1da177e2005-04-16 15:20:36 -0700103
104/*
105 isdnhdlc_decode - decodes HDLC frames from a transparent bit stream.
106
107 The source buffer is scanned for valid HDLC frames looking for
108 flags (01111110) to indicate the start of a frame. If the start of
109 the frame is found, the bit stuffing is removed (0 after 5 1's).
110 When a new flag is found, the complete frame has been received
111 and the CRC is checked.
112 If a valid frame is found, the function returns the frame length
113 excluding the CRC with the bit HDLC_END_OF_FRAME set.
114 If the beginning of a valid frame is found, the function returns
115 the length.
116 If a framing error is found (too many 1s and not a flag) the function
117 returns the length with the bit HDLC_FRAMING_ERROR set.
118 If a CRC error is found the function returns the length with the
119 bit HDLC_CRC_ERROR set.
120 If the frame length exceeds the destination buffer size, the function
121 returns the length with the bit HDLC_LENGTH_ERROR set.
122
123 src - source buffer
124 slen - source buffer length
125 count - number of bytes removed (decoded) from the source buffer
126 dst _ destination buffer
127 dsize - destination buffer size
128 returns - number of decoded bytes in the destination buffer and status
129 flag.
130 */
Karsten Keil6bd4bcd2009-07-08 19:11:09 +0200131int isdnhdlc_decode(struct isdnhdlc_vars *hdlc, const u8 *src, int slen,
132 int *count, u8 *dst, int dsize)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700133{
Karsten Keil6bd4bcd2009-07-08 19:11:09 +0200134 int status = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700135
Karsten Keil6bd4bcd2009-07-08 19:11:09 +0200136 static const unsigned char fast_flag[] = {
137 0x00, 0x00, 0x00, 0x20, 0x30, 0x38, 0x3c, 0x3e, 0x3f
Linus Torvalds1da177e2005-04-16 15:20:36 -0700138 };
139
Karsten Keil6bd4bcd2009-07-08 19:11:09 +0200140 static const unsigned char fast_flag_value[] = {
141 0x00, 0x7e, 0xfc, 0xf9, 0xf3, 0xe7, 0xcf, 0x9f, 0x3f
Linus Torvalds1da177e2005-04-16 15:20:36 -0700142 };
143
Karsten Keil6bd4bcd2009-07-08 19:11:09 +0200144 static const unsigned char fast_abort[] = {
145 0x00, 0x00, 0x80, 0xc0, 0xe0, 0xf0, 0xf8, 0xfc, 0xfe, 0xff
Linus Torvalds1da177e2005-04-16 15:20:36 -0700146 };
147
Karsten Keil6bd4bcd2009-07-08 19:11:09 +0200148#define handle_fast_flag(h) \
149 do {\
150 if (h->cbin == fast_flag[h->bit_shift]) {\
151 h->ffvalue = fast_flag_value[h->bit_shift];\
152 h->state = HDLC_FAST_FLAG;\
153 h->ffbit_shift = h->bit_shift;\
154 h->bit_shift = 1;\
155 } else {\
156 h->state = HDLC_GET_DATA;\
157 h->data_received = 0;\
158 } \
159 } while (0)
160
161#define handle_abort(h) \
162 do {\
163 h->shift_reg = fast_abort[h->ffbit_shift - 1];\
164 h->hdlc_bits1 = h->ffbit_shift - 2;\
165 if (h->hdlc_bits1 < 0)\
166 h->hdlc_bits1 = 0;\
167 h->data_bits = h->ffbit_shift - 1;\
168 h->state = HDLC_GET_DATA;\
169 h->data_received = 0;\
170 } while (0)
171
Linus Torvalds1da177e2005-04-16 15:20:36 -0700172 *count = slen;
173
Karsten Keil6bd4bcd2009-07-08 19:11:09 +0200174 while (slen > 0) {
175 if (hdlc->bit_shift == 0) {
Karsten Keilc38fc3b2009-07-08 20:31:42 +0200176 /* the code is for bitreverse streams */
177 if (hdlc->do_bitreverse == 0)
178 hdlc->cbin = bitrev8(*src++);
179 else
180 hdlc->cbin = *src++;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700181 slen--;
182 hdlc->bit_shift = 8;
Karsten Keil6bd4bcd2009-07-08 19:11:09 +0200183 if (hdlc->do_adapt56)
184 hdlc->bit_shift--;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700185 }
186
Karsten Keil6bd4bcd2009-07-08 19:11:09 +0200187 switch (hdlc->state) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700188 case STOPPED:
189 return 0;
190 case HDLC_FAST_IDLE:
Karsten Keil6bd4bcd2009-07-08 19:11:09 +0200191 if (hdlc->cbin == 0xff) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700192 hdlc->bit_shift = 0;
193 break;
194 }
195 hdlc->state = HDLC_GET_FLAG_B0;
196 hdlc->hdlc_bits1 = 0;
197 hdlc->bit_shift = 8;
198 break;
199 case HDLC_GET_FLAG_B0:
Karsten Keil6bd4bcd2009-07-08 19:11:09 +0200200 if (!(hdlc->cbin & 0x80)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700201 hdlc->state = HDLC_GETFLAG_B1A6;
202 hdlc->hdlc_bits1 = 0;
203 } else {
Karsten Keil6bd4bcd2009-07-08 19:11:09 +0200204 if ((!hdlc->do_adapt56) &&
205 (++hdlc->hdlc_bits1 >= 8) &&
206 (hdlc->bit_shift == 1))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700207 hdlc->state = HDLC_FAST_IDLE;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700208 }
Karsten Keil6bd4bcd2009-07-08 19:11:09 +0200209 hdlc->cbin <<= 1;
210 hdlc->bit_shift--;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700211 break;
212 case HDLC_GETFLAG_B1A6:
Karsten Keil6bd4bcd2009-07-08 19:11:09 +0200213 if (hdlc->cbin & 0x80) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700214 hdlc->hdlc_bits1++;
Karsten Keil6bd4bcd2009-07-08 19:11:09 +0200215 if (hdlc->hdlc_bits1 == 6)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700216 hdlc->state = HDLC_GETFLAG_B7;
Karsten Keil6bd4bcd2009-07-08 19:11:09 +0200217 } else
Linus Torvalds1da177e2005-04-16 15:20:36 -0700218 hdlc->hdlc_bits1 = 0;
Karsten Keil6bd4bcd2009-07-08 19:11:09 +0200219 hdlc->cbin <<= 1;
220 hdlc->bit_shift--;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700221 break;
222 case HDLC_GETFLAG_B7:
Karsten Keil6bd4bcd2009-07-08 19:11:09 +0200223 if (hdlc->cbin & 0x80) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700224 hdlc->state = HDLC_GET_FLAG_B0;
225 } else {
226 hdlc->state = HDLC_GET_DATA;
227 hdlc->crc = 0xffff;
228 hdlc->shift_reg = 0;
229 hdlc->hdlc_bits1 = 0;
230 hdlc->data_bits = 0;
231 hdlc->data_received = 0;
232 }
Karsten Keil6bd4bcd2009-07-08 19:11:09 +0200233 hdlc->cbin <<= 1;
234 hdlc->bit_shift--;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700235 break;
236 case HDLC_GET_DATA:
Karsten Keil6bd4bcd2009-07-08 19:11:09 +0200237 if (hdlc->cbin & 0x80) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700238 hdlc->hdlc_bits1++;
Karsten Keil6bd4bcd2009-07-08 19:11:09 +0200239 switch (hdlc->hdlc_bits1) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700240 case 6:
241 break;
242 case 7:
Karsten Keil6bd4bcd2009-07-08 19:11:09 +0200243 if (hdlc->data_received)
244 /* bad frame */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700245 status = -HDLC_FRAMING_ERROR;
Karsten Keil6bd4bcd2009-07-08 19:11:09 +0200246 if (!hdlc->do_adapt56) {
247 if (hdlc->cbin == fast_abort
248 [hdlc->bit_shift + 1]) {
249 hdlc->state =
250 HDLC_FAST_IDLE;
251 hdlc->bit_shift = 1;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700252 break;
253 }
Karsten Keil6bd4bcd2009-07-08 19:11:09 +0200254 } else
Linus Torvalds1da177e2005-04-16 15:20:36 -0700255 hdlc->state = HDLC_GET_FLAG_B0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700256 break;
257 default:
Karsten Keil6bd4bcd2009-07-08 19:11:09 +0200258 hdlc->shift_reg >>= 1;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700259 hdlc->shift_reg |= 0x80;
260 hdlc->data_bits++;
261 break;
262 }
263 } else {
Karsten Keil6bd4bcd2009-07-08 19:11:09 +0200264 switch (hdlc->hdlc_bits1) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700265 case 5:
266 break;
267 case 6:
Karsten Keil6bd4bcd2009-07-08 19:11:09 +0200268 if (hdlc->data_received)
269 status = check_frame(hdlc);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700270 hdlc->crc = 0xffff;
271 hdlc->shift_reg = 0;
272 hdlc->data_bits = 0;
Karsten Keil6bd4bcd2009-07-08 19:11:09 +0200273 if (!hdlc->do_adapt56)
274 handle_fast_flag(hdlc);
275 else {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700276 hdlc->state = HDLC_GET_DATA;
277 hdlc->data_received = 0;
278 }
279 break;
280 default:
Karsten Keil6bd4bcd2009-07-08 19:11:09 +0200281 hdlc->shift_reg >>= 1;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700282 hdlc->data_bits++;
283 break;
284 }
285 hdlc->hdlc_bits1 = 0;
286 }
287 if (status) {
288 hdlc->dstpos = 0;
289 *count -= slen;
290 hdlc->cbin <<= 1;
291 hdlc->bit_shift--;
292 return status;
293 }
Karsten Keil6bd4bcd2009-07-08 19:11:09 +0200294 if (hdlc->data_bits == 8) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700295 hdlc->data_bits = 0;
296 hdlc->data_received = 1;
Karsten Keil6bd4bcd2009-07-08 19:11:09 +0200297 hdlc->crc = crc_ccitt_byte(hdlc->crc,
298 hdlc->shift_reg);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700299
Karsten Keil6bd4bcd2009-07-08 19:11:09 +0200300 /* good byte received */
301 if (hdlc->dstpos < dsize)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700302 dst[hdlc->dstpos++] = hdlc->shift_reg;
Karsten Keil6bd4bcd2009-07-08 19:11:09 +0200303 else {
304 /* frame too long */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700305 status = -HDLC_LENGTH_ERROR;
306 hdlc->dstpos = 0;
307 }
308 }
309 hdlc->cbin <<= 1;
310 hdlc->bit_shift--;
311 break;
312 case HDLC_FAST_FLAG:
Karsten Keil6bd4bcd2009-07-08 19:11:09 +0200313 if (hdlc->cbin == hdlc->ffvalue) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700314 hdlc->bit_shift = 0;
315 break;
316 } else {
Karsten Keil6bd4bcd2009-07-08 19:11:09 +0200317 if (hdlc->cbin == 0xff) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700318 hdlc->state = HDLC_FAST_IDLE;
Karsten Keil6bd4bcd2009-07-08 19:11:09 +0200319 hdlc->bit_shift = 0;
320 } else if (hdlc->ffbit_shift == 8) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700321 hdlc->state = HDLC_GETFLAG_B7;
322 break;
Karsten Keil6bd4bcd2009-07-08 19:11:09 +0200323 } else
324 handle_abort(hdlc);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700325 }
326 break;
327 default:
328 break;
329 }
330 }
331 *count -= slen;
332 return 0;
333}
Karsten Keil6bd4bcd2009-07-08 19:11:09 +0200334EXPORT_SYMBOL(isdnhdlc_decode);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700335/*
336 isdnhdlc_encode - encodes HDLC frames to a transparent bit stream.
337
338 The bit stream starts with a beginning flag (01111110). After
339 that each byte is added to the bit stream with bit stuffing added
340 (0 after 5 1's).
341 When the last byte has been removed from the source buffer, the
342 CRC (2 bytes is added) and the frame terminates with the ending flag.
343 For the dchannel, the idle character (all 1's) is also added at the end.
344 If this function is called with empty source buffer (slen=0), flags or
345 idle character will be generated.
346
347 src - source buffer
348 slen - source buffer length
349 count - number of bytes removed (encoded) from source buffer
350 dst _ destination buffer
351 dsize - destination buffer size
352 returns - number of encoded bytes in the destination buffer
353*/
Karsten Keil6bd4bcd2009-07-08 19:11:09 +0200354int isdnhdlc_encode(struct isdnhdlc_vars *hdlc, const u8 *src, u16 slen,
355 int *count, u8 *dst, int dsize)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700356{
357 static const unsigned char xfast_flag_value[] = {
Karsten Keil6bd4bcd2009-07-08 19:11:09 +0200358 0x7e, 0x3f, 0x9f, 0xcf, 0xe7, 0xf3, 0xf9, 0xfc, 0x7e
Linus Torvalds1da177e2005-04-16 15:20:36 -0700359 };
360
361 int len = 0;
362
363 *count = slen;
364
Karsten Keilf3fad222009-07-08 20:58:33 +0200365 /* special handling for one byte frames */
366 if ((slen == 1) && (hdlc->state == HDLC_SEND_FAST_FLAG))
367 hdlc->state = HDLC_SENDFLAG_ONE;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700368 while (dsize > 0) {
Karsten Keil6bd4bcd2009-07-08 19:11:09 +0200369 if (hdlc->bit_shift == 0) {
370 if (slen && !hdlc->do_closing) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700371 hdlc->shift_reg = *src++;
372 slen--;
373 if (slen == 0)
Karsten Keil6bd4bcd2009-07-08 19:11:09 +0200374 /* closing sequence, CRC + flag(s) */
375 hdlc->do_closing = 1;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700376 hdlc->bit_shift = 8;
377 } else {
Karsten Keil6bd4bcd2009-07-08 19:11:09 +0200378 if (hdlc->state == HDLC_SEND_DATA) {
379 if (hdlc->data_received) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700380 hdlc->state = HDLC_SEND_CRC1;
381 hdlc->crc ^= 0xffff;
382 hdlc->bit_shift = 8;
Karsten Keil6bd4bcd2009-07-08 19:11:09 +0200383 hdlc->shift_reg =
384 hdlc->crc & 0xff;
385 } else if (!hdlc->do_adapt56)
386 hdlc->state =
387 HDLC_SEND_FAST_FLAG;
388 else
389 hdlc->state =
390 HDLC_SENDFLAG_B0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700391 }
392
393 }
394 }
395
Karsten Keil6bd4bcd2009-07-08 19:11:09 +0200396 switch (hdlc->state) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700397 case STOPPED:
398 while (dsize--)
399 *dst++ = 0xff;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700400 return dsize;
401 case HDLC_SEND_FAST_FLAG:
402 hdlc->do_closing = 0;
Karsten Keil6bd4bcd2009-07-08 19:11:09 +0200403 if (slen == 0) {
Karsten Keilc38fc3b2009-07-08 20:31:42 +0200404 /* the code is for bitreverse streams */
405 if (hdlc->do_bitreverse == 0)
406 *dst++ = bitrev8(hdlc->ffvalue);
407 else
408 *dst++ = hdlc->ffvalue;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700409 len++;
410 dsize--;
411 break;
412 }
Karsten Keilf3fad222009-07-08 20:58:33 +0200413 /* fall through */
414 case HDLC_SENDFLAG_ONE:
Karsten Keil6bd4bcd2009-07-08 19:11:09 +0200415 if (hdlc->bit_shift == 8) {
416 hdlc->cbin = hdlc->ffvalue >>
417 (8 - hdlc->data_bits);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700418 hdlc->state = HDLC_SEND_DATA;
419 hdlc->crc = 0xffff;
420 hdlc->hdlc_bits1 = 0;
421 hdlc->data_received = 1;
422 }
423 break;
424 case HDLC_SENDFLAG_B0:
425 hdlc->do_closing = 0;
426 hdlc->cbin <<= 1;
427 hdlc->data_bits++;
428 hdlc->hdlc_bits1 = 0;
429 hdlc->state = HDLC_SENDFLAG_B1A6;
430 break;
431 case HDLC_SENDFLAG_B1A6:
432 hdlc->cbin <<= 1;
433 hdlc->data_bits++;
434 hdlc->cbin++;
Karsten Keil6bd4bcd2009-07-08 19:11:09 +0200435 if (++hdlc->hdlc_bits1 == 6)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700436 hdlc->state = HDLC_SENDFLAG_B7;
437 break;
438 case HDLC_SENDFLAG_B7:
439 hdlc->cbin <<= 1;
440 hdlc->data_bits++;
Karsten Keil6bd4bcd2009-07-08 19:11:09 +0200441 if (slen == 0) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700442 hdlc->state = HDLC_SENDFLAG_B0;
443 break;
444 }
Karsten Keil6bd4bcd2009-07-08 19:11:09 +0200445 if (hdlc->bit_shift == 8) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700446 hdlc->state = HDLC_SEND_DATA;
447 hdlc->crc = 0xffff;
448 hdlc->hdlc_bits1 = 0;
449 hdlc->data_received = 1;
450 }
451 break;
452 case HDLC_SEND_FIRST_FLAG:
453 hdlc->data_received = 1;
Karsten Keil6bd4bcd2009-07-08 19:11:09 +0200454 if (hdlc->data_bits == 8) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700455 hdlc->state = HDLC_SEND_DATA;
456 hdlc->crc = 0xffff;
457 hdlc->hdlc_bits1 = 0;
458 break;
459 }
460 hdlc->cbin <<= 1;
461 hdlc->data_bits++;
Karsten Keil6bd4bcd2009-07-08 19:11:09 +0200462 if (hdlc->shift_reg & 0x01)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700463 hdlc->cbin++;
464 hdlc->shift_reg >>= 1;
465 hdlc->bit_shift--;
Karsten Keil6bd4bcd2009-07-08 19:11:09 +0200466 if (hdlc->bit_shift == 0) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700467 hdlc->state = HDLC_SEND_DATA;
468 hdlc->crc = 0xffff;
469 hdlc->hdlc_bits1 = 0;
470 }
471 break;
472 case HDLC_SEND_DATA:
473 hdlc->cbin <<= 1;
474 hdlc->data_bits++;
Karsten Keil6bd4bcd2009-07-08 19:11:09 +0200475 if (hdlc->hdlc_bits1 == 5) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700476 hdlc->hdlc_bits1 = 0;
477 break;
478 }
Karsten Keil6bd4bcd2009-07-08 19:11:09 +0200479 if (hdlc->bit_shift == 8)
480 hdlc->crc = crc_ccitt_byte(hdlc->crc,
481 hdlc->shift_reg);
482 if (hdlc->shift_reg & 0x01) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700483 hdlc->hdlc_bits1++;
484 hdlc->cbin++;
485 hdlc->shift_reg >>= 1;
486 hdlc->bit_shift--;
487 } else {
488 hdlc->hdlc_bits1 = 0;
489 hdlc->shift_reg >>= 1;
490 hdlc->bit_shift--;
491 }
492 break;
493 case HDLC_SEND_CRC1:
494 hdlc->cbin <<= 1;
495 hdlc->data_bits++;
Karsten Keil6bd4bcd2009-07-08 19:11:09 +0200496 if (hdlc->hdlc_bits1 == 5) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700497 hdlc->hdlc_bits1 = 0;
498 break;
499 }
Karsten Keil6bd4bcd2009-07-08 19:11:09 +0200500 if (hdlc->shift_reg & 0x01) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700501 hdlc->hdlc_bits1++;
502 hdlc->cbin++;
503 hdlc->shift_reg >>= 1;
504 hdlc->bit_shift--;
505 } else {
506 hdlc->hdlc_bits1 = 0;
507 hdlc->shift_reg >>= 1;
508 hdlc->bit_shift--;
509 }
Karsten Keil6bd4bcd2009-07-08 19:11:09 +0200510 if (hdlc->bit_shift == 0) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700511 hdlc->shift_reg = (hdlc->crc >> 8);
512 hdlc->state = HDLC_SEND_CRC2;
513 hdlc->bit_shift = 8;
514 }
515 break;
516 case HDLC_SEND_CRC2:
517 hdlc->cbin <<= 1;
518 hdlc->data_bits++;
Karsten Keil6bd4bcd2009-07-08 19:11:09 +0200519 if (hdlc->hdlc_bits1 == 5) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700520 hdlc->hdlc_bits1 = 0;
521 break;
522 }
Karsten Keil6bd4bcd2009-07-08 19:11:09 +0200523 if (hdlc->shift_reg & 0x01) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700524 hdlc->hdlc_bits1++;
525 hdlc->cbin++;
526 hdlc->shift_reg >>= 1;
527 hdlc->bit_shift--;
528 } else {
529 hdlc->hdlc_bits1 = 0;
530 hdlc->shift_reg >>= 1;
531 hdlc->bit_shift--;
532 }
Karsten Keil6bd4bcd2009-07-08 19:11:09 +0200533 if (hdlc->bit_shift == 0) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700534 hdlc->shift_reg = 0x7e;
535 hdlc->state = HDLC_SEND_CLOSING_FLAG;
536 hdlc->bit_shift = 8;
537 }
538 break;
539 case HDLC_SEND_CLOSING_FLAG:
540 hdlc->cbin <<= 1;
541 hdlc->data_bits++;
Karsten Keil6bd4bcd2009-07-08 19:11:09 +0200542 if (hdlc->hdlc_bits1 == 5) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700543 hdlc->hdlc_bits1 = 0;
544 break;
545 }
Karsten Keil6bd4bcd2009-07-08 19:11:09 +0200546 if (hdlc->shift_reg & 0x01)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700547 hdlc->cbin++;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700548 hdlc->shift_reg >>= 1;
549 hdlc->bit_shift--;
Karsten Keil6bd4bcd2009-07-08 19:11:09 +0200550 if (hdlc->bit_shift == 0) {
551 hdlc->ffvalue =
552 xfast_flag_value[hdlc->data_bits];
553 if (hdlc->dchannel) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700554 hdlc->ffvalue = 0x7e;
555 hdlc->state = HDLC_SEND_IDLE1;
556 hdlc->bit_shift = 8-hdlc->data_bits;
Karsten Keil6bd4bcd2009-07-08 19:11:09 +0200557 if (hdlc->bit_shift == 0)
558 hdlc->state =
559 HDLC_SEND_FAST_IDLE;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700560 } else {
Karsten Keil6bd4bcd2009-07-08 19:11:09 +0200561 if (!hdlc->do_adapt56) {
562 hdlc->state =
563 HDLC_SEND_FAST_FLAG;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700564 hdlc->data_received = 0;
565 } else {
566 hdlc->state = HDLC_SENDFLAG_B0;
567 hdlc->data_received = 0;
568 }
Karsten Keil6bd4bcd2009-07-08 19:11:09 +0200569 /* Finished this frame, send flags */
570 if (dsize > 1)
571 dsize = 1;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700572 }
573 }
574 break;
575 case HDLC_SEND_IDLE1:
576 hdlc->do_closing = 0;
577 hdlc->cbin <<= 1;
578 hdlc->cbin++;
579 hdlc->data_bits++;
580 hdlc->bit_shift--;
Karsten Keil6bd4bcd2009-07-08 19:11:09 +0200581 if (hdlc->bit_shift == 0) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700582 hdlc->state = HDLC_SEND_FAST_IDLE;
583 hdlc->bit_shift = 0;
584 }
585 break;
586 case HDLC_SEND_FAST_IDLE:
587 hdlc->do_closing = 0;
588 hdlc->cbin = 0xff;
589 hdlc->data_bits = 8;
Karsten Keil6bd4bcd2009-07-08 19:11:09 +0200590 if (hdlc->bit_shift == 8) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700591 hdlc->cbin = 0x7e;
592 hdlc->state = HDLC_SEND_FIRST_FLAG;
593 } else {
Karsten Keilc38fc3b2009-07-08 20:31:42 +0200594 /* the code is for bitreverse streams */
595 if (hdlc->do_bitreverse == 0)
596 *dst++ = bitrev8(hdlc->cbin);
597 else
598 *dst++ = hdlc->cbin;
Karsten Keil6bd4bcd2009-07-08 19:11:09 +0200599 hdlc->bit_shift = 0;
600 hdlc->data_bits = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700601 len++;
602 dsize = 0;
603 }
604 break;
605 default:
606 break;
607 }
Karsten Keil6bd4bcd2009-07-08 19:11:09 +0200608 if (hdlc->do_adapt56) {
609 if (hdlc->data_bits == 7) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700610 hdlc->cbin <<= 1;
611 hdlc->cbin++;
612 hdlc->data_bits++;
613 }
614 }
Karsten Keil6bd4bcd2009-07-08 19:11:09 +0200615 if (hdlc->data_bits == 8) {
Karsten Keilc38fc3b2009-07-08 20:31:42 +0200616 /* the code is for bitreverse streams */
617 if (hdlc->do_bitreverse == 0)
618 *dst++ = bitrev8(hdlc->cbin);
619 else
620 *dst++ = hdlc->cbin;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700621 hdlc->data_bits = 0;
622 len++;
623 dsize--;
624 }
625 }
626 *count -= slen;
627
628 return len;
629}
Linus Torvalds1da177e2005-04-16 15:20:36 -0700630EXPORT_SYMBOL(isdnhdlc_encode);