2010-06-22 00:04:32

by Tilman Schmidt

[permalink] [raw]
Subject: [PATCH 0/5] late Gigaset fixes for 2.6.35

Karsten, David,

following are five patches to the Gigaset driver fixing reported
problems with two CAPI applications (Asterisk and capifax). They
fix serious bugs, though not technically regressions, and I would
therefore appreciate if they could still make it into release
2.6.35. Otherwise please queue them for the first stable cycle.

These should also go into 2.6.34-stable. They apply cleanly to
2.6.34 except for some offsets in file drivers/isdn/gigaset/capi.c.

I'll follow up with a series of less urgent patches which I'll
propose for eventual inclusion in release 2.6.36.

Thanks,
Tilman

Tilman Schmidt (5):
isdn/gigaset: honor CAPI application's buffer size request
isdn/gigaset: correct CAPI voice connection encoding
isdn/gigaset: correct CAPI DATA_B3 Delivery Confirmation
isdn/gigaset: encode HLC and BC together
isdn/gigaset: correct CAPI connection state storage

drivers/isdn/gigaset/asyncdata.c | 44 +---
drivers/isdn/gigaset/capi.c | 407 ++++++++++++++++++++++++++-----------
drivers/isdn/gigaset/common.c | 36 ++---
drivers/isdn/gigaset/ev-layer.c | 4 +-
drivers/isdn/gigaset/gigaset.h | 38 +++--
drivers/isdn/gigaset/i4l.c | 21 ++
drivers/isdn/gigaset/isocdata.c | 72 +++-----
7 files changed, 382 insertions(+), 240 deletions(-)


2010-06-22 00:04:35

by Tilman Schmidt

[permalink] [raw]
Subject: [PATCH 2/5] isdn/gigaset: correct CAPI voice connection encoding

Make the Gigaset CAPI driver select L2_VOICE (AT^SBPR=2) as the
layer 2 encoding for transparent connections, like the ISDN4Linux
variant. L2_BITSYNC (AT^SBPR=0) mutes internal connections and
distorts external ones.

Impact: bugfix
Signed-off-by: Tilman Schmidt <[email protected]>
---
drivers/isdn/gigaset/capi.c | 8 ++++----
1 files changed, 4 insertions(+), 4 deletions(-)

diff --git a/drivers/isdn/gigaset/capi.c b/drivers/isdn/gigaset/capi.c
index 245a608..cb55ead 100644
--- a/drivers/isdn/gigaset/capi.c
+++ b/drivers/isdn/gigaset/capi.c
@@ -1327,13 +1327,13 @@ static void do_connect_req(struct gigaset_capi_ctr *iif,
bcs->proto2 = L2_HDLC;
break;
case 1:
- bcs->proto2 = L2_BITSYNC;
+ bcs->proto2 = L2_VOICE;
break;
default:
dev_warn(cs->dev,
"B1 Protocol %u unsupported, using Transparent\n",
cmsg->B1protocol);
- bcs->proto2 = L2_BITSYNC;
+ bcs->proto2 = L2_VOICE;
}
if (cmsg->B2protocol != 1)
dev_warn(cs->dev,
@@ -1456,13 +1456,13 @@ static void do_connect_resp(struct gigaset_capi_ctr *iif,
bcs->proto2 = L2_HDLC;
break;
case 1:
- bcs->proto2 = L2_BITSYNC;
+ bcs->proto2 = L2_VOICE;
break;
default:
dev_warn(cs->dev,
"B1 Protocol %u unsupported, using Transparent\n",
cmsg->B1protocol);
- bcs->proto2 = L2_BITSYNC;
+ bcs->proto2 = L2_VOICE;
}
if (cmsg->B2protocol != 1)
dev_warn(cs->dev,
--
1.6.5.3.298.g39add

2010-06-22 00:04:40

by Tilman Schmidt

[permalink] [raw]
Subject: [PATCH 3/5] isdn/gigaset: correct CAPI DATA_B3 Delivery Confirmation

The Gigaset CAPI driver handled all DATA_B3_REQ messages as if the
Delivery Confirmation flag bit was set, delaying the emission of the
DATA_B3_CONF reply until the data was actually transmitted. Some
CAPI applications (notably Asterisk) aren't happy with that
behaviour. Change it to actually evaluate the Delivery Confirmation
flag as described the CAPI specification.

Impact: bugfix
Signed-off-by: Tilman Schmidt <[email protected]>
---
drivers/isdn/gigaset/capi.c | 83 ++++++++++++++++++++++++++----------------
1 files changed, 51 insertions(+), 32 deletions(-)

diff --git a/drivers/isdn/gigaset/capi.c b/drivers/isdn/gigaset/capi.c
index cb55ead..e685123 100644
--- a/drivers/isdn/gigaset/capi.c
+++ b/drivers/isdn/gigaset/capi.c
@@ -320,6 +320,39 @@ static const char *format_ie(const char *ie)
return result;
}

+/*
+ * emit DATA_B3_CONF message
+ */
+static void send_data_b3_conf(struct cardstate *cs, struct capi_ctr *ctr,
+ u16 appl, u16 msgid, int channel,
+ u16 handle, u16 info)
+{
+ struct sk_buff *cskb;
+ u8 *msg;
+
+ cskb = alloc_skb(CAPI_DATA_B3_CONF_LEN, GFP_ATOMIC);
+ if (!cskb) {
+ dev_err(cs->dev, "%s: out of memory\n", __func__);
+ return;
+ }
+ /* frequent message, avoid _cmsg overhead */
+ msg = __skb_put(cskb, CAPI_DATA_B3_CONF_LEN);
+ CAPIMSG_SETLEN(msg, CAPI_DATA_B3_CONF_LEN);
+ CAPIMSG_SETAPPID(msg, appl);
+ CAPIMSG_SETCOMMAND(msg, CAPI_DATA_B3);
+ CAPIMSG_SETSUBCOMMAND(msg, CAPI_CONF);
+ CAPIMSG_SETMSGID(msg, msgid);
+ CAPIMSG_SETCONTROLLER(msg, ctr->cnr);
+ CAPIMSG_SETPLCI_PART(msg, channel);
+ CAPIMSG_SETNCCI_PART(msg, 1);
+ CAPIMSG_SETHANDLE_CONF(msg, handle);
+ CAPIMSG_SETINFO_CONF(msg, info);
+
+ /* emit message */
+ dump_rawmsg(DEBUG_MCMD, __func__, msg);
+ capi_ctr_handle_message(ctr, appl, cskb);
+}
+

/*
* driver interface functions
@@ -340,7 +373,6 @@ void gigaset_skb_sent(struct bc_state *bcs, struct sk_buff *dskb)
struct gigaset_capi_ctr *iif = cs->iif;
struct gigaset_capi_appl *ap = bcs->ap;
unsigned char *req = skb_mac_header(dskb);
- struct sk_buff *cskb;
u16 flags;

/* update statistics */
@@ -357,34 +389,17 @@ void gigaset_skb_sent(struct bc_state *bcs, struct sk_buff *dskb)
return;
}

