blob: f2f108fcec4d8a1e1a41532acedc856d2048d1a1 [file] [log] [blame]
Hansjoerg Lipp07dc1f92006-03-26 01:38:37 -08001/*
2 * Common data handling layer for ser_gigaset and usb_gigaset
3 *
4 * Copyright (c) 2005 by Tilman Schmidt <tilman@imap.cc>,
5 * Hansjoerg Lipp <hjlipp@web.de>,
Tilman Schmidt70440cf2006-04-10 22:55:14 -07006 * Stefan Eilers.
Hansjoerg Lipp07dc1f92006-03-26 01:38:37 -08007 *
8 * =====================================================================
9 * This program is free software; you can redistribute it and/or
10 * modify it under the terms of the GNU General Public License as
11 * published by the Free Software Foundation; either version 2 of
12 * the License, or (at your option) any later version.
13 * =====================================================================
Hansjoerg Lipp07dc1f92006-03-26 01:38:37 -080014 */
15
Tilman Schmidt2869b232007-02-12 00:52:34 -080016/* not set by Kbuild when building both ser_gigaset and usb_gigaset */
17#ifndef KBUILD_MODNAME
18#define KBUILD_MODNAME "asy_gigaset"
19#endif
20
Hansjoerg Lipp07dc1f92006-03-26 01:38:37 -080021#include "gigaset.h"
22#include <linux/crc-ccitt.h>
Akinobu Mita17b3cff2006-12-08 02:36:30 -080023#include <linux/bitrev.h>
Hansjoerg Lipp07dc1f92006-03-26 01:38:37 -080024
25//#define GIG_M10x_STUFF_VOICE_DATA
26
27/* check if byte must be stuffed/escaped
28 * I'm not sure which data should be encoded.
29 * Therefore I will go the hard way and decode every value
30 * less than 0x20, the flag sequence and the control escape char.
31 */
32static inline int muststuff(unsigned char c)
33{
34 if (c < PPP_TRANS) return 1;
35 if (c == PPP_FLAG) return 1;
36 if (c == PPP_ESCAPE) return 1;
37 /* other possible candidates: */
38 /* 0x91: XON with parity set */
39 /* 0x93: XOFF with parity set */
40 return 0;
41}
42
43/* == data input =========================================================== */
44
45/* process a block of received bytes in command mode (modem response)
46 * Return value:
47 * number of processed bytes
48 */
49static inline int cmd_loop(unsigned char c, unsigned char *src, int numbytes,
Tilman Schmidt784d5852006-04-10 22:55:04 -070050 struct inbuf_t *inbuf)
Hansjoerg Lipp07dc1f92006-03-26 01:38:37 -080051{
52 struct cardstate *cs = inbuf->cs;
53 unsigned cbytes = cs->cbytes;
54 int inputstate = inbuf->inputstate;
55 int startbytes = numbytes;
56
57 for (;;) {
58 cs->respdata[cbytes] = c;
59 if (c == 10 || c == 13) {
Tilman Schmidt784d5852006-04-10 22:55:04 -070060 gig_dbg(DEBUG_TRANSCMD, "%s: End of Command (%d Bytes)",
61 __func__, cbytes);
Hansjoerg Lipp07dc1f92006-03-26 01:38:37 -080062 cs->cbytes = cbytes;
Tilman Schmidt917f5082006-04-10 22:55:00 -070063 gigaset_handle_modem_response(cs); /* can change
64 cs->dle */
Hansjoerg Lipp07dc1f92006-03-26 01:38:37 -080065 cbytes = 0;
66
67 if (cs->dle &&
68 !(inputstate & INS_DLE_command)) {
69 inputstate &= ~INS_command;
70 break;
71 }
72 } else {
73 /* advance in line buffer, checking for overflow */
74 if (cbytes < MAX_RESP_SIZE - 1)
75 cbytes++;
76 else
Tilman Schmidt784d5852006-04-10 22:55:04 -070077 dev_warn(cs->dev, "response too large\n");
Hansjoerg Lipp07dc1f92006-03-26 01:38:37 -080078 }
79
80 if (!numbytes)
81 break;
82 c = *src++;
83 --numbytes;
84 if (c == DLE_FLAG &&
85 (cs->dle || inputstate & INS_DLE_command)) {
86 inputstate |= INS_DLE_char;
87 break;
88 }
89 }
90
91 cs->cbytes = cbytes;
92 inbuf->inputstate = inputstate;
93
94 return startbytes - numbytes;
95}
96
97/* process a block of received bytes in lock mode (tty i/f)
98 * Return value:
99 * number of processed bytes
100 */
101static inline int lock_loop(unsigned char *src, int numbytes,
Tilman Schmidt784d5852006-04-10 22:55:04 -0700102 struct inbuf_t *inbuf)
Hansjoerg Lipp07dc1f92006-03-26 01:38:37 -0800103{
104 struct cardstate *cs = inbuf->cs;
105
Tilman Schmidt917f5082006-04-10 22:55:00 -0700106 gigaset_dbg_buffer(DEBUG_LOCKCMD, "received response",
Tilman Schmidt01371502006-04-10 22:55:11 -0700107 numbytes, src);
Hansjoerg Lipp07dc1f92006-03-26 01:38:37 -0800108 gigaset_if_receive(cs, src, numbytes);
109
110 return numbytes;
111}
112
113/* process a block of received bytes in HDLC data mode
114 * Collect HDLC frames, undoing byte stuffing and watching for DLE escapes.
115 * When a frame is complete, check the FCS and pass valid frames to the LL.
116 * If DLE is encountered, return immediately to let the caller handle it.
117 * Return value:
118 * number of processed bytes
119 * numbytes (all bytes processed) on error --FIXME
120 */
121static inline int hdlc_loop(unsigned char c, unsigned char *src, int numbytes,
Tilman Schmidt784d5852006-04-10 22:55:04 -0700122 struct inbuf_t *inbuf)
Hansjoerg Lipp07dc1f92006-03-26 01:38:37 -0800123{
124 struct cardstate *cs = inbuf->cs;
125 struct bc_state *bcs = inbuf->bcs;
Tilman Schmidtd48c7782006-04-10 22:55:08 -0700126 int inputstate = bcs->inputstate;
127 __u16 fcs = bcs->fcs;
128 struct sk_buff *skb = bcs->skb;
Hansjoerg Lipp07dc1f92006-03-26 01:38:37 -0800129 unsigned char error;
130 struct sk_buff *compskb;
131 int startbytes = numbytes;
132 int l;
133
Hansjoerg Lipp07dc1f92006-03-26 01:38:37 -0800134 if (unlikely(inputstate & INS_byte_stuff)) {
135 inputstate &= ~INS_byte_stuff;
136 goto byte_stuff;
137 }
138 for (;;) {
139 if (unlikely(c == PPP_ESCAPE)) {
140 if (unlikely(!numbytes)) {
141 inputstate |= INS_byte_stuff;
142 break;
143 }
144 c = *src++;
145 --numbytes;
146 if (unlikely(c == DLE_FLAG &&
147 (cs->dle ||
148 inbuf->inputstate & INS_DLE_command))) {
149 inbuf->inputstate |= INS_DLE_char;
150 inputstate |= INS_byte_stuff;
151 break;
152 }
153byte_stuff:
154 c ^= PPP_TRANS;
155#ifdef CONFIG_GIGASET_DEBUG
156 if (unlikely(!muststuff(c)))
Tilman Schmidt784d5852006-04-10 22:55:04 -0700157 gig_dbg(DEBUG_HDLC, "byte stuffed: 0x%02x", c);
Hansjoerg Lipp07dc1f92006-03-26 01:38:37 -0800158#endif
159 } else if (unlikely(c == PPP_FLAG)) {
160 if (unlikely(inputstate & INS_skip_frame)) {
161 if (!(inputstate & INS_have_data)) { /* 7E 7E */
Hansjoerg Lipp07dc1f92006-03-26 01:38:37 -0800162#ifdef CONFIG_GIGASET_DEBUG
163 ++bcs->emptycount;
164#endif
165 } else
Tilman Schmidt784d5852006-04-10 22:55:04 -0700166 gig_dbg(DEBUG_HDLC,
Hansjoerg Lipp07dc1f92006-03-26 01:38:37 -0800167 "7e----------------------------");
168
169 /* end of frame */
170 error = 1;
171 gigaset_rcv_error(NULL, cs, bcs);
172 } else if (!(inputstate & INS_have_data)) { /* 7E 7E */
Hansjoerg Lipp07dc1f92006-03-26 01:38:37 -0800173#ifdef CONFIG_GIGASET_DEBUG
174 ++bcs->emptycount;
175#endif
176 break;
177 } else {
Tilman Schmidt784d5852006-04-10 22:55:04 -0700178 gig_dbg(DEBUG_HDLC,
179 "7e----------------------------");
Hansjoerg Lipp07dc1f92006-03-26 01:38:37 -0800180
181 /* end of frame */
182 error = 0;
183
184 if (unlikely(fcs != PPP_GOODFCS)) {
Tilman Schmidt784d5852006-04-10 22:55:04 -0700185 dev_err(cs->dev,
186 "Packet checksum at %lu failed, "
187 "packet is corrupted (%u bytes)!\n",
Hansjoerg Lipp07dc1f92006-03-26 01:38:37 -0800188 bcs->rcvbytes, skb->len);
189 compskb = NULL;
190 gigaset_rcv_error(compskb, cs, bcs);
191 error = 1;
192 } else {
193 if (likely((l = skb->len) > 2)) {
194 skb->tail -= 2;
195 skb->len -= 2;
196 } else {
197 dev_kfree_skb(skb);
198 skb = NULL;
199 inputstate |= INS_skip_frame;
200 if (l == 1) {
Tilman Schmidt784d5852006-04-10 22:55:04 -0700201 dev_err(cs->dev,
202 "invalid packet size (1)!\n");
Hansjoerg Lipp07dc1f92006-03-26 01:38:37 -0800203 error = 1;
Tilman Schmidt784d5852006-04-10 22:55:04 -0700204 gigaset_rcv_error(NULL,
205 cs, bcs);
Hansjoerg Lipp07dc1f92006-03-26 01:38:37 -0800206 }
207 }
208 if (likely(!(error ||
209 (inputstate &
210 INS_skip_frame)))) {
211 gigaset_rcv_skb(skb, cs, bcs);
212 }
213 }
214 }
215
216 if (unlikely(error))
217 if (skb)
218 dev_kfree_skb(skb);
219
220 fcs = PPP_INITFCS;
221 inputstate &= ~(INS_have_data | INS_skip_frame);
222 if (unlikely(bcs->ignore)) {
223 inputstate |= INS_skip_frame;
224 skb = NULL;
225 } else if (likely((skb = dev_alloc_skb(SBUFSIZE + HW_HDR_LEN)) != NULL)) {
226 skb_reserve(skb, HW_HDR_LEN);
227 } else {
Tilman Schmidt784d5852006-04-10 22:55:04 -0700228 dev_warn(cs->dev,
229 "could not allocate new skb\n");
Hansjoerg Lipp07dc1f92006-03-26 01:38:37 -0800230 inputstate |= INS_skip_frame;
231 }
232
233 break;
234#ifdef CONFIG_GIGASET_DEBUG
235 } else if (unlikely(muststuff(c))) {
236 /* Should not happen. Possible after ZDLE=1<CR><LF>. */
Tilman Schmidt784d5852006-04-10 22:55:04 -0700237 gig_dbg(DEBUG_HDLC, "not byte stuffed: 0x%02x", c);
Hansjoerg Lipp07dc1f92006-03-26 01:38:37 -0800238#endif
239 }
240
241 /* add character */
242
243#ifdef CONFIG_GIGASET_DEBUG
244 if (unlikely(!(inputstate & INS_have_data))) {
Tilman Schmidt784d5852006-04-10 22:55:04 -0700245 gig_dbg(DEBUG_HDLC, "7e (%d x) ================",
246 bcs->emptycount);
Hansjoerg Lipp07dc1f92006-03-26 01:38:37 -0800247 bcs->emptycount = 0;
248 }
249#endif
250
251 inputstate |= INS_have_data;
252
253 if (likely(!(inputstate & INS_skip_frame))) {
254 if (unlikely(skb->len == SBUFSIZE)) {
Tilman Schmidt784d5852006-04-10 22:55:04 -0700255 dev_warn(cs->dev, "received packet too long\n");
Hansjoerg Lipp07dc1f92006-03-26 01:38:37 -0800256 dev_kfree_skb_any(skb);
257 skb = NULL;
258 inputstate |= INS_skip_frame;
259 break;
260 }
Tilman Schmidt443e1f42006-04-10 22:55:13 -0700261 *__skb_put(skb, 1) = c;
Hansjoerg Lipp07dc1f92006-03-26 01:38:37 -0800262 fcs = crc_ccitt_byte(fcs, c);
263 }
264
265 if (unlikely(!numbytes))
266 break;
267 c = *src++;
268 --numbytes;
269 if (unlikely(c == DLE_FLAG &&
270 (cs->dle ||
271 inbuf->inputstate & INS_DLE_command))) {
272 inbuf->inputstate |= INS_DLE_char;
273 break;
274 }
275 }
276 bcs->inputstate = inputstate;
277 bcs->fcs = fcs;
278 bcs->skb = skb;
279 return startbytes - numbytes;
280}
281
282/* process a block of received bytes in transparent data mode
283 * Invert bytes, undoing byte stuffing and watching for DLE escapes.
284 * If DLE is encountered, return immediately to let the caller handle it.
285 * Return value:
286 * number of processed bytes
287 * numbytes (all bytes processed) on error --FIXME
288 */
289static inline int iraw_loop(unsigned char c, unsigned char *src, int numbytes,
Tilman Schmidt784d5852006-04-10 22:55:04 -0700290 struct inbuf_t *inbuf)
Hansjoerg Lipp07dc1f92006-03-26 01:38:37 -0800291{
292 struct cardstate *cs = inbuf->cs;
293 struct bc_state *bcs = inbuf->bcs;
Tilman Schmidtd48c7782006-04-10 22:55:08 -0700294 int inputstate = bcs->inputstate;
295 struct sk_buff *skb = bcs->skb;
Hansjoerg Lipp07dc1f92006-03-26 01:38:37 -0800296 int startbytes = numbytes;
297
Hansjoerg Lipp07dc1f92006-03-26 01:38:37 -0800298 for (;;) {
299 /* add character */
300 inputstate |= INS_have_data;
301
302 if (likely(!(inputstate & INS_skip_frame))) {
303 if (unlikely(skb->len == SBUFSIZE)) {
304 //FIXME just pass skb up and allocate a new one
Tilman Schmidt784d5852006-04-10 22:55:04 -0700305 dev_warn(cs->dev, "received packet too long\n");
Hansjoerg Lipp07dc1f92006-03-26 01:38:37 -0800306 dev_kfree_skb_any(skb);
307 skb = NULL;
308 inputstate |= INS_skip_frame;
309 break;
310 }
Akinobu Mita17b3cff2006-12-08 02:36:30 -0800311 *__skb_put(skb, 1) = bitrev8(c);
Hansjoerg Lipp07dc1f92006-03-26 01:38:37 -0800312 }
313
314 if (unlikely(!numbytes))
315 break;
316 c = *src++;
317 --numbytes;
318 if (unlikely(c == DLE_FLAG &&
319 (cs->dle ||
320 inbuf->inputstate & INS_DLE_command))) {
321 inbuf->inputstate |= INS_DLE_char;
322 break;
323 }
324 }
325
326 /* pass data up */
327 if (likely(inputstate & INS_have_data)) {
328 if (likely(!(inputstate & INS_skip_frame))) {
329 gigaset_rcv_skb(skb, cs, bcs);
330 }
331 inputstate &= ~(INS_have_data | INS_skip_frame);
332 if (unlikely(bcs->ignore)) {
333 inputstate |= INS_skip_frame;
334 skb = NULL;
335 } else if (likely((skb = dev_alloc_skb(SBUFSIZE + HW_HDR_LEN))
336 != NULL)) {
337 skb_reserve(skb, HW_HDR_LEN);
338 } else {
Tilman Schmidt784d5852006-04-10 22:55:04 -0700339 dev_warn(cs->dev, "could not allocate new skb\n");
Hansjoerg Lipp07dc1f92006-03-26 01:38:37 -0800340 inputstate |= INS_skip_frame;
341 }
342 }
343
344 bcs->inputstate = inputstate;
345 bcs->skb = skb;
346 return startbytes - numbytes;
347}
348
349/* process a block of data received from the device
350 */
351void gigaset_m10x_input(struct inbuf_t *inbuf)
352{
353 struct cardstate *cs;
354 unsigned tail, head, numbytes;
355 unsigned char *src, c;
356 int procbytes;
357
358 head = atomic_read(&inbuf->head);
359 tail = atomic_read(&inbuf->tail);
Tilman Schmidt784d5852006-04-10 22:55:04 -0700360 gig_dbg(DEBUG_INTR, "buffer state: %u -> %u", head, tail);
Hansjoerg Lipp07dc1f92006-03-26 01:38:37 -0800361
362 if (head != tail) {
363 cs = inbuf->cs;
364 src = inbuf->data + head;
365 numbytes = (head > tail ? RBUFSIZE : tail) - head;
Tilman Schmidt784d5852006-04-10 22:55:04 -0700366 gig_dbg(DEBUG_INTR, "processing %u bytes", numbytes);
Hansjoerg Lipp07dc1f92006-03-26 01:38:37 -0800367
368 while (numbytes) {
369 if (atomic_read(&cs->mstate) == MS_LOCKED) {
370 procbytes = lock_loop(src, numbytes, inbuf);
371 src += procbytes;
372 numbytes -= procbytes;
373 } else {
374 c = *src++;
375 --numbytes;
376 if (c == DLE_FLAG && (cs->dle ||
377 inbuf->inputstate & INS_DLE_command)) {
378 if (!(inbuf->inputstate & INS_DLE_char)) {
379 inbuf->inputstate |= INS_DLE_char;
380 goto nextbyte;
381 }
382 /* <DLE> <DLE> => <DLE> in data stream */
383 inbuf->inputstate &= ~INS_DLE_char;
384 }
385
386 if (!(inbuf->inputstate & INS_DLE_char)) {
387
Tilman Schmidt917f5082006-04-10 22:55:00 -0700388 /* FIXME use function pointers? */
Hansjoerg Lipp07dc1f92006-03-26 01:38:37 -0800389 if (inbuf->inputstate & INS_command)
390 procbytes = cmd_loop(c, src, numbytes, inbuf);
391 else if (inbuf->bcs->proto2 == ISDN_PROTO_L2_HDLC)
392 procbytes = hdlc_loop(c, src, numbytes, inbuf);
393 else
394 procbytes = iraw_loop(c, src, numbytes, inbuf);
395
396 src += procbytes;
397 numbytes -= procbytes;
Tilman Schmidt784d5852006-04-10 22:55:04 -0700398 } else { /* DLE char */
Hansjoerg Lipp07dc1f92006-03-26 01:38:37 -0800399 inbuf->inputstate &= ~INS_DLE_char;
400 switch (c) {
401 case 'X': /*begin of command*/
402#ifdef CONFIG_GIGASET_DEBUG
403 if (inbuf->inputstate & INS_command)
Tilman Schmidt784d5852006-04-10 22:55:04 -0700404 dev_err(cs->dev,
405 "received <DLE> 'X' in command mode\n");
Hansjoerg Lipp07dc1f92006-03-26 01:38:37 -0800406#endif
407 inbuf->inputstate |=
408 INS_command | INS_DLE_command;
409 break;
410 case '.': /*end of command*/
411#ifdef CONFIG_GIGASET_DEBUG
412 if (!(inbuf->inputstate & INS_command))
Tilman Schmidt784d5852006-04-10 22:55:04 -0700413 dev_err(cs->dev,
414 "received <DLE> '.' in hdlc mode\n");
Hansjoerg Lipp07dc1f92006-03-26 01:38:37 -0800415#endif
416 inbuf->inputstate &= cs->dle ?
417 ~(INS_DLE_command|INS_command)
418 : ~INS_DLE_command;
419 break;
420 //case DLE_FLAG: /*DLE_FLAG in data stream*/ /* schon oben behandelt! */
421 default:
Tilman Schmidt784d5852006-04-10 22:55:04 -0700422 dev_err(cs->dev,
423 "received 0x10 0x%02x!\n",
424 (int) c);
Hansjoerg Lipp07dc1f92006-03-26 01:38:37 -0800425 /* FIXME: reset driver?? */
426 }
427 }
428 }
429nextbyte:
430 if (!numbytes) {
431 /* end of buffer, check for wrap */
432 if (head > tail) {
433 head = 0;
434 src = inbuf->data;
435 numbytes = tail;
436 } else {
437 head = tail;
438 break;
439 }
440 }
441 }
442
Tilman Schmidt784d5852006-04-10 22:55:04 -0700443 gig_dbg(DEBUG_INTR, "setting head to %u", head);
Hansjoerg Lipp07dc1f92006-03-26 01:38:37 -0800444 atomic_set(&inbuf->head, head);
445 }
446}
Adrian Bunkd5c16822007-02-20 13:58:17 -0800447EXPORT_SYMBOL_GPL(gigaset_m10x_input);
Hansjoerg Lipp07dc1f92006-03-26 01:38:37 -0800448
449
450/* == data output ========================================================== */
451
452/* Encoding of a PPP packet into an octet stuffed HDLC frame
453 * with FCS, opening and closing flags.
454 * parameters:
455 * skb skb containing original packet (freed upon return)
456 * head number of headroom bytes to allocate in result skb
457 * tail number of tailroom bytes to allocate in result skb
458 * Return value:
459 * pointer to newly allocated skb containing the result frame
460 */
461static struct sk_buff *HDLC_Encode(struct sk_buff *skb, int head, int tail)
462{
463 struct sk_buff *hdlc_skb;
464 __u16 fcs;
465 unsigned char c;
466 unsigned char *cp;
467 int len;
468 unsigned int stuf_cnt;
469
470 stuf_cnt = 0;
471 fcs = PPP_INITFCS;
472 cp = skb->data;
473 len = skb->len;
474 while (len--) {
475 if (muststuff(*cp))
476 stuf_cnt++;
477 fcs = crc_ccitt_byte(fcs, *cp++);
478 }
Tilman Schmidt784d5852006-04-10 22:55:04 -0700479 fcs ^= 0xffff; /* complement */
Hansjoerg Lipp07dc1f92006-03-26 01:38:37 -0800480
481 /* size of new buffer: original size + number of stuffing bytes
482 * + 2 bytes FCS + 2 stuffing bytes for FCS (if needed) + 2 flag bytes
483 */
484 hdlc_skb = dev_alloc_skb(skb->len + stuf_cnt + 6 + tail + head);
485 if (!hdlc_skb) {
Hansjoerg Lipp07dc1f92006-03-26 01:38:37 -0800486 dev_kfree_skb(skb);
487 return NULL;
488 }
489 skb_reserve(hdlc_skb, head);
490
491 /* Copy acknowledge request into new skb */
492 memcpy(hdlc_skb->head, skb->head, 2);
493
494 /* Add flag sequence in front of everything.. */
495 *(skb_put(hdlc_skb, 1)) = PPP_FLAG;
496
497 /* Perform byte stuffing while copying data. */
498 while (skb->len--) {
499 if (muststuff(*skb->data)) {
500 *(skb_put(hdlc_skb, 1)) = PPP_ESCAPE;
501 *(skb_put(hdlc_skb, 1)) = (*skb->data++) ^ PPP_TRANS;
502 } else
503 *(skb_put(hdlc_skb, 1)) = *skb->data++;
504 }
505
506 /* Finally add FCS (byte stuffed) and flag sequence */
Tilman Schmidt784d5852006-04-10 22:55:04 -0700507 c = (fcs & 0x00ff); /* least significant byte first */
Hansjoerg Lipp07dc1f92006-03-26 01:38:37 -0800508 if (muststuff(c)) {
509 *(skb_put(hdlc_skb, 1)) = PPP_ESCAPE;
510 c ^= PPP_TRANS;
511 }
512 *(skb_put(hdlc_skb, 1)) = c;
513
514 c = ((fcs >> 8) & 0x00ff);
515 if (muststuff(c)) {
516 *(skb_put(hdlc_skb, 1)) = PPP_ESCAPE;
517 c ^= PPP_TRANS;
518 }
519 *(skb_put(hdlc_skb, 1)) = c;
520
521 *(skb_put(hdlc_skb, 1)) = PPP_FLAG;
522
523 dev_kfree_skb(skb);
524 return hdlc_skb;
525}
526
527/* Encoding of a raw packet into an octet stuffed bit inverted frame
528 * parameters:
529 * skb skb containing original packet (freed upon return)
530 * head number of headroom bytes to allocate in result skb
531 * tail number of tailroom bytes to allocate in result skb
532 * Return value:
533 * pointer to newly allocated skb containing the result frame
534 */
535static struct sk_buff *iraw_encode(struct sk_buff *skb, int head, int tail)
536{
537 struct sk_buff *iraw_skb;
538 unsigned char c;
539 unsigned char *cp;
540 int len;
541
542 /* worst case: every byte must be stuffed */
543 iraw_skb = dev_alloc_skb(2*skb->len + tail + head);
544 if (!iraw_skb) {
Hansjoerg Lipp07dc1f92006-03-26 01:38:37 -0800545 dev_kfree_skb(skb);
546 return NULL;
547 }
548 skb_reserve(iraw_skb, head);
549
550 cp = skb->data;
551 len = skb->len;
552 while (len--) {
Akinobu Mita17b3cff2006-12-08 02:36:30 -0800553 c = bitrev8(*cp++);
Hansjoerg Lipp07dc1f92006-03-26 01:38:37 -0800554 if (c == DLE_FLAG)
555 *(skb_put(iraw_skb, 1)) = c;
556 *(skb_put(iraw_skb, 1)) = c;
557 }
558 dev_kfree_skb(skb);
559 return iraw_skb;
560}
561
562/* gigaset_send_skb
563 * called by common.c to queue an skb for sending
564 * and start transmission if necessary
565 * parameters:
566 * B Channel control structure
567 * skb
568 * Return value:
569 * number of bytes accepted for sending
570 * (skb->len if ok, 0 if out of buffer space)
571 * or error code (< 0, eg. -EINVAL)
572 */
573int gigaset_m10x_send_skb(struct bc_state *bcs, struct sk_buff *skb)
574{
Tilman Schmidtd48c7782006-04-10 22:55:08 -0700575 unsigned len = skb->len;
Tilman Schmidt69049cc2006-04-10 22:55:16 -0700576 unsigned long flags;
Hansjoerg Lipp07dc1f92006-03-26 01:38:37 -0800577
578 if (bcs->proto2 == ISDN_PROTO_L2_HDLC)
579 skb = HDLC_Encode(skb, HW_HDR_LEN, 0);
580 else
581 skb = iraw_encode(skb, HW_HDR_LEN, 0);
Tilman Schmidt784d5852006-04-10 22:55:04 -0700582 if (!skb) {
Tilman Schmidt69049cc2006-04-10 22:55:16 -0700583 err("unable to allocate memory for encoding!\n");
Hansjoerg Lipp07dc1f92006-03-26 01:38:37 -0800584 return -ENOMEM;
Tilman Schmidt784d5852006-04-10 22:55:04 -0700585 }
Hansjoerg Lipp07dc1f92006-03-26 01:38:37 -0800586
587 skb_queue_tail(&bcs->squeue, skb);
Tilman Schmidt69049cc2006-04-10 22:55:16 -0700588 spin_lock_irqsave(&bcs->cs->lock, flags);
589 if (bcs->cs->connected)
590 tasklet_schedule(&bcs->cs->write_tasklet);
591 spin_unlock_irqrestore(&bcs->cs->lock, flags);
Hansjoerg Lipp07dc1f92006-03-26 01:38:37 -0800592
593 return len; /* ok so far */
594}
Adrian Bunkd5c16822007-02-20 13:58:17 -0800595EXPORT_SYMBOL_GPL(gigaset_m10x_send_skb);