Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
awilliam
GitHub Repository: awilliam/linux-vfio
Path: blob/master/drivers/isdn/mISDN/hwchannel.c
15111 views
1
/*
2
*
3
* Author Karsten Keil <[email protected]>
4
*
5
* Copyright 2008 by Karsten Keil <[email protected]>
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/gfp.h>
19
#include <linux/module.h>
20
#include <linux/mISDNhw.h>
21
22
static void
23
dchannel_bh(struct work_struct *ws)
24
{
25
struct dchannel *dch = container_of(ws, struct dchannel, workq);
26
struct sk_buff *skb;
27
int err;
28
29
if (test_and_clear_bit(FLG_RECVQUEUE, &dch->Flags)) {
30
while ((skb = skb_dequeue(&dch->rqueue))) {
31
if (likely(dch->dev.D.peer)) {
32
err = dch->dev.D.recv(dch->dev.D.peer, skb);
33
if (err)
34
dev_kfree_skb(skb);
35
} else
36
dev_kfree_skb(skb);
37
}
38
}
39
if (test_and_clear_bit(FLG_PHCHANGE, &dch->Flags)) {
40
if (dch->phfunc)
41
dch->phfunc(dch);
42
}
43
}
44
45
static void
46
bchannel_bh(struct work_struct *ws)
47
{
48
struct bchannel *bch = container_of(ws, struct bchannel, workq);
49
struct sk_buff *skb;
50
int err;
51
52
if (test_and_clear_bit(FLG_RECVQUEUE, &bch->Flags)) {
53
while ((skb = skb_dequeue(&bch->rqueue))) {
54
bch->rcount--;
55
if (likely(bch->ch.peer)) {
56
err = bch->ch.recv(bch->ch.peer, skb);
57
if (err)
58
dev_kfree_skb(skb);
59
} else
60
dev_kfree_skb(skb);
61
}
62
}
63
}
64
65
int
66
mISDN_initdchannel(struct dchannel *ch, int maxlen, void *phf)
67
{
68
test_and_set_bit(FLG_HDLC, &ch->Flags);
69
ch->maxlen = maxlen;
70
ch->hw = NULL;
71
ch->rx_skb = NULL;
72
ch->tx_skb = NULL;
73
ch->tx_idx = 0;
74
ch->phfunc = phf;
75
skb_queue_head_init(&ch->squeue);
76
skb_queue_head_init(&ch->rqueue);
77
INIT_LIST_HEAD(&ch->dev.bchannels);
78
INIT_WORK(&ch->workq, dchannel_bh);
79
return 0;
80
}
81
EXPORT_SYMBOL(mISDN_initdchannel);
82
83
int
84
mISDN_initbchannel(struct bchannel *ch, int maxlen)
85
{
86
ch->Flags = 0;
87
ch->maxlen = maxlen;
88
ch->hw = NULL;
89
ch->rx_skb = NULL;
90
ch->tx_skb = NULL;
91
ch->tx_idx = 0;
92
skb_queue_head_init(&ch->rqueue);
93
ch->rcount = 0;
94
ch->next_skb = NULL;
95
INIT_WORK(&ch->workq, bchannel_bh);
96
return 0;
97
}
98
EXPORT_SYMBOL(mISDN_initbchannel);
99
100
int
101
mISDN_freedchannel(struct dchannel *ch)
102
{
103
if (ch->tx_skb) {
104
dev_kfree_skb(ch->tx_skb);
105
ch->tx_skb = NULL;
106
}
107
if (ch->rx_skb) {
108
dev_kfree_skb(ch->rx_skb);
109
ch->rx_skb = NULL;
110
}
111
skb_queue_purge(&ch->squeue);
112
skb_queue_purge(&ch->rqueue);
113
flush_work_sync(&ch->workq);
114
return 0;
115
}
116
EXPORT_SYMBOL(mISDN_freedchannel);
117
118
void
119
mISDN_clear_bchannel(struct bchannel *ch)
120
{
121
if (ch->tx_skb) {
122
dev_kfree_skb(ch->tx_skb);
123
ch->tx_skb = NULL;
124
}
125
ch->tx_idx = 0;
126
if (ch->rx_skb) {
127
dev_kfree_skb(ch->rx_skb);
128
ch->rx_skb = NULL;
129
}
130
if (ch->next_skb) {
131
dev_kfree_skb(ch->next_skb);
132
ch->next_skb = NULL;
133
}
134
test_and_clear_bit(FLG_TX_BUSY, &ch->Flags);
135
test_and_clear_bit(FLG_TX_NEXT, &ch->Flags);
136
test_and_clear_bit(FLG_ACTIVE, &ch->Flags);
137
}
138
EXPORT_SYMBOL(mISDN_clear_bchannel);
139
140
int
141
mISDN_freebchannel(struct bchannel *ch)
142
{
143
mISDN_clear_bchannel(ch);
144
skb_queue_purge(&ch->rqueue);
145
ch->rcount = 0;
146
flush_work_sync(&ch->workq);
147
return 0;
148
}
149
EXPORT_SYMBOL(mISDN_freebchannel);
150
151
static inline u_int
152
get_sapi_tei(u_char *p)
153
{
154
u_int sapi, tei;
155
156
sapi = *p >> 2;
157
tei = p[1] >> 1;
158
return sapi | (tei << 8);
159
}
160
161
void
162
recv_Dchannel(struct dchannel *dch)
163
{
164
struct mISDNhead *hh;
165
166
if (dch->rx_skb->len < 2) { /* at least 2 for sapi / tei */
167
dev_kfree_skb(dch->rx_skb);
168
dch->rx_skb = NULL;
169
return;
170
}
171
hh = mISDN_HEAD_P(dch->rx_skb);
172
hh->prim = PH_DATA_IND;
173
hh->id = get_sapi_tei(dch->rx_skb->data);
174
skb_queue_tail(&dch->rqueue, dch->rx_skb);
175
dch->rx_skb = NULL;
176
schedule_event(dch, FLG_RECVQUEUE);
177
}
178
EXPORT_SYMBOL(recv_Dchannel);
179
180
void
181
recv_Echannel(struct dchannel *ech, struct dchannel *dch)
182
{
183
struct mISDNhead *hh;
184
185
if (ech->rx_skb->len < 2) { /* at least 2 for sapi / tei */
186
dev_kfree_skb(ech->rx_skb);
187
ech->rx_skb = NULL;
188
return;
189
}
190
hh = mISDN_HEAD_P(ech->rx_skb);
191
hh->prim = PH_DATA_E_IND;
192
hh->id = get_sapi_tei(ech->rx_skb->data);
193
skb_queue_tail(&dch->rqueue, ech->rx_skb);
194
ech->rx_skb = NULL;
195
schedule_event(dch, FLG_RECVQUEUE);
196
}
197
EXPORT_SYMBOL(recv_Echannel);
198
199
void
200
recv_Bchannel(struct bchannel *bch, unsigned int id)
201
{
202
struct mISDNhead *hh;
203
204
hh = mISDN_HEAD_P(bch->rx_skb);
205
hh->prim = PH_DATA_IND;
206
hh->id = id;
207
if (bch->rcount >= 64) {
208
printk(KERN_WARNING "B-channel %p receive queue overflow, "
209
"flushing!\n", bch);
210
skb_queue_purge(&bch->rqueue);
211
bch->rcount = 0;
212
return;
213
}
214
bch->rcount++;
215
skb_queue_tail(&bch->rqueue, bch->rx_skb);
216
bch->rx_skb = NULL;
217
schedule_event(bch, FLG_RECVQUEUE);
218
}
219
EXPORT_SYMBOL(recv_Bchannel);
220
221
void
222
recv_Dchannel_skb(struct dchannel *dch, struct sk_buff *skb)
223
{
224
skb_queue_tail(&dch->rqueue, skb);
225
schedule_event(dch, FLG_RECVQUEUE);
226
}
227
EXPORT_SYMBOL(recv_Dchannel_skb);
228
229
void
230
recv_Bchannel_skb(struct bchannel *bch, struct sk_buff *skb)
231
{
232
if (bch->rcount >= 64) {
233
printk(KERN_WARNING "B-channel %p receive queue overflow, "
234
"flushing!\n", bch);
235
skb_queue_purge(&bch->rqueue);
236
bch->rcount = 0;
237
}
238
bch->rcount++;
239
skb_queue_tail(&bch->rqueue, skb);
240
schedule_event(bch, FLG_RECVQUEUE);
241
}
242
EXPORT_SYMBOL(recv_Bchannel_skb);
243
244
static void
245
confirm_Dsend(struct dchannel *dch)
246
{
247
struct sk_buff *skb;
248
249
skb = _alloc_mISDN_skb(PH_DATA_CNF, mISDN_HEAD_ID(dch->tx_skb),
250
0, NULL, GFP_ATOMIC);
251
if (!skb) {
252
printk(KERN_ERR "%s: no skb id %x\n", __func__,
253
mISDN_HEAD_ID(dch->tx_skb));
254
return;
255
}
256
skb_queue_tail(&dch->rqueue, skb);
257
schedule_event(dch, FLG_RECVQUEUE);
258
}
259
260
int
261
get_next_dframe(struct dchannel *dch)
262
{
263
dch->tx_idx = 0;
264
dch->tx_skb = skb_dequeue(&dch->squeue);
265
if (dch->tx_skb) {
266
confirm_Dsend(dch);
267
return 1;
268
}
269
dch->tx_skb = NULL;
270
test_and_clear_bit(FLG_TX_BUSY, &dch->Flags);
271
return 0;
272
}
273
EXPORT_SYMBOL(get_next_dframe);
274
275
void
276
confirm_Bsend(struct bchannel *bch)
277
{
278
struct sk_buff *skb;
279
280
if (bch->rcount >= 64) {
281
printk(KERN_WARNING "B-channel %p receive queue overflow, "
282
"flushing!\n", bch);
283
skb_queue_purge(&bch->rqueue);
284
bch->rcount = 0;
285
}
286
skb = _alloc_mISDN_skb(PH_DATA_CNF, mISDN_HEAD_ID(bch->tx_skb),
287
0, NULL, GFP_ATOMIC);
288
if (!skb) {
289
printk(KERN_ERR "%s: no skb id %x\n", __func__,
290
mISDN_HEAD_ID(bch->tx_skb));
291
return;
292
}
293
bch->rcount++;
294
skb_queue_tail(&bch->rqueue, skb);
295
schedule_event(bch, FLG_RECVQUEUE);
296
}
297
EXPORT_SYMBOL(confirm_Bsend);
298
299
int
300
get_next_bframe(struct bchannel *bch)
301
{
302
bch->tx_idx = 0;
303
if (test_bit(FLG_TX_NEXT, &bch->Flags)) {
304
bch->tx_skb = bch->next_skb;
305
if (bch->tx_skb) {
306
bch->next_skb = NULL;
307
test_and_clear_bit(FLG_TX_NEXT, &bch->Flags);
308
if (!test_bit(FLG_TRANSPARENT, &bch->Flags))
309
confirm_Bsend(bch); /* not for transparent */
310
return 1;
311
} else {
312
test_and_clear_bit(FLG_TX_NEXT, &bch->Flags);
313
printk(KERN_WARNING "B TX_NEXT without skb\n");
314
}
315
}
316
bch->tx_skb = NULL;
317
test_and_clear_bit(FLG_TX_BUSY, &bch->Flags);
318
return 0;
319
}
320
EXPORT_SYMBOL(get_next_bframe);
321
322
void
323
queue_ch_frame(struct mISDNchannel *ch, u_int pr, int id, struct sk_buff *skb)
324
{
325
struct mISDNhead *hh;
326
327
if (!skb) {
328
_queue_data(ch, pr, id, 0, NULL, GFP_ATOMIC);
329
} else {
330
if (ch->peer) {
331
hh = mISDN_HEAD_P(skb);
332
hh->prim = pr;
333
hh->id = id;
334
if (!ch->recv(ch->peer, skb))
335
return;
336
}
337
dev_kfree_skb(skb);
338
}
339
}
340
EXPORT_SYMBOL(queue_ch_frame);
341
342
int
343
dchannel_senddata(struct dchannel *ch, struct sk_buff *skb)
344
{
345
/* check oversize */
346
if (skb->len <= 0) {
347
printk(KERN_WARNING "%s: skb too small\n", __func__);
348
return -EINVAL;
349
}
350
if (skb->len > ch->maxlen) {
351
printk(KERN_WARNING "%s: skb too large(%d/%d)\n",
352
__func__, skb->len, ch->maxlen);
353
return -EINVAL;
354
}
355
/* HW lock must be obtained */
356
if (test_and_set_bit(FLG_TX_BUSY, &ch->Flags)) {
357
skb_queue_tail(&ch->squeue, skb);
358
return 0;
359
} else {
360
/* write to fifo */
361
ch->tx_skb = skb;
362
ch->tx_idx = 0;
363
return 1;
364
}
365
}
366
EXPORT_SYMBOL(dchannel_senddata);
367
368
int
369
bchannel_senddata(struct bchannel *ch, struct sk_buff *skb)
370
{
371
372
/* check oversize */
373
if (skb->len <= 0) {
374
printk(KERN_WARNING "%s: skb too small\n", __func__);
375
return -EINVAL;
376
}
377
if (skb->len > ch->maxlen) {
378
printk(KERN_WARNING "%s: skb too large(%d/%d)\n",
379
__func__, skb->len, ch->maxlen);
380
return -EINVAL;
381
}
382
/* HW lock must be obtained */
383
/* check for pending next_skb */
384
if (ch->next_skb) {
385
printk(KERN_WARNING
386
"%s: next_skb exist ERROR (skb->len=%d next_skb->len=%d)\n",
387
__func__, skb->len, ch->next_skb->len);
388
return -EBUSY;
389
}
390
if (test_and_set_bit(FLG_TX_BUSY, &ch->Flags)) {
391
test_and_set_bit(FLG_TX_NEXT, &ch->Flags);
392
ch->next_skb = skb;
393
return 0;
394
} else {
395
/* write to fifo */
396
ch->tx_skb = skb;
397
ch->tx_idx = 0;
398
return 1;
399
}
400
}
401
EXPORT_SYMBOL(bchannel_senddata);
402
403