Commit | Line | Data |
---|---|---|
07dc1f9f HL |
1 | /* |
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>, | |
70440cf2 | 6 | * Stefan Eilers. |
07dc1f9f HL |
7 | * |
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 | * ===================================================================== | |
07dc1f9f HL |
14 | */ |
15 | ||
16 | #include "gigaset.h" | |
17 | #include <linux/crc-ccitt.h> | |
17b3cff0 | 18 | #include <linux/bitrev.h> |
07dc1f9f | 19 | |
07dc1f9f HL |
20 | /* check if byte must be stuffed/escaped |
21 | * I'm not sure which data should be encoded. | |
22 | * Therefore I will go the hard way and decode every value | |
23 | * less than 0x20, the flag sequence and the control escape char. | |
24 | */ | |
25 | static inline int muststuff(unsigned char c) | |
26 | { | |
27 | if (c < PPP_TRANS) return 1; | |
28 | if (c == PPP_FLAG) return 1; | |
29 | if (c == PPP_ESCAPE) return 1; | |
30 | /* other possible candidates: */ | |
31 | /* 0x91: XON with parity set */ | |
32 | /* 0x93: XOFF with parity set */ | |
33 | return 0; | |
34 | } | |
35 | ||
36 | /* == data input =========================================================== */ | |
37 | ||
38 | /* process a block of received bytes in command mode (modem response) | |
39 | * Return value: | |
40 | * number of processed bytes | |
41 | */ | |
42 | static inline int cmd_loop(unsigned char c, unsigned char *src, int numbytes, | |
784d5858 | 43 | struct inbuf_t *inbuf) |
07dc1f9f HL |
44 | { |
45 | struct cardstate *cs = inbuf->cs; | |
46 | unsigned cbytes = cs->cbytes; | |
47 | int inputstate = inbuf->inputstate; | |
48 | int startbytes = numbytes; | |
49 | ||
50 | for (;;) { | |
51 | cs->respdata[cbytes] = c; | |
52 | if (c == 10 || c == 13) { | |
784d5858 TS |
53 | gig_dbg(DEBUG_TRANSCMD, "%s: End of Command (%d Bytes)", |
54 | __func__, cbytes); | |
07dc1f9f | 55 | cs->cbytes = cbytes; |
917f5085 TS |
56 | gigaset_handle_modem_response(cs); /* can change |
57 | cs->dle */ | |
07dc1f9f HL |
58 | cbytes = 0; |
59 | ||
60 | if (cs->dle && | |
61 | !(inputstate & INS_DLE_command)) { | |
62 | inputstate &= ~INS_command; | |
63 | break; | |
64 | } | |
65 | } else { | |
66 | /* advance in line buffer, checking for overflow */ | |
67 | if (cbytes < MAX_RESP_SIZE - 1) | |
68 | cbytes++; | |
69 | else | |
784d5858 | 70 | dev_warn(cs->dev, "response too large\n"); |
07dc1f9f HL |
71 | } |
72 | ||
73 | if (!numbytes) | |
74 | break; | |
75 | c = *src++; | |
76 | --numbytes; | |
77 | if (c == DLE_FLAG && | |
78 | (cs->dle || inputstate & INS_DLE_command)) { | |
79 | inputstate |= INS_DLE_char; | |
80 | break; | |
81 | } | |
82 | } | |
83 | ||
84 | cs->cbytes = cbytes; | |
85 | inbuf->inputstate = inputstate; | |
86 | ||
87 | return startbytes - numbytes; | |
88 | } | |
89 | ||
90 | /* process a block of received bytes in lock mode (tty i/f) | |
91 | * Return value: | |
92 | * number of processed bytes | |
93 | */ | |
94 | static inline int lock_loop(unsigned char *src, int numbytes, | |
784d5858 | 95 | struct inbuf_t *inbuf) |
07dc1f9f HL |
96 | { |
97 | struct cardstate *cs = inbuf->cs; | |
98 | ||
917f5085 | 99 | gigaset_dbg_buffer(DEBUG_LOCKCMD, "received response", |
01371500 | 100 | numbytes, src); |
07dc1f9f HL |
101 | gigaset_if_receive(cs, src, numbytes); |
102 | ||
103 | return numbytes; | |
104 | } | |
105 | ||
106 | /* process a block of received bytes in HDLC data mode | |
107 | * Collect HDLC frames, undoing byte stuffing and watching for DLE escapes. | |
108 | * When a frame is complete, check the FCS and pass valid frames to the LL. | |
109 | * If DLE is encountered, return immediately to let the caller handle it. | |
110 | * Return value: | |
111 | * number of processed bytes | |
112 | * numbytes (all bytes processed) on error --FIXME | |
113 | */ | |
114 | static inline int hdlc_loop(unsigned char c, unsigned char *src, int numbytes, | |
784d5858 | 115 | struct inbuf_t *inbuf) |
07dc1f9f HL |
116 | { |
117 | struct cardstate *cs = inbuf->cs; | |
118 | struct bc_state *bcs = inbuf->bcs; | |
d48c7784 TS |
119 | int inputstate = bcs->inputstate; |
120 | __u16 fcs = bcs->fcs; | |
121 | struct sk_buff *skb = bcs->skb; | |
07dc1f9f HL |
122 | unsigned char error; |
123 | struct sk_buff *compskb; | |
124 | int startbytes = numbytes; | |
125 | int l; | |
126 | ||
07dc1f9f HL |
127 | if (unlikely(inputstate & INS_byte_stuff)) { |
128 | inputstate &= ~INS_byte_stuff; | |
129 | goto byte_stuff; | |
130 | } | |
131 | for (;;) { | |
132 | if (unlikely(c == PPP_ESCAPE)) { | |
133 | if (unlikely(!numbytes)) { | |
134 | inputstate |= INS_byte_stuff; | |
135 | break; | |
136 | } | |
137 | c = *src++; | |
138 | --numbytes; | |
139 | if (unlikely(c == DLE_FLAG && | |
140 | (cs->dle || | |
141 | inbuf->inputstate & INS_DLE_command))) { | |
142 | inbuf->inputstate |= INS_DLE_char; | |
143 | inputstate |= INS_byte_stuff; | |
144 | break; | |
145 | } | |
146 | byte_stuff: | |
147 | c ^= PPP_TRANS; | |
07dc1f9f | 148 | if (unlikely(!muststuff(c))) |
784d5858 | 149 | gig_dbg(DEBUG_HDLC, "byte stuffed: 0x%02x", c); |
07dc1f9f HL |
150 | } else if (unlikely(c == PPP_FLAG)) { |
151 | if (unlikely(inputstate & INS_skip_frame)) { | |
07dc1f9f | 152 | #ifdef CONFIG_GIGASET_DEBUG |
236b87c2 | 153 | if (!(inputstate & INS_have_data)) { /* 7E 7E */ |
07dc1f9f | 154 | ++bcs->emptycount; |
07dc1f9f | 155 | } else |
784d5858 | 156 | gig_dbg(DEBUG_HDLC, |
07dc1f9f | 157 | "7e----------------------------"); |
236b87c2 | 158 | #endif |
07dc1f9f HL |
159 | |
160 | /* end of frame */ | |
161 | error = 1; | |
162 | gigaset_rcv_error(NULL, cs, bcs); | |
163 | } else if (!(inputstate & INS_have_data)) { /* 7E 7E */ | |
07dc1f9f HL |
164 | #ifdef CONFIG_GIGASET_DEBUG |
165 | ++bcs->emptycount; | |
166 | #endif | |
167 | break; | |
168 | } else { | |
784d5858 TS |
169 | gig_dbg(DEBUG_HDLC, |
170 | "7e----------------------------"); | |
07dc1f9f HL |
171 | |
172 | /* end of frame */ | |
173 | error = 0; | |
174 | ||
175 | if (unlikely(fcs != PPP_GOODFCS)) { | |
784d5858 | 176 | dev_err(cs->dev, |
0a305720 TS |
177 | "Checksum failed, %u bytes corrupted!\n", |
178 | skb->len); | |
07dc1f9f HL |
179 | compskb = NULL; |
180 | gigaset_rcv_error(compskb, cs, bcs); | |
181 | error = 1; | |
182 | } else { | |
183 | if (likely((l = skb->len) > 2)) { | |
184 | skb->tail -= 2; | |
185 | skb->len -= 2; | |
186 | } else { | |
187 | dev_kfree_skb(skb); | |
188 | skb = NULL; | |
189 | inputstate |= INS_skip_frame; | |
190 | if (l == 1) { | |
784d5858 TS |
191 | dev_err(cs->dev, |
192 | "invalid packet size (1)!\n"); | |
07dc1f9f | 193 | error = 1; |
784d5858 TS |
194 | gigaset_rcv_error(NULL, |
195 | cs, bcs); | |
07dc1f9f HL |
196 | } |
197 | } | |
198 | if (likely(!(error || | |
199 | (inputstate & | |
200 | INS_skip_frame)))) { | |
201 | gigaset_rcv_skb(skb, cs, bcs); | |
202 | } | |
203 | } | |
204 | } | |
205 | ||
206 | if (unlikely(error)) | |
207 | if (skb) | |
208 | dev_kfree_skb(skb); | |
209 | ||
210 | fcs = PPP_INITFCS; | |
211 | inputstate &= ~(INS_have_data | INS_skip_frame); | |
212 | if (unlikely(bcs->ignore)) { | |
213 | inputstate |= INS_skip_frame; | |
214 | skb = NULL; | |
215 | } else if (likely((skb = dev_alloc_skb(SBUFSIZE + HW_HDR_LEN)) != NULL)) { | |
216 | skb_reserve(skb, HW_HDR_LEN); | |
217 | } else { | |
784d5858 TS |
218 | dev_warn(cs->dev, |
219 | "could not allocate new skb\n"); | |
07dc1f9f HL |
220 | inputstate |= INS_skip_frame; |
221 | } | |
222 | ||
223 | break; | |
07dc1f9f HL |
224 | } else if (unlikely(muststuff(c))) { |
225 | /* Should not happen. Possible after ZDLE=1<CR><LF>. */ | |
784d5858 | 226 | gig_dbg(DEBUG_HDLC, "not byte stuffed: 0x%02x", c); |
07dc1f9f HL |
227 | } |
228 | ||
229 | /* add character */ | |
230 | ||
231 | #ifdef CONFIG_GIGASET_DEBUG | |
232 | if (unlikely(!(inputstate & INS_have_data))) { | |
784d5858 TS |
233 | gig_dbg(DEBUG_HDLC, "7e (%d x) ================", |
234 | bcs->emptycount); | |
07dc1f9f HL |
235 | bcs->emptycount = 0; |
236 | } | |
237 | #endif | |
238 | ||
239 | inputstate |= INS_have_data; | |
240 | ||
241 | if (likely(!(inputstate & INS_skip_frame))) { | |
242 | if (unlikely(skb->len == SBUFSIZE)) { | |
784d5858 | 243 | dev_warn(cs->dev, "received packet too long\n"); |
07dc1f9f HL |
244 | dev_kfree_skb_any(skb); |
245 | skb = NULL; | |
246 | inputstate |= INS_skip_frame; | |
247 | break; | |
248 | } | |
443e1f45 | 249 | *__skb_put(skb, 1) = c; |
07dc1f9f HL |
250 | fcs = crc_ccitt_byte(fcs, c); |
251 | } | |
252 | ||
253 | if (unlikely(!numbytes)) | |
254 | break; | |
255 | c = *src++; | |
256 | --numbytes; | |
257 | if (unlikely(c == DLE_FLAG && | |
258 | (cs->dle || | |
259 | inbuf->inputstate & INS_DLE_command))) { | |
260 | inbuf->inputstate |= INS_DLE_char; | |
261 | break; | |
262 | } | |
263 | } | |
264 | bcs->inputstate = inputstate; | |
265 | bcs->fcs = fcs; | |
266 | bcs->skb = skb; | |
267 | return startbytes - numbytes; | |
268 | } | |
269 | ||
270 | /* process a block of received bytes in transparent data mode | |
271 | * Invert bytes, undoing byte stuffing and watching for DLE escapes. | |
272 | * If DLE is encountered, return immediately to let the caller handle it. | |
273 | * Return value: | |
274 | * number of processed bytes | |
275 | * numbytes (all bytes processed) on error --FIXME | |
276 | */ | |
277 | static inline int iraw_loop(unsigned char c, unsigned char *src, int numbytes, | |
784d5858 | 278 | struct inbuf_t *inbuf) |
07dc1f9f HL |
279 | { |
280 | struct cardstate *cs = inbuf->cs; | |
281 | struct bc_state *bcs = inbuf->bcs; | |
d48c7784 TS |
282 | int inputstate = bcs->inputstate; |
283 | struct sk_buff *skb = bcs->skb; | |
07dc1f9f HL |
284 | int startbytes = numbytes; |
285 | ||
07dc1f9f HL |
286 | for (;;) { |
287 | /* add character */ | |
288 | inputstate |= INS_have_data; | |
289 | ||
290 | if (likely(!(inputstate & INS_skip_frame))) { | |
291 | if (unlikely(skb->len == SBUFSIZE)) { | |
292 | //FIXME just pass skb up and allocate a new one | |
784d5858 | 293 | dev_warn(cs->dev, "received packet too long\n"); |
07dc1f9f HL |
294 | dev_kfree_skb_any(skb); |
295 | skb = NULL; | |
296 | inputstate |= INS_skip_frame; | |
297 | break; | |
298 | } | |
17b3cff0 | 299 | *__skb_put(skb, 1) = bitrev8(c); |
07dc1f9f HL |
300 | } |
301 | ||
302 | if (unlikely(!numbytes)) | |
303 | break; | |
304 | c = *src++; | |
305 | --numbytes; | |
306 | if (unlikely(c == DLE_FLAG && | |
307 | (cs->dle || | |
308 | inbuf->inputstate & INS_DLE_command))) { | |
309 | inbuf->inputstate |= INS_DLE_char; | |
310 | break; | |
311 | } | |
312 | } | |
313 | ||
314 | /* pass data up */ | |
315 | if (likely(inputstate & INS_have_data)) { | |
316 | if (likely(!(inputstate & INS_skip_frame))) { | |
317 | gigaset_rcv_skb(skb, cs, bcs); | |
318 | } | |
319 | inputstate &= ~(INS_have_data | INS_skip_frame); | |
320 | if (unlikely(bcs->ignore)) { | |
321 | inputstate |= INS_skip_frame; | |
322 | skb = NULL; | |
323 | } else if (likely((skb = dev_alloc_skb(SBUFSIZE + HW_HDR_LEN)) | |
324 | != NULL)) { | |
325 | skb_reserve(skb, HW_HDR_LEN); | |
326 | } else { | |
784d5858 | 327 | dev_warn(cs->dev, "could not allocate new skb\n"); |
07dc1f9f HL |
328 | inputstate |= INS_skip_frame; |
329 | } | |
330 | } | |
331 | ||
332 | bcs->inputstate = inputstate; | |
333 | bcs->skb = skb; | |
334 | return startbytes - numbytes; | |
335 | } | |
336 | ||
337 | /* process a block of data received from the device | |
338 | */ | |
339 | void gigaset_m10x_input(struct inbuf_t *inbuf) | |
340 | { | |
341 | struct cardstate *cs; | |
342 | unsigned tail, head, numbytes; | |
343 | unsigned char *src, c; | |
344 | int procbytes; | |
345 | ||
9d4bee2b TS |
346 | head = inbuf->head; |
347 | tail = inbuf->tail; | |
784d5858 | 348 | gig_dbg(DEBUG_INTR, "buffer state: %u -> %u", head, tail); |
07dc1f9f HL |
349 | |
350 | if (head != tail) { | |
351 | cs = inbuf->cs; | |
352 | src = inbuf->data + head; | |
353 | numbytes = (head > tail ? RBUFSIZE : tail) - head; | |
784d5858 | 354 | gig_dbg(DEBUG_INTR, "processing %u bytes", numbytes); |
07dc1f9f HL |
355 | |
356 | while (numbytes) { | |
9d4bee2b | 357 | if (cs->mstate == MS_LOCKED) { |
07dc1f9f HL |
358 | procbytes = lock_loop(src, numbytes, inbuf); |
359 | src += procbytes; | |
360 | numbytes -= procbytes; | |
361 | } else { | |
362 | c = *src++; | |
363 | --numbytes; | |
364 | if (c == DLE_FLAG && (cs->dle || | |
365 | inbuf->inputstate & INS_DLE_command)) { | |
366 | if (!(inbuf->inputstate & INS_DLE_char)) { | |
367 | inbuf->inputstate |= INS_DLE_char; | |
368 | goto nextbyte; | |
369 | } | |
370 | /* <DLE> <DLE> => <DLE> in data stream */ | |
371 | inbuf->inputstate &= ~INS_DLE_char; | |
372 | } | |
373 | ||
374 | if (!(inbuf->inputstate & INS_DLE_char)) { | |
375 | ||
917f5085 | 376 | /* FIXME use function pointers? */ |
07dc1f9f HL |
377 | if (inbuf->inputstate & INS_command) |
378 | procbytes = cmd_loop(c, src, numbytes, inbuf); | |
379 | else if (inbuf->bcs->proto2 == ISDN_PROTO_L2_HDLC) | |
380 | procbytes = hdlc_loop(c, src, numbytes, inbuf); | |
381 | else | |
382 | procbytes = iraw_loop(c, src, numbytes, inbuf); | |
383 | ||
384 | src += procbytes; | |
385 | numbytes -= procbytes; | |
784d5858 | 386 | } else { /* DLE char */ |
07dc1f9f HL |
387 | inbuf->inputstate &= ~INS_DLE_char; |
388 | switch (c) { | |
389 | case 'X': /*begin of command*/ | |
07dc1f9f | 390 | if (inbuf->inputstate & INS_command) |
236b87c2 | 391 | dev_warn(cs->dev, |
784d5858 | 392 | "received <DLE> 'X' in command mode\n"); |
07dc1f9f HL |
393 | inbuf->inputstate |= |
394 | INS_command | INS_DLE_command; | |
395 | break; | |
396 | case '.': /*end of command*/ | |
07dc1f9f | 397 | if (!(inbuf->inputstate & INS_command)) |
236b87c2 | 398 | dev_warn(cs->dev, |
784d5858 | 399 | "received <DLE> '.' in hdlc mode\n"); |
07dc1f9f HL |
400 | inbuf->inputstate &= cs->dle ? |
401 | ~(INS_DLE_command|INS_command) | |
402 | : ~INS_DLE_command; | |
403 | break; | |
404 | //case DLE_FLAG: /*DLE_FLAG in data stream*/ /* schon oben behandelt! */ | |
405 | default: | |
784d5858 TS |
406 | dev_err(cs->dev, |
407 | "received 0x10 0x%02x!\n", | |
408 | (int) c); | |
07dc1f9f HL |
409 | /* FIXME: reset driver?? */ |
410 | } | |
411 | } | |
412 | } | |
413 | nextbyte: | |
414 | if (!numbytes) { | |
415 | /* end of buffer, check for wrap */ | |
416 | if (head > tail) { | |
417 | head = 0; | |
418 | src = inbuf->data; | |
419 | numbytes = tail; | |
420 | } else { | |
421 | head = tail; | |
422 | break; | |
423 | } | |
424 | } | |
425 | } | |
426 | ||
784d5858 | 427 | gig_dbg(DEBUG_INTR, "setting head to %u", head); |
9d4bee2b | 428 | inbuf->head = head; |
07dc1f9f HL |
429 | } |
430 | } | |
d5c1682f | 431 | EXPORT_SYMBOL_GPL(gigaset_m10x_input); |
07dc1f9f HL |
432 | |
433 | ||
434 | /* == data output ========================================================== */ | |
435 | ||
436 | /* Encoding of a PPP packet into an octet stuffed HDLC frame | |
437 | * with FCS, opening and closing flags. | |
438 | * parameters: | |
439 | * skb skb containing original packet (freed upon return) | |
440 | * head number of headroom bytes to allocate in result skb | |
441 | * tail number of tailroom bytes to allocate in result skb | |
442 | * Return value: | |
443 | * pointer to newly allocated skb containing the result frame | |
444 | */ | |
445 | static struct sk_buff *HDLC_Encode(struct sk_buff *skb, int head, int tail) | |
446 | { | |
447 | struct sk_buff *hdlc_skb; | |
448 | __u16 fcs; | |
449 | unsigned char c; | |
450 | unsigned char *cp; | |
451 | int len; | |
452 | unsigned int stuf_cnt; | |
453 | ||
454 | stuf_cnt = 0; | |
455 | fcs = PPP_INITFCS; | |
456 | cp = skb->data; | |
457 | len = skb->len; | |
458 | while (len--) { | |
459 | if (muststuff(*cp)) | |
460 | stuf_cnt++; | |
461 | fcs = crc_ccitt_byte(fcs, *cp++); | |
462 | } | |
784d5858 | 463 | fcs ^= 0xffff; /* complement */ |
07dc1f9f HL |
464 | |
465 | /* size of new buffer: original size + number of stuffing bytes | |
466 | * + 2 bytes FCS + 2 stuffing bytes for FCS (if needed) + 2 flag bytes | |
467 | */ | |
468 | hdlc_skb = dev_alloc_skb(skb->len + stuf_cnt + 6 + tail + head); | |
469 | if (!hdlc_skb) { | |
07dc1f9f HL |
470 | dev_kfree_skb(skb); |
471 | return NULL; | |
472 | } | |
473 | skb_reserve(hdlc_skb, head); | |
474 | ||
475 | /* Copy acknowledge request into new skb */ | |
476 | memcpy(hdlc_skb->head, skb->head, 2); | |
477 | ||
478 | /* Add flag sequence in front of everything.. */ | |
479 | *(skb_put(hdlc_skb, 1)) = PPP_FLAG; | |
480 | ||
481 | /* Perform byte stuffing while copying data. */ | |
482 | while (skb->len--) { | |
483 | if (muststuff(*skb->data)) { | |
484 | *(skb_put(hdlc_skb, 1)) = PPP_ESCAPE; | |
485 | *(skb_put(hdlc_skb, 1)) = (*skb->data++) ^ PPP_TRANS; | |
486 | } else | |
487 | *(skb_put(hdlc_skb, 1)) = *skb->data++; | |
488 | } | |
489 | ||
490 | /* Finally add FCS (byte stuffed) and flag sequence */ | |
784d5858 | 491 | c = (fcs & 0x00ff); /* least significant byte first */ |
07dc1f9f HL |
492 | if (muststuff(c)) { |
493 | *(skb_put(hdlc_skb, 1)) = PPP_ESCAPE; | |
494 | c ^= PPP_TRANS; | |
495 | } | |
496 | *(skb_put(hdlc_skb, 1)) = c; | |
497 | ||
498 | c = ((fcs >> 8) & 0x00ff); | |
499 | if (muststuff(c)) { | |
500 | *(skb_put(hdlc_skb, 1)) = PPP_ESCAPE; | |
501 | c ^= PPP_TRANS; | |
502 | } | |
503 | *(skb_put(hdlc_skb, 1)) = c; | |
504 | ||
505 | *(skb_put(hdlc_skb, 1)) = PPP_FLAG; | |
506 | ||
507 | dev_kfree_skb(skb); | |
508 | return hdlc_skb; | |
509 | } | |
510 | ||
511 | /* Encoding of a raw packet into an octet stuffed bit inverted frame | |
512 | * parameters: | |
513 | * skb skb containing original packet (freed upon return) | |
514 | * head number of headroom bytes to allocate in result skb | |
515 | * tail number of tailroom bytes to allocate in result skb | |
516 | * Return value: | |
517 | * pointer to newly allocated skb containing the result frame | |
518 | */ | |
519 | static struct sk_buff *iraw_encode(struct sk_buff *skb, int head, int tail) | |
520 | { | |
521 | struct sk_buff *iraw_skb; | |
522 | unsigned char c; | |
523 | unsigned char *cp; | |
524 | int len; | |
525 | ||
526 | /* worst case: every byte must be stuffed */ | |
527 | iraw_skb = dev_alloc_skb(2*skb->len + tail + head); | |
528 | if (!iraw_skb) { | |
07dc1f9f HL |
529 | dev_kfree_skb(skb); |
530 | return NULL; | |
531 | } | |
532 | skb_reserve(iraw_skb, head); | |
533 | ||
534 | cp = skb->data; | |
535 | len = skb->len; | |
536 | while (len--) { | |
17b3cff0 | 537 | c = bitrev8(*cp++); |
07dc1f9f HL |
538 | if (c == DLE_FLAG) |
539 | *(skb_put(iraw_skb, 1)) = c; | |
540 | *(skb_put(iraw_skb, 1)) = c; | |
541 | } | |
542 | dev_kfree_skb(skb); | |
543 | return iraw_skb; | |
544 | } | |
545 | ||
546 | /* gigaset_send_skb | |
547 | * called by common.c to queue an skb for sending | |
548 | * and start transmission if necessary | |
549 | * parameters: | |
550 | * B Channel control structure | |
551 | * skb | |
552 | * Return value: | |
553 | * number of bytes accepted for sending | |
554 | * (skb->len if ok, 0 if out of buffer space) | |
555 | * or error code (< 0, eg. -EINVAL) | |
556 | */ | |
557 | int gigaset_m10x_send_skb(struct bc_state *bcs, struct sk_buff *skb) | |
558 | { | |
d48c7784 | 559 | unsigned len = skb->len; |
69049cc8 | 560 | unsigned long flags; |
07dc1f9f HL |
561 | |
562 | if (bcs->proto2 == ISDN_PROTO_L2_HDLC) | |
563 | skb = HDLC_Encode(skb, HW_HDR_LEN, 0); | |
564 | else | |
565 | skb = iraw_encode(skb, HW_HDR_LEN, 0); | |
784d5858 | 566 | if (!skb) { |
5002779d TS |
567 | dev_err(bcs->cs->dev, |
568 | "unable to allocate memory for encoding!\n"); | |
07dc1f9f | 569 | return -ENOMEM; |
784d5858 | 570 | } |
07dc1f9f HL |
571 | |
572 | skb_queue_tail(&bcs->squeue, skb); | |
69049cc8 TS |
573 | spin_lock_irqsave(&bcs->cs->lock, flags); |
574 | if (bcs->cs->connected) | |
575 | tasklet_schedule(&bcs->cs->write_tasklet); | |
576 | spin_unlock_irqrestore(&bcs->cs->lock, flags); | |
07dc1f9f HL |
577 | |
578 | return len; /* ok so far */ | |
579 | } | |
d5c1682f | 580 | EXPORT_SYMBOL_GPL(gigaset_m10x_send_skb); |