#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 */
        u16 nextMessageNumber;
        u32 listenInfoMask;
        u32 listenCIPmask;
-       int connected;
 };
 
 /* CAPI specific controller data structure */
        }
 
        /* 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;
        }
        }
 
        /* 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;
        u32 actCIPmask;
        struct sk_buff *skb;
        unsigned int msgsize;
+       unsigned long flags;
        int i;
 
        /*
                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) {
                        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);
        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,
        }
        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);
 }
 
        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++,
 {
        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",
                                    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
 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);
 }
 
 /**
 {
        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;
         * 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));
        }
        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);
 }
 
        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);
 }
 
 /*
                = 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);
                }
-
 }
 
 /*
        char **commands;
        char *s;
        u8 *pp;
+       unsigned long flags;
        int i, l, lbc, lhlc;
        u16 info;
 
                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);
                goto error;
        }
        gigaset_schedule_event(cs);
-       ap->connected = APCONN_SETUP;
        send_conf(iif, ap, skb, CapiSuccess);
        return;
 
        _cmsg *cmsg = &iif->acmsg;
        struct bc_state *bcs;
        struct gigaset_capi_appl *oap;
+       unsigned long flags;
        int channel;
 
        /* decode message */
        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);
                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",
 {
        struct cardstate *cs = iif->ctr.driverdata;
        _cmsg *cmsg = &iif->acmsg;
+       struct bc_state *bcs;
        int channel;
 
        /* decode message */
                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;
 
        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,
        }
 
        /* 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
 {
        struct cardstate *cs = iif->ctr.driverdata;
        _cmsg *cmsg = &iif->acmsg;
+       struct bc_state *bcs;
        int channel;
 
        /* decode message */
                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;
        }
                           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);
                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);
        }
 
        /* reject if logical connection not active */
-       if (ap->connected < APCONN_ACTIVE) {
+       if (bcs->apconnstate < APCONN_ACTIVE) {
                send_conf(iif, ap, skb, CapiMessageNotSupportedInCurrentState);
                return;
        }
        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;
        }