blob: b80e55ab89141387ee6ee54922000639aabbee7c [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)
5 * 2002 Wolfgang Mües <wolfgang@iksw-muees.de>
6 * 2001 Frode Isaksen <fisaksen@bewan.com>
7 * 2001 Kai Germaschewski <kai.germaschewski@gmx.de>
Linus Torvalds1da177e2005-04-16 15:20:36 -07008 *
Karsten Keil6bd4bcd2009-07-08 19:11:09 +02009 * This program is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; either version 2 of the License, or
12 * (at your option) any later version.
Linus Torvalds1da177e2005-04-16 15:20:36 -070013 *
Karsten Keil6bd4bcd2009-07-08 19:11:09 +020014 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
Linus Torvalds1da177e2005-04-16 15:20:36 -070018 *
Karsten Keil6bd4bcd2009-07-08 19:11:09 +020019 * You should have received a copy of the GNU General Public License
20 * along with this program; if not, write to the Free Software
21 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
Linus Torvalds1da177e2005-04-16 15:20:36 -070022 */
23
24#include <linux/module.h>
25#include <linux/init.h>
26#include <linux/crc-ccitt.h>
Karsten Keilcb3824b2009-07-08 14:21:12 +020027#include <linux/isdn/hdlc.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070028
29/*-------------------------------------------------------------------*/
30
Jan Engelhardt96de0e22007-10-19 23:21:04 +020031MODULE_AUTHOR("Wolfgang Mües <wolfgang@iksw-muees.de>, "
Linus Torvalds1da177e2005-04-16 15:20:36 -070032 "Frode Isaksen <fisaksen@bewan.com>, "
33 "Kai Germaschewski <kai.germaschewski@gmx.de>");
34MODULE_DESCRIPTION("General purpose ISDN HDLC decoder");
35MODULE_LICENSE("GPL");
36
37/*-------------------------------------------------------------------*/
38
Linus Torvalds1da177e2005-04-16 15:20:36 -070039enum {
Karsten Keil6bd4bcd2009-07-08 19:11:09 +020040 HDLC_FAST_IDLE, HDLC_GET_FLAG_B0, HDLC_GETFLAG_B1A6, HDLC_GETFLAG_B7,
41 HDLC_GET_DATA, HDLC_FAST_FLAG
Linus Torvalds1da177e2005-04-16 15:20:36 -070042};
43
44enum {
Karsten Keil6bd4bcd2009-07-08 19:11:09 +020045 HDLC_SEND_DATA, HDLC_SEND_CRC1, HDLC_SEND_FAST_FLAG,
46 HDLC_SEND_FIRST_FLAG, HDLC_SEND_CRC2, HDLC_SEND_CLOSING_FLAG,
47 HDLC_SEND_IDLE1, HDLC_SEND_FAST_IDLE, HDLC_SENDFLAG_B0,
48 HDLC_SENDFLAG_B1A6, HDLC_SENDFLAG_B7, STOPPED
Linus Torvalds1da177e2005-04-16 15:20:36 -070049};
50
Karsten Keil6bd4bcd2009-07-08 19:11:09 +020051void isdnhdlc_rcv_init(struct isdnhdlc_vars *hdlc, int do_adapt56)
Linus Torvalds1da177e2005-04-16 15:20:36 -070052{
Karsten Keil6bd4bcd2009-07-08 19:11:09 +020053 hdlc->bit_shift = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -070054 hdlc->hdlc_bits1 = 0;
55 hdlc->data_bits = 0;
56 hdlc->ffbit_shift = 0;
57 hdlc->data_received = 0;
58 hdlc->state = HDLC_GET_DATA;
59 hdlc->do_adapt56 = do_adapt56;
60 hdlc->dchannel = 0;
61 hdlc->crc = 0;
62 hdlc->cbin = 0;
63 hdlc->shift_reg = 0;
64 hdlc->ffvalue = 0;
65 hdlc->dstpos = 0;
66}
Karsten Keil6bd4bcd2009-07-08 19:11:09 +020067EXPORT_SYMBOL(isdnhdlc_out_init);
Linus Torvalds1da177e2005-04-16 15:20:36 -070068
Karsten Keil6bd4bcd2009-07-08 19:11:09 +020069void isdnhdlc_out_init(struct isdnhdlc_vars *hdlc, int is_d_channel,
70 int do_adapt56)
Linus Torvalds1da177e2005-04-16 15:20:36 -070071{
Karsten Keil6bd4bcd2009-07-08 19:11:09 +020072 hdlc->bit_shift = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -070073 hdlc->hdlc_bits1 = 0;
74 hdlc->data_bits = 0;
75 hdlc->ffbit_shift = 0;
76 hdlc->data_received = 0;
77 hdlc->do_closing = 0;
78 hdlc->ffvalue = 0;
79 if (is_d_channel) {
80 hdlc->dchannel = 1;
81 hdlc->state = HDLC_SEND_FIRST_FLAG;
82 } else {
83 hdlc->dchannel = 0;
84 hdlc->state = HDLC_SEND_FAST_FLAG;
85 hdlc->ffvalue = 0x7e;
86 }
87 hdlc->cbin = 0x7e;
88 hdlc->bit_shift = 0;
Karsten Keil6bd4bcd2009-07-08 19:11:09 +020089 if (do_adapt56) {
Linus Torvalds1da177e2005-04-16 15:20:36 -070090 hdlc->do_adapt56 = 1;
91 hdlc->data_bits = 0;
92 hdlc->state = HDLC_SENDFLAG_B0;
93 } else {
94 hdlc->do_adapt56 = 0;
95 hdlc->data_bits = 8;
96 }
97 hdlc->shift_reg = 0;
98}
Karsten Keil6bd4bcd2009-07-08 19:11:09 +020099EXPORT_SYMBOL(isdnhdlc_rcv_init);
100
101static int
102check_frame(struct isdnhdlc_vars *hdlc)
103{
104 int status;
105
106 if (hdlc->dstpos < 2) /* too small - framing error */
107 status = -HDLC_FRAMING_ERROR;
108 else if (hdlc->crc != 0xf0b8) /* crc error */
109 status = -HDLC_CRC_ERROR;
110 else {
111 /* remove CRC */
112 hdlc->dstpos -= 2;
113 /* good frame */
114 status = hdlc->dstpos;
115 }
116 return status;
117}
Linus Torvalds1da177e2005-04-16 15:20:36 -0700118
119/*
120 isdnhdlc_decode - decodes HDLC frames from a transparent bit stream.
121
122 The source buffer is scanned for valid HDLC frames looking for
123 flags (01111110) to indicate the start of a frame. If the start of
124 the frame is found, the bit stuffing is removed (0 after 5 1's).
125 When a new flag is found, the complete frame has been received
126 and the CRC is checked.
127 If a valid frame is found, the function returns the frame length
128 excluding the CRC with the bit HDLC_END_OF_FRAME set.
129 If the beginning of a valid frame is found, the function returns
130 the length.
131 If a framing error is found (too many 1s and not a flag) the function
132 returns the length with the bit HDLC_FRAMING_ERROR set.
133 If a CRC error is found the function returns the length with the
134 bit HDLC_CRC_ERROR set.
135 If the frame length exceeds the destination buffer size, the function
136 returns the length with the bit HDLC_LENGTH_ERROR set.
137
138 src - source buffer
139 slen - source buffer length
140 count - number of bytes removed (decoded) from the source buffer
141 dst _ destination buffer
142 dsize - destination buffer size
143 returns - number of decoded bytes in the destination buffer and status
144 flag.
145 */
Karsten Keil6bd4bcd2009-07-08 19:11:09 +0200146int isdnhdlc_decode(struct isdnhdlc_vars *hdlc, const u8 *src, int slen,
147 int *count, u8 *dst, int dsize)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700148{
Karsten Keil6bd4bcd2009-07-08 19:11:09 +0200149 int status = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700150
Karsten Keil6bd4bcd2009-07-08 19:11:09 +0200151 static const unsigned char fast_flag[] = {
152 0x00, 0x00, 0x00, 0x20, 0x30, 0x38, 0x3c, 0x3e, 0x3f
Linus Torvalds1da177e2005-04-16 15:20:36 -0700153 };
154
Karsten Keil6bd4bcd2009-07-08 19:11:09 +0200155 static const unsigned char fast_flag_value[] = {
156 0x00, 0x7e, 0xfc, 0xf9, 0xf3, 0xe7, 0xcf, 0x9f, 0x3f
Linus Torvalds1da177e2005-04-16 15:20:36 -0700157 };
158
Karsten Keil6bd4bcd2009-07-08 19:11:09 +0200159 static const unsigned char fast_abort[] = {
160 0x00, 0x00, 0x80, 0xc0, 0xe0, 0xf0, 0xf8, 0xfc, 0xfe, 0xff
Linus Torvalds1da177e2005-04-16 15:20:36 -0700161 };
162
Karsten Keil6bd4bcd2009-07-08 19:11:09 +0200163#define handle_fast_flag(h) \
164 do {\
165 if (h->cbin == fast_flag[h->bit_shift]) {\
166 h->ffvalue = fast_flag_value[h->bit_shift];\
167 h->state = HDLC_FAST_FLAG;\
168 h->ffbit_shift = h->bit_shift;\
169 h->bit_shift = 1;\
170 } else {\
171 h->state = HDLC_GET_DATA;\
172 h->data_received = 0;\
173 } \
174 } while (0)
175
176#define handle_abort(h) \
177 do {\
178 h->shift_reg = fast_abort[h->ffbit_shift - 1];\
179 h->hdlc_bits1 = h->ffbit_shift - 2;\
180 if (h->hdlc_bits1 < 0)\
181 h->hdlc_bits1 = 0;\
182 h->data_bits = h->ffbit_shift - 1;\
183 h->state = HDLC_GET_DATA;\
184 h->data_received = 0;\
185 } while (0)
186
Linus Torvalds1da177e2005-04-16 15:20:36 -0700187 *count = slen;
188
Karsten Keil6bd4bcd2009-07-08 19:11:09 +0200189 while (slen > 0) {
190 if (hdlc->bit_shift == 0) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700191 hdlc->cbin = *src++;
192 slen--;
193 hdlc->bit_shift = 8;
Karsten Keil6bd4bcd2009-07-08 19:11:09 +0200194 if (hdlc->do_adapt56)
195 hdlc->bit_shift--;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700196 }
197
Karsten Keil6bd4bcd2009-07-08 19:11:09 +0200198 switch (hdlc->state) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700199 case STOPPED:
200 return 0;
201 case HDLC_FAST_IDLE:
Karsten Keil6bd4bcd2009-07-08 19:11:09 +0200202 if (hdlc->cbin == 0xff) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700203 hdlc->bit_shift = 0;
204 break;
205 }
206 hdlc->state = HDLC_GET_FLAG_B0;
207 hdlc->hdlc_bits1 = 0;
208 hdlc->bit_shift = 8;
209 break;
210 case HDLC_GET_FLAG_B0:
Karsten Keil6bd4bcd2009-07-08 19:11:09 +0200211 if (!(hdlc->cbin & 0x80)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700212 hdlc->state = HDLC_GETFLAG_B1A6;
213 hdlc->hdlc_bits1 = 0;
214 } else {
Karsten Keil6bd4bcd2009-07-08 19:11:09 +0200215 if ((!hdlc->do_adapt56) &&
216 (++hdlc->hdlc_bits1 >= 8) &&
217 (hdlc->bit_shift == 1))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700218 hdlc->state = HDLC_FAST_IDLE;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700219 }
Karsten Keil6bd4bcd2009-07-08 19:11:09 +0200220 hdlc->cbin <<= 1;
221 hdlc->bit_shift--;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700222 break;
223 case HDLC_GETFLAG_B1A6:
Karsten Keil6bd4bcd2009-07-08 19:11:09 +0200224 if (hdlc->cbin & 0x80) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700225 hdlc->hdlc_bits1++;
Karsten Keil6bd4bcd2009-07-08 19:11:09 +0200226 if (hdlc->hdlc_bits1 == 6)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700227 hdlc->state = HDLC_GETFLAG_B7;
Karsten Keil6bd4bcd2009-07-08 19:11:09 +0200228 } else
Linus Torvalds1da177e2005-04-16 15:20:36 -0700229 hdlc->hdlc_bits1 = 0;
Karsten Keil6bd4bcd2009-07-08 19:11:09 +0200230 hdlc->cbin <<= 1;
231 hdlc->bit_shift--;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700232 break;
233 case HDLC_GETFLAG_B7:
Karsten Keil6bd4bcd2009-07-08 19:11:09 +0200234 if (hdlc->cbin & 0x80) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700235 hdlc->state = HDLC_GET_FLAG_B0;
236 } else {
237 hdlc->state = HDLC_GET_DATA;
238 hdlc->crc = 0xffff;
239 hdlc->shift_reg = 0;
240 hdlc->hdlc_bits1 = 0;
241 hdlc->data_bits = 0;
242 hdlc->data_received = 0;
243 }
Karsten Keil6bd4bcd2009-07-08 19:11:09 +0200244 hdlc->cbin <<= 1;
245 hdlc->bit_shift--;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700246 break;
247 case HDLC_GET_DATA:
Karsten Keil6bd4bcd2009-07-08 19:11:09 +0200248 if (hdlc->cbin & 0x80) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700249 hdlc->hdlc_bits1++;
Karsten Keil6bd4bcd2009-07-08 19:11:09 +0200250 switch (hdlc->hdlc_bits1) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700251 case 6:
252 break;
253 case 7:
Karsten Keil6bd4bcd2009-07-08 19:11:09 +0200254 if (hdlc->data_received)
255 /* bad frame */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700256 status = -HDLC_FRAMING_ERROR;
Karsten Keil6bd4bcd2009-07-08 19:11:09 +0200257 if (!hdlc->do_adapt56) {
258 if (hdlc->cbin == fast_abort
259 [hdlc->bit_shift + 1]) {
260 hdlc->state =
261 HDLC_FAST_IDLE;
262 hdlc->bit_shift = 1;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700263 break;
264 }
Karsten Keil6bd4bcd2009-07-08 19:11:09 +0200265 } else
Linus Torvalds1da177e2005-04-16 15:20:36 -0700266 hdlc->state = HDLC_GET_FLAG_B0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700267 break;
268 default:
Karsten Keil6bd4bcd2009-07-08 19:11:09 +0200269 hdlc->shift_reg >>= 1;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700270 hdlc->shift_reg |= 0x80;
271 hdlc->data_bits++;
272 break;
273 }
274 } else {
Karsten Keil6bd4bcd2009-07-08 19:11:09 +0200275 switch (hdlc->hdlc_bits1) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700276 case 5:
277 break;
278 case 6:
Karsten Keil6bd4bcd2009-07-08 19:11:09 +0200279 if (hdlc->data_received)
280 status = check_frame(hdlc);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700281 hdlc->crc = 0xffff;
282 hdlc->shift_reg = 0;
283 hdlc->data_bits = 0;
Karsten Keil6bd4bcd2009-07-08 19:11:09 +0200284 if (!hdlc->do_adapt56)
285 handle_fast_flag(hdlc);
286 else {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700287 hdlc->state = HDLC_GET_DATA;
288 hdlc->data_received = 0;
289 }
290 break;
291 default:
Karsten Keil6bd4bcd2009-07-08 19:11:09 +0200292 hdlc->shift_reg >>= 1;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700293 hdlc->data_bits++;
294 break;
295 }
296 hdlc->hdlc_bits1 = 0;
297 }
298 if (status) {
299 hdlc->dstpos = 0;
300 *count -= slen;
301 hdlc->cbin <<= 1;
302 hdlc->bit_shift--;
303 return status;
304 }
Karsten Keil6bd4bcd2009-07-08 19:11:09 +0200305 if (hdlc->data_bits == 8) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700306 hdlc->data_bits = 0;
307 hdlc->data_received = 1;
Karsten Keil6bd4bcd2009-07-08 19:11:09 +0200308 hdlc->crc = crc_ccitt_byte(hdlc->crc,
309 hdlc->shift_reg);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700310
Karsten Keil6bd4bcd2009-07-08 19:11:09 +0200311 /* good byte received */
312 if (hdlc->dstpos < dsize)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700313 dst[hdlc->dstpos++] = hdlc->shift_reg;
Karsten Keil6bd4bcd2009-07-08 19:11:09 +0200314 else {
315 /* frame too long */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700316 status = -HDLC_LENGTH_ERROR;
317 hdlc->dstpos = 0;
318 }
319 }
320 hdlc->cbin <<= 1;
321 hdlc->bit_shift--;
322 break;
323 case HDLC_FAST_FLAG:
Karsten Keil6bd4bcd2009-07-08 19:11:09 +0200324 if (hdlc->cbin == hdlc->ffvalue) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700325 hdlc->bit_shift = 0;
326 break;
327 } else {
Karsten Keil6bd4bcd2009-07-08 19:11:09 +0200328 if (hdlc->cbin == 0xff) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700329 hdlc->state = HDLC_FAST_IDLE;
Karsten Keil6bd4bcd2009-07-08 19:11:09 +0200330 hdlc->bit_shift = 0;
331 } else if (hdlc->ffbit_shift == 8) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700332 hdlc->state = HDLC_GETFLAG_B7;
333 break;
Karsten Keil6bd4bcd2009-07-08 19:11:09 +0200334 } else
335 handle_abort(hdlc);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700336 }
337 break;
338 default:
339 break;
340 }
341 }
342 *count -= slen;
343 return 0;
344}
Karsten Keil6bd4bcd2009-07-08 19:11:09 +0200345EXPORT_SYMBOL(isdnhdlc_decode);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700346/*
347 isdnhdlc_encode - encodes HDLC frames to a transparent bit stream.
348
349 The bit stream starts with a beginning flag (01111110). After
350 that each byte is added to the bit stream with bit stuffing added
351 (0 after 5 1's).
352 When the last byte has been removed from the source buffer, the
353 CRC (2 bytes is added) and the frame terminates with the ending flag.
354 For the dchannel, the idle character (all 1's) is also added at the end.
355 If this function is called with empty source buffer (slen=0), flags or
356 idle character will be generated.
357
358 src - source buffer
359 slen - source buffer length
360 count - number of bytes removed (encoded) from source buffer
361 dst _ destination buffer
362 dsize - destination buffer size
363 returns - number of encoded bytes in the destination buffer
364*/
Karsten Keil6bd4bcd2009-07-08 19:11:09 +0200365int isdnhdlc_encode(struct isdnhdlc_vars *hdlc, const u8 *src, u16 slen,
366 int *count, u8 *dst, int dsize)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700367{
368 static const unsigned char xfast_flag_value[] = {
Karsten Keil6bd4bcd2009-07-08 19:11:09 +0200369 0x7e, 0x3f, 0x9f, 0xcf, 0xe7, 0xf3, 0xf9, 0xfc, 0x7e
Linus Torvalds1da177e2005-04-16 15:20:36 -0700370 };
371
372 int len = 0;
373
374 *count = slen;
375
376 while (dsize > 0) {
Karsten Keil6bd4bcd2009-07-08 19:11:09 +0200377 if (hdlc->bit_shift == 0) {
378 if (slen && !hdlc->do_closing) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700379 hdlc->shift_reg = *src++;
380 slen--;
381 if (slen == 0)
Karsten Keil6bd4bcd2009-07-08 19:11:09 +0200382 /* closing sequence, CRC + flag(s) */
383 hdlc->do_closing = 1;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700384 hdlc->bit_shift = 8;
385 } else {
Karsten Keil6bd4bcd2009-07-08 19:11:09 +0200386 if (hdlc->state == HDLC_SEND_DATA) {
387 if (hdlc->data_received) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700388 hdlc->state = HDLC_SEND_CRC1;
389 hdlc->crc ^= 0xffff;
390 hdlc->bit_shift = 8;
Karsten Keil6bd4bcd2009-07-08 19:11:09 +0200391 hdlc->shift_reg =
392 hdlc->crc & 0xff;
393 } else if (!hdlc->do_adapt56)
394 hdlc->state =
395 HDLC_SEND_FAST_FLAG;
396 else
397 hdlc->state =
398 HDLC_SENDFLAG_B0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700399 }
400
401 }
402 }
403
Karsten Keil6bd4bcd2009-07-08 19:11:09 +0200404 switch (hdlc->state) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700405 case STOPPED:
406 while (dsize--)
407 *dst++ = 0xff;
408
409 return dsize;
410 case HDLC_SEND_FAST_FLAG:
411 hdlc->do_closing = 0;
Karsten Keil6bd4bcd2009-07-08 19:11:09 +0200412 if (slen == 0) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700413 *dst++ = hdlc->ffvalue;
414 len++;
415 dsize--;
416 break;
417 }
Karsten Keil6bd4bcd2009-07-08 19:11:09 +0200418 if (hdlc->bit_shift == 8) {
419 hdlc->cbin = hdlc->ffvalue >>
420 (8 - hdlc->data_bits);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700421 hdlc->state = HDLC_SEND_DATA;
422 hdlc->crc = 0xffff;
423 hdlc->hdlc_bits1 = 0;
424 hdlc->data_received = 1;
425 }
426 break;
427 case HDLC_SENDFLAG_B0:
428 hdlc->do_closing = 0;
429 hdlc->cbin <<= 1;
430 hdlc->data_bits++;
431 hdlc->hdlc_bits1 = 0;
432 hdlc->state = HDLC_SENDFLAG_B1A6;
433 break;
434 case HDLC_SENDFLAG_B1A6:
435 hdlc->cbin <<= 1;
436 hdlc->data_bits++;
437 hdlc->cbin++;
Karsten Keil6bd4bcd2009-07-08 19:11:09 +0200438 if (++hdlc->hdlc_bits1 == 6)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700439 hdlc->state = HDLC_SENDFLAG_B7;
440 break;
441 case HDLC_SENDFLAG_B7:
442 hdlc->cbin <<= 1;
443 hdlc->data_bits++;
Karsten Keil6bd4bcd2009-07-08 19:11:09 +0200444 if (slen == 0) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700445 hdlc->state = HDLC_SENDFLAG_B0;
446 break;
447 }
Karsten Keil6bd4bcd2009-07-08 19:11:09 +0200448 if (hdlc->bit_shift == 8) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700449 hdlc->state = HDLC_SEND_DATA;
450 hdlc->crc = 0xffff;
451 hdlc->hdlc_bits1 = 0;
452 hdlc->data_received = 1;
453 }
454 break;
455 case HDLC_SEND_FIRST_FLAG:
456 hdlc->data_received = 1;
Karsten Keil6bd4bcd2009-07-08 19:11:09 +0200457 if (hdlc->data_bits == 8) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700458 hdlc->state = HDLC_SEND_DATA;
459 hdlc->crc = 0xffff;
460 hdlc->hdlc_bits1 = 0;
461 break;
462 }
463 hdlc->cbin <<= 1;
464 hdlc->data_bits++;
Karsten Keil6bd4bcd2009-07-08 19:11:09 +0200465 if (hdlc->shift_reg & 0x01)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700466 hdlc->cbin++;
467 hdlc->shift_reg >>= 1;
468 hdlc->bit_shift--;
Karsten Keil6bd4bcd2009-07-08 19:11:09 +0200469 if (hdlc->bit_shift == 0) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700470 hdlc->state = HDLC_SEND_DATA;
471 hdlc->crc = 0xffff;
472 hdlc->hdlc_bits1 = 0;
473 }
474 break;
475 case HDLC_SEND_DATA:
476 hdlc->cbin <<= 1;
477 hdlc->data_bits++;
Karsten Keil6bd4bcd2009-07-08 19:11:09 +0200478 if (hdlc->hdlc_bits1 == 5) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700479 hdlc->hdlc_bits1 = 0;
480 break;
481 }
Karsten Keil6bd4bcd2009-07-08 19:11:09 +0200482 if (hdlc->bit_shift == 8)
483 hdlc->crc = crc_ccitt_byte(hdlc->crc,
484 hdlc->shift_reg);
485 if (hdlc->shift_reg & 0x01) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700486 hdlc->hdlc_bits1++;
487 hdlc->cbin++;
488 hdlc->shift_reg >>= 1;
489 hdlc->bit_shift--;
490 } else {
491 hdlc->hdlc_bits1 = 0;
492 hdlc->shift_reg >>= 1;
493 hdlc->bit_shift--;
494 }
495 break;
496 case HDLC_SEND_CRC1:
497 hdlc->cbin <<= 1;
498 hdlc->data_bits++;
Karsten Keil6bd4bcd2009-07-08 19:11:09 +0200499 if (hdlc->hdlc_bits1 == 5) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700500 hdlc->hdlc_bits1 = 0;
501 break;
502 }
Karsten Keil6bd4bcd2009-07-08 19:11:09 +0200503 if (hdlc->shift_reg & 0x01) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700504 hdlc->hdlc_bits1++;
505 hdlc->cbin++;
506 hdlc->shift_reg >>= 1;
507 hdlc->bit_shift--;
508 } else {
509 hdlc->hdlc_bits1 = 0;
510 hdlc->shift_reg >>= 1;
511 hdlc->bit_shift--;
512 }
Karsten Keil6bd4bcd2009-07-08 19:11:09 +0200513 if (hdlc->bit_shift == 0) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700514 hdlc->shift_reg = (hdlc->crc >> 8);
515 hdlc->state = HDLC_SEND_CRC2;
516 hdlc->bit_shift = 8;
517 }
518 break;
519 case HDLC_SEND_CRC2:
520 hdlc->cbin <<= 1;
521 hdlc->data_bits++;
Karsten Keil6bd4bcd2009-07-08 19:11:09 +0200522 if (hdlc->hdlc_bits1 == 5) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700523 hdlc->hdlc_bits1 = 0;
524 break;
525 }
Karsten Keil6bd4bcd2009-07-08 19:11:09 +0200526 if (hdlc->shift_reg & 0x01) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700527 hdlc->hdlc_bits1++;
528 hdlc->cbin++;
529 hdlc->shift_reg >>= 1;
530 hdlc->bit_shift--;
531 } else {
532 hdlc->hdlc_bits1 = 0;
533 hdlc->shift_reg >>= 1;
534 hdlc->bit_shift--;
535 }
Karsten Keil6bd4bcd2009-07-08 19:11:09 +0200536 if (hdlc->bit_shift == 0) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700537 hdlc->shift_reg = 0x7e;
538 hdlc->state = HDLC_SEND_CLOSING_FLAG;
539 hdlc->bit_shift = 8;
540 }
541 break;
542 case HDLC_SEND_CLOSING_FLAG:
543 hdlc->cbin <<= 1;
544 hdlc->data_bits++;
Karsten Keil6bd4bcd2009-07-08 19:11:09 +0200545 if (hdlc->hdlc_bits1 == 5) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700546 hdlc->hdlc_bits1 = 0;
547 break;
548 }
Karsten Keil6bd4bcd2009-07-08 19:11:09 +0200549 if (hdlc->shift_reg & 0x01)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700550 hdlc->cbin++;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700551 hdlc->shift_reg >>= 1;
552 hdlc->bit_shift--;
Karsten Keil6bd4bcd2009-07-08 19:11:09 +0200553 if (hdlc->bit_shift == 0) {
554 hdlc->ffvalue =
555 xfast_flag_value[hdlc->data_bits];
556 if (hdlc->dchannel) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700557 hdlc->ffvalue = 0x7e;
558 hdlc->state = HDLC_SEND_IDLE1;
559 hdlc->bit_shift = 8-hdlc->data_bits;
Karsten Keil6bd4bcd2009-07-08 19:11:09 +0200560 if (hdlc->bit_shift == 0)
561 hdlc->state =
562 HDLC_SEND_FAST_IDLE;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700563 } else {
Karsten Keil6bd4bcd2009-07-08 19:11:09 +0200564 if (!hdlc->do_adapt56) {
565 hdlc->state =
566 HDLC_SEND_FAST_FLAG;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700567 hdlc->data_received = 0;
568 } else {
569 hdlc->state = HDLC_SENDFLAG_B0;
570 hdlc->data_received = 0;
571 }
Karsten Keil6bd4bcd2009-07-08 19:11:09 +0200572 /* Finished this frame, send flags */
573 if (dsize > 1)
574 dsize = 1;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700575 }
576 }
577 break;
578 case HDLC_SEND_IDLE1:
579 hdlc->do_closing = 0;
580 hdlc->cbin <<= 1;
581 hdlc->cbin++;
582 hdlc->data_bits++;
583 hdlc->bit_shift--;
Karsten Keil6bd4bcd2009-07-08 19:11:09 +0200584 if (hdlc->bit_shift == 0) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700585 hdlc->state = HDLC_SEND_FAST_IDLE;
586 hdlc->bit_shift = 0;
587 }
588 break;
589 case HDLC_SEND_FAST_IDLE:
590 hdlc->do_closing = 0;
591 hdlc->cbin = 0xff;
592 hdlc->data_bits = 8;
Karsten Keil6bd4bcd2009-07-08 19:11:09 +0200593 if (hdlc->bit_shift == 8) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700594 hdlc->cbin = 0x7e;
595 hdlc->state = HDLC_SEND_FIRST_FLAG;
596 } else {
597 *dst++ = hdlc->cbin;
Karsten Keil6bd4bcd2009-07-08 19:11:09 +0200598 hdlc->bit_shift = 0;
599 hdlc->data_bits = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700600 len++;
601 dsize = 0;
602 }
603 break;
604 default:
605 break;
606 }
Karsten Keil6bd4bcd2009-07-08 19:11:09 +0200607 if (hdlc->do_adapt56) {
608 if (hdlc->data_bits == 7) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700609 hdlc->cbin <<= 1;
610 hdlc->cbin++;
611 hdlc->data_bits++;
612 }
613 }
Karsten Keil6bd4bcd2009-07-08 19:11:09 +0200614 if (hdlc->data_bits == 8) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700615 *dst++ = hdlc->cbin;
616 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);