xref: /openbmc/linux/drivers/isdn/mISDN/hwchannel.c (revision 8fa5723aa7e053d498336b48448b292fc2e0458b)
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);
366