Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit e7752ee2 authored by Tilman Schmidt's avatar Tilman Schmidt Committed by David S. Miller
Browse files

isdn/gigaset: honor CAPI application's buffer size request



Fix the Gigaset CAPI driver to limit the length of a connection's
payload data receive buffers to the corresponding CAPI application's
data buffer size, as some real-life CAPI applications tend to be
rather unhappy if they receive bigger data blocks than requested.

Impact: bugfix
Signed-off-by: default avatarTilman Schmidt <tilman@imap.cc>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent ed770f01
Loading
Loading
Loading
Loading
+11 −33
Original line number Diff line number Diff line
@@ -126,26 +126,6 @@ static unsigned lock_loop(unsigned numbytes, struct inbuf_t *inbuf)
	return numbytes;
}

/* set up next receive skb for data mode
 */
static void new_rcv_skb(struct bc_state *bcs)
{
	struct cardstate *cs = bcs->cs;
	unsigned short hw_hdr_len = cs->hw_hdr_len;

	if (bcs->ignore) {
		bcs->skb = NULL;
		return;
	}

	bcs->skb = dev_alloc_skb(SBUFSIZE + hw_hdr_len);
	if (bcs->skb == NULL) {
		dev_warn(cs->dev, "could not allocate new skb\n");
		return;
	}
	skb_reserve(bcs->skb, hw_hdr_len);
}

/* process a block of received bytes in HDLC data mode
 * (mstate != MS_LOCKED && !(inputstate & INS_command) && proto2 == L2_HDLC)
 * Collect HDLC frames, undoing byte stuffing and watching for DLE escapes.
@@ -159,8 +139,8 @@ static unsigned hdlc_loop(unsigned numbytes, struct inbuf_t *inbuf)
	struct cardstate *cs = inbuf->cs;
	struct bc_state *bcs = cs->bcs;
	int inputstate = bcs->inputstate;
	__u16 fcs = bcs->fcs;
	struct sk_buff *skb = bcs->skb;
	__u16 fcs = bcs->rx_fcs;
	struct sk_buff *skb = bcs->rx_skb;
	unsigned char *src = inbuf->data + inbuf->head;
	unsigned procbytes = 0;
	unsigned char c;
@@ -245,8 +225,7 @@ static unsigned hdlc_loop(unsigned numbytes, struct inbuf_t *inbuf)

				/* prepare reception of next frame */
				inputstate &= ~INS_have_data;
				new_rcv_skb(bcs);
				skb = bcs->skb;
				skb = gigaset_new_rx_skb(bcs);
			} else {
				/* empty frame (7E 7E) */
#ifdef CONFIG_GIGASET_DEBUG
@@ -255,8 +234,7 @@ static unsigned hdlc_loop(unsigned numbytes, struct inbuf_t *inbuf)
				if (!skb) {
					/* skipped (?) */
					gigaset_isdn_rcv_err(bcs);
					new_rcv_skb(bcs);
					skb = bcs->skb;
					skb = gigaset_new_rx_skb(bcs);
				}
			}