- /* ToDo: honor unset "delivery confirmation" bit */
+ /*
+ * send DATA_B3_CONF if "delivery confirmation" bit was set in request;
+ * otherwise it has already been sent by do_data_b3_req()
+ */
flags = CAPIMSG_FLAGS(req);
-
- /* build DATA_B3_CONF message */
- cskb = alloc_skb(CAPI_DATA_B3_CONF_LEN, GFP_ATOMIC);
- if (!cskb) {
- dev_err(cs->dev, "%s: out of memory\n", __func__);
- return;
- }
- /* frequent message, avoid _cmsg overhead */
- CAPIMSG_SETLEN(cskb->data, CAPI_DATA_B3_CONF_LEN);
- CAPIMSG_SETAPPID(cskb->data, ap->id);
- CAPIMSG_SETCOMMAND(cskb->data, CAPI_DATA_B3);
- CAPIMSG_SETSUBCOMMAND(cskb->data, CAPI_CONF);
- CAPIMSG_SETMSGID(cskb->data, CAPIMSG_MSGID(req));
- CAPIMSG_SETCONTROLLER(cskb->data, iif->ctr.cnr);
- CAPIMSG_SETPLCI_PART(cskb->data, bcs->channel + 1);
- CAPIMSG_SETNCCI_PART(cskb->data, 1);
- CAPIMSG_SETHANDLE_CONF(cskb->data, CAPIMSG_HANDLE_REQ(req));
- if (flags & ~CAPI_FLAGS_DELIVERY_CONFIRMATION)
- CAPIMSG_SETINFO_CONF(cskb->data,
- CapiFlagsNotSupportedByProtocol);
- else
- CAPIMSG_SETINFO_CONF(cskb->data, CAPI_NOERROR);
-
- /* emit message */
- dump_rawmsg(DEBUG_LLDATA, "DATA_B3_CONF", cskb->data);
- capi_ctr_handle_message(&iif->ctr, ap->id, cskb);
+ if (flags & CAPI_FLAGS_DELIVERY_CONFIRMATION)
+ send_data_b3_conf(cs, &iif->ctr, ap->id, CAPIMSG_MSGID(req),
+ bcs->channel + 1, CAPIMSG_HANDLE_REQ(req),
+ (flags & ~CAPI_FLAGS_DELIVERY_CONFIRMATION) ?
+ CapiFlagsNotSupportedByProtocol :
+ CAPI_NOERROR);
}
EXPORT_SYMBOL_GPL(gigaset_skb_sent);

@@ -1795,6 +1810,8 @@ static void do_data_b3_req(struct gigaset_capi_ctr *iif,
u16 msglen = CAPIMSG_LEN(skb->data);
u16 datalen = CAPIMSG_DATALEN(skb->data);
u16 flags = CAPIMSG_FLAGS(skb->data);
+ u16 msgid = CAPIMSG_MSGID(skb->data);
+ u16 handle = CAPIMSG_HANDLE_REQ(skb->data);

/* frequent message, avoid _cmsg overhead */
dump_rawmsg(DEBUG_LLDATA, "DATA_B3_REQ", skb->data);
@@ -1845,12 +1862,14 @@ static void do_data_b3_req(struct gigaset_capi_ctr *iif,
return;
}

- /* DATA_B3_CONF reply will be sent by gigaset_skb_sent() */
-
/*
- * ToDo: honor unset "delivery confirmation" bit
- * (send DATA_B3_CONF immediately?)
+ * DATA_B3_CONF will be sent by gigaset_skb_sent() only if "delivery
+ * confirmation" bit is set; otherwise we have to send it now
*/
+ if (!(flags & CAPI_FLAGS_DELIVERY_CONFIRMATION))
+ send_data_b3_conf(cs, &iif->ctr, ap->id, msgid, channel, handle,
+ flags ? CapiFlagsNotSupportedByProtocol
+ : CAPI_NOERROR);
}

/*
--
1.6.5.3.298.g39add

2010-06-22 00:04:46

by Tilman Schmidt

[permalink] [raw]
Subject: [PATCH 4/5] isdn/gigaset: encode HLC and BC together

Adapt to buggy device firmware which accepts setting HLC only in the
same command line as BC, by encoding HLC and BC in a single command
if both are specified, and rejecting HLC without BC.

Impact: bugfix
Signed-off-by: Tilman Schmidt <[email protected]>
---
drivers/isdn/gigaset/capi.c | 83 +++++++++++++++++++++++---------------
drivers/isdn/gigaset/ev-layer.c | 4 +-
drivers/isdn/gigaset/gigaset.h | 5 +-
3 files changed, 53 insertions(+), 39 deletions(-)

diff --git a/drivers/isdn/gigaset/capi.c b/drivers/isdn/gigaset/capi.c
index e685123..7d9549e 100644
--- a/drivers/isdn/gigaset/capi.c
+++ b/drivers/isdn/gigaset/capi.c
@@ -1166,7 +1166,7 @@ static void do_connect_req(struct gigaset_capi_ctr *iif,
char **commands;
char *s;
u8 *pp;
- int i, l;
+ int i, l, lbc, lhlc;
u16 info;

/* decode message */
@@ -1293,43 +1293,60 @@ static void do_connect_req(struct gigaset_capi_ctr *iif,
goto error;
}

