• Home
  • History
  • Annotate
  • Line#
  • Navigate
  • Raw
  • Download
  • only in /asuswrt-rt-n18u-9.0.0.4.380.2695/release/src-rt-6.x.4708/linux/linux-2.6.36/drivers/isdn/mISDN/
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/gfp.h>
19#include <linux/module.h>
20#include <linux/mISDNhw.h>
21
22static void
23dchannel_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
45static void
46bchannel_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
65int
66mISDN_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}
81EXPORT_SYMBOL(mISDN_initdchannel);
82
83int
84mISDN_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}
98EXPORT_SYMBOL(mISDN_initbchannel);
99
100int
101mISDN_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_scheduled_work();
114	return 0;
115}
116EXPORT_SYMBOL(mISDN_freedchannel);
117
118void
119mISDN_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}
138EXPORT_SYMBOL(mISDN_clear_bchannel);
139
140int
141mISDN_freebchannel(struct bchannel *ch)
142{
143	mISDN_clear_bchannel(ch);
144	skb_queue_purge(&ch->rqueue);
145	ch->rcount = 0;
146	flush_scheduled_work();
147	return 0;
148}
149EXPORT_SYMBOL(mISDN_freebchannel);
150
151static inline u_int
152get_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
161void
162recv_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}
178EXPORT_SYMBOL(recv_Dchannel);
179
180void
181recv_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}
197EXPORT_SYMBOL(recv_Echannel);
198
199void
200recv_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			"fushing!\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}
219EXPORT_SYMBOL(recv_Bchannel);
220
221void
222recv_Dchannel_skb(struct dchannel *dch, struct sk_buff *skb)
223{
224	skb_queue_tail(&dch->rqueue, skb);
225	schedule_event(dch, FLG_RECVQUEUE);
226}
227EXPORT_SYMBOL(recv_Dchannel_skb);
228
229void
230recv_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			"fushing!\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}
242EXPORT_SYMBOL(recv_Bchannel_skb);
243
244static void
245confirm_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
260int
261get_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}
273EXPORT_SYMBOL(get_next_dframe);
274
275void
276confirm_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			"fushing!\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}
297EXPORT_SYMBOL(confirm_Bsend);
298
299int
300get_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}
320EXPORT_SYMBOL(get_next_bframe);
321
322void
323queue_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}
340EXPORT_SYMBOL(queue_ch_frame);
341
342int
343dchannel_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}
366EXPORT_SYMBOL(dchannel_senddata);
367
368int
369bchannel_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}
401EXPORT_SYMBOL(bchannel_senddata);
402