18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci *
48c2ecf20Sopenharmony_ci * Author	Karsten Keil <kkeil@novell.com>
58c2ecf20Sopenharmony_ci *
68c2ecf20Sopenharmony_ci * Copyright 2008  by Karsten Keil <kkeil@novell.com>
78c2ecf20Sopenharmony_ci */
88c2ecf20Sopenharmony_ci
98c2ecf20Sopenharmony_ci#include <linux/gfp.h>
108c2ecf20Sopenharmony_ci#include <linux/module.h>
118c2ecf20Sopenharmony_ci#include <linux/mISDNhw.h>
128c2ecf20Sopenharmony_ci
138c2ecf20Sopenharmony_cistatic void
148c2ecf20Sopenharmony_cidchannel_bh(struct work_struct *ws)
158c2ecf20Sopenharmony_ci{
168c2ecf20Sopenharmony_ci	struct dchannel	*dch  = container_of(ws, struct dchannel, workq);
178c2ecf20Sopenharmony_ci	struct sk_buff	*skb;
188c2ecf20Sopenharmony_ci	int		err;
198c2ecf20Sopenharmony_ci
208c2ecf20Sopenharmony_ci	if (test_and_clear_bit(FLG_RECVQUEUE, &dch->Flags)) {
218c2ecf20Sopenharmony_ci		while ((skb = skb_dequeue(&dch->rqueue))) {
228c2ecf20Sopenharmony_ci			if (likely(dch->dev.D.peer)) {
238c2ecf20Sopenharmony_ci				err = dch->dev.D.recv(dch->dev.D.peer, skb);
248c2ecf20Sopenharmony_ci				if (err)
258c2ecf20Sopenharmony_ci					dev_kfree_skb(skb);
268c2ecf20Sopenharmony_ci			} else
278c2ecf20Sopenharmony_ci				dev_kfree_skb(skb);
288c2ecf20Sopenharmony_ci		}
298c2ecf20Sopenharmony_ci	}
308c2ecf20Sopenharmony_ci	if (test_and_clear_bit(FLG_PHCHANGE, &dch->Flags)) {
318c2ecf20Sopenharmony_ci		if (dch->phfunc)
328c2ecf20Sopenharmony_ci			dch->phfunc(dch);
338c2ecf20Sopenharmony_ci	}
348c2ecf20Sopenharmony_ci}
358c2ecf20Sopenharmony_ci
368c2ecf20Sopenharmony_cistatic void
378c2ecf20Sopenharmony_cibchannel_bh(struct work_struct *ws)
388c2ecf20Sopenharmony_ci{
398c2ecf20Sopenharmony_ci	struct bchannel	*bch  = container_of(ws, struct bchannel, workq);
408c2ecf20Sopenharmony_ci	struct sk_buff	*skb;
418c2ecf20Sopenharmony_ci	int		err;
428c2ecf20Sopenharmony_ci
438c2ecf20Sopenharmony_ci	if (test_and_clear_bit(FLG_RECVQUEUE, &bch->Flags)) {
448c2ecf20Sopenharmony_ci		while ((skb = skb_dequeue(&bch->rqueue))) {
458c2ecf20Sopenharmony_ci			bch->rcount--;
468c2ecf20Sopenharmony_ci			if (likely(bch->ch.peer)) {
478c2ecf20Sopenharmony_ci				err = bch->ch.recv(bch->ch.peer, skb);
488c2ecf20Sopenharmony_ci				if (err)
498c2ecf20Sopenharmony_ci					dev_kfree_skb(skb);
508c2ecf20Sopenharmony_ci			} else
518c2ecf20Sopenharmony_ci				dev_kfree_skb(skb);
528c2ecf20Sopenharmony_ci		}
538c2ecf20Sopenharmony_ci	}
548c2ecf20Sopenharmony_ci}
558c2ecf20Sopenharmony_ci
568c2ecf20Sopenharmony_ciint
578c2ecf20Sopenharmony_cimISDN_initdchannel(struct dchannel *ch, int maxlen, void *phf)
588c2ecf20Sopenharmony_ci{
598c2ecf20Sopenharmony_ci	test_and_set_bit(FLG_HDLC, &ch->Flags);
608c2ecf20Sopenharmony_ci	ch->maxlen = maxlen;
618c2ecf20Sopenharmony_ci	ch->hw = NULL;
628c2ecf20Sopenharmony_ci	ch->rx_skb = NULL;
638c2ecf20Sopenharmony_ci	ch->tx_skb = NULL;
648c2ecf20Sopenharmony_ci	ch->tx_idx = 0;
658c2ecf20Sopenharmony_ci	ch->phfunc = phf;
668c2ecf20Sopenharmony_ci	skb_queue_head_init(&ch->squeue);
678c2ecf20Sopenharmony_ci	skb_queue_head_init(&ch->rqueue);
688c2ecf20Sopenharmony_ci	INIT_LIST_HEAD(&ch->dev.bchannels);
698c2ecf20Sopenharmony_ci	INIT_WORK(&ch->workq, dchannel_bh);
708c2ecf20Sopenharmony_ci	return 0;
718c2ecf20Sopenharmony_ci}
728c2ecf20Sopenharmony_ciEXPORT_SYMBOL(mISDN_initdchannel);
738c2ecf20Sopenharmony_ci
748c2ecf20Sopenharmony_ciint
758c2ecf20Sopenharmony_cimISDN_initbchannel(struct bchannel *ch, unsigned short maxlen,
768c2ecf20Sopenharmony_ci		   unsigned short minlen)
778c2ecf20Sopenharmony_ci{
788c2ecf20Sopenharmony_ci	ch->Flags = 0;
798c2ecf20Sopenharmony_ci	ch->minlen = minlen;
808c2ecf20Sopenharmony_ci	ch->next_minlen = minlen;
818c2ecf20Sopenharmony_ci	ch->init_minlen = minlen;
828c2ecf20Sopenharmony_ci	ch->maxlen = maxlen;
838c2ecf20Sopenharmony_ci	ch->next_maxlen = maxlen;
848c2ecf20Sopenharmony_ci	ch->init_maxlen = maxlen;
858c2ecf20Sopenharmony_ci	ch->hw = NULL;
868c2ecf20Sopenharmony_ci	ch->rx_skb = NULL;
878c2ecf20Sopenharmony_ci	ch->tx_skb = NULL;
888c2ecf20Sopenharmony_ci	ch->tx_idx = 0;
898c2ecf20Sopenharmony_ci	skb_queue_head_init(&ch->rqueue);
908c2ecf20Sopenharmony_ci	ch->rcount = 0;
918c2ecf20Sopenharmony_ci	ch->next_skb = NULL;
928c2ecf20Sopenharmony_ci	INIT_WORK(&ch->workq, bchannel_bh);
938c2ecf20Sopenharmony_ci	return 0;
948c2ecf20Sopenharmony_ci}
958c2ecf20Sopenharmony_ciEXPORT_SYMBOL(mISDN_initbchannel);
968c2ecf20Sopenharmony_ci
978c2ecf20Sopenharmony_ciint
988c2ecf20Sopenharmony_cimISDN_freedchannel(struct dchannel *ch)
998c2ecf20Sopenharmony_ci{
1008c2ecf20Sopenharmony_ci	if (ch->tx_skb) {
1018c2ecf20Sopenharmony_ci		dev_kfree_skb(ch->tx_skb);
1028c2ecf20Sopenharmony_ci		ch->tx_skb = NULL;
1038c2ecf20Sopenharmony_ci	}
1048c2ecf20Sopenharmony_ci	if (ch->rx_skb) {
1058c2ecf20Sopenharmony_ci		dev_kfree_skb(ch->rx_skb);
1068c2ecf20Sopenharmony_ci		ch->rx_skb = NULL;
1078c2ecf20Sopenharmony_ci	}
1088c2ecf20Sopenharmony_ci	skb_queue_purge(&ch->squeue);
1098c2ecf20Sopenharmony_ci	skb_queue_purge(&ch->rqueue);
1108c2ecf20Sopenharmony_ci	flush_work(&ch->workq);
1118c2ecf20Sopenharmony_ci	return 0;
1128c2ecf20Sopenharmony_ci}
1138c2ecf20Sopenharmony_ciEXPORT_SYMBOL(mISDN_freedchannel);
1148c2ecf20Sopenharmony_ci
1158c2ecf20Sopenharmony_civoid
1168c2ecf20Sopenharmony_cimISDN_clear_bchannel(struct bchannel *ch)
1178c2ecf20Sopenharmony_ci{
1188c2ecf20Sopenharmony_ci	if (ch->tx_skb) {
1198c2ecf20Sopenharmony_ci		dev_kfree_skb(ch->tx_skb);
1208c2ecf20Sopenharmony_ci		ch->tx_skb = NULL;
1218c2ecf20Sopenharmony_ci	}
1228c2ecf20Sopenharmony_ci	ch->tx_idx = 0;
1238c2ecf20Sopenharmony_ci	if (ch->rx_skb) {
1248c2ecf20Sopenharmony_ci		dev_kfree_skb(ch->rx_skb);
1258c2ecf20Sopenharmony_ci		ch->rx_skb = NULL;
1268c2ecf20Sopenharmony_ci	}
1278c2ecf20Sopenharmony_ci	if (ch->next_skb) {
1288c2ecf20Sopenharmony_ci		dev_kfree_skb(ch->next_skb);
1298c2ecf20Sopenharmony_ci		ch->next_skb = NULL;
1308c2ecf20Sopenharmony_ci	}
1318c2ecf20Sopenharmony_ci	test_and_clear_bit(FLG_TX_BUSY, &ch->Flags);
1328c2ecf20Sopenharmony_ci	test_and_clear_bit(FLG_TX_NEXT, &ch->Flags);
1338c2ecf20Sopenharmony_ci	test_and_clear_bit(FLG_ACTIVE, &ch->Flags);
1348c2ecf20Sopenharmony_ci	test_and_clear_bit(FLG_FILLEMPTY, &ch->Flags);
1358c2ecf20Sopenharmony_ci	test_and_clear_bit(FLG_TX_EMPTY, &ch->Flags);
1368c2ecf20Sopenharmony_ci	test_and_clear_bit(FLG_RX_OFF, &ch->Flags);
1378c2ecf20Sopenharmony_ci	ch->dropcnt = 0;
1388c2ecf20Sopenharmony_ci	ch->minlen = ch->init_minlen;
1398c2ecf20Sopenharmony_ci	ch->next_minlen = ch->init_minlen;
1408c2ecf20Sopenharmony_ci	ch->maxlen = ch->init_maxlen;
1418c2ecf20Sopenharmony_ci	ch->next_maxlen = ch->init_maxlen;
1428c2ecf20Sopenharmony_ci	skb_queue_purge(&ch->rqueue);
1438c2ecf20Sopenharmony_ci	ch->rcount = 0;
1448c2ecf20Sopenharmony_ci}
1458c2ecf20Sopenharmony_ciEXPORT_SYMBOL(mISDN_clear_bchannel);
1468c2ecf20Sopenharmony_ci
1478c2ecf20Sopenharmony_civoid
1488c2ecf20Sopenharmony_cimISDN_freebchannel(struct bchannel *ch)
1498c2ecf20Sopenharmony_ci{
1508c2ecf20Sopenharmony_ci	cancel_work_sync(&ch->workq);
1518c2ecf20Sopenharmony_ci	mISDN_clear_bchannel(ch);
1528c2ecf20Sopenharmony_ci}
1538c2ecf20Sopenharmony_ciEXPORT_SYMBOL(mISDN_freebchannel);
1548c2ecf20Sopenharmony_ci
1558c2ecf20Sopenharmony_ciint
1568c2ecf20Sopenharmony_cimISDN_ctrl_bchannel(struct bchannel *bch, struct mISDN_ctrl_req *cq)
1578c2ecf20Sopenharmony_ci{
1588c2ecf20Sopenharmony_ci	int ret = 0;
1598c2ecf20Sopenharmony_ci
1608c2ecf20Sopenharmony_ci	switch (cq->op) {
1618c2ecf20Sopenharmony_ci	case MISDN_CTRL_GETOP:
1628c2ecf20Sopenharmony_ci		cq->op = MISDN_CTRL_RX_BUFFER | MISDN_CTRL_FILL_EMPTY |
1638c2ecf20Sopenharmony_ci			 MISDN_CTRL_RX_OFF;
1648c2ecf20Sopenharmony_ci		break;
1658c2ecf20Sopenharmony_ci	case MISDN_CTRL_FILL_EMPTY:
1668c2ecf20Sopenharmony_ci		if (cq->p1) {
1678c2ecf20Sopenharmony_ci			memset(bch->fill, cq->p2 & 0xff, MISDN_BCH_FILL_SIZE);
1688c2ecf20Sopenharmony_ci			test_and_set_bit(FLG_FILLEMPTY, &bch->Flags);
1698c2ecf20Sopenharmony_ci		} else {
1708c2ecf20Sopenharmony_ci			test_and_clear_bit(FLG_FILLEMPTY, &bch->Flags);
1718c2ecf20Sopenharmony_ci		}
1728c2ecf20Sopenharmony_ci		break;
1738c2ecf20Sopenharmony_ci	case MISDN_CTRL_RX_OFF:
1748c2ecf20Sopenharmony_ci		/* read back dropped byte count */
1758c2ecf20Sopenharmony_ci		cq->p2 = bch->dropcnt;
1768c2ecf20Sopenharmony_ci		if (cq->p1)
1778c2ecf20Sopenharmony_ci			test_and_set_bit(FLG_RX_OFF, &bch->Flags);
1788c2ecf20Sopenharmony_ci		else
1798c2ecf20Sopenharmony_ci			test_and_clear_bit(FLG_RX_OFF, &bch->Flags);
1808c2ecf20Sopenharmony_ci		bch->dropcnt = 0;
1818c2ecf20Sopenharmony_ci		break;
1828c2ecf20Sopenharmony_ci	case MISDN_CTRL_RX_BUFFER:
1838c2ecf20Sopenharmony_ci		if (cq->p2 > MISDN_CTRL_RX_SIZE_IGNORE)
1848c2ecf20Sopenharmony_ci			bch->next_maxlen = cq->p2;
1858c2ecf20Sopenharmony_ci		if (cq->p1 > MISDN_CTRL_RX_SIZE_IGNORE)
1868c2ecf20Sopenharmony_ci			bch->next_minlen = cq->p1;
1878c2ecf20Sopenharmony_ci		/* we return the old values */
1888c2ecf20Sopenharmony_ci		cq->p1 = bch->minlen;
1898c2ecf20Sopenharmony_ci		cq->p2 = bch->maxlen;
1908c2ecf20Sopenharmony_ci		break;
1918c2ecf20Sopenharmony_ci	default:
1928c2ecf20Sopenharmony_ci		pr_info("mISDN unhandled control %x operation\n", cq->op);
1938c2ecf20Sopenharmony_ci		ret = -EINVAL;
1948c2ecf20Sopenharmony_ci		break;
1958c2ecf20Sopenharmony_ci	}
1968c2ecf20Sopenharmony_ci	return ret;
1978c2ecf20Sopenharmony_ci}
1988c2ecf20Sopenharmony_ciEXPORT_SYMBOL(mISDN_ctrl_bchannel);
1998c2ecf20Sopenharmony_ci
2008c2ecf20Sopenharmony_cistatic inline u_int
2018c2ecf20Sopenharmony_ciget_sapi_tei(u_char *p)
2028c2ecf20Sopenharmony_ci{
2038c2ecf20Sopenharmony_ci	u_int	sapi, tei;
2048c2ecf20Sopenharmony_ci
2058c2ecf20Sopenharmony_ci	sapi = *p >> 2;
2068c2ecf20Sopenharmony_ci	tei = p[1] >> 1;
2078c2ecf20Sopenharmony_ci	return sapi | (tei << 8);
2088c2ecf20Sopenharmony_ci}
2098c2ecf20Sopenharmony_ci
2108c2ecf20Sopenharmony_civoid
2118c2ecf20Sopenharmony_cirecv_Dchannel(struct dchannel *dch)
2128c2ecf20Sopenharmony_ci{
2138c2ecf20Sopenharmony_ci	struct mISDNhead *hh;
2148c2ecf20Sopenharmony_ci
2158c2ecf20Sopenharmony_ci	if (dch->rx_skb->len < 2) { /* at least 2 for sapi / tei */
2168c2ecf20Sopenharmony_ci		dev_kfree_skb(dch->rx_skb);
2178c2ecf20Sopenharmony_ci		dch->rx_skb = NULL;
2188c2ecf20Sopenharmony_ci		return;
2198c2ecf20Sopenharmony_ci	}
2208c2ecf20Sopenharmony_ci	hh = mISDN_HEAD_P(dch->rx_skb);
2218c2ecf20Sopenharmony_ci	hh->prim = PH_DATA_IND;
2228c2ecf20Sopenharmony_ci	hh->id = get_sapi_tei(dch->rx_skb->data);
2238c2ecf20Sopenharmony_ci	skb_queue_tail(&dch->rqueue, dch->rx_skb);
2248c2ecf20Sopenharmony_ci	dch->rx_skb = NULL;
2258c2ecf20Sopenharmony_ci	schedule_event(dch, FLG_RECVQUEUE);
2268c2ecf20Sopenharmony_ci}
2278c2ecf20Sopenharmony_ciEXPORT_SYMBOL(recv_Dchannel);
2288c2ecf20Sopenharmony_ci
2298c2ecf20Sopenharmony_civoid
2308c2ecf20Sopenharmony_cirecv_Echannel(struct dchannel *ech, struct dchannel *dch)
2318c2ecf20Sopenharmony_ci{
2328c2ecf20Sopenharmony_ci	struct mISDNhead *hh;
2338c2ecf20Sopenharmony_ci
2348c2ecf20Sopenharmony_ci	if (ech->rx_skb->len < 2) { /* at least 2 for sapi / tei */
2358c2ecf20Sopenharmony_ci		dev_kfree_skb(ech->rx_skb);
2368c2ecf20Sopenharmony_ci		ech->rx_skb = NULL;
2378c2ecf20Sopenharmony_ci		return;
2388c2ecf20Sopenharmony_ci	}
2398c2ecf20Sopenharmony_ci	hh = mISDN_HEAD_P(ech->rx_skb);
2408c2ecf20Sopenharmony_ci	hh->prim = PH_DATA_E_IND;
2418c2ecf20Sopenharmony_ci	hh->id = get_sapi_tei(ech->rx_skb->data);
2428c2ecf20Sopenharmony_ci	skb_queue_tail(&dch->rqueue, ech->rx_skb);
2438c2ecf20Sopenharmony_ci	ech->rx_skb = NULL;
2448c2ecf20Sopenharmony_ci	schedule_event(dch, FLG_RECVQUEUE);
2458c2ecf20Sopenharmony_ci}
2468c2ecf20Sopenharmony_ciEXPORT_SYMBOL(recv_Echannel);
2478c2ecf20Sopenharmony_ci
2488c2ecf20Sopenharmony_civoid
2498c2ecf20Sopenharmony_cirecv_Bchannel(struct bchannel *bch, unsigned int id, bool force)
2508c2ecf20Sopenharmony_ci{
2518c2ecf20Sopenharmony_ci	struct mISDNhead *hh;
2528c2ecf20Sopenharmony_ci
2538c2ecf20Sopenharmony_ci	/* if allocation did fail upper functions still may call us */
2548c2ecf20Sopenharmony_ci	if (unlikely(!bch->rx_skb))
2558c2ecf20Sopenharmony_ci		return;
2568c2ecf20Sopenharmony_ci	if (unlikely(!bch->rx_skb->len)) {
2578c2ecf20Sopenharmony_ci		/* we have no data to send - this may happen after recovery
2588c2ecf20Sopenharmony_ci		 * from overflow or too small allocation.
2598c2ecf20Sopenharmony_ci		 * We need to free the buffer here */
2608c2ecf20Sopenharmony_ci		dev_kfree_skb(bch->rx_skb);
2618c2ecf20Sopenharmony_ci		bch->rx_skb = NULL;
2628c2ecf20Sopenharmony_ci	} else {
2638c2ecf20Sopenharmony_ci		if (test_bit(FLG_TRANSPARENT, &bch->Flags) &&
2648c2ecf20Sopenharmony_ci		    (bch->rx_skb->len < bch->minlen) && !force)
2658c2ecf20Sopenharmony_ci				return;
2668c2ecf20Sopenharmony_ci		hh = mISDN_HEAD_P(bch->rx_skb);
2678c2ecf20Sopenharmony_ci		hh->prim = PH_DATA_IND;
2688c2ecf20Sopenharmony_ci		hh->id = id;
2698c2ecf20Sopenharmony_ci		if (bch->rcount >= 64) {
2708c2ecf20Sopenharmony_ci			printk(KERN_WARNING
2718c2ecf20Sopenharmony_ci			       "B%d receive queue overflow - flushing!\n",
2728c2ecf20Sopenharmony_ci			       bch->nr);
2738c2ecf20Sopenharmony_ci			skb_queue_purge(&bch->rqueue);
2748c2ecf20Sopenharmony_ci		}
2758c2ecf20Sopenharmony_ci		bch->rcount++;
2768c2ecf20Sopenharmony_ci		skb_queue_tail(&bch->rqueue, bch->rx_skb);
2778c2ecf20Sopenharmony_ci		bch->rx_skb = NULL;
2788c2ecf20Sopenharmony_ci		schedule_event(bch, FLG_RECVQUEUE);
2798c2ecf20Sopenharmony_ci	}
2808c2ecf20Sopenharmony_ci}
2818c2ecf20Sopenharmony_ciEXPORT_SYMBOL(recv_Bchannel);
2828c2ecf20Sopenharmony_ci
2838c2ecf20Sopenharmony_civoid
2848c2ecf20Sopenharmony_cirecv_Dchannel_skb(struct dchannel *dch, struct sk_buff *skb)
2858c2ecf20Sopenharmony_ci{
2868c2ecf20Sopenharmony_ci	skb_queue_tail(&dch->rqueue, skb);
2878c2ecf20Sopenharmony_ci	schedule_event(dch, FLG_RECVQUEUE);
2888c2ecf20Sopenharmony_ci}
2898c2ecf20Sopenharmony_ciEXPORT_SYMBOL(recv_Dchannel_skb);
2908c2ecf20Sopenharmony_ci
2918c2ecf20Sopenharmony_civoid
2928c2ecf20Sopenharmony_cirecv_Bchannel_skb(struct bchannel *bch, struct sk_buff *skb)
2938c2ecf20Sopenharmony_ci{
2948c2ecf20Sopenharmony_ci	if (bch->rcount >= 64) {
2958c2ecf20Sopenharmony_ci		printk(KERN_WARNING "B-channel %p receive queue overflow, "
2968c2ecf20Sopenharmony_ci		       "flushing!\n", bch);
2978c2ecf20Sopenharmony_ci		skb_queue_purge(&bch->rqueue);
2988c2ecf20Sopenharmony_ci		bch->rcount = 0;
2998c2ecf20Sopenharmony_ci	}
3008c2ecf20Sopenharmony_ci	bch->rcount++;
3018c2ecf20Sopenharmony_ci	skb_queue_tail(&bch->rqueue, skb);
3028c2ecf20Sopenharmony_ci	schedule_event(bch, FLG_RECVQUEUE);
3038c2ecf20Sopenharmony_ci}
3048c2ecf20Sopenharmony_ciEXPORT_SYMBOL(recv_Bchannel_skb);
3058c2ecf20Sopenharmony_ci
3068c2ecf20Sopenharmony_cistatic void
3078c2ecf20Sopenharmony_ciconfirm_Dsend(struct dchannel *dch)
3088c2ecf20Sopenharmony_ci{
3098c2ecf20Sopenharmony_ci	struct sk_buff	*skb;
3108c2ecf20Sopenharmony_ci
3118c2ecf20Sopenharmony_ci	skb = _alloc_mISDN_skb(PH_DATA_CNF, mISDN_HEAD_ID(dch->tx_skb),
3128c2ecf20Sopenharmony_ci			       0, NULL, GFP_ATOMIC);
3138c2ecf20Sopenharmony_ci	if (!skb) {
3148c2ecf20Sopenharmony_ci		printk(KERN_ERR "%s: no skb id %x\n", __func__,
3158c2ecf20Sopenharmony_ci		       mISDN_HEAD_ID(dch->tx_skb));
3168c2ecf20Sopenharmony_ci		return;
3178c2ecf20Sopenharmony_ci	}
3188c2ecf20Sopenharmony_ci	skb_queue_tail(&dch->rqueue, skb);
3198c2ecf20Sopenharmony_ci	schedule_event(dch, FLG_RECVQUEUE);
3208c2ecf20Sopenharmony_ci}
3218c2ecf20Sopenharmony_ci
3228c2ecf20Sopenharmony_ciint
3238c2ecf20Sopenharmony_ciget_next_dframe(struct dchannel *dch)
3248c2ecf20Sopenharmony_ci{
3258c2ecf20Sopenharmony_ci	dch->tx_idx = 0;
3268c2ecf20Sopenharmony_ci	dch->tx_skb = skb_dequeue(&dch->squeue);
3278c2ecf20Sopenharmony_ci	if (dch->tx_skb) {
3288c2ecf20Sopenharmony_ci		confirm_Dsend(dch);
3298c2ecf20Sopenharmony_ci		return 1;
3308c2ecf20Sopenharmony_ci	}
3318c2ecf20Sopenharmony_ci	dch->tx_skb = NULL;
3328c2ecf20Sopenharmony_ci	test_and_clear_bit(FLG_TX_BUSY, &dch->Flags);
3338c2ecf20Sopenharmony_ci	return 0;
3348c2ecf20Sopenharmony_ci}
3358c2ecf20Sopenharmony_ciEXPORT_SYMBOL(get_next_dframe);
3368c2ecf20Sopenharmony_ci
3378c2ecf20Sopenharmony_cistatic void
3388c2ecf20Sopenharmony_ciconfirm_Bsend(struct bchannel *bch)
3398c2ecf20Sopenharmony_ci{
3408c2ecf20Sopenharmony_ci	struct sk_buff	*skb;
3418c2ecf20Sopenharmony_ci
3428c2ecf20Sopenharmony_ci	if (bch->rcount >= 64) {
3438c2ecf20Sopenharmony_ci		printk(KERN_WARNING "B-channel %p receive queue overflow, "
3448c2ecf20Sopenharmony_ci		       "flushing!\n", bch);
3458c2ecf20Sopenharmony_ci		skb_queue_purge(&bch->rqueue);
3468c2ecf20Sopenharmony_ci		bch->rcount = 0;
3478c2ecf20Sopenharmony_ci	}
3488c2ecf20Sopenharmony_ci	skb = _alloc_mISDN_skb(PH_DATA_CNF, mISDN_HEAD_ID(bch->tx_skb),
3498c2ecf20Sopenharmony_ci			       0, NULL, GFP_ATOMIC);
3508c2ecf20Sopenharmony_ci	if (!skb) {
3518c2ecf20Sopenharmony_ci		printk(KERN_ERR "%s: no skb id %x\n", __func__,
3528c2ecf20Sopenharmony_ci		       mISDN_HEAD_ID(bch->tx_skb));
3538c2ecf20Sopenharmony_ci		return;
3548c2ecf20Sopenharmony_ci	}
3558c2ecf20Sopenharmony_ci	bch->rcount++;
3568c2ecf20Sopenharmony_ci	skb_queue_tail(&bch->rqueue, skb);
3578c2ecf20Sopenharmony_ci	schedule_event(bch, FLG_RECVQUEUE);
3588c2ecf20Sopenharmony_ci}
3598c2ecf20Sopenharmony_ci
3608c2ecf20Sopenharmony_ciint
3618c2ecf20Sopenharmony_ciget_next_bframe(struct bchannel *bch)
3628c2ecf20Sopenharmony_ci{
3638c2ecf20Sopenharmony_ci	bch->tx_idx = 0;
3648c2ecf20Sopenharmony_ci	if (test_bit(FLG_TX_NEXT, &bch->Flags)) {
3658c2ecf20Sopenharmony_ci		bch->tx_skb = bch->next_skb;
3668c2ecf20Sopenharmony_ci		if (bch->tx_skb) {
3678c2ecf20Sopenharmony_ci			bch->next_skb = NULL;
3688c2ecf20Sopenharmony_ci			test_and_clear_bit(FLG_TX_NEXT, &bch->Flags);
3698c2ecf20Sopenharmony_ci			/* confirm imediately to allow next data */
3708c2ecf20Sopenharmony_ci			confirm_Bsend(bch);
3718c2ecf20Sopenharmony_ci			return 1;
3728c2ecf20Sopenharmony_ci		} else {
3738c2ecf20Sopenharmony_ci			test_and_clear_bit(FLG_TX_NEXT, &bch->Flags);
3748c2ecf20Sopenharmony_ci			printk(KERN_WARNING "B TX_NEXT without skb\n");
3758c2ecf20Sopenharmony_ci		}
3768c2ecf20Sopenharmony_ci	}
3778c2ecf20Sopenharmony_ci	bch->tx_skb = NULL;
3788c2ecf20Sopenharmony_ci	test_and_clear_bit(FLG_TX_BUSY, &bch->Flags);
3798c2ecf20Sopenharmony_ci	return 0;
3808c2ecf20Sopenharmony_ci}
3818c2ecf20Sopenharmony_ciEXPORT_SYMBOL(get_next_bframe);
3828c2ecf20Sopenharmony_ci
3838c2ecf20Sopenharmony_civoid
3848c2ecf20Sopenharmony_ciqueue_ch_frame(struct mISDNchannel *ch, u_int pr, int id, struct sk_buff *skb)
3858c2ecf20Sopenharmony_ci{
3868c2ecf20Sopenharmony_ci	struct mISDNhead *hh;
3878c2ecf20Sopenharmony_ci
3888c2ecf20Sopenharmony_ci	if (!skb) {
3898c2ecf20Sopenharmony_ci		_queue_data(ch, pr, id, 0, NULL, GFP_ATOMIC);
3908c2ecf20Sopenharmony_ci	} else {
3918c2ecf20Sopenharmony_ci		if (ch->peer) {
3928c2ecf20Sopenharmony_ci			hh = mISDN_HEAD_P(skb);
3938c2ecf20Sopenharmony_ci			hh->prim = pr;
3948c2ecf20Sopenharmony_ci			hh->id = id;
3958c2ecf20Sopenharmony_ci			if (!ch->recv(ch->peer, skb))
3968c2ecf20Sopenharmony_ci				return;
3978c2ecf20Sopenharmony_ci		}
3988c2ecf20Sopenharmony_ci		dev_kfree_skb(skb);
3998c2ecf20Sopenharmony_ci	}
4008c2ecf20Sopenharmony_ci}
4018c2ecf20Sopenharmony_ciEXPORT_SYMBOL(queue_ch_frame);
4028c2ecf20Sopenharmony_ci
4038c2ecf20Sopenharmony_ciint
4048c2ecf20Sopenharmony_cidchannel_senddata(struct dchannel *ch, struct sk_buff *skb)
4058c2ecf20Sopenharmony_ci{
4068c2ecf20Sopenharmony_ci	/* check oversize */
4078c2ecf20Sopenharmony_ci	if (skb->len <= 0) {
4088c2ecf20Sopenharmony_ci		printk(KERN_WARNING "%s: skb too small\n", __func__);
4098c2ecf20Sopenharmony_ci		return -EINVAL;
4108c2ecf20Sopenharmony_ci	}
4118c2ecf20Sopenharmony_ci	if (skb->len > ch->maxlen) {
4128c2ecf20Sopenharmony_ci		printk(KERN_WARNING "%s: skb too large(%d/%d)\n",
4138c2ecf20Sopenharmony_ci		       __func__, skb->len, ch->maxlen);
4148c2ecf20Sopenharmony_ci		return -EINVAL;
4158c2ecf20Sopenharmony_ci	}
4168c2ecf20Sopenharmony_ci	/* HW lock must be obtained */
4178c2ecf20Sopenharmony_ci	if (test_and_set_bit(FLG_TX_BUSY, &ch->Flags)) {
4188c2ecf20Sopenharmony_ci		skb_queue_tail(&ch->squeue, skb);
4198c2ecf20Sopenharmony_ci		return 0;
4208c2ecf20Sopenharmony_ci	} else {
4218c2ecf20Sopenharmony_ci		/* write to fifo */
4228c2ecf20Sopenharmony_ci		ch->tx_skb = skb;
4238c2ecf20Sopenharmony_ci		ch->tx_idx = 0;
4248c2ecf20Sopenharmony_ci		return 1;
4258c2ecf20Sopenharmony_ci	}
4268c2ecf20Sopenharmony_ci}
4278c2ecf20Sopenharmony_ciEXPORT_SYMBOL(dchannel_senddata);
4288c2ecf20Sopenharmony_ci
4298c2ecf20Sopenharmony_ciint
4308c2ecf20Sopenharmony_cibchannel_senddata(struct bchannel *ch, struct sk_buff *skb)
4318c2ecf20Sopenharmony_ci{
4328c2ecf20Sopenharmony_ci
4338c2ecf20Sopenharmony_ci	/* check oversize */
4348c2ecf20Sopenharmony_ci	if (skb->len <= 0) {
4358c2ecf20Sopenharmony_ci		printk(KERN_WARNING "%s: skb too small\n", __func__);
4368c2ecf20Sopenharmony_ci		return -EINVAL;
4378c2ecf20Sopenharmony_ci	}
4388c2ecf20Sopenharmony_ci	if (skb->len > ch->maxlen) {
4398c2ecf20Sopenharmony_ci		printk(KERN_WARNING "%s: skb too large(%d/%d)\n",
4408c2ecf20Sopenharmony_ci		       __func__, skb->len, ch->maxlen);
4418c2ecf20Sopenharmony_ci		return -EINVAL;
4428c2ecf20Sopenharmony_ci	}
4438c2ecf20Sopenharmony_ci	/* HW lock must be obtained */
4448c2ecf20Sopenharmony_ci	/* check for pending next_skb */
4458c2ecf20Sopenharmony_ci	if (ch->next_skb) {
4468c2ecf20Sopenharmony_ci		printk(KERN_WARNING
4478c2ecf20Sopenharmony_ci		       "%s: next_skb exist ERROR (skb->len=%d next_skb->len=%d)\n",
4488c2ecf20Sopenharmony_ci		       __func__, skb->len, ch->next_skb->len);
4498c2ecf20Sopenharmony_ci		return -EBUSY;
4508c2ecf20Sopenharmony_ci	}
4518c2ecf20Sopenharmony_ci	if (test_and_set_bit(FLG_TX_BUSY, &ch->Flags)) {
4528c2ecf20Sopenharmony_ci		test_and_set_bit(FLG_TX_NEXT, &ch->Flags);
4538c2ecf20Sopenharmony_ci		ch->next_skb = skb;
4548c2ecf20Sopenharmony_ci		return 0;
4558c2ecf20Sopenharmony_ci	} else {
4568c2ecf20Sopenharmony_ci		/* write to fifo */
4578c2ecf20Sopenharmony_ci		ch->tx_skb = skb;
4588c2ecf20Sopenharmony_ci		ch->tx_idx = 0;
4598c2ecf20Sopenharmony_ci		confirm_Bsend(ch);
4608c2ecf20Sopenharmony_ci		return 1;
4618c2ecf20Sopenharmony_ci	}
4628c2ecf20Sopenharmony_ci}
4638c2ecf20Sopenharmony_ciEXPORT_SYMBOL(bchannel_senddata);
4648c2ecf20Sopenharmony_ci
4658c2ecf20Sopenharmony_ci/* The function allocates a new receive skb on demand with a size for the
4668c2ecf20Sopenharmony_ci * requirements of the current protocol. It returns the tailroom of the
4678c2ecf20Sopenharmony_ci * receive skb or an error.
4688c2ecf20Sopenharmony_ci */
4698c2ecf20Sopenharmony_ciint
4708c2ecf20Sopenharmony_cibchannel_get_rxbuf(struct bchannel *bch, int reqlen)
4718c2ecf20Sopenharmony_ci{
4728c2ecf20Sopenharmony_ci	int len;
4738c2ecf20Sopenharmony_ci
4748c2ecf20Sopenharmony_ci	if (bch->rx_skb) {
4758c2ecf20Sopenharmony_ci		len = skb_tailroom(bch->rx_skb);
4768c2ecf20Sopenharmony_ci		if (len < reqlen) {
4778c2ecf20Sopenharmony_ci			pr_warn("B%d no space for %d (only %d) bytes\n",
4788c2ecf20Sopenharmony_ci				bch->nr, reqlen, len);
4798c2ecf20Sopenharmony_ci			if (test_bit(FLG_TRANSPARENT, &bch->Flags)) {
4808c2ecf20Sopenharmony_ci				/* send what we have now and try a new buffer */
4818c2ecf20Sopenharmony_ci				recv_Bchannel(bch, 0, true);
4828c2ecf20Sopenharmony_ci			} else {
4838c2ecf20Sopenharmony_ci				/* on HDLC we have to drop too big frames */
4848c2ecf20Sopenharmony_ci				return -EMSGSIZE;
4858c2ecf20Sopenharmony_ci			}
4868c2ecf20Sopenharmony_ci		} else {
4878c2ecf20Sopenharmony_ci			return len;
4888c2ecf20Sopenharmony_ci		}
4898c2ecf20Sopenharmony_ci	}
4908c2ecf20Sopenharmony_ci	/* update current min/max length first */
4918c2ecf20Sopenharmony_ci	if (unlikely(bch->maxlen != bch->next_maxlen))
4928c2ecf20Sopenharmony_ci		bch->maxlen = bch->next_maxlen;
4938c2ecf20Sopenharmony_ci	if (unlikely(bch->minlen != bch->next_minlen))
4948c2ecf20Sopenharmony_ci		bch->minlen = bch->next_minlen;
4958c2ecf20Sopenharmony_ci	if (unlikely(reqlen > bch->maxlen))
4968c2ecf20Sopenharmony_ci		return -EMSGSIZE;
4978c2ecf20Sopenharmony_ci	if (test_bit(FLG_TRANSPARENT, &bch->Flags)) {
4988c2ecf20Sopenharmony_ci		if (reqlen >= bch->minlen) {
4998c2ecf20Sopenharmony_ci			len = reqlen;
5008c2ecf20Sopenharmony_ci		} else {
5018c2ecf20Sopenharmony_ci			len = 2 * bch->minlen;
5028c2ecf20Sopenharmony_ci			if (len > bch->maxlen)
5038c2ecf20Sopenharmony_ci				len = bch->maxlen;
5048c2ecf20Sopenharmony_ci		}
5058c2ecf20Sopenharmony_ci	} else {
5068c2ecf20Sopenharmony_ci		/* with HDLC we do not know the length yet */
5078c2ecf20Sopenharmony_ci		len = bch->maxlen;
5088c2ecf20Sopenharmony_ci	}
5098c2ecf20Sopenharmony_ci	bch->rx_skb = mI_alloc_skb(len, GFP_ATOMIC);
5108c2ecf20Sopenharmony_ci	if (!bch->rx_skb) {
5118c2ecf20Sopenharmony_ci		pr_warn("B%d receive no memory for %d bytes\n", bch->nr, len);
5128c2ecf20Sopenharmony_ci		len = -ENOMEM;
5138c2ecf20Sopenharmony_ci	}
5148c2ecf20Sopenharmony_ci	return len;
5158c2ecf20Sopenharmony_ci}
5168c2ecf20Sopenharmony_ciEXPORT_SYMBOL(bchannel_get_rxbuf);
517