- /* check/encode parameter: BC */
- if (cmsg->BC && cmsg->BC[0]) {
- /* explicit BC overrides CIP */
- l = 2*cmsg->BC[0] + 7;
+ /*
+ * check/encode parameters: BC & HLC
+ * must be encoded together as device doesn't accept HLC separately
+ * explicit parameters override values derived from CIP
+ */
+
+ /* determine lengths */
+ if (cmsg->BC && cmsg->BC[0]) /* BC specified explicitly */
+ lbc = 2*cmsg->BC[0];
+ else if (cip2bchlc[cmsg->CIPValue].bc) /* BC derived from CIP */
+ lbc = strlen(cip2bchlc[cmsg->CIPValue].bc);
+ else /* no BC */
+ lbc = 0;
+ if (cmsg->HLC && cmsg->HLC[0]) /* HLC specified explicitly */
+ lhlc = 2*cmsg->HLC[0];
+ else if (cip2bchlc[cmsg->CIPValue].hlc) /* HLC derived from CIP */
+ lhlc = strlen(cip2bchlc[cmsg->CIPValue].hlc);
+ else /* no HLC */
+ lhlc = 0;
+
+ if (lbc) {
+ /* have BC: allocate and assemble command string */
+ l = lbc + 7; /* "^SBC=" + value + "\r" + null byte */
+ if (lhlc)
+ l += lhlc + 7; /* ";^SHLC=" + value */
commands[AT_BC] = kmalloc(l, GFP_KERNEL);
if (!commands[AT_BC])
goto oom;
strcpy(commands[AT_BC], "^SBC=");
- decode_ie(cmsg->BC, commands[AT_BC]+5);
+ if (cmsg->BC && cmsg->BC[0]) /* BC specified explicitly */
+ decode_ie(cmsg->BC, commands[AT_BC] + 5);
+ else /* BC derived from CIP */
+ strcpy(commands[AT_BC] + 5,
+ cip2bchlc[cmsg->CIPValue].bc);
+ if (lhlc) {
+ strcpy(commands[AT_BC] + lbc + 5, ";^SHLC=");
+ if (cmsg->HLC && cmsg->HLC[0])
+ /* HLC specified explicitly */
+ decode_ie(cmsg->HLC,
+ commands[AT_BC] + lbc + 12);
+ else /* HLC derived from CIP */
+ strcpy(commands[AT_BC] + lbc + 12,
+ cip2bchlc[cmsg->CIPValue].hlc);
+ }
strcpy(commands[AT_BC] + l - 2, "\r");
- } else if (cip2bchlc[cmsg->CIPValue].bc) {
- l = strlen(cip2bchlc[cmsg->CIPValue].bc) + 7;
- commands[AT_BC] = kmalloc(l, GFP_KERNEL);
- if (!commands[AT_BC])
- goto oom;
- snprintf(commands[AT_BC], l, "^SBC=%s\r",
- cip2bchlc[cmsg->CIPValue].bc);
- }
-
- /* check/encode parameter: HLC */
- if (cmsg->HLC && cmsg->HLC[0]) {
- /* explicit HLC overrides CIP */
- l = 2*cmsg->HLC[0] + 7;
- commands[AT_HLC] = kmalloc(l, GFP_KERNEL);
- if (!commands[AT_HLC])
- goto oom;
- strcpy(commands[AT_HLC], "^SHLC=");
- decode_ie(cmsg->HLC, commands[AT_HLC]+5);
- strcpy(commands[AT_HLC] + l - 2, "\r");
- } else if (cip2bchlc[cmsg->CIPValue].hlc) {
- l = strlen(cip2bchlc[cmsg->CIPValue].hlc) + 7;
- commands[AT_HLC] = kmalloc(l, GFP_KERNEL);
- if (!commands[AT_HLC])
- goto oom;
- snprintf(commands[AT_HLC], l, "^SHLC=%s\r",
- cip2bchlc[cmsg->CIPValue].hlc);
- }
+ } else {
+ /* no BC */
+ if (lhlc) {
+ dev_notice(cs->dev, "%s: cannot set HLC without BC\n",
+ "CONNECT_REQ");
+ info = CapiIllMessageParmCoding; /* ? */
+ goto error;
+ }
+ }

/* check/encode parameter: B Protocol */
if (cmsg->BProtocol == CAPI_DEFAULT) {
diff --git a/drivers/isdn/gigaset/ev-layer.c b/drivers/isdn/gigaset/ev-layer.c
index 206c380..ceaef9a 100644
--- a/drivers/isdn/gigaset/ev-layer.c
+++ b/drivers/isdn/gigaset/ev-layer.c
@@ -282,9 +282,7 @@ struct reply_t gigaset_tab_cid[] =
/* dial */
{EV_DIAL, -1, -1, -1, -1, -1, {ACT_DIAL} },
{RSP_INIT, 0, 0, SEQ_DIAL, 601, 5, {ACT_CMD+AT_BC} },
-{RSP_OK, 601, 601, -1, 602, 5, {ACT_CMD+AT_HLC} },
-{RSP_NULL, 602, 602, -1, 603, 5, {ACT_CMD+AT_PROTO} },
-{RSP_OK, 602, 602, -1, 603, 5, {ACT_CMD+AT_PROTO} },
+{RSP_OK, 601, 601, -1, 603, 5, {ACT_CMD+AT_PROTO} },
{RSP_OK, 603, 603, -1, 604, 5, {ACT_CMD+AT_TYPE} },
{RSP_OK, 604, 604, -1, 605, 5, {ACT_CMD+AT_MSN} },
{RSP_NULL, 605, 605, -1, 606, 5, {ACT_CMD+AT_CLIP} },
diff --git a/drivers/isdn/gigaset/gigaset.h b/drivers/isdn/gigaset/gigaset.h
index f77ec54..c4e6c26 100644
--- a/drivers/isdn/gigaset/gigaset.h
+++ b/drivers/isdn/gigaset/gigaset.h
@@ -186,10 +186,9 @@ void gigaset_dbg_buffer(enum debuglevel level, const unsigned char *msg,
#define AT_BC 3
#define AT_PROTO 4
#define AT_TYPE 5
-#define AT_HLC 6
-#define AT_CLIP 7
+#define AT_CLIP 6
/* total number */
-#define AT_NUM 8
+#define AT_NUM 7

/* variables in struct at_state_t */
#define VAR_ZSAU 0
--
1.6.5.3.298.g39add

2010-06-22 00:04:54

by Tilman Schmidt

[permalink] [raw]
Subject: [PATCH 1/5] 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: Tilman Schmidt <[email protected]>
---
drivers/isdn/gigaset/asyncdata.c | 44 ++++++-----------------
drivers/isdn/gigaset/capi.c | 8 ++++
drivers/isdn/gigaset/common.c | 32 ++++------------
drivers/isdn/gigaset/gigaset.h | 29 +++++++++++----
drivers/isdn/gigaset/i4l.c | 21 +++++++++++
drivers/isdn/gigaset/isocdata.c | 72 +++++++++++++------------------------
6 files changed, 94 insertions(+), 112 deletions(-)

diff --git a/drivers/isdn/gigaset/asyncdata.c b/drivers/isdn/gigaset/asyncdata.c
index c5016bd..c3b1dc3 100644
--- a/drivers/isdn/gigaset/asyncdata.c
+++ b/drivers/isdn/gigaset/asyncdata.c
@@ -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 @@ byte_stuff:

/* 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 @@ byte_stuff:
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 @@ byte_stuff:
#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 @@ byte_stuff:
}

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;
diff --git a/drivers/isdn/gigaset/capi.c b/drivers/isdn/gigaset/capi.c
index 8f78f15..245a608 100644
--- a/drivers/isdn/gigaset/capi.c
+++ b/drivers/isdn/gigaset/capi.c
@@ -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 */
diff --git a/drivers/isdn/gigaset/common.c b/drivers/isdn/gigaset/common.c
index f6f45f2..9778fab 100644
--- a/drivers/isdn/gigaset/common.c
+++ b/drivers/isdn/gigaset/common.c
@@ -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);
}
diff --git a/drivers/isdn/gigaset/gigaset.h b/drivers/isdn/gigaset/gigaset.h
index 05947f9..f77ec54 100644
--- a/drivers/isdn/gigaset/gigaset.h
+++ b/drivers/isdn/gigaset/gigaset.h
@@ -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,
diff --git a/drivers/isdn/gigaset/i4l.c b/drivers/isdn/gigaset/i4l.c
index c22e5ac..f01c3c2 100644
--- a/drivers/isdn/gigaset/i4l.c
+++ b/drivers/isdn/gigaset/i4l.c
@@ -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;
diff --git a/drivers/isdn/gigaset/isocdata.c b/drivers/isdn/gigaset/isocdata.c
index 16fd3bd..2dfd346 100644
--- a/drivers/isdn/gigaset/isocdata.c
+++ b/drivers/isdn/gigaset/isocdata.c
@@ -500,19 +500,18 @@ int gigaset_isoc_buildframe(struct bc_state *bcs, unsigned char *in, int len)
*/
static inline void hdlc_putbyte(unsigned char c, struct bc_state *bcs)
{
- bcs->fcs = crc_ccitt_byte(bcs->fcs, c);
- if (unlikely(bcs->skb == NULL)) {
+ bcs->rx_fcs = crc_ccitt_byte(bcs->rx_fcs, c);
+ if (bcs->rx_skb == NULL)
/* skipping */
return;
- }
- if (unlikely(bcs->skb->len == SBUFSIZE)) {
+ if (bcs->rx_skb->len >= bcs->rx_bufsize) {
dev_warn(bcs->cs->dev, "received oversized packet discarded\n");
bcs->hw.bas->giants++;
- dev_kfree_skb_any(bcs->skb);
- bcs->skb = NULL;
+ dev_kfree_skb_any(bcs->rx_skb);
+ bcs->rx_skb = NULL;
return;
}
- *__skb_put(bcs->skb, 1) = c;
+ *__skb_put(bcs->rx_skb, 1) = c;
}

/* hdlc_flush
@@ -521,18 +520,13 @@ static inline void hdlc_putbyte(unsigned char c, struct bc_state *bcs)
static inline void hdlc_flush(struct bc_state *bcs)
{
/* clear skb or allocate new if not skipping */
- if (likely(bcs->skb != NULL))
- skb_trim(bcs->skb, 0);
- else if (!bcs->ignore) {
- bcs->skb = dev_alloc_skb(SBUFSIZE + bcs->cs->hw_hdr_len);
- if (bcs->skb)
- skb_reserve(bcs->skb, bcs->cs->hw_hdr_len);
- else
- dev_err(bcs->cs->dev, "could not allocate skb\n");
- }
+ if (bcs->rx_skb != NULL)
+ skb_trim(bcs->rx_skb, 0);
+ else
+ gigaset_new_rx_skb(bcs);

/* reset packet state */
- bcs->fcs = PPP_INITFCS;
+ bcs->rx_fcs = PPP_INITFCS;
}

