Commit | Line | Data |
---|---|---|
1802d0be | 1 | // SPDX-License-Identifier: GPL-2.0-only |
1b2b03f8 KK |
2 | /* |
3 | * | |
4 | * Author Karsten Keil <kkeil@novell.com> | |
5 | * | |
6 | * Copyright 2008 by Karsten Keil <kkeil@novell.com> | |
1b2b03f8 KK |
7 | */ |
8 | ||
5a0e3ad6 | 9 | #include <linux/gfp.h> |
1b2b03f8 KK |
10 | #include <linux/module.h> |
11 | #include <linux/mISDNhw.h> | |
12 | ||
13 | static void | |
14 | dchannel_bh(struct work_struct *ws) | |
15 | { | |
16 | struct dchannel *dch = container_of(ws, struct dchannel, workq); | |
17 | struct sk_buff *skb; | |
18 | int err; | |
19 | ||
20 | if (test_and_clear_bit(FLG_RECVQUEUE, &dch->Flags)) { | |
21 | while ((skb = skb_dequeue(&dch->rqueue))) { | |
22 | if (likely(dch->dev.D.peer)) { | |
23 | err = dch->dev.D.recv(dch->dev.D.peer, skb); | |
24 | if (err) | |
25 | dev_kfree_skb(skb); | |
26 | } else | |
27 | dev_kfree_skb(skb); | |
28 | } | |
29 | } | |
30 | if (test_and_clear_bit(FLG_PHCHANGE, &dch->Flags)) { | |
31 | if (dch->phfunc) | |
32 | dch->phfunc(dch); | |
33 | } | |
34 | } | |
35 | ||
36 | static void | |
37 | bchannel_bh(struct work_struct *ws) | |
38 | { | |
39 | struct bchannel *bch = container_of(ws, struct bchannel, workq); | |
40 | struct sk_buff *skb; | |
41 | int err; | |
42 | ||
43 | if (test_and_clear_bit(FLG_RECVQUEUE, &bch->Flags)) { | |
44 | while ((skb = skb_dequeue(&bch->rqueue))) { | |
1b2b03f8 KK |
45 | bch->rcount--; |
46 | if (likely(bch->ch.peer)) { | |
47 | err = bch->ch.recv(bch->ch.peer, skb); | |
48 | if (err) | |
49 | dev_kfree_skb(skb); | |
50 | } else | |
51 | dev_kfree_skb(skb); | |
52 | } | |
53 | } | |
54 | } | |
55 | ||
56 | int | |
57 | mISDN_initdchannel(struct dchannel *ch, int maxlen, void *phf) | |
58 | { | |
59 | test_and_set_bit(FLG_HDLC, &ch->Flags); | |
60 | ch->maxlen = maxlen; | |
61 | ch->hw = NULL; | |
62 | ch->rx_skb = NULL; | |
63 | ch->tx_skb = NULL; | |
64 | ch->tx_idx = 0; | |
65 | ch->phfunc = phf; | |
66 | skb_queue_head_init(&ch->squeue); | |
67 | skb_queue_head_init(&ch->rqueue); | |
68 | INIT_LIST_HEAD(&ch->dev.bchannels); | |
69 | INIT_WORK(&ch->workq, dchannel_bh); | |
70 | return 0; | |
71 | } | |
72 | EXPORT_SYMBOL(mISDN_initdchannel); | |
73 | ||
74 | int | |
034005a0 KK |
75 | mISDN_initbchannel(struct bchannel *ch, unsigned short maxlen, |
76 | unsigned short minlen) | |
1b2b03f8 KK |
77 | { |
78 | ch->Flags = 0; | |
034005a0 KK |
79 | ch->minlen = minlen; |
80 | ch->next_minlen = minlen; | |
81 | ch->init_minlen = minlen; | |
1b2b03f8 | 82 | ch->maxlen = maxlen; |
034005a0 KK |
83 | ch->next_maxlen = maxlen; |
84 | ch->init_maxlen = maxlen; | |
1b2b03f8 KK |
85 | ch->hw = NULL; |
86 | ch->rx_skb = NULL; | |
87 | ch->tx_skb = NULL; | |
88 | ch->tx_idx = 0; | |
89 | skb_queue_head_init(&ch->rqueue); | |
90 | ch->rcount = 0; | |
91 | ch->next_skb = NULL; | |
92 | INIT_WORK(&ch->workq, bchannel_bh); | |
93 | return 0; | |
94 | } | |
95 | EXPORT_SYMBOL(mISDN_initbchannel); | |
96 | ||
97 | int | |
98 | mISDN_freedchannel(struct dchannel *ch) | |
99 | { | |
100 | if (ch->tx_skb) { | |
101 | dev_kfree_skb(ch->tx_skb); | |
102 | ch->tx_skb = NULL; | |
103 | } | |
104 | if (ch->rx_skb) { | |
105 | dev_kfree_skb(ch->rx_skb); | |
106 | ch->rx_skb = NULL; | |
107 | } | |
108 | skb_queue_purge(&ch->squeue); | |
109 | skb_queue_purge(&ch->rqueue); | |
43829731 | 110 | flush_work(&ch->workq); |
1b2b03f8 KK |
111 | return 0; |
112 | } | |
113 | EXPORT_SYMBOL(mISDN_freedchannel); | |
114 | ||
fb286f04 KK |
115 | void |
116 | mISDN_clear_bchannel(struct bchannel *ch) | |
1b2b03f8 KK |
117 | { |
118 | if (ch->tx_skb) { | |
119 | dev_kfree_skb(ch->tx_skb); | |
120 | ch->tx_skb = NULL; | |
121 | } | |
fb286f04 | 122 | ch->tx_idx = 0; |
1b2b03f8 KK |
123 | if (ch->rx_skb) { |
124 | dev_kfree_skb(ch->rx_skb); | |
125 | ch->rx_skb = NULL; | |
126 | } | |
127 | if (ch->next_skb) { | |
128 | dev_kfree_skb(ch->next_skb); | |
129 | ch->next_skb = NULL; | |
130 | } | |
fb286f04 KK |
131 | test_and_clear_bit(FLG_TX_BUSY, &ch->Flags); |
132 | test_and_clear_bit(FLG_TX_NEXT, &ch->Flags); | |
133 | test_and_clear_bit(FLG_ACTIVE, &ch->Flags); | |
6d1ee48f KK |
134 | test_and_clear_bit(FLG_FILLEMPTY, &ch->Flags); |
135 | test_and_clear_bit(FLG_TX_EMPTY, &ch->Flags); | |
c27b46e7 KK |
136 | test_and_clear_bit(FLG_RX_OFF, &ch->Flags); |
137 | ch->dropcnt = 0; | |
034005a0 KK |
138 | ch->minlen = ch->init_minlen; |
139 | ch->next_minlen = ch->init_minlen; | |
140 | ch->maxlen = ch->init_maxlen; | |
141 | ch->next_maxlen = ch->init_maxlen; | |
4b921eda KK |
142 | skb_queue_purge(&ch->rqueue); |
143 | ch->rcount = 0; | |
fb286f04 KK |
144 | } |
145 | EXPORT_SYMBOL(mISDN_clear_bchannel); | |
146 | ||
4b921eda | 147 | void |
fb286f04 KK |
148 | mISDN_freebchannel(struct bchannel *ch) |
149 | { | |
4b921eda | 150 | cancel_work_sync(&ch->workq); |
fb286f04 | 151 | mISDN_clear_bchannel(ch); |
1b2b03f8 KK |
152 | } |
153 | EXPORT_SYMBOL(mISDN_freebchannel); | |
154 | ||
034005a0 KK |
155 | int |
156 | mISDN_ctrl_bchannel(struct bchannel *bch, struct mISDN_ctrl_req *cq) | |
157 | { | |
158 | int ret = 0; | |
159 | ||
160 | switch (cq->op) { | |
161 | case MISDN_CTRL_GETOP: | |
c27b46e7 KK |
162 | cq->op = MISDN_CTRL_RX_BUFFER | MISDN_CTRL_FILL_EMPTY | |
163 | MISDN_CTRL_RX_OFF; | |
6d1ee48f KK |
164 | break; |
165 | case MISDN_CTRL_FILL_EMPTY: | |
166 | if (cq->p1) { | |
167 | memset(bch->fill, cq->p2 & 0xff, MISDN_BCH_FILL_SIZE); | |
168 | test_and_set_bit(FLG_FILLEMPTY, &bch->Flags); | |
169 | } else { | |
170 | test_and_clear_bit(FLG_FILLEMPTY, &bch->Flags); | |
171 | } | |
034005a0 | 172 | break; |
c27b46e7 KK |
173 | case MISDN_CTRL_RX_OFF: |
174 | /* read back dropped byte count */ | |
175 | cq->p2 = bch->dropcnt; | |
176 | if (cq->p1) | |
177 | test_and_set_bit(FLG_RX_OFF, &bch->Flags); | |
178 | else | |
179 | test_and_clear_bit(FLG_RX_OFF, &bch->Flags); | |
180 | bch->dropcnt = 0; | |
181 | break; | |
034005a0 KK |
182 | case MISDN_CTRL_RX_BUFFER: |
183 | if (cq->p2 > MISDN_CTRL_RX_SIZE_IGNORE) | |
184 | bch->next_maxlen = cq->p2; | |
185 | if (cq->p1 > MISDN_CTRL_RX_SIZE_IGNORE) | |
186 | bch->next_minlen = cq->p1; | |
187 | /* we return the old values */ | |
188 | cq->p1 = bch->minlen; | |
189 | cq->p2 = bch->maxlen; | |
190 | break; | |
191 | default: | |
192 | pr_info("mISDN unhandled control %x operation\n", cq->op); | |
193 | ret = -EINVAL; | |
194 | break; | |
195 | } | |
196 | return ret; | |
197 | } | |
198 | EXPORT_SYMBOL(mISDN_ctrl_bchannel); | |
199 | ||
1b2b03f8 KK |
200 | static inline u_int |
201 | get_sapi_tei(u_char *p) | |
202 | { | |
203 | u_int sapi, tei; | |
204 | ||
205 | sapi = *p >> 2; | |
206 | tei = p[1] >> 1; | |
207 | return sapi | (tei << 8); | |
208 | } | |
209 | ||
210 | void | |
211 | recv_Dchannel(struct dchannel *dch) | |
212 | { | |
213 | struct mISDNhead *hh; | |
214 | ||
215 | if (dch->rx_skb->len < 2) { /* at least 2 for sapi / tei */ | |
216 | dev_kfree_skb(dch->rx_skb); | |
217 | dch->rx_skb = NULL; | |
218 | return; | |
219 | } | |
220 | hh = mISDN_HEAD_P(dch->rx_skb); | |
221 | hh->prim = PH_DATA_IND; | |
222 | hh->id = get_sapi_tei(dch->rx_skb->data); | |
223 | skb_queue_tail(&dch->rqueue, dch->rx_skb); | |
224 | dch->rx_skb = NULL; | |
225 | schedule_event(dch, FLG_RECVQUEUE); | |
226 | } | |
227 | EXPORT_SYMBOL(recv_Dchannel); | |
228 | ||
1f28fa19 MB |
229 | void |
230 | recv_Echannel(struct dchannel *ech, struct dchannel *dch) | |
231 | { | |
232 | struct mISDNhead *hh; | |
233 | ||
234 | if (ech->rx_skb->len < 2) { /* at least 2 for sapi / tei */ | |
235 | dev_kfree_skb(ech->rx_skb); | |
236 | ech->rx_skb = NULL; | |
237 | return; | |
238 | } | |
239 | hh = mISDN_HEAD_P(ech->rx_skb); | |
240 | hh->prim = PH_DATA_E_IND; | |
241 | hh->id = get_sapi_tei(ech->rx_skb->data); | |
242 | skb_queue_tail(&dch->rqueue, ech->rx_skb); | |
243 | ech->rx_skb = NULL; | |
244 | schedule_event(dch, FLG_RECVQUEUE); | |
245 | } | |
246 | EXPORT_SYMBOL(recv_Echannel); | |
247 | ||
1b2b03f8 | 248 | void |
034005a0 | 249 | recv_Bchannel(struct bchannel *bch, unsigned int id, bool force) |
1b2b03f8 KK |
250 | { |
251 | struct mISDNhead *hh; | |
252 | ||
7206e659 KK |
253 | /* if allocation did fail upper functions still may call us */ |
254 | if (unlikely(!bch->rx_skb)) | |
1b2b03f8 | 255 | return; |
7206e659 KK |
256 | if (unlikely(!bch->rx_skb->len)) { |
257 | /* we have no data to send - this may happen after recovery | |
258 | * from overflow or too small allocation. | |
259 | * We need to free the buffer here */ | |
260 | dev_kfree_skb(bch->rx_skb); | |
261 | bch->rx_skb = NULL; | |
262 | } else { | |
034005a0 KK |
263 | if (test_bit(FLG_TRANSPARENT, &bch->Flags) && |
264 | (bch->rx_skb->len < bch->minlen) && !force) | |
265 | return; | |
7206e659 KK |
266 | hh = mISDN_HEAD_P(bch->rx_skb); |
267 | hh->prim = PH_DATA_IND; | |
268 | hh->id = id; | |
269 | if (bch->rcount >= 64) { | |
270 | printk(KERN_WARNING | |
271 | "B%d receive queue overflow - flushing!\n", | |
272 | bch->nr); | |
273 | skb_queue_purge(&bch->rqueue); | |
274 | } | |
275 | bch->rcount++; | |
276 | skb_queue_tail(&bch->rqueue, bch->rx_skb); | |
277 | bch->rx_skb = NULL; | |
278 | schedule_event(bch, FLG_RECVQUEUE); | |
1b2b03f8 | 279 | } |
1b2b03f8 KK |
280 | } |
281 | EXPORT_SYMBOL(recv_Bchannel); | |
282 | ||
283 | void | |
284 | recv_Dchannel_skb(struct dchannel *dch, struct sk_buff *skb) | |
285 | { | |
286 | skb_queue_tail(&dch->rqueue, skb); | |
287 | schedule_event(dch, FLG_RECVQUEUE); | |
288 | } | |
289 | EXPORT_SYMBOL(recv_Dchannel_skb); | |
290 | ||
291 | void | |
292 | recv_Bchannel_skb(struct bchannel *bch, struct sk_buff *skb) | |
293 | { | |
294 | if (bch->rcount >= 64) { | |
11618496 | 295 | printk(KERN_WARNING "B-channel %p receive queue overflow, " |
475be4d8 | 296 | "flushing!\n", bch); |
11618496 AE |
297 | skb_queue_purge(&bch->rqueue); |
298 | bch->rcount = 0; | |
1b2b03f8 KK |
299 | } |
300 | bch->rcount++; | |
301 | skb_queue_tail(&bch->rqueue, skb); | |
302 | schedule_event(bch, FLG_RECVQUEUE); | |
303 | } | |
304 | EXPORT_SYMBOL(recv_Bchannel_skb); | |
305 | ||
306 | static void | |
307 | confirm_Dsend(struct dchannel *dch) | |
308 | { | |
309 | struct sk_buff *skb; | |
310 | ||
311 | skb = _alloc_mISDN_skb(PH_DATA_CNF, mISDN_HEAD_ID(dch->tx_skb), | |
475be4d8 | 312 | 0, NULL, GFP_ATOMIC); |
1b2b03f8 KK |
313 | if (!skb) { |
314 | printk(KERN_ERR "%s: no skb id %x\n", __func__, | |
475be4d8 | 315 | mISDN_HEAD_ID(dch->tx_skb)); |
1b2b03f8 KK |
316 | return; |
317 | } | |
318 | skb_queue_tail(&dch->rqueue, skb); | |
319 | schedule_event(dch, FLG_RECVQUEUE); | |
320 | } | |
321 | ||
322 | int | |
323 | get_next_dframe(struct dchannel *dch) | |
324 | { | |
325 | dch->tx_idx = 0; | |
326 | dch->tx_skb = skb_dequeue(&dch->squeue); | |
327 | if (dch->tx_skb) { | |
328 | confirm_Dsend(dch); | |
329 | return 1; | |
330 | } | |
331 | dch->tx_skb = NULL; | |
332 | test_and_clear_bit(FLG_TX_BUSY, &dch->Flags); | |
333 | return 0; | |
334 | } | |
335 | EXPORT_SYMBOL(get_next_dframe); | |
336 | ||
8bfddfbe | 337 | static void |
1b2b03f8 KK |
338 | confirm_Bsend(struct bchannel *bch) |
339 | { | |
340 | struct sk_buff *skb; | |
341 | ||
11618496 AE |
342 | if (bch->rcount >= 64) { |
343 | printk(KERN_WARNING "B-channel %p receive queue overflow, " | |
475be4d8 | 344 | "flushing!\n", bch); |
11618496 AE |
345 | skb_queue_purge(&bch->rqueue); |
346 | bch->rcount = 0; | |
347 | } | |
1b2b03f8 | 348 | skb = _alloc_mISDN_skb(PH_DATA_CNF, mISDN_HEAD_ID(bch->tx_skb), |
475be4d8 | 349 | 0, NULL, GFP_ATOMIC); |
1b2b03f8 KK |
350 | if (!skb) { |
351 | printk(KERN_ERR "%s: no skb id %x\n", __func__, | |
475be4d8 | 352 | mISDN_HEAD_ID(bch->tx_skb)); |
1b2b03f8 KK |
353 | return; |
354 | } | |
355 | bch->rcount++; | |
356 | skb_queue_tail(&bch->rqueue, skb); | |
357 | schedule_event(bch, FLG_RECVQUEUE); | |
358 | } | |
1b2b03f8 KK |
359 | |
360 | int | |
361 | get_next_bframe(struct bchannel *bch) | |
362 | { | |
363 | bch->tx_idx = 0; | |
364 | if (test_bit(FLG_TX_NEXT, &bch->Flags)) { | |
365 | bch->tx_skb = bch->next_skb; | |
366 | if (bch->tx_skb) { | |
367 | bch->next_skb = NULL; | |
368 | test_and_clear_bit(FLG_TX_NEXT, &bch->Flags); | |
8bfddfbe KK |
369 | /* confirm imediately to allow next data */ |
370 | confirm_Bsend(bch); | |
1b2b03f8 KK |
371 | return 1; |
372 | } else { | |
373 | test_and_clear_bit(FLG_TX_NEXT, &bch->Flags); | |
374 | printk(KERN_WARNING "B TX_NEXT without skb\n"); | |
375 | } | |
376 | } | |
377 | bch->tx_skb = NULL; | |
378 | test_and_clear_bit(FLG_TX_BUSY, &bch->Flags); | |
379 | return 0; | |
380 | } | |
381 | EXPORT_SYMBOL(get_next_bframe); | |
382 | ||
383 | void | |
384 | queue_ch_frame(struct mISDNchannel *ch, u_int pr, int id, struct sk_buff *skb) | |
385 | { | |
386 | struct mISDNhead *hh; | |
387 | ||
388 | if (!skb) { | |
389 | _queue_data(ch, pr, id, 0, NULL, GFP_ATOMIC); | |
390 | } else { | |
391 | if (ch->peer) { | |
392 | hh = mISDN_HEAD_P(skb); | |
393 | hh->prim = pr; | |
394 | hh->id = id; | |
395 | if (!ch->recv(ch->peer, skb)) | |
396 | return; | |
397 | } | |
398 | dev_kfree_skb(skb); | |
399 | } | |
400 | } | |
401 | EXPORT_SYMBOL(queue_ch_frame); | |
402 | ||
403 | int | |
404 | dchannel_senddata(struct dchannel *ch, struct sk_buff *skb) | |
405 | { | |
406 | /* check oversize */ | |
407 | if (skb->len <= 0) { | |
408 | printk(KERN_WARNING "%s: skb too small\n", __func__); | |
409 | return -EINVAL; | |
410 | } | |
411 | if (skb->len > ch->maxlen) { | |
412 | printk(KERN_WARNING "%s: skb too large(%d/%d)\n", | |
475be4d8 | 413 | __func__, skb->len, ch->maxlen); |
1b2b03f8 KK |
414 | return -EINVAL; |
415 | } | |
416 | /* HW lock must be obtained */ | |
417 | if (test_and_set_bit(FLG_TX_BUSY, &ch->Flags)) { | |
418 | skb_queue_tail(&ch->squeue, skb); | |
419 | return 0; | |
420 | } else { | |
421 | /* write to fifo */ | |
422 | ch->tx_skb = skb; | |
423 | ch->tx_idx = 0; | |
424 | return 1; | |
425 | } | |
426 | } | |
427 | EXPORT_SYMBOL(dchannel_senddata); | |
428 | ||
429 | int | |
430 | bchannel_senddata(struct bchannel *ch, struct sk_buff *skb) | |
431 | { | |
432 | ||
433 | /* check oversize */ | |
434 | if (skb->len <= 0) { | |
435 | printk(KERN_WARNING "%s: skb too small\n", __func__); | |
436 | return -EINVAL; | |
437 | } | |
438 | if (skb->len > ch->maxlen) { | |
439 | printk(KERN_WARNING "%s: skb too large(%d/%d)\n", | |
475be4d8 | 440 | __func__, skb->len, ch->maxlen); |
1b2b03f8 KK |
441 | return -EINVAL; |
442 | } | |
443 | /* HW lock must be obtained */ | |
444 | /* check for pending next_skb */ | |
445 | if (ch->next_skb) { | |
446 | printk(KERN_WARNING | |
475be4d8 JP |
447 | "%s: next_skb exist ERROR (skb->len=%d next_skb->len=%d)\n", |
448 | __func__, skb->len, ch->next_skb->len); | |
1b2b03f8 KK |
449 | return -EBUSY; |
450 | } | |
451 | if (test_and_set_bit(FLG_TX_BUSY, &ch->Flags)) { | |
452 | test_and_set_bit(FLG_TX_NEXT, &ch->Flags); | |
453 | ch->next_skb = skb; | |
454 | return 0; | |
455 | } else { | |
456 | /* write to fifo */ | |
457 | ch->tx_skb = skb; | |
458 | ch->tx_idx = 0; | |
8bfddfbe | 459 | confirm_Bsend(ch); |
1b2b03f8 KK |
460 | return 1; |
461 | } | |
462 | } | |
463 | EXPORT_SYMBOL(bchannel_senddata); | |
7206e659 KK |
464 | |
465 | /* The function allocates a new receive skb on demand with a size for the | |
466 | * requirements of the current protocol. It returns the tailroom of the | |
467 | * receive skb or an error. | |
468 | */ | |
469 | int | |
470 | bchannel_get_rxbuf(struct bchannel *bch, int reqlen) | |
471 | { | |
472 | int len; | |
473 | ||
474 | if (bch->rx_skb) { | |
475 | len = skb_tailroom(bch->rx_skb); | |
476 | if (len < reqlen) { | |
477 | pr_warning("B%d no space for %d (only %d) bytes\n", | |
478 | bch->nr, reqlen, len); | |
479 | if (test_bit(FLG_TRANSPARENT, &bch->Flags)) { | |
480 | /* send what we have now and try a new buffer */ | |
034005a0 | 481 | recv_Bchannel(bch, 0, true); |
7206e659 KK |
482 | } else { |
483 | /* on HDLC we have to drop too big frames */ | |
484 | return -EMSGSIZE; | |
485 | } | |
486 | } else { | |
487 | return len; | |
488 | } | |
489 | } | |
034005a0 KK |
490 | /* update current min/max length first */ |
491 | if (unlikely(bch->maxlen != bch->next_maxlen)) | |
492 | bch->maxlen = bch->next_maxlen; | |
493 | if (unlikely(bch->minlen != bch->next_minlen)) | |
494 | bch->minlen = bch->next_minlen; | |
7206e659 KK |
495 | if (unlikely(reqlen > bch->maxlen)) |
496 | return -EMSGSIZE; | |
034005a0 KK |
497 | if (test_bit(FLG_TRANSPARENT, &bch->Flags)) { |
498 | if (reqlen >= bch->minlen) { | |
499 | len = reqlen; | |
500 | } else { | |
501 | len = 2 * bch->minlen; | |
502 | if (len > bch->maxlen) | |
503 | len = bch->maxlen; | |
504 | } | |
505 | } else { | |
506 | /* with HDLC we do not know the length yet */ | |
7206e659 | 507 | len = bch->maxlen; |
034005a0 | 508 | } |
7206e659 KK |
509 | bch->rx_skb = mI_alloc_skb(len, GFP_ATOMIC); |
510 | if (!bch->rx_skb) { | |
511 | pr_warning("B%d receive no memory for %d bytes\n", | |
512 | bch->nr, len); | |
513 | len = -ENOMEM; | |
514 | } | |
515 | return len; | |
516 | } | |
517 | EXPORT_SYMBOL(bchannel_get_rxbuf); |