xref: /linux/drivers/isdn/mISDN/hwchannel.c (revision c95baf12f5077419db01313ab61c2aac007d40cd)
11802d0beSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
21b2b03f8SKarsten Keil /*
31b2b03f8SKarsten Keil  *
41b2b03f8SKarsten Keil  * Author	Karsten Keil <kkeil@novell.com>
51b2b03f8SKarsten Keil  *
61b2b03f8SKarsten Keil  * Copyright 2008  by Karsten Keil <kkeil@novell.com>
71b2b03f8SKarsten Keil  */
81b2b03f8SKarsten Keil 
95a0e3ad6STejun Heo #include <linux/gfp.h>
101b2b03f8SKarsten Keil #include <linux/module.h>
111b2b03f8SKarsten Keil #include <linux/mISDNhw.h>
121b2b03f8SKarsten Keil 
131b2b03f8SKarsten Keil static void
dchannel_bh(struct work_struct * ws)141b2b03f8SKarsten Keil dchannel_bh(struct work_struct *ws)
151b2b03f8SKarsten Keil {
161b2b03f8SKarsten Keil 	struct dchannel	*dch  = container_of(ws, struct dchannel, workq);
171b2b03f8SKarsten Keil 	struct sk_buff	*skb;
181b2b03f8SKarsten Keil 	int		err;
191b2b03f8SKarsten Keil 
201b2b03f8SKarsten Keil 	if (test_and_clear_bit(FLG_RECVQUEUE, &dch->Flags)) {
211b2b03f8SKarsten Keil 		while ((skb = skb_dequeue(&dch->rqueue))) {
221b2b03f8SKarsten Keil 			if (likely(dch->dev.D.peer)) {
231b2b03f8SKarsten Keil 				err = dch->dev.D.recv(dch->dev.D.peer, skb);
241b2b03f8SKarsten Keil 				if (err)
251b2b03f8SKarsten Keil 					dev_kfree_skb(skb);
261b2b03f8SKarsten Keil 			} else
271b2b03f8SKarsten Keil 				dev_kfree_skb(skb);
281b2b03f8SKarsten Keil 		}
291b2b03f8SKarsten Keil 	}
301b2b03f8SKarsten Keil 	if (test_and_clear_bit(FLG_PHCHANGE, &dch->Flags)) {
311b2b03f8SKarsten Keil 		if (dch->phfunc)
321b2b03f8SKarsten Keil 			dch->phfunc(dch);
331b2b03f8SKarsten Keil 	}
341b2b03f8SKarsten Keil }
351b2b03f8SKarsten Keil 
361b2b03f8SKarsten Keil static void
bchannel_bh(struct work_struct * ws)371b2b03f8SKarsten Keil bchannel_bh(struct work_struct *ws)
381b2b03f8SKarsten Keil {
391b2b03f8SKarsten Keil 	struct bchannel	*bch  = container_of(ws, struct bchannel, workq);
401b2b03f8SKarsten Keil 	struct sk_buff	*skb;
411b2b03f8SKarsten Keil 	int		err;
421b2b03f8SKarsten Keil 
431b2b03f8SKarsten Keil 	if (test_and_clear_bit(FLG_RECVQUEUE, &bch->Flags)) {
441b2b03f8SKarsten Keil 		while ((skb = skb_dequeue(&bch->rqueue))) {
451b2b03f8SKarsten Keil 			bch->rcount--;
461b2b03f8SKarsten Keil 			if (likely(bch->ch.peer)) {
471b2b03f8SKarsten Keil 				err = bch->ch.recv(bch->ch.peer, skb);
481b2b03f8SKarsten Keil 				if (err)
491b2b03f8SKarsten Keil 					dev_kfree_skb(skb);
501b2b03f8SKarsten Keil 			} else
511b2b03f8SKarsten Keil 				dev_kfree_skb(skb);
521b2b03f8SKarsten Keil 		}
531b2b03f8SKarsten Keil 	}
541b2b03f8SKarsten Keil }
551b2b03f8SKarsten Keil 
561b2b03f8SKarsten Keil int
mISDN_initdchannel(struct dchannel * ch,int maxlen,void * phf)571b2b03f8SKarsten Keil mISDN_initdchannel(struct dchannel *ch, int maxlen, void *phf)
581b2b03f8SKarsten Keil {
591b2b03f8SKarsten Keil 	test_and_set_bit(FLG_HDLC, &ch->Flags);
601b2b03f8SKarsten Keil 	ch->maxlen = maxlen;
611b2b03f8SKarsten Keil 	ch->hw = NULL;
621b2b03f8SKarsten Keil 	ch->rx_skb = NULL;
631b2b03f8SKarsten Keil 	ch->tx_skb = NULL;
641b2b03f8SKarsten Keil 	ch->tx_idx = 0;
651b2b03f8SKarsten Keil 	ch->phfunc = phf;
661b2b03f8SKarsten Keil 	skb_queue_head_init(&ch->squeue);
671b2b03f8SKarsten Keil 	skb_queue_head_init(&ch->rqueue);
681b2b03f8SKarsten Keil 	INIT_LIST_HEAD(&ch->dev.bchannels);
691b2b03f8SKarsten Keil 	INIT_WORK(&ch->workq, dchannel_bh);
701b2b03f8SKarsten Keil 	return 0;
711b2b03f8SKarsten Keil }
721b2b03f8SKarsten Keil EXPORT_SYMBOL(mISDN_initdchannel);
731b2b03f8SKarsten Keil 
741b2b03f8SKarsten Keil int
mISDN_initbchannel(struct bchannel * ch,unsigned short maxlen,unsigned short minlen)75034005a0SKarsten Keil mISDN_initbchannel(struct bchannel *ch, unsigned short maxlen,
76034005a0SKarsten Keil 		   unsigned short minlen)
771b2b03f8SKarsten Keil {
781b2b03f8SKarsten Keil 	ch->Flags = 0;
79034005a0SKarsten Keil 	ch->minlen = minlen;
80034005a0SKarsten Keil 	ch->next_minlen = minlen;
81034005a0SKarsten Keil 	ch->init_minlen = minlen;
821b2b03f8SKarsten Keil 	ch->maxlen = maxlen;
83034005a0SKarsten Keil 	ch->next_maxlen = maxlen;
84034005a0SKarsten Keil 	ch->init_maxlen = maxlen;
851b2b03f8SKarsten Keil 	ch->hw = NULL;
861b2b03f8SKarsten Keil 	ch->rx_skb = NULL;
871b2b03f8SKarsten Keil 	ch->tx_skb = NULL;
881b2b03f8SKarsten Keil 	ch->tx_idx = 0;
891b2b03f8SKarsten Keil 	skb_queue_head_init(&ch->rqueue);
901b2b03f8SKarsten Keil 	ch->rcount = 0;
911b2b03f8SKarsten Keil 	ch->next_skb = NULL;
921b2b03f8SKarsten Keil 	INIT_WORK(&ch->workq, bchannel_bh);
931b2b03f8SKarsten Keil 	return 0;
941b2b03f8SKarsten Keil }
951b2b03f8SKarsten Keil EXPORT_SYMBOL(mISDN_initbchannel);
961b2b03f8SKarsten Keil 
971b2b03f8SKarsten Keil int
mISDN_freedchannel(struct dchannel * ch)981b2b03f8SKarsten Keil mISDN_freedchannel(struct dchannel *ch)
991b2b03f8SKarsten Keil {
1001b2b03f8SKarsten Keil 	if (ch->tx_skb) {
1011b2b03f8SKarsten Keil 		dev_kfree_skb(ch->tx_skb);
1021b2b03f8SKarsten Keil 		ch->tx_skb = NULL;
1031b2b03f8SKarsten Keil 	}
1041b2b03f8SKarsten Keil 	if (ch->rx_skb) {
1051b2b03f8SKarsten Keil 		dev_kfree_skb(ch->rx_skb);
1061b2b03f8SKarsten Keil 		ch->rx_skb = NULL;
1071b2b03f8SKarsten Keil 	}
1081b2b03f8SKarsten Keil 	skb_queue_purge(&ch->squeue);
1091b2b03f8SKarsten Keil 	skb_queue_purge(&ch->rqueue);
11043829731STejun Heo 	flush_work(&ch->workq);
1111b2b03f8SKarsten Keil 	return 0;
1121b2b03f8SKarsten Keil }
1131b2b03f8SKarsten Keil EXPORT_SYMBOL(mISDN_freedchannel);
1141b2b03f8SKarsten Keil 
115fb286f04SKarsten Keil void
mISDN_clear_bchannel(struct bchannel * ch)116fb286f04SKarsten Keil mISDN_clear_bchannel(struct bchannel *ch)
1171b2b03f8SKarsten Keil {
1181b2b03f8SKarsten Keil 	if (ch->tx_skb) {
1191b2b03f8SKarsten Keil 		dev_kfree_skb(ch->tx_skb);
1201b2b03f8SKarsten Keil 		ch->tx_skb = NULL;
1211b2b03f8SKarsten Keil 	}
122fb286f04SKarsten Keil 	ch->tx_idx = 0;
1231b2b03f8SKarsten Keil 	if (ch->rx_skb) {
1241b2b03f8SKarsten Keil 		dev_kfree_skb(ch->rx_skb);
1251b2b03f8SKarsten Keil 		ch->rx_skb = NULL;
1261b2b03f8SKarsten Keil 	}
1271b2b03f8SKarsten Keil 	if (ch->next_skb) {
1281b2b03f8SKarsten Keil 		dev_kfree_skb(ch->next_skb);
1291b2b03f8SKarsten Keil 		ch->next_skb = NULL;
1301b2b03f8SKarsten Keil 	}
131fb286f04SKarsten Keil 	test_and_clear_bit(FLG_TX_BUSY, &ch->Flags);
132fb286f04SKarsten Keil 	test_and_clear_bit(FLG_TX_NEXT, &ch->Flags);
133fb286f04SKarsten Keil 	test_and_clear_bit(FLG_ACTIVE, &ch->Flags);
1346d1ee48fSKarsten Keil 	test_and_clear_bit(FLG_FILLEMPTY, &ch->Flags);
1356d1ee48fSKarsten Keil 	test_and_clear_bit(FLG_TX_EMPTY, &ch->Flags);
136c27b46e7SKarsten Keil 	test_and_clear_bit(FLG_RX_OFF, &ch->Flags);
137c27b46e7SKarsten Keil 	ch->dropcnt = 0;
138034005a0SKarsten Keil 	ch->minlen = ch->init_minlen;
139034005a0SKarsten Keil 	ch->next_minlen = ch->init_minlen;
140034005a0SKarsten Keil 	ch->maxlen = ch->init_maxlen;
141034005a0SKarsten Keil 	ch->next_maxlen = ch->init_maxlen;
1424b921edaSKarsten Keil 	skb_queue_purge(&ch->rqueue);
1434b921edaSKarsten Keil 	ch->rcount = 0;
144fb286f04SKarsten Keil }
145fb286f04SKarsten Keil EXPORT_SYMBOL(mISDN_clear_bchannel);
146fb286f04SKarsten Keil 
1474b921edaSKarsten Keil void
mISDN_freebchannel(struct bchannel * ch)148fb286f04SKarsten Keil mISDN_freebchannel(struct bchannel *ch)
149fb286f04SKarsten Keil {
1504b921edaSKarsten Keil 	cancel_work_sync(&ch->workq);
151fb286f04SKarsten Keil 	mISDN_clear_bchannel(ch);
1521b2b03f8SKarsten Keil }
1531b2b03f8SKarsten Keil EXPORT_SYMBOL(mISDN_freebchannel);
1541b2b03f8SKarsten Keil 
155034005a0SKarsten Keil int
mISDN_ctrl_bchannel(struct bchannel * bch,struct mISDN_ctrl_req * cq)156034005a0SKarsten Keil mISDN_ctrl_bchannel(struct bchannel *bch, struct mISDN_ctrl_req *cq)
157034005a0SKarsten Keil {
158034005a0SKarsten Keil 	int ret = 0;
159034005a0SKarsten Keil 
160034005a0SKarsten Keil 	switch (cq->op) {
161034005a0SKarsten Keil 	case MISDN_CTRL_GETOP:
162c27b46e7SKarsten Keil 		cq->op = MISDN_CTRL_RX_BUFFER | MISDN_CTRL_FILL_EMPTY |
163c27b46e7SKarsten Keil 			 MISDN_CTRL_RX_OFF;
1646d1ee48fSKarsten Keil 		break;
1656d1ee48fSKarsten Keil 	case MISDN_CTRL_FILL_EMPTY:
1666d1ee48fSKarsten Keil 		if (cq->p1) {
1676d1ee48fSKarsten Keil 			memset(bch->fill, cq->p2 & 0xff, MISDN_BCH_FILL_SIZE);
1686d1ee48fSKarsten Keil 			test_and_set_bit(FLG_FILLEMPTY, &bch->Flags);
1696d1ee48fSKarsten Keil 		} else {
1706d1ee48fSKarsten Keil 			test_and_clear_bit(FLG_FILLEMPTY, &bch->Flags);
1716d1ee48fSKarsten Keil 		}
172034005a0SKarsten Keil 		break;
173c27b46e7SKarsten Keil 	case MISDN_CTRL_RX_OFF:
174c27b46e7SKarsten Keil 		/* read back dropped byte count */
175c27b46e7SKarsten Keil 		cq->p2 = bch->dropcnt;
176c27b46e7SKarsten Keil 		if (cq->p1)
177c27b46e7SKarsten Keil 			test_and_set_bit(FLG_RX_OFF, &bch->Flags);
178c27b46e7SKarsten Keil 		else
179c27b46e7SKarsten Keil 			test_and_clear_bit(FLG_RX_OFF, &bch->Flags);
180c27b46e7SKarsten Keil 		bch->dropcnt = 0;
181c27b46e7SKarsten Keil 		break;
182034005a0SKarsten Keil 	case MISDN_CTRL_RX_BUFFER:
183034005a0SKarsten Keil 		if (cq->p2 > MISDN_CTRL_RX_SIZE_IGNORE)
184034005a0SKarsten Keil 			bch->next_maxlen = cq->p2;
185034005a0SKarsten Keil 		if (cq->p1 > MISDN_CTRL_RX_SIZE_IGNORE)
186034005a0SKarsten Keil 			bch->next_minlen = cq->p1;
187034005a0SKarsten Keil 		/* we return the old values */
188034005a0SKarsten Keil 		cq->p1 = bch->minlen;
189034005a0SKarsten Keil 		cq->p2 = bch->maxlen;
190034005a0SKarsten Keil 		break;
191034005a0SKarsten Keil 	default:
192034005a0SKarsten Keil 		pr_info("mISDN unhandled control %x operation\n", cq->op);
193034005a0SKarsten Keil 		ret = -EINVAL;
194034005a0SKarsten Keil 		break;
195034005a0SKarsten Keil 	}
196034005a0SKarsten Keil 	return ret;
197034005a0SKarsten Keil }
198034005a0SKarsten Keil EXPORT_SYMBOL(mISDN_ctrl_bchannel);
199034005a0SKarsten Keil 
2001b2b03f8SKarsten Keil static inline u_int
get_sapi_tei(u_char * p)2011b2b03f8SKarsten Keil get_sapi_tei(u_char *p)
2021b2b03f8SKarsten Keil {
2031b2b03f8SKarsten Keil 	u_int	sapi, tei;
2041b2b03f8SKarsten Keil 
2051b2b03f8SKarsten Keil 	sapi = *p >> 2;
2061b2b03f8SKarsten Keil 	tei = p[1] >> 1;
2071b2b03f8SKarsten Keil 	return sapi | (tei << 8);
2081b2b03f8SKarsten Keil }
2091b2b03f8SKarsten Keil 
2101b2b03f8SKarsten Keil void
recv_Dchannel(struct dchannel * dch)2111b2b03f8SKarsten Keil recv_Dchannel(struct dchannel *dch)
2121b2b03f8SKarsten Keil {
2131b2b03f8SKarsten Keil 	struct mISDNhead *hh;
2141b2b03f8SKarsten Keil 
2151b2b03f8SKarsten Keil 	if (dch->rx_skb->len < 2) { /* at least 2 for sapi / tei */
2161b2b03f8SKarsten Keil 		dev_kfree_skb(dch->rx_skb);
2171b2b03f8SKarsten Keil 		dch->rx_skb = NULL;
2181b2b03f8SKarsten Keil 		return;
2191b2b03f8SKarsten Keil 	}
2201b2b03f8SKarsten Keil 	hh = mISDN_HEAD_P(dch->rx_skb);
2211b2b03f8SKarsten Keil 	hh->prim = PH_DATA_IND;
2221b2b03f8SKarsten Keil 	hh->id = get_sapi_tei(dch->rx_skb->data);
2231b2b03f8SKarsten Keil 	skb_queue_tail(&dch->rqueue, dch->rx_skb);
2241b2b03f8SKarsten Keil 	dch->rx_skb = NULL;
2251b2b03f8SKarsten Keil 	schedule_event(dch, FLG_RECVQUEUE);
2261b2b03f8SKarsten Keil }
2271b2b03f8SKarsten Keil EXPORT_SYMBOL(recv_Dchannel);
2281b2b03f8SKarsten Keil 
2291b2b03f8SKarsten Keil void
recv_Echannel(struct dchannel * ech,struct dchannel * dch)2301f28fa19SMartin Bachem recv_Echannel(struct dchannel *ech, struct dchannel *dch)
2311f28fa19SMartin Bachem {
2321f28fa19SMartin Bachem 	struct mISDNhead *hh;
2331f28fa19SMartin Bachem 
2341f28fa19SMartin Bachem 	if (ech->rx_skb->len < 2) { /* at least 2 for sapi / tei */
2351f28fa19SMartin Bachem 		dev_kfree_skb(ech->rx_skb);
2361f28fa19SMartin Bachem 		ech->rx_skb = NULL;
2371f28fa19SMartin Bachem 		return;
2381f28fa19SMartin Bachem 	}
2391f28fa19SMartin Bachem 	hh = mISDN_HEAD_P(ech->rx_skb);
2401f28fa19SMartin Bachem 	hh->prim = PH_DATA_E_IND;
2411f28fa19SMartin Bachem 	hh->id = get_sapi_tei(ech->rx_skb->data);
2421f28fa19SMartin Bachem 	skb_queue_tail(&dch->rqueue, ech->rx_skb);
2431f28fa19SMartin Bachem 	ech->rx_skb = NULL;
2441f28fa19SMartin Bachem 	schedule_event(dch, FLG_RECVQUEUE);
2451f28fa19SMartin Bachem }
2461f28fa19SMartin Bachem EXPORT_SYMBOL(recv_Echannel);
2471f28fa19SMartin Bachem 
2481f28fa19SMartin Bachem void
recv_Bchannel(struct bchannel * bch,unsigned int id,bool force)249034005a0SKarsten Keil recv_Bchannel(struct bchannel *bch, unsigned int id, bool force)
2501b2b03f8SKarsten Keil {
2511b2b03f8SKarsten Keil 	struct mISDNhead *hh;
2521b2b03f8SKarsten Keil 
2537206e659SKarsten Keil 	/* if allocation did fail upper functions still may call us */
2547206e659SKarsten Keil 	if (unlikely(!bch->rx_skb))
2557206e659SKarsten Keil 		return;
2567206e659SKarsten Keil 	if (unlikely(!bch->rx_skb->len)) {
2577206e659SKarsten Keil 		/* we have no data to send - this may happen after recovery
2587206e659SKarsten Keil 		 * from overflow or too small allocation.
2597206e659SKarsten Keil 		 * We need to free the buffer here */
2607206e659SKarsten Keil 		dev_kfree_skb(bch->rx_skb);
2617206e659SKarsten Keil 		bch->rx_skb = NULL;
2627206e659SKarsten Keil 	} else {
263034005a0SKarsten Keil 		if (test_bit(FLG_TRANSPARENT, &bch->Flags) &&
264034005a0SKarsten Keil 		    (bch->rx_skb->len < bch->minlen) && !force)
265034005a0SKarsten Keil 				return;
2661b2b03f8SKarsten Keil 		hh = mISDN_HEAD_P(bch->rx_skb);
2671b2b03f8SKarsten Keil 		hh->prim = PH_DATA_IND;
2687cfa153dSAndreas Eversberg 		hh->id = id;
2691b2b03f8SKarsten Keil 		if (bch->rcount >= 64) {
2707206e659SKarsten Keil 			printk(KERN_WARNING
2717206e659SKarsten Keil 			       "B%d receive queue overflow - flushing!\n",
2727206e659SKarsten Keil 			       bch->nr);
27311618496SAndreas Eversberg 			skb_queue_purge(&bch->rqueue);
2741b2b03f8SKarsten Keil 		}
2751b2b03f8SKarsten Keil 		bch->rcount++;
2761b2b03f8SKarsten Keil 		skb_queue_tail(&bch->rqueue, bch->rx_skb);
2771b2b03f8SKarsten Keil 		bch->rx_skb = NULL;
2781b2b03f8SKarsten Keil 		schedule_event(bch, FLG_RECVQUEUE);
2791b2b03f8SKarsten Keil 	}
2807206e659SKarsten Keil }
2811b2b03f8SKarsten Keil EXPORT_SYMBOL(recv_Bchannel);
2821b2b03f8SKarsten Keil 
2831b2b03f8SKarsten Keil void
recv_Dchannel_skb(struct dchannel * dch,struct sk_buff * skb)2841b2b03f8SKarsten Keil recv_Dchannel_skb(struct dchannel *dch, struct sk_buff *skb)
2851b2b03f8SKarsten Keil {
2861b2b03f8SKarsten Keil 	skb_queue_tail(&dch->rqueue, skb);
2871b2b03f8SKarsten Keil 	schedule_event(dch, FLG_RECVQUEUE);
2881b2b03f8SKarsten Keil }
2891b2b03f8SKarsten Keil EXPORT_SYMBOL(recv_Dchannel_skb);
2901b2b03f8SKarsten Keil 
2911b2b03f8SKarsten Keil void
recv_Bchannel_skb(struct bchannel * bch,struct sk_buff * skb)2921b2b03f8SKarsten Keil recv_Bchannel_skb(struct bchannel *bch, struct sk_buff *skb)
2931b2b03f8SKarsten Keil {
2941b2b03f8SKarsten Keil 	if (bch->rcount >= 64) {
29511618496SAndreas Eversberg 		printk(KERN_WARNING "B-channel %p receive queue overflow, "
2961752a373SPaul Bolle 		       "flushing!\n", bch);
29711618496SAndreas Eversberg 		skb_queue_purge(&bch->rqueue);
29811618496SAndreas Eversberg 		bch->rcount = 0;
2991b2b03f8SKarsten Keil 	}
3001b2b03f8SKarsten Keil 	bch->rcount++;
3011b2b03f8SKarsten Keil 	skb_queue_tail(&bch->rqueue, skb);
3021b2b03f8SKarsten Keil 	schedule_event(bch, FLG_RECVQUEUE);
3031b2b03f8SKarsten Keil }
3041b2b03f8SKarsten Keil EXPORT_SYMBOL(recv_Bchannel_skb);
3051b2b03f8SKarsten Keil 
3061b2b03f8SKarsten Keil static void
confirm_Dsend(struct dchannel * dch)3071b2b03f8SKarsten Keil confirm_Dsend(struct dchannel *dch)
3081b2b03f8SKarsten Keil {
3091b2b03f8SKarsten Keil 	struct sk_buff	*skb;
3101b2b03f8SKarsten Keil 
3111b2b03f8SKarsten Keil 	skb = _alloc_mISDN_skb(PH_DATA_CNF, mISDN_HEAD_ID(dch->tx_skb),
3121b2b03f8SKarsten Keil 			       0, NULL, GFP_ATOMIC);
3131b2b03f8SKarsten Keil 	if (!skb) {
3141b2b03f8SKarsten Keil 		printk(KERN_ERR "%s: no skb id %x\n", __func__,
3151b2b03f8SKarsten Keil 		       mISDN_HEAD_ID(dch->tx_skb));
3161b2b03f8SKarsten Keil 		return;
3171b2b03f8SKarsten Keil 	}
3181b2b03f8SKarsten Keil 	skb_queue_tail(&dch->rqueue, skb);
3191b2b03f8SKarsten Keil 	schedule_event(dch, FLG_RECVQUEUE);
3201b2b03f8SKarsten Keil }
3211b2b03f8SKarsten Keil 
3221b2b03f8SKarsten Keil int
get_next_dframe(struct dchannel * dch)3231b2b03f8SKarsten Keil get_next_dframe(struct dchannel *dch)
3241b2b03f8SKarsten Keil {
3251b2b03f8SKarsten Keil 	dch->tx_idx = 0;
3261b2b03f8SKarsten Keil 	dch->tx_skb = skb_dequeue(&dch->squeue);
3271b2b03f8SKarsten Keil 	if (dch->tx_skb) {
3281b2b03f8SKarsten Keil 		confirm_Dsend(dch);
3291b2b03f8SKarsten Keil 		return 1;
3301b2b03f8SKarsten Keil 	}
3311b2b03f8SKarsten Keil 	dch->tx_skb = NULL;
3321b2b03f8SKarsten Keil 	test_and_clear_bit(FLG_TX_BUSY, &dch->Flags);
3331b2b03f8SKarsten Keil 	return 0;
3341b2b03f8SKarsten Keil }
3351b2b03f8SKarsten Keil EXPORT_SYMBOL(get_next_dframe);
3361b2b03f8SKarsten Keil 
3378bfddfbeSKarsten Keil static void
confirm_Bsend(struct bchannel * bch)3381b2b03f8SKarsten Keil confirm_Bsend(struct bchannel *bch)
3391b2b03f8SKarsten Keil {
3401b2b03f8SKarsten Keil 	struct sk_buff	*skb;
3411b2b03f8SKarsten Keil 
34211618496SAndreas Eversberg 	if (bch->rcount >= 64) {
34311618496SAndreas Eversberg 		printk(KERN_WARNING "B-channel %p receive queue overflow, "
3441752a373SPaul Bolle 		       "flushing!\n", bch);
34511618496SAndreas Eversberg 		skb_queue_purge(&bch->rqueue);
34611618496SAndreas Eversberg 		bch->rcount = 0;
34711618496SAndreas Eversberg 	}
3481b2b03f8SKarsten Keil 	skb = _alloc_mISDN_skb(PH_DATA_CNF, mISDN_HEAD_ID(bch->tx_skb),
3491b2b03f8SKarsten Keil 			       0, NULL, GFP_ATOMIC);
3501b2b03f8SKarsten Keil 	if (!skb) {
3511b2b03f8SKarsten Keil 		printk(KERN_ERR "%s: no skb id %x\n", __func__,
3521b2b03f8SKarsten Keil 		       mISDN_HEAD_ID(bch->tx_skb));
3531b2b03f8SKarsten Keil 		return;
3541b2b03f8SKarsten Keil 	}
3551b2b03f8SKarsten Keil 	bch->rcount++;
3561b2b03f8SKarsten Keil 	skb_queue_tail(&bch->rqueue, skb);
3571b2b03f8SKarsten Keil 	schedule_event(bch, FLG_RECVQUEUE);
3581b2b03f8SKarsten Keil }
3591b2b03f8SKarsten Keil 
3601b2b03f8SKarsten Keil int
get_next_bframe(struct bchannel * bch)3611b2b03f8SKarsten Keil get_next_bframe(struct bchannel *bch)
3621b2b03f8SKarsten Keil {
3631b2b03f8SKarsten Keil 	bch->tx_idx = 0;
3641b2b03f8SKarsten Keil 	if (test_bit(FLG_TX_NEXT, &bch->Flags)) {
3651b2b03f8SKarsten Keil 		bch->tx_skb = bch->next_skb;
3661b2b03f8SKarsten Keil 		if (bch->tx_skb) {
3671b2b03f8SKarsten Keil 			bch->next_skb = NULL;
3681b2b03f8SKarsten Keil 			test_and_clear_bit(FLG_TX_NEXT, &bch->Flags);
3698bfddfbeSKarsten Keil 			/* confirm imediately to allow next data */
3708bfddfbeSKarsten Keil 			confirm_Bsend(bch);
3711b2b03f8SKarsten Keil 			return 1;
3721b2b03f8SKarsten Keil 		} else {
3731b2b03f8SKarsten Keil 			test_and_clear_bit(FLG_TX_NEXT, &bch->Flags);
3741b2b03f8SKarsten Keil 			printk(KERN_WARNING "B TX_NEXT without skb\n");
3751b2b03f8SKarsten Keil 		}
3761b2b03f8SKarsten Keil 	}
3771b2b03f8SKarsten Keil 	bch->tx_skb = NULL;
3781b2b03f8SKarsten Keil 	test_and_clear_bit(FLG_TX_BUSY, &bch->Flags);
3791b2b03f8SKarsten Keil 	return 0;
3801b2b03f8SKarsten Keil }
3811b2b03f8SKarsten Keil EXPORT_SYMBOL(get_next_bframe);
3821b2b03f8SKarsten Keil 
3831b2b03f8SKarsten Keil void
queue_ch_frame(struct mISDNchannel * ch,u_int pr,int id,struct sk_buff * skb)3841b2b03f8SKarsten Keil queue_ch_frame(struct mISDNchannel *ch, u_int pr, int id, struct sk_buff *skb)
3851b2b03f8SKarsten Keil {
3861b2b03f8SKarsten Keil 	struct mISDNhead *hh;
3871b2b03f8SKarsten Keil 
3881b2b03f8SKarsten Keil 	if (!skb) {
3891b2b03f8SKarsten Keil 		_queue_data(ch, pr, id, 0, NULL, GFP_ATOMIC);
3901b2b03f8SKarsten Keil 	} else {
3911b2b03f8SKarsten Keil 		if (ch->peer) {
3921b2b03f8SKarsten Keil 			hh = mISDN_HEAD_P(skb);
3931b2b03f8SKarsten Keil 			hh->prim = pr;
3941b2b03f8SKarsten Keil 			hh->id = id;
3951b2b03f8SKarsten Keil 			if (!ch->recv(ch->peer, skb))
3961b2b03f8SKarsten Keil 				return;
3971b2b03f8SKarsten Keil 		}
3981b2b03f8SKarsten Keil 		dev_kfree_skb(skb);
3991b2b03f8SKarsten Keil 	}
4001b2b03f8SKarsten Keil }
4011b2b03f8SKarsten Keil EXPORT_SYMBOL(queue_ch_frame);
4021b2b03f8SKarsten Keil 
4031b2b03f8SKarsten Keil int
dchannel_senddata(struct dchannel * ch,struct sk_buff * skb)4041b2b03f8SKarsten Keil dchannel_senddata(struct dchannel *ch, struct sk_buff *skb)
4051b2b03f8SKarsten Keil {
4061b2b03f8SKarsten Keil 	/* check oversize */
4071b2b03f8SKarsten Keil 	if (skb->len <= 0) {
4081b2b03f8SKarsten Keil 		printk(KERN_WARNING "%s: skb too small\n", __func__);
4091b2b03f8SKarsten Keil 		return -EINVAL;
4101b2b03f8SKarsten Keil 	}
4111b2b03f8SKarsten Keil 	if (skb->len > ch->maxlen) {
4121b2b03f8SKarsten Keil 		printk(KERN_WARNING "%s: skb too large(%d/%d)\n",
4131b2b03f8SKarsten Keil 		       __func__, skb->len, ch->maxlen);
4141b2b03f8SKarsten Keil 		return -EINVAL;
4151b2b03f8SKarsten Keil 	}
4161b2b03f8SKarsten Keil 	/* HW lock must be obtained */
4171b2b03f8SKarsten Keil 	if (test_and_set_bit(FLG_TX_BUSY, &ch->Flags)) {
4181b2b03f8SKarsten Keil 		skb_queue_tail(&ch->squeue, skb);
4191b2b03f8SKarsten Keil 		return 0;
4201b2b03f8SKarsten Keil 	} else {
4211b2b03f8SKarsten Keil 		/* write to fifo */
4221b2b03f8SKarsten Keil 		ch->tx_skb = skb;
4231b2b03f8SKarsten Keil 		ch->tx_idx = 0;
4241b2b03f8SKarsten Keil 		return 1;
4251b2b03f8SKarsten Keil 	}
4261b2b03f8SKarsten Keil }
4271b2b03f8SKarsten Keil EXPORT_SYMBOL(dchannel_senddata);
4281b2b03f8SKarsten Keil 
4291b2b03f8SKarsten Keil int
bchannel_senddata(struct bchannel * ch,struct sk_buff * skb)4301b2b03f8SKarsten Keil bchannel_senddata(struct bchannel *ch, struct sk_buff *skb)
4311b2b03f8SKarsten Keil {
4321b2b03f8SKarsten Keil 
4331b2b03f8SKarsten Keil 	/* check oversize */
4341b2b03f8SKarsten Keil 	if (skb->len <= 0) {
4351b2b03f8SKarsten Keil 		printk(KERN_WARNING "%s: skb too small\n", __func__);
4361b2b03f8SKarsten Keil 		return -EINVAL;
4371b2b03f8SKarsten Keil 	}
4381b2b03f8SKarsten Keil 	if (skb->len > ch->maxlen) {
4391b2b03f8SKarsten Keil 		printk(KERN_WARNING "%s: skb too large(%d/%d)\n",
4401b2b03f8SKarsten Keil 		       __func__, skb->len, ch->maxlen);
4411b2b03f8SKarsten Keil 		return -EINVAL;
4421b2b03f8SKarsten Keil 	}
4431b2b03f8SKarsten Keil 	/* HW lock must be obtained */
4441b2b03f8SKarsten Keil 	/* check for pending next_skb */
4451b2b03f8SKarsten Keil 	if (ch->next_skb) {
4461b2b03f8SKarsten Keil 		printk(KERN_WARNING
4471b2b03f8SKarsten Keil 		       "%s: next_skb exist ERROR (skb->len=%d next_skb->len=%d)\n",
4481b2b03f8SKarsten Keil 		       __func__, skb->len, ch->next_skb->len);
4491b2b03f8SKarsten Keil 		return -EBUSY;
4501b2b03f8SKarsten Keil 	}
4511b2b03f8SKarsten Keil 	if (test_and_set_bit(FLG_TX_BUSY, &ch->Flags)) {
4521b2b03f8SKarsten Keil 		test_and_set_bit(FLG_TX_NEXT, &ch->Flags);
4531b2b03f8SKarsten Keil 		ch->next_skb = skb;
4541b2b03f8SKarsten Keil 		return 0;
4551b2b03f8SKarsten Keil 	} else {
4561b2b03f8SKarsten Keil 		/* write to fifo */
4571b2b03f8SKarsten Keil 		ch->tx_skb = skb;
4581b2b03f8SKarsten Keil 		ch->tx_idx = 0;
4598bfddfbeSKarsten Keil 		confirm_Bsend(ch);
4601b2b03f8SKarsten Keil 		return 1;
4611b2b03f8SKarsten Keil 	}
4621b2b03f8SKarsten Keil }
4631b2b03f8SKarsten Keil EXPORT_SYMBOL(bchannel_senddata);
4647206e659SKarsten Keil 
4657206e659SKarsten Keil /* The function allocates a new receive skb on demand with a size for the
4667206e659SKarsten Keil  * requirements of the current protocol. It returns the tailroom of the
4677206e659SKarsten Keil  * receive skb or an error.
4687206e659SKarsten Keil  */
4697206e659SKarsten Keil int
bchannel_get_rxbuf(struct bchannel * bch,int reqlen)4707206e659SKarsten Keil bchannel_get_rxbuf(struct bchannel *bch, int reqlen)
4717206e659SKarsten Keil {
4727206e659SKarsten Keil 	int len;
4737206e659SKarsten Keil 
4747206e659SKarsten Keil 	if (bch->rx_skb) {
4757206e659SKarsten Keil 		len = skb_tailroom(bch->rx_skb);
4767206e659SKarsten Keil 		if (len < reqlen) {
477*257daba4SKefeng Wang 			pr_warn("B%d no space for %d (only %d) bytes\n",
4787206e659SKarsten Keil 				bch->nr, reqlen, len);
4797206e659SKarsten Keil 			if (test_bit(FLG_TRANSPARENT, &bch->Flags)) {
4807206e659SKarsten Keil 				/* send what we have now and try a new buffer */
481034005a0SKarsten Keil 				recv_Bchannel(bch, 0, true);
4827206e659SKarsten Keil 			} else {
4837206e659SKarsten Keil 				/* on HDLC we have to drop too big frames */
4847206e659SKarsten Keil 				return -EMSGSIZE;
4857206e659SKarsten Keil 			}
4867206e659SKarsten Keil 		} else {
4877206e659SKarsten Keil 			return len;
4887206e659SKarsten Keil 		}
4897206e659SKarsten Keil 	}
490034005a0SKarsten Keil 	/* update current min/max length first */
491034005a0SKarsten Keil 	if (unlikely(bch->maxlen != bch->next_maxlen))
492034005a0SKarsten Keil 		bch->maxlen = bch->next_maxlen;
493034005a0SKarsten Keil 	if (unlikely(bch->minlen != bch->next_minlen))
494034005a0SKarsten Keil 		bch->minlen = bch->next_minlen;
4957206e659SKarsten Keil 	if (unlikely(reqlen > bch->maxlen))
4967206e659SKarsten Keil 		return -EMSGSIZE;
497034005a0SKarsten Keil 	if (test_bit(FLG_TRANSPARENT, &bch->Flags)) {
498034005a0SKarsten Keil 		if (reqlen >= bch->minlen) {
4997206e659SKarsten Keil 			len = reqlen;
500034005a0SKarsten Keil 		} else {
501034005a0SKarsten Keil 			len = 2 * bch->minlen;
502034005a0SKarsten Keil 			if (len > bch->maxlen)
5037206e659SKarsten Keil 				len = bch->maxlen;
504034005a0SKarsten Keil 		}
505034005a0SKarsten Keil 	} else {
506034005a0SKarsten Keil 		/* with HDLC we do not know the length yet */
507034005a0SKarsten Keil 		len = bch->maxlen;
508034005a0SKarsten Keil 	}
5097206e659SKarsten Keil 	bch->rx_skb = mI_alloc_skb(len, GFP_ATOMIC);
5107206e659SKarsten Keil 	if (!bch->rx_skb) {
511*257daba4SKefeng Wang 		pr_warn("B%d receive no memory for %d bytes\n", bch->nr, len);
5127206e659SKarsten Keil 		len = -ENOMEM;
5137206e659SKarsten Keil 	}
5147206e659SKarsten Keil 	return len;
5157206e659SKarsten Keil }
5167206e659SKarsten Keil EXPORT_SYMBOL(bchannel_get_rxbuf);
517