/* hdlc_done
@@ -549,7 +543,7 @@ static inline void hdlc_done(struct bc_state *bcs)
hdlc_flush(bcs);
return;
}
- procskb = bcs->skb;
+ procskb = bcs->rx_skb;
if (procskb == NULL) {
/* previous error */
gig_dbg(DEBUG_ISO, "%s: skb=NULL", __func__);
@@ -560,8 +554,8 @@ static inline void hdlc_done(struct bc_state *bcs)
bcs->hw.bas->runts++;
dev_kfree_skb_any(procskb);
gigaset_isdn_rcv_err(bcs);
- } else if (bcs->fcs != PPP_GOODFCS) {
- dev_notice(cs->dev, "frame check error (0x%04x)\n", bcs->fcs);
+ } else if (bcs->rx_fcs != PPP_GOODFCS) {
+ dev_notice(cs->dev, "frame check error\n");
bcs->hw.bas->fcserrs++;
dev_kfree_skb_any(procskb);
gigaset_isdn_rcv_err(bcs);
@@ -574,13 +568,8 @@ static inline void hdlc_done(struct bc_state *bcs)
bcs->hw.bas->goodbytes += len;
gigaset_skb_rcvd(bcs, procskb);
}
-
- bcs->skb = dev_alloc_skb(SBUFSIZE + cs->hw_hdr_len);
- if (bcs->skb)
- skb_reserve(bcs->skb, cs->hw_hdr_len);
- else
- dev_err(cs->dev, "could not allocate skb\n");
- bcs->fcs = PPP_INITFCS;
+ gigaset_new_rx_skb(bcs);
+ bcs->rx_fcs = PPP_INITFCS;
}

/* hdlc_frag
@@ -597,8 +586,8 @@ static inline void hdlc_frag(struct bc_state *bcs, unsigned inbits)
dev_notice(bcs->cs->dev, "received partial byte (%d bits)\n", inbits);
bcs->hw.bas->alignerrs++;
gigaset_isdn_rcv_err(bcs);
- __skb_trim(bcs->skb, 0);
- bcs->fcs = PPP_INITFCS;
+ __skb_trim(bcs->rx_skb, 0);
+ bcs->rx_fcs = PPP_INITFCS;
}

/* bit counts lookup table for HDLC bit unstuffing
@@ -847,7 +836,6 @@ static inline void hdlc_unpack(unsigned char *src, unsigned count,
static inline void trans_receive(unsigned char *src, unsigned count,
struct bc_state *bcs)
{
- struct cardstate *cs = bcs->cs;
struct sk_buff *skb;
int dobytes;
unsigned char *dst;
@@ -857,17 +845,11 @@ static inline void trans_receive(unsigned char *src, unsigned count,
hdlc_flush(bcs);
return;
}
- skb = bcs->skb;
- if (unlikely(skb == NULL)) {
- bcs->skb = skb = dev_alloc_skb(SBUFSIZE + cs->hw_hdr_len);
- if (!skb) {
- dev_err(cs->dev, "could not allocate skb\n");
- return;
- }
- skb_reserve(skb, cs->hw_hdr_len);
- }
+ skb = bcs->rx_skb;
+ if (skb == NULL)
+ skb = gigaset_new_rx_skb(bcs);
bcs->hw.bas->goodbytes += skb->len;
- dobytes = TRANSBUFSIZE - skb->len;
+ dobytes = bcs->rx_bufsize - skb->len;
while (count > 0) {
dst = skb_put(skb, count < dobytes ? count : dobytes);
while (count > 0 && dobytes > 0) {
@@ -879,14 +861,10 @@ static inline void trans_receive(unsigned char *src, unsigned count,
dump_bytes(DEBUG_STREAM_DUMP,
"rcv data", skb->data, skb->len);
gigaset_skb_rcvd(bcs, skb);
- bcs->skb = skb =
- dev_alloc_skb(SBUFSIZE + cs->hw_hdr_len);
- if (!skb) {
- dev_err(cs->dev, "could not allocate skb\n");
+ skb = gigaset_new_rx_skb(bcs);
+ if (skb == NULL)
return;
- }
- skb_reserve(skb, cs->hw_hdr_len);
- dobytes = TRANSBUFSIZE;
+ dobytes = bcs->rx_bufsize;
}
}
}
--
1.6.5.3.298.g39add

2010-06-22 00:05:01

by Tilman Schmidt

[permalink] [raw]
Subject: [PATCH 5/5] isdn/gigaset: correct CAPI connection state storage

CAPI applications can handle several connections in parallel,
so one connection state per application isn't sufficient.
Store the connection state in the channel structure instead.

Impact: bugfix
Signed-off-by: Tilman Schmidt <[email protected]>
---
drivers/isdn/gigaset/capi.c | 225 ++++++++++++++++++++++++++++++---------
drivers/isdn/gigaset/common.c | 4 +
drivers/isdn/gigaset/gigaset.h | 4 +-
3 files changed, 180 insertions(+), 53 deletions(-)

diff --git a/drivers/isdn/gigaset/capi.c b/drivers/isdn/gigaset/capi.c
index 7d9549e..c38750c 100644
--- a/drivers/isdn/gigaset/capi.c
+++ b/drivers/isdn/gigaset/capi.c
@@ -70,7 +70,7 @@
#define MAX_NUMBER_DIGITS 20
#define MAX_FMT_IE_LEN 20

-/* values for gigaset_capi_appl.connected */
+/* values for bcs->apconnstate */
#define APCONN_NONE 0 /* inactive/listening */
#define APCONN_SETUP 1 /* connecting */
#define APCONN_ACTIVE 2 /* B channel up */
@@ -84,7 +84,6 @@ struct gigaset_capi_appl {
u16 nextMessageNumber;
u32 listenInfoMask;
u32 listenCIPmask;
- int connected;
};

