Commit | Line | Data |
---|---|---|
1b2b03f8 KK |
1 | /* |
2 | * | |
3 | * Author Karsten Keil <kkeil@novell.com> | |
4 | * | |
5 | * Copyright 2008 by Karsten Keil <kkeil@novell.com> | |
6 | * | |
7 | * This program is free software; you can redistribute it and/or modify | |
8 | * it under the terms of the GNU General Public License version 2 as | |
9 | * published by the Free Software Foundation. | |
10 | * | |
11 | * This program is distributed in the hope that it will be useful, | |
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
14 | * GNU General Public License for more details. | |
15 | * | |
16 | */ | |
17 | ||
18 | #include <linux/module.h> | |
19 | #include <linux/mISDNhw.h> | |
20 | ||
21 | static void | |
22 | dchannel_bh(struct work_struct *ws) | |
23 | { | |
24 | struct dchannel *dch = container_of(ws, struct dchannel, workq); | |
25 | struct sk_buff *skb; | |
26 | int err; | |
27 | ||
28 | if (test_and_clear_bit(FLG_RECVQUEUE, &dch->Flags)) { | |
29 | while ((skb = skb_dequeue(&dch->rqueue))) { | |
30 | if (likely(dch->dev.D.peer)) { | |
31 | err = dch->dev.D.recv(dch->dev.D.peer, skb); | |
32 | if (err) | |
33 | dev_kfree_skb(skb); | |
34 | } else | |
35 | dev_kfree_skb(skb); | |
36 | } | |
37 | } | |
38 | if (test_and_clear_bit(FLG_PHCHANGE, &dch->Flags)) { | |
39 | if (dch->phfunc) | |
40 | dch->phfunc(dch); | |
41 | } | |
42 | } | |
43 | ||
44 | static void | |
45 | bchannel_bh(struct work_struct *ws) | |
46 | { | |
47 | struct bchannel *bch = container_of(ws, struct bchannel, workq); | |
48 | struct sk_buff *skb; | |
49 | int err; | |
50 | ||
51 | if (test_and_clear_bit(FLG_RECVQUEUE, &bch->Flags)) { | |
52 | while ((skb = skb_dequeue(&bch->rqueue))) { | |
53 | if (bch->rcount >= 64) | |
54 | printk(KERN_WARNING "B-channel %p receive " | |
55 | "queue if full, but empties...\n", bch); | |
56 | bch->rcount--; | |
57 | if (likely(bch->ch.peer)) { | |
58 | err = bch->ch.recv(bch->ch.peer, skb); | |
59 | if (err) | |
60 | dev_kfree_skb(skb); | |
61 | } else | |
62 | dev_kfree_skb(skb); | |
63 | } | |
64 | } | |
65 | } | |
66 | ||
67 | int | |
68 | mISDN_initdchannel(struct dchannel *ch, int maxlen, void *phf) | |
69 | { | |
70 | test_and_set_bit(FLG_HDLC, &ch->Flags); | |
71 | ch->maxlen = maxlen; | |
72 | ch->hw = NULL; | |
73 | ch->rx_skb = NULL; | |
74 | ch->tx_skb = NULL; | |
75 | ch->tx_idx = 0; | |
76 | ch->phfunc = phf; | |
77 | skb_queue_head_init(&ch->squeue); | |
78 | skb_queue_head_init(&ch->rqueue); | |
79 | INIT_LIST_HEAD(&ch->dev.bchannels); | |
80 | INIT_WORK(&ch->workq, dchannel_bh); | |
81 | return 0; | |
82 | } | |
83 | EXPORT_SYMBOL(mISDN_initdchannel); | |
84 | ||
85 | int | |
86 | mISDN_initbchannel(struct bchannel *ch, int maxlen) | |
87 | { | |
88 | ch->Flags = 0; | |
89 | ch->maxlen = maxlen; | |
90 | ch->hw = NULL; | |
91 | ch->rx_skb = NULL; | |
92 | ch->tx_skb = NULL; | |
93 | ch->tx_idx = 0; | |
94 | skb_queue_head_init(&ch->rqueue); | |
95 | ch->rcount = 0; | |
96 | ch->next_skb = NULL; | |
97 | INIT_WORK(&ch->workq, bchannel_bh); | |
98 | return 0; | |
99 | } | |
100 | EXPORT_SYMBOL(mISDN_initbchannel); | |
101 | ||
102 | int | |
103 | mISDN_freedchannel(struct dchannel *ch) | |
104 | { | |
105 | if (ch->tx_skb) { | |
106 | dev_kfree_skb(ch->tx_skb); | |
107 | ch->tx_skb = NULL; | |
108 | } | |
109 | if (ch->rx_skb) { | |
110 | dev_kfree_skb(ch->rx_skb); | |
111 | ch->rx_skb = NULL; | |
112 | } | |
113 | skb_queue_purge(&ch->squeue); | |
114 | skb_queue_purge(&ch->rqueue); | |
115 | flush_scheduled_work(); | |
116 | return 0; | |
117 | } | |
118 | EXPORT_SYMBOL(mISDN_freedchannel); | |
119 | ||
120 | int | |
121 | mISDN_freebchannel(struct bchannel *ch) | |
122 | { | |
123 | if (ch->tx_skb) { | |
124 | dev_kfree_skb(ch->tx_skb); | |
125 | ch->tx_skb = NULL; | |
126 | } | |
127 | if (ch->rx_skb) { | |
128 | dev_kfree_skb(ch->rx_skb); | |
129 | ch->rx_skb = NULL; | |
130 | } | |
131 | if (ch->next_skb) { | |
132 | dev_kfree_skb(ch->next_skb); | |
133 | ch->next_skb = NULL; | |
134 | } | |
135 | skb_queue_purge(&ch->rqueue); | |
136 | ch->rcount = 0; | |
137 | flush_scheduled_work(); | |
138 | return 0; | |
139 | } | |
140 | EXPORT_SYMBOL(mISDN_freebchannel); | |
141 | ||
142 | static inline u_int | |
143 | get_sapi_tei(u_char *p) | |
144 | { | |
145 | u_int sapi, tei; | |
146 | ||
147 | sapi = *p >> 2; | |
148 | tei = p[1] >> 1; | |
149 | return sapi | (tei << 8); | |
150 | } | |
151 | ||
152 | void | |
153 | recv_Dchannel(struct dchannel *dch) | |
154 | { | |
155 | struct mISDNhead *hh; | |
156 | ||
157 | if (dch->rx_skb->len < 2) { /* at least 2 for sapi / tei */ | |
158 | dev_kfree_skb(dch->rx_skb); | |
159 | dch->rx_skb = NULL; | |
160 | return; | |
161 | } | |
162 | hh = mISDN_HEAD_P(dch->rx_skb); | |
163 | hh->prim = PH_DATA_IND; | |
164 | hh->id = get_sapi_tei(dch->rx_skb->data); | |
165 | skb_queue_tail(&dch->rqueue, dch->rx_skb); | |
166 | dch->rx_skb = NULL; | |
167 | schedule_event(dch, FLG_RECVQUEUE); | |
168 | } | |
169 | EXPORT_SYMBOL(recv_Dchannel); | |
170 | ||
171 | void | |
172 | recv_Bchannel(struct bchannel *bch) | |
173 | { | |
174 | struct mISDNhead *hh; | |
175 | ||
176 | hh = mISDN_HEAD_P(bch->rx_skb); | |
177 | hh->prim = PH_DATA_IND; | |
178 | hh->id = MISDN_ID_ANY; | |
179 | if (bch->rcount >= 64) { | |
180 | dev_kfree_skb(bch->rx_skb); | |
181 | bch->rx_skb = NULL; | |
182 | return; | |
183 | } | |
184 | bch->rcount++; | |
185 | skb_queue_tail(&bch->rqueue, bch->rx_skb); | |
186 | bch->rx_skb = NULL; | |
187 | schedule_event(bch, FLG_RECVQUEUE); | |
188 | } | |
189 | EXPORT_SYMBOL(recv_Bchannel); | |
190 | ||
191 | void | |
192 | recv_Dchannel_skb(struct dchannel *dch, struct sk_buff *skb) | |
193 | { | |
194 | skb_queue_tail(&dch->rqueue, skb); | |
195 | schedule_event(dch, FLG_RECVQUEUE); | |
196 | } | |
197 | EXPORT_SYMBOL(recv_Dchannel_skb); | |
198 | ||
199 | void | |
200 | recv_Bchannel_skb(struct bchannel *bch, struct sk_buff *skb) | |
201 | { | |
202 | if (bch->rcount >= 64) { | |
203 | dev_kfree_skb(skb); | |
204 | return; | |
205 | } | |
206 | bch->rcount++; | |
207 | skb_queue_tail(&bch->rqueue, skb); | |
208 | schedule_event(bch, FLG_RECVQUEUE); | |
209 | } | |
210 | EXPORT_SYMBOL(recv_Bchannel_skb); | |
211 | ||
212 | static void | |
213 | confirm_Dsend(struct dchannel *dch) | |
214 | { | |
215 | struct sk_buff *skb; | |
216 | ||
217 | skb = _alloc_mISDN_skb(PH_DATA_CNF, mISDN_HEAD_ID(dch->tx_skb), | |
218 | 0, NULL, GFP_ATOMIC); | |
219 | if (!skb) { | |
220 | printk(KERN_ERR "%s: no skb id %x\n", __func__, | |
221 | mISDN_HEAD_ID(dch->tx_skb)); | |
222 | return; | |
223 | } | |
224 | skb_queue_tail(&dch->rqueue, skb); | |
225 | schedule_event(dch, FLG_RECVQUEUE); | |
226 | } | |
227 | ||
228 | int | |
229 | get_next_dframe(struct dchannel *dch) | |
230 | { | |
231 | dch->tx_idx = 0; | |
232 | dch->tx_skb = skb_dequeue(&dch->squeue); | |
233 | if (dch->tx_skb) { | |
234 | confirm_Dsend(dch); | |
235 | return 1; | |
236 | } | |
237 | dch->tx_skb = NULL; | |
238 | test_and_clear_bit(FLG_TX_BUSY, &dch->Flags); | |
239 | return 0; | |
240 | } | |
241 | EXPORT_SYMBOL(get_next_dframe); | |
242 | ||
243 | void | |
244 | confirm_Bsend(struct bchannel *bch) | |
245 | { | |
246 | struct sk_buff *skb; | |
247 | ||
248 | if (bch->rcount >= 64) | |
249 | return; | |
250 | skb = _alloc_mISDN_skb(PH_DATA_CNF, mISDN_HEAD_ID(bch->tx_skb), | |
251 | 0, NULL, GFP_ATOMIC); | |
252 | if (!skb) { | |
253 | printk(KERN_ERR "%s: no skb id %x\n", __func__, | |
254 | mISDN_HEAD_ID(bch->tx_skb)); | |
255 | return; | |
256 | } | |
257 | bch->rcount++; | |
258 | skb_queue_tail(&bch->rqueue, skb); | |
259 | schedule_event(bch, FLG_RECVQUEUE); | |
260 | } | |
261 | EXPORT_SYMBOL(confirm_Bsend); | |
262 | ||
263 | int | |
264 | get_next_bframe(struct bchannel *bch) | |
265 | { | |
266 | bch->tx_idx = 0; | |
267 | if (test_bit(FLG_TX_NEXT, &bch->Flags)) { | |
268 | bch->tx_skb = bch->next_skb; | |
269 | if (bch->tx_skb) { | |
270 | bch->next_skb = NULL; | |
271 | test_and_clear_bit(FLG_TX_NEXT, &bch->Flags); | |
272 | if (!test_bit(FLG_TRANSPARENT, &bch->Flags)) | |
273 | confirm_Bsend(bch); /* not for transparent */ | |
274 | return 1; | |
275 | } else { | |
276 | test_and_clear_bit(FLG_TX_NEXT, &bch->Flags); | |
277 | printk(KERN_WARNING "B TX_NEXT without skb\n"); | |
278 | } | |
279 | } | |
280 | bch->tx_skb = NULL; | |
281 | test_and_clear_bit(FLG_TX_BUSY, &bch->Flags); | |
282 | return 0; | |
283 | } | |
284 | EXPORT_SYMBOL(get_next_bframe); | |
285 | ||
286 | void | |
287 | queue_ch_frame(struct mISDNchannel *ch, u_int pr, int id, struct sk_buff *skb) | |
288 | { | |
289 | struct mISDNhead *hh; | |
290 | ||
291 | if (!skb) { | |
292 | _queue_data(ch, pr, id, 0, NULL, GFP_ATOMIC); | |
293 | } else { | |
294 | if (ch->peer) { | |
295 | hh = mISDN_HEAD_P(skb); | |
296 | hh->prim = pr; | |
297 | hh->id = id; | |
298 | if (!ch->recv(ch->peer, skb)) | |
299 | return; | |
300 | } | |
301 | dev_kfree_skb(skb); | |
302 | } | |
303 | } | |
304 | EXPORT_SYMBOL(queue_ch_frame); | |
305 | ||
306 | int | |
307 | dchannel_senddata(struct dchannel *ch, struct sk_buff *skb) | |
308 | { | |
309 | /* check oversize */ | |
310 | if (skb->len <= 0) { | |
311 | printk(KERN_WARNING "%s: skb too small\n", __func__); | |
312 | return -EINVAL; | |
313 | } | |
314 | if (skb->len > ch->maxlen) { | |
315 | printk(KERN_WARNING "%s: skb too large(%d/%d)\n", | |
316 | __func__, skb->len, ch->maxlen); | |
317 | return -EINVAL; | |
318 | } | |
319 | /* HW lock must be obtained */ | |
320 | if (test_and_set_bit(FLG_TX_BUSY, &ch->Flags)) { | |
321 | skb_queue_tail(&ch->squeue, skb); | |
322 | return 0; | |
323 | } else { | |
324 | /* write to fifo */ | |
325 | ch->tx_skb = skb; | |
326 | ch->tx_idx = 0; | |
327 | return 1; | |
328 | } | |
329 | } | |
330 | EXPORT_SYMBOL(dchannel_senddata); | |
331 | ||
332 | int | |
333 | bchannel_senddata(struct bchannel *ch, struct sk_buff *skb) | |
334 | { | |
335 | ||
336 | /* check oversize */ | |
337 | if (skb->len <= 0) { | |
338 | printk(KERN_WARNING "%s: skb too small\n", __func__); | |
339 | return -EINVAL; | |
340 | } | |
341 | if (skb->len > ch->maxlen) { | |
342 | printk(KERN_WARNING "%s: skb too large(%d/%d)\n", | |
343 | __func__, skb->len, ch->maxlen); | |
344 | return -EINVAL; | |
345 | } | |
346 | /* HW lock must be obtained */ | |
347 | /* check for pending next_skb */ | |
348 | if (ch->next_skb) { | |
349 | printk(KERN_WARNING | |
350 | "%s: next_skb exist ERROR (skb->len=%d next_skb->len=%d)\n", | |
351 | __func__, skb->len, ch->next_skb->len); | |
352 | return -EBUSY; | |
353 | } | |
354 | if (test_and_set_bit(FLG_TX_BUSY, &ch->Flags)) { | |
355 | test_and_set_bit(FLG_TX_NEXT, &ch->Flags); | |
356 | ch->next_skb = skb; | |
357 | return 0; | |
358 | } else { | |
359 | /* write to fifo */ | |
360 | ch->tx_skb = skb; | |
361 | ch->tx_idx = 0; | |
362 | return 1; | |
363 | } | |
364 | } | |
365 | EXPORT_SYMBOL(bchannel_senddata); |