@@ -279,11 +257,11 @@ static unsigned hdlc_loop(unsigned numbytes, struct inbuf_t *inbuf)
#endif
		inputstate |= INS_have_data;
		if (skb) {
			if (skb->len == SBUFSIZE) {
			if (skb->len >= bcs->rx_bufsize) {
				dev_warn(cs->dev, "received packet too long\n");
				dev_kfree_skb_any(skb);
				/* skip remainder of packet */
				bcs->skb = skb = NULL;
				bcs->rx_skb = skb = NULL;
			} else {
				*__skb_put(skb, 1) = c;
				fcs = crc_ccitt_byte(fcs, c);
@@ -292,7 +270,7 @@ static unsigned hdlc_loop(unsigned numbytes, struct inbuf_t *inbuf)
	}

	bcs->inputstate = inputstate;
	bcs->fcs = fcs;
	bcs->rx_fcs = fcs;
	return procbytes;
}

@@ -308,18 +286,18 @@ static unsigned iraw_loop(unsigned numbytes, struct inbuf_t *inbuf)
	struct cardstate *cs = inbuf->cs;
	struct bc_state *bcs = cs->bcs;
	int inputstate = bcs->inputstate;
	struct sk_buff *skb = bcs->skb;
	struct sk_buff *skb = bcs->rx_skb;
	unsigned char *src = inbuf->data + inbuf->head;
	unsigned procbytes = 0;
	unsigned char c;

	if (!skb) {
		/* skip this block */
		new_rcv_skb(bcs);
		gigaset_new_rx_skb(bcs);
		return numbytes;
	}

	while (procbytes < numbytes && skb->len < SBUFSIZE) {
	while (procbytes < numbytes && skb->len < bcs->rx_bufsize) {
		c = *src++;
		procbytes++;

@@ -343,7 +321,7 @@ static unsigned iraw_loop(unsigned numbytes, struct inbuf_t *inbuf)
	if (inputstate & INS_have_data) {
		gigaset_skb_rcvd(bcs, skb);
		inputstate &= ~INS_have_data;
		new_rcv_skb(bcs);
		gigaset_new_rx_skb(bcs);
	}

	bcs->inputstate = inputstate;
+8 −0
Original line number Diff line number Diff line
@@ -80,6 +80,7 @@ struct gigaset_capi_appl {
	struct list_head ctrlist;
	struct gigaset_capi_appl *bcnext;
	u16 id;
	struct capi_register_params rp;
	u16 nextMessageNumber;
	u32 listenInfoMask;
	u32 listenCIPmask;
@@ -945,6 +946,7 @@ static void gigaset_register_appl(struct capi_ctr *ctr, u16 appl,
		return;
	}
	ap->id = appl;
	ap->rp = *rp;

	list_add(&ap->ctrlist, &iif->appls);
}
@@ -1166,6 +1168,9 @@ static void do_connect_req(struct gigaset_capi_ctr *iif,
	}
	ap->bcnext = NULL;
	bcs->ap = ap;
	bcs->rx_bufsize = ap->rp.datablklen;
	dev_kfree_skb(bcs->rx_skb);
	gigaset_new_rx_skb(bcs);
	cmsg->adr.adrPLCI |= (bcs->channel + 1) << 8;

	/* build command table */
@@ -1435,6 +1440,9 @@ static void do_connect_resp(struct gigaset_capi_ctr *iif,
					CapiCallGivenToOtherApplication);
		ap->bcnext = NULL;
		bcs->ap = ap;
		bcs->rx_bufsize = ap->rp.datablklen;
		dev_kfree_skb(bcs->rx_skb);
		gigaset_new_rx_skb(bcs);
		bcs->chstate |= CHS_NOTIFY_LL;

		/* check/encode B channel protocol */
+8 −24
Original line number Diff line number Diff line
@@ -399,8 +399,8 @@ static void gigaset_freebcs(struct bc_state *bcs)
	gig_dbg(DEBUG_INIT, "clearing bcs[%d]->at_state", bcs->channel);
	clear_at_state(&bcs->at_state);
	gig_dbg(DEBUG_INIT, "freeing bcs[%d]->skb", bcs->channel);
	dev_kfree_skb(bcs->skb);
	bcs->skb = NULL;
	dev_kfree_skb(bcs->rx_skb);
	bcs->rx_skb = NULL;

	for (i = 0; i < AT_NUM; ++i) {
		kfree(bcs->commands[i]);
@@ -634,19 +634,10 @@ static struct bc_state *gigaset_initbcs(struct bc_state *bcs,
	bcs->emptycount = 0;
#endif

	gig_dbg(DEBUG_INIT, "allocating bcs[%d]->skb", channel);
	bcs->fcs = PPP_INITFCS;
	bcs->rx_bufsize = 0;
	bcs->rx_skb = NULL;
	bcs->rx_fcs = PPP_INITFCS;
	bcs->inputstate = 0;
	if (cs->ignoreframes) {
		bcs->skb = NULL;
	} else {
		bcs->skb = dev_alloc_skb(SBUFSIZE + cs->hw_hdr_len);
		if (bcs->skb != NULL)
			skb_reserve(bcs->skb, cs->hw_hdr_len);
		else
			pr_err("out of memory\n");
	}

	bcs->channel = channel;
	bcs->cs = cs;

@@ -663,11 +654,6 @@ static struct bc_state *gigaset_initbcs(struct bc_state *bcs,
		return bcs;

	gig_dbg(DEBUG_INIT, "  failed");

	gig_dbg(DEBUG_INIT, "  freeing bcs[%d]->skb", channel);
	dev_kfree_skb(bcs->skb);
	bcs->skb = NULL;

	return NULL;
}

@@ -839,14 +825,12 @@ void gigaset_bcs_reinit(struct bc_state *bcs)
	bcs->emptycount = 0;
#endif

	bcs->fcs = PPP_INITFCS;
	bcs->rx_fcs = PPP_INITFCS;
	bcs->chstate = 0;

	bcs->ignore = cs->ignoreframes;
	if (bcs->ignore) {
		dev_kfree_skb(bcs->skb);
		bcs->skb = NULL;
	}
	dev_kfree_skb(bcs->rx_skb);
	bcs->rx_skb = NULL;

	cs->ops->reinitbcshw(bcs);
}
+21 −8
Original line number Diff line number Diff line
@@ -45,10 +45,6 @@
#define MAX_EVENTS 64		/* size of event queue */

#define RBUFSIZE 8192
#define SBUFSIZE 4096		/* sk_buff payload size */

#define TRANSBUFSIZE 768	/* bytes per skb for transparent receive */
#define MAX_BUF_SIZE (SBUFSIZE - 2)	/* Max. size of a data packet from LL */

/* compile time options */
#define GIG_MAJOR 0
@@ -380,8 +376,10 @@ struct bc_state {

	struct at_state_t at_state;

	__u16 fcs;
	struct sk_buff *skb;
	/* receive buffer */
	unsigned rx_bufsize;		/* max size accepted by application */
	struct sk_buff *rx_skb;
	__u16 rx_fcs;
	int inputstate;			/* see INS_XXXX */

	int channel;
@@ -801,8 +799,23 @@ static inline void gigaset_bchannel_up(struct bc_state *bcs)
	gigaset_schedule_event(bcs->cs);
}

/* handling routines for sk_buff */
/* ============================= */
/* set up next receive skb for data mode */
static inline struct sk_buff *gigaset_new_rx_skb(struct bc_state *bcs)
{
	struct cardstate *cs = bcs->cs;
	unsigned short hw_hdr_len = cs->hw_hdr_len;

	if (bcs->ignore) {
		bcs->rx_skb = NULL;
	} else {
		bcs->rx_skb = dev_alloc_skb(bcs->rx_bufsize + hw_hdr_len);
		if (bcs->rx_skb == NULL)
			dev_warn(cs->dev, "could not allocate skb\n");
		else
			skb_reserve(bcs->rx_skb, hw_hdr_len);
	}
	return bcs->rx_skb;
}

/* append received bytes to inbuf */
int gigaset_fill_inbuf(struct inbuf_t *inbuf, const unsigned char *src,
+21 −0
Original line number Diff line number Diff line
@@ -16,7 +16,10 @@
#include "gigaset.h"
#include <linux/isdnif.h>

#define SBUFSIZE	4096	/* sk_buff payload size */
#define TRANSBUFSIZE	768	/* bytes per skb for transparent receive */
#define HW_HDR_LEN	2	/* Header size used to store ack info */
#define MAX_BUF_SIZE	(SBUFSIZE - HW_HDR_LEN)	/* max data packet from LL */

/* == Handling of I4L IO =====================================================*/

@@ -231,6 +234,15 @@ static int command_from_LL(isdn_ctrl *cntrl)
			dev_err(cs->dev, "ISDN_CMD_DIAL: channel not free\n");
			return -EBUSY;
		}
		switch (bcs->proto2) {
		case L2_HDLC:
			bcs->rx_bufsize = SBUFSIZE;
			break;
		default:			/* assume transparent */
			bcs->rx_bufsize = TRANSBUFSIZE;
		}
		dev_kfree_skb(bcs->rx_skb);
		gigaset_new_rx_skb(bcs);

		commands = kzalloc(AT_NUM*(sizeof *commands), GFP_ATOMIC);
		if (!commands) {
@@ -314,6 +326,15 @@ static int command_from_LL(isdn_ctrl *cntrl)
			return -EINVAL;
		}
		bcs = cs->bcs + ch;
		switch (bcs->proto2) {
		case L2_HDLC:
			bcs->rx_bufsize = SBUFSIZE;
			break;
		default:			/* assume transparent */
			bcs->rx_bufsize = TRANSBUFSIZE;
		}
		dev_kfree_skb(bcs->rx_skb);
		gigaset_new_rx_skb(bcs);
		if (!gigaset_add_event(cs, &bcs->at_state,
				       EV_ACCEPT, NULL, 0, NULL))
			return -ENOMEM;
Loading