/* CAPI specific controller data structure */
@@ -384,7 +383,7 @@ void gigaset_skb_sent(struct bc_state *bcs, struct sk_buff *dskb)
}

/* don't send further B3 messages if disconnected */
- if (ap->connected < APCONN_ACTIVE) {
+ if (bcs->apconnstate < APCONN_ACTIVE) {
gig_dbg(DEBUG_LLDATA, "disconnected, discarding ack");
return;
}
@@ -428,7 +427,7 @@ void gigaset_skb_rcvd(struct bc_state *bcs, struct sk_buff *skb)
}

/* don't send further B3 messages if disconnected */
- if (ap->connected < APCONN_ACTIVE) {
+ if (bcs->apconnstate < APCONN_ACTIVE) {
gig_dbg(DEBUG_LLDATA, "disconnected, discarding data");
dev_kfree_skb_any(skb);
return;
@@ -500,6 +499,7 @@ int gigaset_isdn_icall(struct at_state_t *at_state)
u32 actCIPmask;
struct sk_buff *skb;
unsigned int msgsize;
+ unsigned long flags;
int i;

/*
@@ -624,7 +624,14 @@ int gigaset_isdn_icall(struct at_state_t *at_state)
format_ie(iif->hcmsg.CalledPartyNumber));

/* scan application list for matching listeners */
- bcs->ap = NULL;
+ spin_lock_irqsave(&bcs->aplock, flags);
+ if (bcs->ap != NULL || bcs->apconnstate != APCONN_NONE) {
+ dev_warn(cs->dev, "%s: channel not properly cleared (%p/%d)\n",
+ __func__, bcs->ap, bcs->apconnstate);
+ bcs->ap = NULL;
+ bcs->apconnstate = APCONN_NONE;
+ }
+ spin_unlock_irqrestore(&bcs->aplock, flags);
actCIPmask = 1 | (1 << iif->hcmsg.CIPValue);
list_for_each_entry(ap, &iif->appls, ctrlist)
if (actCIPmask & ap->listenCIPmask) {
@@ -642,10 +649,12 @@ int gigaset_isdn_icall(struct at_state_t *at_state)
dump_cmsg(DEBUG_CMD, __func__, &iif->hcmsg);

/* add to listeners on this B channel, update state */
+ spin_lock_irqsave(&bcs->aplock, flags);
ap->bcnext = bcs->ap;
bcs->ap = ap;
bcs->chstate |= CHS_NOTIFY_LL;
- ap->connected = APCONN_SETUP;
+ bcs->apconnstate = APCONN_SETUP;
+ spin_unlock_irqrestore(&bcs->aplock, flags);

/* emit message */
capi_ctr_handle_message(&iif->ctr, ap->id, skb);
@@ -670,7 +679,7 @@ static void send_disconnect_ind(struct bc_state *bcs,
struct gigaset_capi_ctr *iif = cs->iif;
struct sk_buff *skb;

- if (ap->connected == APCONN_NONE)
+ if (bcs->apconnstate == APCONN_NONE)
return;

capi_cmsg_header(&iif->hcmsg, ap->id, CAPI_DISCONNECT, CAPI_IND,
@@ -684,7 +693,6 @@ static void send_disconnect_ind(struct bc_state *bcs,
}
capi_cmsg2message(&iif->hcmsg, __skb_put(skb, CAPI_DISCONNECT_IND_LEN));
dump_cmsg(DEBUG_CMD, __func__, &iif->hcmsg);
- ap->connected = APCONN_NONE;
capi_ctr_handle_message(&iif->ctr, ap->id, skb);
}

@@ -701,9 +709,9 @@ static void send_disconnect_b3_ind(struct bc_state *bcs,
struct sk_buff *skb;

/* nothing to do if no logical connection active */
- if (ap->connected < APCONN_ACTIVE)
+ if (bcs->apconnstate < APCONN_ACTIVE)
return;
- ap->connected = APCONN_SETUP;
+ bcs->apconnstate = APCONN_SETUP;

capi_cmsg_header(&iif->hcmsg, ap->id, CAPI_DISCONNECT_B3, CAPI_IND,
ap->nextMessageNumber++,
@@ -730,14 +738,25 @@ void gigaset_isdn_connD(struct bc_state *bcs)
{
struct cardstate *cs = bcs->cs;
struct gigaset_capi_ctr *iif = cs->iif;
- struct gigaset_capi_appl *ap = bcs->ap;
+ struct gigaset_capi_appl *ap;
struct sk_buff *skb;
unsigned int msgsize;
+ unsigned long flags;

+ spin_lock_irqsave(&bcs->aplock, flags);
+ ap = bcs->ap;
if (!ap) {
+ spin_unlock_irqrestore(&bcs->aplock, flags);
dev_err(cs->dev, "%s: no application\n", __func__);
return;
}
+ if (bcs->apconnstate == APCONN_NONE) {
+ spin_unlock_irqrestore(&bcs->aplock, flags);
+ dev_warn(cs->dev, "%s: application %u not connected\n",
+ __func__, ap->id);
+ return;
+ }
+ spin_unlock_irqrestore(&bcs->aplock, flags);
while (ap->bcnext) {
/* this should never happen */
dev_warn(cs->dev, "%s: dropping extra application %u\n",
@@ -746,11 +765,6 @@ void gigaset_isdn_connD(struct bc_state *bcs)
CapiCallGivenToOtherApplication);
ap->bcnext = ap->bcnext->bcnext;
}
- if (ap->connected == APCONN_NONE) {
- dev_warn(cs->dev, "%s: application %u not connected\n",
- __func__, ap->id);
- return;
- }

/* prepare CONNECT_ACTIVE_IND message
* Note: LLC not supported by device
@@ -788,17 +802,24 @@ void gigaset_isdn_connD(struct bc_state *bcs)
void gigaset_isdn_hupD(struct bc_state *bcs)
{
struct gigaset_capi_appl *ap;
+ unsigned long flags;

/*
* ToDo: pass on reason code reported by device
* (requires ev-layer state machine extension to collect
* ZCAU device reply)
*/
- for (ap = bcs->ap; ap != NULL; ap = ap->bcnext) {
+ spin_lock_irqsave(&bcs->aplock, flags);
+ while (bcs->ap != NULL) {
+ ap = bcs->ap;
+ bcs->ap = ap->bcnext;
+ spin_unlock_irqrestore(&bcs->aplock, flags);
send_disconnect_b3_ind(bcs, ap);
send_disconnect_ind(bcs, ap, 0);
+ spin_lock_irqsave(&bcs->aplock, flags);
}
- bcs->ap = NULL;
+ bcs->apconnstate = APCONN_NONE;
+ spin_unlock_irqrestore(&bcs->aplock, flags);
}

/**
@@ -812,24 +833,21 @@ void gigaset_isdn_connB(struct bc_state *bcs)
{
struct cardstate *cs = bcs->cs;
struct gigaset_capi_ctr *iif = cs->iif;
- struct gigaset_capi_appl *ap = bcs->ap;
+ struct gigaset_capi_appl *ap;
struct sk_buff *skb;
+ unsigned long flags;
unsigned int msgsize;
u8 command;

+ spin_lock_irqsave(&bcs->aplock, flags);
+ ap = bcs->ap;
if (!ap) {
+ spin_unlock_irqrestore(&bcs->aplock, flags);
dev_err(cs->dev, "%s: no application\n", __func__);
return;
}
- while (ap->bcnext) {
- /* this should never happen */
- dev_warn(cs->dev, "%s: dropping extra application %u\n",
- __func__, ap->bcnext->id);
- send_disconnect_ind(bcs, ap->bcnext,
- CapiCallGivenToOtherApplication);
- ap->bcnext = ap->bcnext->bcnext;
- }
- if (!ap->connected) {
+ if (!bcs->apconnstate) {
+ spin_unlock_irqrestore(&bcs->aplock, flags);
dev_warn(cs->dev, "%s: application %u not connected\n",
__func__, ap->id);
return;
@@ -841,13 +859,26 @@ void gigaset_isdn_connB(struct bc_state *bcs)
* CONNECT_B3_ACTIVE_IND in reply to CONNECT_B3_RESP
* Parameters in both cases always: NCCI = 1, NCPI empty
*/
- if (ap->connected >= APCONN_ACTIVE) {
+ if (bcs->apconnstate >= APCONN_ACTIVE) {
command = CAPI_CONNECT_B3_ACTIVE;
msgsize = CAPI_CONNECT_B3_ACTIVE_IND_BASELEN;
} else {
command = CAPI_CONNECT_B3;
msgsize = CAPI_CONNECT_B3_IND_BASELEN;
}
+ bcs->apconnstate = APCONN_ACTIVE;
+
+ spin_unlock_irqrestore(&bcs->aplock, flags);
+
+ while (ap->bcnext) {
+ /* this should never happen */
+ dev_warn(cs->dev, "%s: dropping extra application %u\n",
+ __func__, ap->bcnext->id);
+ send_disconnect_ind(bcs, ap->bcnext,
+ CapiCallGivenToOtherApplication);
+ ap->bcnext = ap->bcnext->bcnext;
+ }
+
capi_cmsg_header(&iif->hcmsg, ap->id, command, CAPI_IND,
ap->nextMessageNumber++,
iif->ctr.cnr | ((bcs->channel + 1) << 8) | (1 << 16));
@@ -858,7 +889,6 @@ void gigaset_isdn_connB(struct bc_state *bcs)
}
capi_cmsg2message(&iif->hcmsg, __skb_put(skb, msgsize));
dump_cmsg(DEBUG_CMD, __func__, &iif->hcmsg);
- ap->connected = APCONN_ACTIVE;
capi_ctr_handle_message(&iif->ctr, ap->id, skb);
}

@@ -964,6 +994,61 @@ static void gigaset_register_appl(struct capi_ctr *ctr, u16 appl,
ap->rp = *rp;

list_add(&ap->ctrlist, &iif->appls);
+ dev_info(cs->dev, "application %u registered\n", ap->id);
+}
+
+/*
+ * remove CAPI application from channel
+ * helper function to keep indentation levels down and stay in 80 columns
+ */
+
+static inline void remove_appl_from_channel(struct bc_state *bcs,
+ struct gigaset_capi_appl *ap)
+{
+ struct cardstate *cs = bcs->cs;
+ struct gigaset_capi_appl *bcap;
+ unsigned long flags;
+ int prevconnstate;
+
+ spin_lock_irqsave(&bcs->aplock, flags);
+ bcap = bcs->ap;
+ if (bcap == NULL) {
+ spin_unlock_irqrestore(&bcs->aplock, flags);
+ return;
+ }
+
+ /* check first application on channel */
+ if (bcap == ap) {
+ bcs->ap = ap->bcnext;
+ if (bcs->ap != NULL) {
+ spin_unlock_irqrestore(&bcs->aplock, flags);
+ return;
+ }
+
+ /* none left, clear channel state */
+ prevconnstate = bcs->apconnstate;
+ bcs->apconnstate = APCONN_NONE;
+ spin_unlock_irqrestore(&bcs->aplock, flags);
+
+ if (prevconnstate == APCONN_ACTIVE) {
+ dev_notice(cs->dev, "%s: hanging up channel %u\n",
+ __func__, bcs->channel);
+ gigaset_add_event(cs, &bcs->at_state,
+ EV_HUP, NULL, 0, NULL);
+ gigaset_schedule_event(cs);
+ }
+ return;
+ }
+
+ /* check remaining list */
+ do {
+ if (bcap->bcnext == ap) {
+ bcap->bcnext = bcap->bcnext->bcnext;
+ return;
+ }
+ bcap = bcap->bcnext;
+ } while (bcap != NULL);
+ spin_unlock_irqrestore(&bcs->aplock, flags);
}

/*
@@ -975,19 +1060,19 @@ static void gigaset_release_appl(struct capi_ctr *ctr, u16 appl)
= container_of(ctr, struct gigaset_capi_ctr, ctr);
struct cardstate *cs = iif->ctr.driverdata;
struct gigaset_capi_appl *ap, *tmp;
+ unsigned ch;

list_for_each_entry_safe(ap, tmp, &iif->appls, ctrlist)
if (ap->id == appl) {
- if (ap->connected != APCONN_NONE) {
- dev_err(cs->dev,
- "%s: application %u still connected\n",
- __func__, ap->id);
- /* ToDo: clear active connection */
- }
+ /* remove from any channels */
+ for (ch = 0; ch < cs->channels; ch++)
+ remove_appl_from_channel(&cs->bcs[ch], ap);
+
+ /* remove from registration list */
list_del(&ap->ctrlist);
kfree(ap);
+ dev_info(cs->dev, "application %u released\n", appl);
}
-
}

/*
@@ -1166,6 +1251,7 @@ static void do_connect_req(struct gigaset_capi_ctr *iif,
char **commands;
char *s;
u8 *pp;
+ unsigned long flags;
int i, l, lbc, lhlc;
u16 info;

@@ -1181,8 +1267,15 @@ static void do_connect_req(struct gigaset_capi_ctr *iif,
send_conf(iif, ap, skb, CapiNoPlciAvailable);
return;
}
+ spin_lock_irqsave(&bcs->aplock, flags);
+ if (bcs->ap != NULL || bcs->apconnstate != APCONN_NONE)
+ dev_warn(cs->dev, "%s: channel not properly cleared (%p/%d)\n",
+ __func__, bcs->ap, bcs->apconnstate);
ap->bcnext = NULL;
bcs->ap = ap;
+ bcs->apconnstate = APCONN_SETUP;
+ spin_unlock_irqrestore(&bcs->aplock, flags);
+
bcs->rx_bufsize = ap->rp.datablklen;
dev_kfree_skb(bcs->rx_skb);
gigaset_new_rx_skb(bcs);
@@ -1419,7 +1512,6 @@ static void do_connect_req(struct gigaset_capi_ctr *iif,
goto error;
}
gigaset_schedule_event(cs);
- ap->connected = APCONN_SETUP;
send_conf(iif, ap, skb, CapiSuccess);
return;

@@ -1447,6 +1539,7 @@ static void do_connect_resp(struct gigaset_capi_ctr *iif,
_cmsg *cmsg = &iif->acmsg;
struct bc_state *bcs;
struct gigaset_capi_appl *oap;
+ unsigned long flags;
int channel;

/* decode message */
@@ -1466,12 +1559,21 @@ static void do_connect_resp(struct gigaset_capi_ctr *iif,
switch (cmsg->Reject) {
case 0: /* Accept */
/* drop all competing applications, keep only this one */
- for (oap = bcs->ap; oap != NULL; oap = oap->bcnext)
- if (oap != ap)
+ spin_lock_irqsave(&bcs->aplock, flags);
+ while (bcs->ap != NULL) {
+ oap = bcs->ap;
+ bcs->ap = oap->bcnext;
+ if (oap != ap) {
+ spin_unlock_irqrestore(&bcs->aplock, flags);
send_disconnect_ind(bcs, oap,
CapiCallGivenToOtherApplication);
+ spin_lock_irqsave(&bcs->aplock, flags);
+ }
+ }
ap->bcnext = NULL;
bcs->ap = ap;
+ spin_unlock_irqrestore(&bcs->aplock, flags);
+
bcs->rx_bufsize = ap->rp.datablklen;
dev_kfree_skb(bcs->rx_skb);
gigaset_new_rx_skb(bcs);
@@ -1542,31 +1644,45 @@ static void do_connect_resp(struct gigaset_capi_ctr *iif,
send_disconnect_ind(bcs, ap, 0);

/* remove it from the list of listening apps */
+ spin_lock_irqsave(&bcs->aplock, flags);
if (bcs->ap == ap) {
bcs->ap = ap->bcnext;
- if (bcs->ap == NULL)
+ if (bcs->ap == NULL) {
/* last one: stop ev-layer hupD notifications */
+ bcs->apconnstate = APCONN_NONE;
bcs->chstate &= ~CHS_NOTIFY_LL;
+ }
+ spin_unlock_irqrestore(&bcs->aplock, flags);
return;
}
for (oap = bcs->ap; oap != NULL; oap = oap->bcnext) {
if (oap->bcnext == ap) {
oap->bcnext = oap->bcnext->bcnext;
+ spin_unlock_irqrestore(&bcs->aplock, flags);
return;
}
}
+ spin_unlock_irqrestore(&bcs->aplock, flags);
dev_err(cs->dev, "%s: application %u not found\n",
__func__, ap->id);
return;

default: /* Reject */
/* drop all competing applications, keep only this one */
- for (oap = bcs->ap; oap != NULL; oap = oap->bcnext)
- if (oap != ap)
+ spin_lock_irqsave(&bcs->aplock, flags);
+ while (bcs->ap != NULL) {
+ oap = bcs->ap;
+ bcs->ap = oap->bcnext;
+ if (oap != ap) {
+ spin_unlock_irqrestore(&bcs->aplock, flags);
send_disconnect_ind(bcs, oap,
CapiCallGivenToOtherApplication);
+ spin_lock_irqsave(&bcs->aplock, flags);
+ }
+ }
ap->bcnext = NULL;
bcs->ap = ap;
+ spin_unlock_irqrestore(&bcs->aplock, flags);

/* reject call - will trigger DISCONNECT_IND for this app */
dev_info(cs->dev, "%s: Reject=%x\n",
@@ -1589,6 +1705,7 @@ static void do_connect_b3_req(struct gigaset_capi_ctr *iif,
{
struct cardstate *cs = iif->ctr.driverdata;
_cmsg *cmsg = &iif->acmsg;
+ struct bc_state *bcs;
int channel;

/* decode message */
@@ -1603,9 +1720,10 @@ static void do_connect_b3_req(struct gigaset_capi_ctr *iif,
send_conf(iif, ap, skb, CapiIllContrPlciNcci);
return;
}
+ bcs = &cs->bcs[channel-1];

/* mark logical connection active */
- ap->connected = APCONN_ACTIVE;
+ bcs->apconnstate = APCONN_ACTIVE;

/* build NCCI: always 1 (one B3 connection only) */
cmsg->adr.adrNCCI |= 1 << 16;
@@ -1651,7 +1769,7 @@ static void do_connect_b3_resp(struct gigaset_capi_ctr *iif,

if (cmsg->Reject) {
/* Reject: clear B3 connect received flag */
- ap->connected = APCONN_SETUP;
+ bcs->apconnstate = APCONN_SETUP;

/* trigger hangup, causing eventual DISCONNECT_IND */
if (!gigaset_add_event(cs, &bcs->at_state,
@@ -1723,11 +1841,11 @@ static void do_disconnect_req(struct gigaset_capi_ctr *iif,
}

/* skip if DISCONNECT_IND already sent */
- if (!ap->connected)
+ if (!bcs->apconnstate)
return;

/* check for active logical connection */
- if (ap->connected >= APCONN_ACTIVE) {
+ if (bcs->apconnstate >= APCONN_ACTIVE) {
/*
* emit DISCONNECT_B3_IND with cause 0x3301
* use separate cmsg structure, as the content of iif->acmsg
@@ -1776,6 +1894,7 @@ static void do_disconnect_b3_req(struct gigaset_capi_ctr *iif,
{
struct cardstate *cs = iif->ctr.driverdata;
_cmsg *cmsg = &iif->acmsg;
+ struct bc_state *bcs;
int channel;

/* decode message */
@@ -1791,17 +1910,17 @@ static void do_disconnect_b3_req(struct gigaset_capi_ctr *iif,
send_conf(iif, ap, skb, CapiIllContrPlciNcci);
return;
}
+ bcs = &cs->bcs[channel-1];

/* reject if logical connection not active */
- if (ap->connected < APCONN_ACTIVE) {
+ if (bcs->apconnstate < APCONN_ACTIVE) {
send_conf(iif, ap, skb,
CapiMessageNotSupportedInCurrentState);
return;
}

/* trigger hangup, causing eventual DISCONNECT_B3_IND */
- if (!gigaset_add_event(cs, &cs->bcs[channel-1].at_state,
- EV_HUP, NULL, 0, NULL)) {
+ if (!gigaset_add_event(cs, &bcs->at_state, EV_HUP, NULL, 0, NULL)) {
send_conf(iif, ap, skb, CAPI_MSGOSRESOURCEERR);
return;
}
@@ -1822,6 +1941,7 @@ static void do_data_b3_req(struct gigaset_capi_ctr *iif,
struct sk_buff *skb)
{
struct cardstate *cs = iif->ctr.driverdata;
+ struct bc_state *bcs;
int channel = CAPIMSG_PLCI_PART(skb->data);
u16 ncci = CAPIMSG_NCCI_PART(skb->data);
u16 msglen = CAPIMSG_LEN(skb->data);
@@ -1844,6 +1964,7 @@ static void do_data_b3_req(struct gigaset_capi_ctr *iif,
send_conf(iif, ap, skb, CapiIllContrPlciNcci);
return;
}
+ bcs = &cs->bcs[channel-1];
if (msglen != CAPI_DATA_B3_REQ_LEN && msglen != CAPI_DATA_B3_REQ_LEN64)
dev_notice(cs->dev, "%s: unexpected length %d\n",
"DATA_B3_REQ", msglen);
@@ -1863,7 +1984,7 @@ static void do_data_b3_req(struct gigaset_capi_ctr *iif,
}

/* reject if logical connection not active */
- if (ap->connected < APCONN_ACTIVE) {
+ if (bcs->apconnstate < APCONN_ACTIVE) {
send_conf(iif, ap, skb, CapiMessageNotSupportedInCurrentState);
return;
}
@@ -1874,7 +1995,7 @@ static void do_data_b3_req(struct gigaset_capi_ctr *iif,
skb_pull(skb, msglen);

/* pass to device-specific module */
- if (cs->ops->send_skb(&cs->bcs[channel-1], skb) < 0) {
+ if (cs->ops->send_skb(bcs, skb) < 0) {
send_conf(iif, ap, skb, CAPI_MSGOSRESOURCEERR);
return;
}
diff --git a/drivers/isdn/gigaset/common.c b/drivers/isdn/gigaset/common.c
index 9778fab..5d4befb 100644
--- a/drivers/isdn/gigaset/common.c
+++ b/drivers/isdn/gigaset/common.c
@@ -649,6 +649,10 @@ static struct bc_state *gigaset_initbcs(struct bc_state *bcs,
for (i = 0; i < AT_NUM; ++i)
bcs->commands[i] = NULL;

+ spin_lock_init(&bcs->aplock);
+ bcs->ap = NULL;
+ bcs->apconnstate = 0;
+
gig_dbg(DEBUG_INIT, " setting up bcs[%d]->hw", channel);
if (cs->ops->initbcshw(bcs))
return bcs;
diff --git a/drivers/isdn/gigaset/gigaset.h b/drivers/isdn/gigaset/gigaset.h
index c4e6c26..8738b08 100644
--- a/drivers/isdn/gigaset/gigaset.h
+++ b/drivers/isdn/gigaset/gigaset.h
@@ -403,7 +403,9 @@ struct bc_state {
struct bas_bc_state *bas; /* usb hardware driver (base) */
} hw;

- void *ap; /* LL application structure */
+ void *ap; /* associated LL application */
+ int apconnstate; /* LL application connection state */
+ spinlock_t aplock;
};

struct cardstate {
--
1.6.5.3.298.g39add

2010-06-22 04:04:55

by Greg KH

[permalink] [raw]
Subject: Re: [stable] [PATCH 0/5] late Gigaset fixes for 2.6.35

On Tue, Jun 22, 2010 at 02:28:41AM +0200, Tilman Schmidt wrote:
> Karsten, David,
>
> following are five patches to the Gigaset driver fixing reported
> problems with two CAPI applications (Asterisk and capifax). They
> fix serious bugs, though not technically regressions, and I would
> therefore appreciate if they could still make it into release
> 2.6.35. Otherwise please queue them for the first stable cycle.
>
> These should also go into 2.6.34-stable. They apply cleanly to
> 2.6.34 except for some offsets in file drivers/isdn/gigaset/capi.c.

To get a patch in the -stable tree, please just add a simple:
Cc: stable <[email protected]>
to the body of the patch in the signed-off-by area. That way, when the
patches go to Linus, I get automatically notified of them and you don't
have to do anything. If you don't, then I have to go and dig and watch
the tree to see if they are really applied or not, which, when
confronted with 200+ patches that need to be manually watched for this
way, does not scale...

thanks,

greg k-h

2010-06-26 04:31:17

by David Miller

[permalink] [raw]
Subject: Re: [PATCH 0/5] late Gigaset fixes for 2.6.35

From: Tilman Schmidt <[email protected]>
Date: Tue, 22 Jun 2010 01:54:04 +0200 (CEST)

> following are five patches to the Gigaset driver fixing reported
> problems with two CAPI applications (Asterisk and capifax). They
> fix serious bugs, though not technically regressions, and I would
> therefore appreciate if they could still make it into release
> 2.6.35.

All applied to net-2.6, thanks.