blob: df345ce73f48db80a24ec0f4002b88190187f4c0 [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,
50 HDLC_SENDFLAG_B1A6, HDLC_SENDFLAG_B7, STOPPED
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
365 while (dsize > 0) {
Karsten Keil6bd4bcd2009-07-08 19:11:09 +0200366 if (hdlc->bit_shift == 0) {
367 if (slen && !hdlc->do_closing) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700368 hdlc->shift_reg = *src++;
369 slen--;
370 if (slen == 0)
Karsten Keil6bd4bcd2009-07-08 19:11:09 +0200371 /* closing sequence, CRC + flag(s) */
372 hdlc->do_closing = 1;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700373 hdlc->bit_shift = 8;
374 } else {
Karsten Keil6bd4bcd2009-07-08 19:11:09 +0200375 if (hdlc->state == HDLC_SEND_DATA) {
376 if (hdlc->data_received) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700377 hdlc->state = HDLC_SEND_CRC1;
378 hdlc->crc ^= 0xffff;
379 hdlc->bit_shift = 8;
Karsten Keil6bd4bcd2009-07-08 19:11:09 +0200380 hdlc->shift_reg =
381 hdlc->crc & 0xff;
382 } else if (!hdlc->do_adapt56)
383 hdlc->state =
384 HDLC_SEND_FAST_FLAG;
385 else
386 hdlc->state =
387 HDLC_SENDFLAG_B0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700388 }
389
390 }
391 }
392
Karsten Keil6bd4bcd2009-07-08 19:11:09 +0200393 switch (hdlc->state) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700394 case STOPPED:
395 while (dsize--)
396 *dst++ = 0xff;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700397 return dsize;
398 case HDLC_SEND_FAST_FLAG:
399 hdlc->do_closing = 0;
Karsten Keil6bd4bcd2009-07-08 19:11:09 +0200400 if (slen == 0) {
Karsten Keilc38fc3b2009-07-08 20:31:42 +0200401 /* the code is for bitreverse streams */
402 if (hdlc->do_bitreverse == 0)
403 *dst++ = bitrev8(hdlc->ffvalue);
404 else
405 *dst++ = hdlc->ffvalue;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700406 len++;
407 dsize--;
408 break;
409 }
Karsten Keil6bd4bcd2009-07-08 19:11:09 +0200410 if (hdlc->bit_shift == 8) {
411 hdlc->cbin = hdlc->ffvalue >>
412 (8 - hdlc->data_bits);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700413 hdlc->state = HDLC_SEND_DATA;
414 hdlc->crc = 0xffff;
415 hdlc->hdlc_bits1 = 0;
416 hdlc->data_received = 1;
417 }
418 break;
419 case HDLC_SENDFLAG_B0:
420 hdlc->do_closing = 0;
421 hdlc->cbin <<= 1;
422 hdlc->data_bits++;
423 hdlc->hdlc_bits1 = 0;
424 hdlc->state = HDLC_SENDFLAG_B1A6;
425 break;
426 case HDLC_SENDFLAG_B1A6:
427 hdlc->cbin <<= 1;
428 hdlc->data_bits++;
429 hdlc->cbin++;
Karsten Keil6bd4bcd2009-07-08 19:11:09 +0200430 if (++hdlc->hdlc_bits1 == 6)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700431 hdlc->state = HDLC_SENDFLAG_B7;
432 break;
433 case HDLC_SENDFLAG_B7:
434 hdlc->cbin <<= 1;
435 hdlc->data_bits++;
Karsten Keil6bd4bcd2009-07-08 19:11:09 +0200436 if (slen == 0) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700437 hdlc->state = HDLC_SENDFLAG_B0;
438 break;
439 }
Karsten Keil6bd4bcd2009-07-08 19:11:09 +0200440 if (hdlc->bit_shift == 8) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700441 hdlc->state = HDLC_SEND_DATA;
442 hdlc->crc = 0xffff;
443 hdlc->hdlc_bits1 = 0;
444 hdlc->data_received = 1;
445 }
446 break;
447 case HDLC_SEND_FIRST_FLAG:
448 hdlc->data_received = 1;
Karsten Keil6bd4bcd2009-07-08 19:11:09 +0200449 if (hdlc->data_bits == 8) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700450 hdlc->state = HDLC_SEND_DATA;
451 hdlc->crc = 0xffff;
452 hdlc->hdlc_bits1 = 0;
453 break;
454 }
455 hdlc->cbin <<= 1;
456 hdlc->data_bits++;
Karsten Keil6bd4bcd2009-07-08 19:11:09 +0200457 if (hdlc->shift_reg & 0x01)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700458 hdlc->cbin++;
459 hdlc->shift_reg >>= 1;
460 hdlc->bit_shift--;
Karsten Keil6bd4bcd2009-07-08 19:11:09 +0200461 if (hdlc->bit_shift == 0) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700462 hdlc->state = HDLC_SEND_DATA;
463 hdlc->crc = 0xffff;
464 hdlc->hdlc_bits1 = 0;
465 }
466 break;
467 case HDLC_SEND_DATA:
468 hdlc->cbin <<= 1;
469 hdlc->data_bits++;
Karsten Keil6bd4bcd2009-07-08 19:11:09 +0200470 if (hdlc->hdlc_bits1 == 5) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700471 hdlc->hdlc_bits1 = 0;
472 break;
473 }
Karsten Keil6bd4bcd2009-07-08 19:11:09 +0200474 if (hdlc->bit_shift == 8)
475 hdlc->crc = crc_ccitt_byte(hdlc->crc,
476 hdlc->shift_reg);
477 if (hdlc->shift_reg & 0x01) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700478 hdlc->hdlc_bits1++;
479 hdlc->cbin++;
480 hdlc->shift_reg >>= 1;
481 hdlc->bit_shift--;
482 } else {
483 hdlc->hdlc_bits1 = 0;
484 hdlc->shift_reg >>= 1;
485 hdlc->bit_shift--;
486 }
487 break;
488 case HDLC_SEND_CRC1:
489 hdlc->cbin <<= 1;
490 hdlc->data_bits++;
Karsten Keil6bd4bcd2009-07-08 19:11:09 +0200491 if (hdlc->hdlc_bits1 == 5) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700492 hdlc->hdlc_bits1 = 0;
493 break;
494 }
Karsten Keil6bd4bcd2009-07-08 19:11:09 +0200495 if (hdlc->shift_reg & 0x01) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700496 hdlc->hdlc_bits1++;
497 hdlc->cbin++;
498 hdlc->shift_reg >>= 1;
499 hdlc->bit_shift--;
500 } else {
501 hdlc->hdlc_bits1 = 0;
502 hdlc->shift_reg >>= 1;
503 hdlc->bit_shift--;
504 }
Karsten Keil6bd4bcd2009-07-08 19:11:09 +0200505 if (hdlc->bit_shift == 0) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700506 hdlc->shift_reg = (hdlc->crc >> 8);
507 hdlc->state = HDLC_SEND_CRC2;
508 hdlc->bit_shift = 8;
509 }
510 break;
511 case HDLC_SEND_CRC2:
512 hdlc->cbin <<= 1;
513 hdlc->data_bits++;
Karsten Keil6bd4bcd2009-07-08 19:11:09 +0200514 if (hdlc->hdlc_bits1 == 5) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700515 hdlc->hdlc_bits1 = 0;
516 break;
517 }
Karsten Keil6bd4bcd2009-07-08 19:11:09 +0200518 if (hdlc->shift_reg & 0x01) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700519 hdlc->hdlc_bits1++;
520 hdlc->cbin++;
521 hdlc->shift_reg >>= 1;
522 hdlc->bit_shift--;
523 } else {
524 hdlc->hdlc_bits1 = 0;
525 hdlc->shift_reg >>= 1;
526 hdlc->bit_shift--;
527 }
Karsten Keil6bd4bcd2009-07-08 19:11:09 +0200528 if (hdlc->bit_shift == 0) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700529 hdlc->shift_reg = 0x7e;
530 hdlc->state = HDLC_SEND_CLOSING_FLAG;
531 hdlc->bit_shift = 8;
532 }
533 break;
534 case HDLC_SEND_CLOSING_FLAG:
535 hdlc->cbin <<= 1;
536 hdlc->data_bits++;
Karsten Keil6bd4bcd2009-07-08 19:11:09 +0200537 if (hdlc->hdlc_bits1 == 5) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700538 hdlc->hdlc_bits1 = 0;
539 break;
540 }
Karsten Keil6bd4bcd2009-07-08 19:11:09 +0200541 if (hdlc->shift_reg & 0x01)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700542 hdlc->cbin++;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700543 hdlc->shift_reg >>= 1;
544 hdlc->bit_shift--;
Karsten Keil6bd4bcd2009-07-08 19:11:09 +0200545 if (hdlc->bit_shift == 0) {
546 hdlc->ffvalue =
547 xfast_flag_value[hdlc->data_bits];
548 if (hdlc->dchannel) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700549 hdlc->ffvalue = 0x7e;
550 hdlc->state = HDLC_SEND_IDLE1;
551 hdlc->bit_shift = 8-hdlc->data_bits;
Karsten Keil6bd4bcd2009-07-08 19:11:09 +0200552 if (hdlc->bit_shift == 0)
553 hdlc->state =
554 HDLC_SEND_FAST_IDLE;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700555 } else {
Karsten Keil6bd4bcd2009-07-08 19:11:09 +0200556 if (!hdlc->do_adapt56) {
557 hdlc->state =
558 HDLC_SEND_FAST_FLAG;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700559 hdlc->data_received = 0;
560 } else {
561 hdlc->state = HDLC_SENDFLAG_B0;
562 hdlc->data_received = 0;
563 }
Karsten Keil6bd4bcd2009-07-08 19:11:09 +0200564 /* Finished this frame, send flags */
565 if (dsize > 1)
566 dsize = 1;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700567 }
568 }
569 break;
570 case HDLC_SEND_IDLE1:
571 hdlc->do_closing = 0;
572 hdlc->cbin <<= 1;
573 hdlc->cbin++;
574 hdlc->data_bits++;
575 hdlc->bit_shift--;
Karsten Keil6bd4bcd2009-07-08 19:11:09 +0200576 if (hdlc->bit_shift == 0) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700577 hdlc->state = HDLC_SEND_FAST_IDLE;
578 hdlc->bit_shift = 0;
579 }
580 break;
581 case HDLC_SEND_FAST_IDLE:
582 hdlc->do_closing = 0;
583 hdlc->cbin = 0xff;
584 hdlc->data_bits = 8;
Karsten Keil6bd4bcd2009-07-08 19:11:09 +0200585 if (hdlc->bit_shift == 8) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700586 hdlc->cbin = 0x7e;
587 hdlc->state = HDLC_SEND_FIRST_FLAG;
588 } else {
Karsten Keilc38fc3b2009-07-08 20:31:42 +0200589 /* the code is for bitreverse streams */
590 if (hdlc->do_bitreverse == 0)
591 *dst++ = bitrev8(hdlc->cbin);
592 else
593 *dst++ = hdlc->cbin;
Karsten Keil6bd4bcd2009-07-08 19:11:09 +0200594 hdlc->bit_shift = 0;
595 hdlc->data_bits = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700596 len++;
597 dsize = 0;
598 }
599 break;
600 default:
601 break;
602 }
Karsten Keil6bd4bcd2009-07-08 19:11:09 +0200603 if (hdlc->do_adapt56) {
604 if (hdlc->data_bits == 7) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700605 hdlc->cbin <<= 1;
606 hdlc->cbin++;
607 hdlc->data_bits++;
608 }
609 }
Karsten Keil6bd4bcd2009-07-08 19:11:09 +0200610 if (hdlc->data_bits == 8) {
Karsten Keilc38fc3b2009-07-08 20:31:42 +0200611 /* the code is for bitreverse streams */
612 if (hdlc->do_bitreverse == 0)
613 *dst++ = bitrev8(hdlc->cbin);
614 else
615 *dst++ = hdlc->cbin;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700616 hdlc->data_bits = 0;
617 len++;
618 dsize--;
619 }
620 }
621 *count -= slen;
622
623 return len;
624}
Linus Torvalds1da177e2005-04-16 15:20:36 -0700625EXPORT_SYMBOL(isdnhdlc_encode);