include cleanup: Update gfp.h and slab.h includes to prepare for breaking implicit...
[pandora-kernel.git] / drivers / isdn / capi / kcapi.c
index b0bacf3..bd00dce 100644 (file)
@@ -27,6 +27,7 @@
 #include <linux/init.h>
 #include <linux/moduleparam.h>
 #include <linux/delay.h>
+#include <linux/slab.h>
 #include <asm/uaccess.h>
 #include <linux/isdn/capicmd.h>
 #include <linux/isdn/capiutil.h>
 #include <linux/b1lli.h>
 #endif
 #include <linux/mutex.h>
-
-static char *revision = "$Revision: 1.1.2.8 $";
-
-/* ------------------------------------------------------------- */
+#include <linux/rcupdate.h>
 
 static int showcapimsgs = 0;
 
@@ -48,12 +46,10 @@ module_param(showcapimsgs, uint, 0);
 
 /* ------------------------------------------------------------- */
 
-struct capi_notifier {
+struct capictr_event {
        struct work_struct work;
-       unsigned int cmd;
+       unsigned int type;
        u32 controller;
-       u16 applid;
-       u32 ncci;
 };
 
 /* ------------------------------------------------------------- */
@@ -65,30 +61,31 @@ static char capi_manufakturer[64] = "AVM Berlin";
 #define NCCI2CTRL(ncci)    (((ncci) >> 24) & 0x7f)
 
 LIST_HEAD(capi_drivers);
-DEFINE_RWLOCK(capi_drivers_list_lock);
+DEFINE_MUTEX(capi_drivers_lock);
 
-static DEFINE_RWLOCK(application_lock);
-static DEFINE_MUTEX(controller_mutex);
+struct capi_ctr *capi_controller[CAPI_MAXCONTR];
+DEFINE_MUTEX(capi_controller_lock);
 
 struct capi20_appl *capi_applications[CAPI_MAXAPPL];
-struct capi_ctr *capi_cards[CAPI_MAXCONTR];
 
-static int ncards;
+static int ncontrollers;
+
+static BLOCKING_NOTIFIER_HEAD(ctr_notifier_list);
 
 /* -------- controller ref counting -------------------------------------- */
 
 static inline struct capi_ctr *
-capi_ctr_get(struct capi_ctr *card)
+capi_ctr_get(struct capi_ctr *ctr)
 {
-       if (!try_module_get(card->owner))
+       if (!try_module_get(ctr->owner))
                return NULL;
-       return card;
+       return ctr;
 }
 
 static inline void
-capi_ctr_put(struct capi_ctr *card)
+capi_ctr_put(struct capi_ctr *ctr)
 {
-       module_put(card->owner);
+       module_put(ctr->owner);
 }
 
 /* ------------------------------------------------------------- */
@@ -98,7 +95,7 @@ static inline struct capi_ctr *get_capi_ctr_by_nr(u16 contr)
        if (contr - 1 >= CAPI_MAXCONTR)
                return NULL;
 
-       return capi_cards[contr - 1];
+       return capi_controller[contr - 1];
 }
 
 static inline struct capi20_appl *get_capi_appl_by_nr(u16 applid)
@@ -106,7 +103,7 @@ static inline struct capi20_appl *get_capi_appl_by_nr(u16 applid)
        if (applid - 1 >= CAPI_MAXAPPL)
                return NULL;
 
-       return capi_applications[applid - 1];
+       return rcu_dereference(capi_applications[applid - 1]);
 }
 
 /* -------- util functions ------------------------------------ */
@@ -148,106 +145,159 @@ static inline int capi_subcmd_valid(u8 subcmd)
 
 /* ------------------------------------------------------------ */
 
-static void register_appl(struct capi_ctr *card, u16 applid, capi_register_params *rparam)
+static void
+register_appl(struct capi_ctr *ctr, u16 applid, capi_register_params *rparam)
 {
-       card = capi_ctr_get(card);
+       ctr = capi_ctr_get(ctr);
 
-       if (card)
-               card->register_appl(card, applid, rparam);
+       if (ctr)
+               ctr->register_appl(ctr, applid, rparam);
        else
-               printk(KERN_WARNING "%s: cannot get card resources\n", __func__);
+               printk(KERN_WARNING "%s: cannot get controller resources\n",
+                      __func__);
 }
 
 
-static void release_appl(struct capi_ctr *card, u16 applid)
+static void release_appl(struct capi_ctr *ctr, u16 applid)
 {
        DBG("applid %#x", applid);
        
-       card->release_appl(card, applid);
-       capi_ctr_put(card);
+       ctr->release_appl(ctr, applid);
+       capi_ctr_put(ctr);
 }
 
-/* -------- KCI_CONTRUP --------------------------------------- */
-
 static void notify_up(u32 contr)
 {
-       struct capi_ctr *card = get_capi_ctr_by_nr(contr);
        struct capi20_appl *ap;
+       struct capi_ctr *ctr;
        u16 applid;
 
-       if (showcapimsgs & 1) {
+       mutex_lock(&capi_controller_lock);
+
+       if (showcapimsgs & 1)
                printk(KERN_DEBUG "kcapi: notify up contr %d\n", contr);
-       }
-       if (!card) {
+
+       ctr = get_capi_ctr_by_nr(contr);
+       if (ctr) {
+               if (ctr->state == CAPI_CTR_RUNNING)
+                       goto unlock_out;
+
+               ctr->state = CAPI_CTR_RUNNING;
+
+               for (applid = 1; applid <= CAPI_MAXAPPL; applid++) {
+                       ap = get_capi_appl_by_nr(applid);
+                       if (!ap)
+                               continue;
+                       register_appl(ctr, applid, &ap->rparam);
+               }
+
+               wake_up_interruptible_all(&ctr->state_wait_queue);
+       } else
                printk(KERN_WARNING "%s: invalid contr %d\n", __func__, contr);
-               return;
-       }
-       for (applid = 1; applid <= CAPI_MAXAPPL; applid++) {
-               ap = get_capi_appl_by_nr(applid);
-               if (!ap || ap->release_in_progress) continue;
-               register_appl(card, applid, &ap->rparam);
-               if (ap->callback && !ap->release_in_progress)
-                       ap->callback(KCI_CONTRUP, contr, &card->profile);
-       }
-}
 
-/* -------- KCI_CONTRDOWN ------------------------------------- */
+unlock_out:
+       mutex_unlock(&capi_controller_lock);
+}
 
-static void notify_down(u32 contr)
+static void ctr_down(struct capi_ctr *ctr, int new_state)
 {
        struct capi20_appl *ap;
        u16 applid;
 
-       if (showcapimsgs & 1) {
-               printk(KERN_DEBUG "kcapi: notify down contr %d\n", contr);
-       }
+       if (ctr->state == CAPI_CTR_DETECTED || ctr->state == CAPI_CTR_DETACHED)
+               return;
+
+       ctr->state = new_state;
+
+       memset(ctr->manu, 0, sizeof(ctr->manu));
+       memset(&ctr->version, 0, sizeof(ctr->version));
+       memset(&ctr->profile, 0, sizeof(ctr->profile));
+       memset(ctr->serial, 0, sizeof(ctr->serial));
 
        for (applid = 1; applid <= CAPI_MAXAPPL; applid++) {
                ap = get_capi_appl_by_nr(applid);
-               if (ap && ap->callback && !ap->release_in_progress)
-                       ap->callback(KCI_CONTRDOWN, contr, NULL);
+               if (ap)
+                       capi_ctr_put(ctr);
        }
+
+       wake_up_interruptible_all(&ctr->state_wait_queue);
 }
 
-static void notify_handler(struct work_struct *work)
+static void notify_down(u32 contr)
 {
-       struct capi_notifier *np =
-               container_of(work, struct capi_notifier, work);
+       struct capi_ctr *ctr;
+
+       mutex_lock(&capi_controller_lock);
+
+       if (showcapimsgs & 1)
+               printk(KERN_DEBUG "kcapi: notify down contr %d\n", contr);
+
+       ctr = get_capi_ctr_by_nr(contr);
+       if (ctr)
+               ctr_down(ctr, CAPI_CTR_DETECTED);
+       else
+               printk(KERN_WARNING "%s: invalid contr %d\n", __func__, contr);
 
-       switch (np->cmd) {
-       case KCI_CONTRUP:
-               notify_up(np->controller);
+       mutex_unlock(&capi_controller_lock);
+}
+
+static int
+notify_handler(struct notifier_block *nb, unsigned long val, void *v)
+{
+       u32 contr = (long)v;
+
+       switch (val) {
+       case CAPICTR_UP:
+               notify_up(contr);
                break;
-       case KCI_CONTRDOWN:
-               notify_down(np->controller);
+       case CAPICTR_DOWN:
+               notify_down(contr);
                break;
        }
+       return NOTIFY_OK;
+}
 
-       kfree(np);
+static void do_notify_work(struct work_struct *work)
+{
+       struct capictr_event *event =
+               container_of(work, struct capictr_event, work);
+
+       blocking_notifier_call_chain(&ctr_notifier_list, event->type,
+                                    (void *)(long)event->controller);
+       kfree(event);
 }
 
 /*
  * The notifier will result in adding/deleteing of devices. Devices can
  * only removed in user process, not in bh.
  */
-static int notify_push(unsigned int cmd, u32 controller, u16 applid, u32 ncci)
+static int notify_push(unsigned int event_type, u32 controller)
 {
-       struct capi_notifier *np = kmalloc(sizeof(*np), GFP_ATOMIC);
+       struct capictr_event *event = kmalloc(sizeof(*event), GFP_ATOMIC);
 
-       if (!np)
+       if (!event)
                return -ENOMEM;
 
-       INIT_WORK(&np->work, notify_handler);
-       np->cmd = cmd;
-       np->controller = controller;
-       np->applid = applid;
-       np->ncci = ncci;
+       INIT_WORK(&event->work, do_notify_work);
+       event->type = event_type;
+       event->controller = controller;
 
-       schedule_work(&np->work);
+       schedule_work(&event->work);
        return 0;
 }
 
-       
+int register_capictr_notifier(struct notifier_block *nb)
+{
+       return blocking_notifier_chain_register(&ctr_notifier_list, nb);
+}
+EXPORT_SYMBOL_GPL(register_capictr_notifier);
+
+int unregister_capictr_notifier(struct notifier_block *nb)
+{
+       return blocking_notifier_chain_unregister(&ctr_notifier_list, nb);
+}
+EXPORT_SYMBOL_GPL(unregister_capictr_notifier);
+
 /* -------- Receiver ------------------------------------------ */
 
 static void recv_handler(struct work_struct *work)
@@ -273,68 +323,70 @@ static void recv_handler(struct work_struct *work)
 
 /**
  * capi_ctr_handle_message() - handle incoming CAPI message
- * @card:      controller descriptor structure.
+ * @ctr:       controller descriptor structure.
  * @appl:      application ID.
  * @skb:       message.
  *
  * Called by hardware driver to pass a CAPI message to the application.
  */
 
-void capi_ctr_handle_message(struct capi_ctr * card, u16 appl, struct sk_buff *skb)
+void capi_ctr_handle_message(struct capi_ctr *ctr, u16 appl,
+                            struct sk_buff *skb)
 {
        struct capi20_appl *ap;
        int showctl = 0;
        u8 cmd, subcmd;
-       unsigned long flags;
        _cdebbuf *cdb;
 
-       if (card->cardstate != CARD_RUNNING) {
+       if (ctr->state != CAPI_CTR_RUNNING) {
                cdb = capi_message2str(skb->data);
                if (cdb) {
                        printk(KERN_INFO "kcapi: controller [%03d] not active, got: %s",
-                               card->cnr, cdb->buf);
+                               ctr->cnr, cdb->buf);
                        cdebbuf_free(cdb);
                } else
                        printk(KERN_INFO "kcapi: controller [%03d] not active, cannot trace\n",
-                               card->cnr);
+                               ctr->cnr);
                goto error;
        }
 
        cmd = CAPIMSG_COMMAND(skb->data);
         subcmd = CAPIMSG_SUBCOMMAND(skb->data);
        if (cmd == CAPI_DATA_B3 && subcmd == CAPI_IND) {
-               card->nrecvdatapkt++;
-               if (card->traceflag > 2) showctl |= 2;
+               ctr->nrecvdatapkt++;
+               if (ctr->traceflag > 2)
+                       showctl |= 2;
        } else {
-               card->nrecvctlpkt++;
-               if (card->traceflag) showctl |= 2;
+               ctr->nrecvctlpkt++;
+               if (ctr->traceflag)
+                       showctl |= 2;
        }
-       showctl |= (card->traceflag & 1);
+       showctl |= (ctr->traceflag & 1);
        if (showctl & 2) {
                if (showctl & 1) {
                        printk(KERN_DEBUG "kcapi: got [%03d] id#%d %s len=%u\n",
-                              card->cnr, CAPIMSG_APPID(skb->data),
+                              ctr->cnr, CAPIMSG_APPID(skb->data),
                               capi_cmd2str(cmd, subcmd),
                               CAPIMSG_LEN(skb->data));
                } else {
                        cdb = capi_message2str(skb->data);
                        if (cdb) {
                                printk(KERN_DEBUG "kcapi: got [%03d] %s\n",
-                                       card->cnr, cdb->buf);
+                                       ctr->cnr, cdb->buf);
                                cdebbuf_free(cdb);
                        } else
                                printk(KERN_DEBUG "kcapi: got [%03d] id#%d %s len=%u, cannot trace\n",
-                                       card->cnr, CAPIMSG_APPID(skb->data),
+                                       ctr->cnr, CAPIMSG_APPID(skb->data),
                                        capi_cmd2str(cmd, subcmd),
                                        CAPIMSG_LEN(skb->data));
                }
 
        }
 
-       read_lock_irqsave(&application_lock, flags);
+       rcu_read_lock();
        ap = get_capi_appl_by_nr(CAPIMSG_APPID(skb->data));
-       if ((!ap) || (ap->release_in_progress)) {
-               read_unlock_irqrestore(&application_lock, flags);
+       if (!ap) {
+               rcu_read_unlock();
                cdb = capi_message2str(skb->data);
                if (cdb) {
                        printk(KERN_ERR "kcapi: handle_message: applid %d state released (%s)\n",
@@ -348,7 +400,7 @@ void capi_ctr_handle_message(struct capi_ctr * card, u16 appl, struct sk_buff *s
        }
        skb_queue_tail(&ap->recv_queue, skb);
        schedule_work(&ap->recv_work);
-       read_unlock_irqrestore(&application_lock, flags);
+       rcu_read_unlock();
 
        return;
 
@@ -360,74 +412,54 @@ EXPORT_SYMBOL(capi_ctr_handle_message);
 
 /**
  * capi_ctr_ready() - signal CAPI controller ready
- * @card:      controller descriptor structure.
+ * @ctr:       controller descriptor structure.
  *
  * Called by hardware driver to signal that the controller is up and running.
  */
 
-void capi_ctr_ready(struct capi_ctr * card)
+void capi_ctr_ready(struct capi_ctr *ctr)
 {
-       card->cardstate = CARD_RUNNING;
+       printk(KERN_NOTICE "kcapi: controller [%03d] \"%s\" ready.\n",
+              ctr->cnr, ctr->name);
 
-        printk(KERN_NOTICE "kcapi: card [%03d] \"%s\" ready.\n",
-              card->cnr, card->name);
-
-       notify_push(KCI_CONTRUP, card->cnr, 0, 0);
+       notify_push(CAPICTR_UP, ctr->cnr);
 }
 
 EXPORT_SYMBOL(capi_ctr_ready);
 
 /**
  * capi_ctr_down() - signal CAPI controller not ready
- * @card:      controller descriptor structure.
+ * @ctr:       controller descriptor structure.
  *
  * Called by hardware driver to signal that the controller is down and
  * unavailable for use.
  */
 
-void capi_ctr_down(struct capi_ctr * card)
+void capi_ctr_down(struct capi_ctr *ctr)
 {
-       u16 appl;
-
-       DBG("");
-
-        if (card->cardstate == CARD_DETECTED)
-               return;
-
-        card->cardstate = CARD_DETECTED;
-
-       memset(card->manu, 0, sizeof(card->manu));
-       memset(&card->version, 0, sizeof(card->version));
-       memset(&card->profile, 0, sizeof(card->profile));
-       memset(card->serial, 0, sizeof(card->serial));
-
-       for (appl = 1; appl <= CAPI_MAXAPPL; appl++) {
-               struct capi20_appl *ap = get_capi_appl_by_nr(appl);
-               if (!ap || ap->release_in_progress)
-                       continue;
+       printk(KERN_NOTICE "kcapi: controller [%03d] down.\n", ctr->cnr);
 
-               capi_ctr_put(card);
-       }
-
-       printk(KERN_NOTICE "kcapi: card [%03d] down.\n", card->cnr);
-
-       notify_push(KCI_CONTRDOWN, card->cnr, 0, 0);
+       notify_push(CAPICTR_DOWN, ctr->cnr);
 }
 
 EXPORT_SYMBOL(capi_ctr_down);
 
 /**
  * capi_ctr_suspend_output() - suspend controller
- * @card:      controller descriptor structure.
+ * @ctr:       controller descriptor structure.
  *
  * Called by hardware driver to stop data flow.
+ *
+ * Note: The caller is responsible for synchronizing concurrent state changes
+ * as well as invocations of capi_ctr_handle_message.
  */
 
-void capi_ctr_suspend_output(struct capi_ctr *card)
+void capi_ctr_suspend_output(struct capi_ctr *ctr)
 {
-       if (!card->blocked) {
-               printk(KERN_DEBUG "kcapi: card [%03d] suspend\n", card->cnr);
-               card->blocked = 1;
+       if (!ctr->blocked) {
+               printk(KERN_DEBUG "kcapi: controller [%03d] suspend\n",
+                      ctr->cnr);
+               ctr->blocked = 1;
        }
 }
 
@@ -435,16 +467,20 @@ EXPORT_SYMBOL(capi_ctr_suspend_output);
 
 /**
  * capi_ctr_resume_output() - resume controller
- * @card:      controller descriptor structure.
+ * @ctr:       controller descriptor structure.
  *
  * Called by hardware driver to resume data flow.
+ *
+ * Note: The caller is responsible for synchronizing concurrent state changes
+ * as well as invocations of capi_ctr_handle_message.
  */
 
-void capi_ctr_resume_output(struct capi_ctr *card)
+void capi_ctr_resume_output(struct capi_ctr *ctr)
 {
-       if (card->blocked) {
-               printk(KERN_DEBUG "kcapi: card [%03d] resume\n", card->cnr);
-               card->blocked = 0;
+       if (ctr->blocked) {
+               printk(KERN_DEBUG "kcapi: controller [%03d] resumed\n",
+                      ctr->cnr);
+               ctr->blocked = 0;
        }
 }
 
@@ -454,47 +490,48 @@ EXPORT_SYMBOL(capi_ctr_resume_output);
 
 /**
  * attach_capi_ctr() - register CAPI controller
- * @card:      controller descriptor structure.
+ * @ctr:       controller descriptor structure.
  *
  * Called by hardware driver to register a controller with the CAPI subsystem.
  * Return value: 0 on success, error code < 0 on error
  */
 
-int
-attach_capi_ctr(struct capi_ctr *card)
+int attach_capi_ctr(struct capi_ctr *ctr)
 {
        int i;
 
-       mutex_lock(&controller_mutex);
+       mutex_lock(&capi_controller_lock);
 
        for (i = 0; i < CAPI_MAXCONTR; i++) {
-               if (capi_cards[i] == NULL)
+               if (!capi_controller[i])
                        break;
        }
        if (i == CAPI_MAXCONTR) {
-               mutex_unlock(&controller_mutex);
+               mutex_unlock(&capi_controller_lock);
                printk(KERN_ERR "kcapi: out of controller slots\n");
                return -EBUSY;
        }
-       capi_cards[i] = card;
+       capi_controller[i] = ctr;
+
+       ctr->nrecvctlpkt = 0;
+       ctr->nrecvdatapkt = 0;
+       ctr->nsentctlpkt = 0;
+       ctr->nsentdatapkt = 0;
+       ctr->cnr = i + 1;
+       ctr->state = CAPI_CTR_DETECTED;
+       ctr->blocked = 0;
+       ctr->traceflag = showcapimsgs;
+       init_waitqueue_head(&ctr->state_wait_queue);
 
-       mutex_unlock(&controller_mutex);
+       sprintf(ctr->procfn, "capi/controllers/%d", ctr->cnr);
+       ctr->procent = proc_create_data(ctr->procfn, 0, NULL, ctr->proc_fops, ctr);
 
-       card->nrecvctlpkt = 0;
-       card->nrecvdatapkt = 0;
-       card->nsentctlpkt = 0;
-       card->nsentdatapkt = 0;
-       card->cnr = i + 1;
-       card->cardstate = CARD_DETECTED;
-       card->blocked = 0;
-       card->traceflag = showcapimsgs;
+       ncontrollers++;
 
-       sprintf(card->procfn, "capi/controllers/%d", card->cnr);
-       card->procent = proc_create_data(card->procfn, 0, NULL, card->proc_fops, card);
+       mutex_unlock(&capi_controller_lock);
 
-       ncards++;
-       printk(KERN_NOTICE "kcapi: Controller [%03d]: %s attached\n",
-                       card->cnr, card->name);
+       printk(KERN_NOTICE "kcapi: controller [%03d]: %s attached\n",
+                       ctr->cnr, ctr->name);
        return 0;
 }
 
@@ -502,29 +539,38 @@ EXPORT_SYMBOL(attach_capi_ctr);
 
 /**
  * detach_capi_ctr() - unregister CAPI controller
- * @card:      controller descriptor structure.
+ * @ctr:       controller descriptor structure.
  *
  * Called by hardware driver to remove the registration of a controller
  * with the CAPI subsystem.
  * Return value: 0 on success, error code < 0 on error
  */
 
-int detach_capi_ctr(struct capi_ctr *card)
+int detach_capi_ctr(struct capi_ctr *ctr)
 {
-        if (card->cardstate != CARD_DETECTED)
-               capi_ctr_down(card);
+       int err = 0;
 
-       ncards--;
+       mutex_lock(&capi_controller_lock);
 
-       if (card->procent) {
-          remove_proc_entry(card->procfn, NULL);
-          card->procent = NULL;
+       ctr_down(ctr, CAPI_CTR_DETACHED);
+
+       if (capi_controller[ctr->cnr - 1] != ctr) {
+               err = -EINVAL;
+               goto unlock_out;
        }
-       capi_cards[card->cnr - 1] = NULL;
-       printk(KERN_NOTICE "kcapi: Controller [%03d]: %s unregistered\n",
-                       card->cnr, card->name);
+       capi_controller[ctr->cnr - 1] = NULL;
+       ncontrollers--;
 
-       return 0;
+       if (ctr->procent)
+               remove_proc_entry(ctr->procfn, NULL);
+
+       printk(KERN_NOTICE "kcapi: controller [%03d]: %s unregistered\n",
+              ctr->cnr, ctr->name);
+
+unlock_out:
+       mutex_unlock(&capi_controller_lock);
+
+       return err;
 }
 
 EXPORT_SYMBOL(detach_capi_ctr);
@@ -538,11 +584,9 @@ EXPORT_SYMBOL(detach_capi_ctr);
 
 void register_capi_driver(struct capi_driver *driver)
 {
-       unsigned long flags;
-
-       write_lock_irqsave(&capi_drivers_list_lock, flags);
+       mutex_lock(&capi_drivers_lock);
        list_add_tail(&driver->list, &capi_drivers);
-       write_unlock_irqrestore(&capi_drivers_list_lock, flags);
+       mutex_unlock(&capi_drivers_lock);
 }
 
 EXPORT_SYMBOL(register_capi_driver);
@@ -556,11 +600,9 @@ EXPORT_SYMBOL(register_capi_driver);
 
 void unregister_capi_driver(struct capi_driver *driver)
 {
-       unsigned long flags;
-
-       write_lock_irqsave(&capi_drivers_list_lock, flags);
+       mutex_lock(&capi_drivers_lock);
        list_del(&driver->list);
-       write_unlock_irqrestore(&capi_drivers_list_lock, flags);
+       mutex_unlock(&capi_drivers_lock);
 }
 
 EXPORT_SYMBOL(unregister_capi_driver);
@@ -578,12 +620,21 @@ EXPORT_SYMBOL(unregister_capi_driver);
 
 u16 capi20_isinstalled(void)
 {
+       u16 ret = CAPI_REGNOTINSTALLED;
        int i;
-       for (i = 0; i < CAPI_MAXCONTR; i++) {
-               if (capi_cards[i] && capi_cards[i]->cardstate == CARD_RUNNING)
-                       return CAPI_NOERROR;
-       }
-       return CAPI_REGNOTINSTALLED;
+
+       mutex_lock(&capi_controller_lock);
+
+       for (i = 0; i < CAPI_MAXCONTR; i++)
+               if (capi_controller[i] &&
+                   capi_controller[i]->state == CAPI_CTR_RUNNING) {
+                       ret = CAPI_NOERROR;
+                       break;
+               }
+
+       mutex_unlock(&capi_controller_lock);
+
+       return ret;
 }
 
 EXPORT_SYMBOL(capi20_isinstalled);
@@ -604,46 +655,43 @@ u16 capi20_register(struct capi20_appl *ap)
 {
        int i;
        u16 applid;
-       unsigned long flags;
 
        DBG("");
 
        if (ap->rparam.datablklen < 128)
                return CAPI_LOGBLKSIZETOSMALL;
 
-       write_lock_irqsave(&application_lock, flags);
+       ap->nrecvctlpkt = 0;
+       ap->nrecvdatapkt = 0;
+       ap->nsentctlpkt = 0;
+       ap->nsentdatapkt = 0;
+       mutex_init(&ap->recv_mtx);
+       skb_queue_head_init(&ap->recv_queue);
+       INIT_WORK(&ap->recv_work, recv_handler);
+       ap->release_in_progress = 0;
+
+       mutex_lock(&capi_controller_lock);
 
        for (applid = 1; applid <= CAPI_MAXAPPL; applid++) {
                if (capi_applications[applid - 1] == NULL)
                        break;
        }
        if (applid > CAPI_MAXAPPL) {
-               write_unlock_irqrestore(&application_lock, flags);
+               mutex_unlock(&capi_controller_lock);
                return CAPI_TOOMANYAPPLS;
        }
 
        ap->applid = applid;
        capi_applications[applid - 1] = ap;
 
-       ap->nrecvctlpkt = 0;
-       ap->nrecvdatapkt = 0;
-       ap->nsentctlpkt = 0;
-       ap->nsentdatapkt = 0;
-       ap->callback = NULL;
-       mutex_init(&ap->recv_mtx);
-       skb_queue_head_init(&ap->recv_queue);
-       INIT_WORK(&ap->recv_work, recv_handler);
-       ap->release_in_progress = 0;
-
-       write_unlock_irqrestore(&application_lock, flags);
-       
-       mutex_lock(&controller_mutex);
        for (i = 0; i < CAPI_MAXCONTR; i++) {
-               if (!capi_cards[i] || capi_cards[i]->cardstate != CARD_RUNNING)
+               if (!capi_controller[i] ||
+                   capi_controller[i]->state != CAPI_CTR_RUNNING)
                        continue;
-               register_appl(capi_cards[i], applid, &ap->rparam);
+               register_appl(capi_controller[i], applid, &ap->rparam);
        }
-       mutex_unlock(&controller_mutex);
+
+       mutex_unlock(&capi_controller_lock);
 
        if (showcapimsgs & 1) {
                printk(KERN_DEBUG "kcapi: appl %d up\n", applid);
@@ -667,22 +715,24 @@ EXPORT_SYMBOL(capi20_register);
 u16 capi20_release(struct capi20_appl *ap)
 {
        int i;
-       unsigned long flags;
 
        DBG("applid %#x", ap->applid);
 
-       write_lock_irqsave(&application_lock, flags);
+       mutex_lock(&capi_controller_lock);
+
        ap->release_in_progress = 1;
        capi_applications[ap->applid - 1] = NULL;
-       write_unlock_irqrestore(&application_lock, flags);
 
-       mutex_lock(&controller_mutex);
+       synchronize_rcu();
+
        for (i = 0; i < CAPI_MAXCONTR; i++) {
-               if (!capi_cards[i] || capi_cards[i]->cardstate != CARD_RUNNING)
+               if (!capi_controller[i] ||
+                   capi_controller[i]->state != CAPI_CTR_RUNNING)
                        continue;
-               release_appl(capi_cards[i], ap->applid);
+               release_appl(capi_controller[i], ap->applid);
        }
-       mutex_unlock(&controller_mutex);
+
+       mutex_unlock(&capi_controller_lock);
 
        flush_scheduled_work();
        skb_queue_purge(&ap->recv_queue);
@@ -707,13 +757,13 @@ EXPORT_SYMBOL(capi20_release);
 
 u16 capi20_put_message(struct capi20_appl *ap, struct sk_buff *skb)
 {
-       struct capi_ctr *card;
+       struct capi_ctr *ctr;
        int showctl = 0;
        u8 cmd, subcmd;
 
        DBG("applid %#x", ap->applid);
  
-       if (ncards == 0)
+       if (ncontrollers == 0)
                return CAPI_REGNOTINSTALLED;
        if ((ap->applid == 0) || ap->release_in_progress)
                return CAPI_ILLAPPNR;
@@ -721,28 +771,33 @@ u16 capi20_put_message(struct capi20_appl *ap, struct sk_buff *skb)
            || !capi_cmd_valid(CAPIMSG_COMMAND(skb->data))
            || !capi_subcmd_valid(CAPIMSG_SUBCOMMAND(skb->data)))
                return CAPI_ILLCMDORSUBCMDORMSGTOSMALL;
-       card = get_capi_ctr_by_nr(CAPIMSG_CONTROLLER(skb->data));
-       if (!card || card->cardstate != CARD_RUNNING) {
-               card = get_capi_ctr_by_nr(1); // XXX why?
-               if (!card || card->cardstate != CARD_RUNNING) 
-                       return CAPI_REGNOTINSTALLED;
-       }
-       if (card->blocked)
+
+       /*
+        * The controller reference is protected by the existence of the
+        * application passed to us. We assume that the caller properly
+        * synchronizes this service with capi20_release.
+        */
+       ctr = get_capi_ctr_by_nr(CAPIMSG_CONTROLLER(skb->data));
+       if (!ctr || ctr->state != CAPI_CTR_RUNNING)
+               return CAPI_REGNOTINSTALLED;
+       if (ctr->blocked)
                return CAPI_SENDQUEUEFULL;
 
        cmd = CAPIMSG_COMMAND(skb->data);
         subcmd = CAPIMSG_SUBCOMMAND(skb->data);
 
        if (cmd == CAPI_DATA_B3 && subcmd== CAPI_REQ) {
-               card->nsentdatapkt++;
+               ctr->nsentdatapkt++;
                ap->nsentdatapkt++;
-               if (card->traceflag > 2) showctl |= 2;
+               if (ctr->traceflag > 2)
+                       showctl |= 2;
        } else {
-               card->nsentctlpkt++;
+               ctr->nsentctlpkt++;
                ap->nsentctlpkt++;
-               if (card->traceflag) showctl |= 2;
+               if (ctr->traceflag)
+                       showctl |= 2;
        }
-       showctl |= (card->traceflag & 1);
+       showctl |= (ctr->traceflag & 1);
        if (showctl & 2) {
                if (showctl & 1) {
                        printk(KERN_DEBUG "kcapi: put [%03d] id#%d %s len=%u\n",
@@ -765,7 +820,7 @@ u16 capi20_put_message(struct capi20_appl *ap, struct sk_buff *skb)
                                        CAPIMSG_LEN(skb->data));
                }
        }
-       return card->send_message(card, skb);
+       return ctr->send_message(ctr, skb);
 }
 
 EXPORT_SYMBOL(capi20_put_message);
@@ -782,17 +837,25 @@ EXPORT_SYMBOL(capi20_put_message);
 
 u16 capi20_get_manufacturer(u32 contr, u8 *buf)
 {
-       struct capi_ctr *card;
+       struct capi_ctr *ctr;
+       u16 ret;
 
        if (contr == 0) {
                strlcpy(buf, capi_manufakturer, CAPI_MANUFACTURER_LEN);
                return CAPI_NOERROR;
        }
-       card = get_capi_ctr_by_nr(contr);
-       if (!card || card->cardstate != CARD_RUNNING) 
-               return CAPI_REGNOTINSTALLED;
-       strlcpy(buf, card->manu, CAPI_MANUFACTURER_LEN);
-       return CAPI_NOERROR;
+
+       mutex_lock(&capi_controller_lock);
+
+       ctr = get_capi_ctr_by_nr(contr);
+       if (ctr && ctr->state == CAPI_CTR_RUNNING) {
+               strlcpy(buf, ctr->manu, CAPI_MANUFACTURER_LEN);
+               ret = CAPI_NOERROR;
+       } else
+               ret = CAPI_REGNOTINSTALLED;
+
+       mutex_unlock(&capi_controller_lock);
+       return ret;
 }
 
 EXPORT_SYMBOL(capi20_get_manufacturer);
@@ -809,18 +872,25 @@ EXPORT_SYMBOL(capi20_get_manufacturer);
 
 u16 capi20_get_version(u32 contr, struct capi_version *verp)
 {
-       struct capi_ctr *card;
+       struct capi_ctr *ctr;
+       u16 ret;
 
        if (contr == 0) {
                *verp = driver_version;
                return CAPI_NOERROR;
        }
-       card = get_capi_ctr_by_nr(contr);
-       if (!card || card->cardstate != CARD_RUNNING) 
-               return CAPI_REGNOTINSTALLED;
 
-       memcpy((void *) verp, &card->version, sizeof(capi_version));
-       return CAPI_NOERROR;
+       mutex_lock(&capi_controller_lock);
+
+       ctr = get_capi_ctr_by_nr(contr);
+       if (ctr && ctr->state == CAPI_CTR_RUNNING) {
+               memcpy(verp, &ctr->version, sizeof(capi_version));
+               ret = CAPI_NOERROR;
+       } else
+               ret = CAPI_REGNOTINSTALLED;
+
+       mutex_unlock(&capi_controller_lock);
+       return ret;
 }
 
 EXPORT_SYMBOL(capi20_get_version);
@@ -837,18 +907,25 @@ EXPORT_SYMBOL(capi20_get_version);
 
 u16 capi20_get_serial(u32 contr, u8 *serial)
 {
-       struct capi_ctr *card;
+       struct capi_ctr *ctr;
+       u16 ret;
 
        if (contr == 0) {
                strlcpy(serial, driver_serial, CAPI_SERIAL_LEN);
                return CAPI_NOERROR;
        }
-       card = get_capi_ctr_by_nr(contr);
-       if (!card || card->cardstate != CARD_RUNNING) 
-               return CAPI_REGNOTINSTALLED;
 
-       strlcpy((void *) serial, card->serial, CAPI_SERIAL_LEN);
-       return CAPI_NOERROR;
+       mutex_lock(&capi_controller_lock);
+
+       ctr = get_capi_ctr_by_nr(contr);
+       if (ctr && ctr->state == CAPI_CTR_RUNNING) {
+               strlcpy(serial, ctr->serial, CAPI_SERIAL_LEN);
+               ret = CAPI_NOERROR;
+       } else
+               ret = CAPI_REGNOTINSTALLED;
+
+       mutex_unlock(&capi_controller_lock);
+       return ret;
 }
 
 EXPORT_SYMBOL(capi20_get_serial);
@@ -865,23 +942,65 @@ EXPORT_SYMBOL(capi20_get_serial);
 
 u16 capi20_get_profile(u32 contr, struct capi_profile *profp)
 {
-       struct capi_ctr *card;
+       struct capi_ctr *ctr;
+       u16 ret;
 
        if (contr == 0) {
-               profp->ncontroller = ncards;
+               profp->ncontroller = ncontrollers;
                return CAPI_NOERROR;
        }
-       card = get_capi_ctr_by_nr(contr);
-       if (!card || card->cardstate != CARD_RUNNING) 
-               return CAPI_REGNOTINSTALLED;
 
-       memcpy((void *) profp, &card->profile,
-                       sizeof(struct capi_profile));
-       return CAPI_NOERROR;
+       mutex_lock(&capi_controller_lock);
+
+       ctr = get_capi_ctr_by_nr(contr);
+       if (ctr && ctr->state == CAPI_CTR_RUNNING) {
+               memcpy(profp, &ctr->profile, sizeof(struct capi_profile));
+               ret = CAPI_NOERROR;
+       } else
+               ret = CAPI_REGNOTINSTALLED;
+
+       mutex_unlock(&capi_controller_lock);
+       return ret;
 }
 
 EXPORT_SYMBOL(capi20_get_profile);
 
+/* Must be called with capi_controller_lock held. */
+static int wait_on_ctr_state(struct capi_ctr *ctr, unsigned int state)
+{
+       DEFINE_WAIT(wait);
+       int retval = 0;
+
+       ctr = capi_ctr_get(ctr);
+       if (!ctr)
+               return -ESRCH;
+
+       for (;;) {
+               prepare_to_wait(&ctr->state_wait_queue, &wait,
+                               TASK_INTERRUPTIBLE);
+
+               if (ctr->state == state)
+                       break;
+               if (ctr->state == CAPI_CTR_DETACHED) {
+                       retval = -ESRCH;
+                       break;
+               }
+               if (signal_pending(current)) {
+                       retval = -EINTR;
+                       break;
+               }
+
+               mutex_unlock(&capi_controller_lock);
+               schedule();
+               mutex_lock(&capi_controller_lock);
+       }
+       finish_wait(&ctr->state_wait_queue, &wait);
+
+       capi_ctr_put(ctr);
+
+       return retval;
+}
+
 #ifdef AVMB1_COMPAT
 static int old_capi_manufacturer(unsigned int cmd, void __user *data)
 {
@@ -889,11 +1008,10 @@ static int old_capi_manufacturer(unsigned int cmd, void __user *data)
        avmb1_extcarddef cdef;
        avmb1_resetdef rdef;
        capicardparams cparams;
-       struct capi_ctr *card;
+       struct capi_ctr *ctr;
        struct capi_driver *driver = NULL;
        capiloaddata ldata;
        struct list_head *l;
-       unsigned long flags;
        int retval;
 
        switch (cmd) {
@@ -913,7 +1031,8 @@ static int old_capi_manufacturer(unsigned int cmd, void __user *data)
                cparams.irq = cdef.irq;
                cparams.cardnr = cdef.cardnr;
 
-               read_lock_irqsave(&capi_drivers_list_lock, flags);
+               mutex_lock(&capi_drivers_lock);
+
                 switch (cdef.cardtype) {
                        case AVM_CARDTYPE_B1:
                                list_for_each(l, &capi_drivers) {
@@ -934,18 +1053,15 @@ static int old_capi_manufacturer(unsigned int cmd, void __user *data)
                                break;
                }
                if (!driver) {
-                       read_unlock_irqrestore(&capi_drivers_list_lock, flags);
                        printk(KERN_ERR "kcapi: driver not loaded.\n");
-                       return -EIO;
-               }
-               if (!driver->add_card) {
-                       read_unlock_irqrestore(&capi_drivers_list_lock, flags);
+                       retval = -EIO;
+               } else if (!driver->add_card) {
                        printk(KERN_ERR "kcapi: driver has no add card function.\n");
-                       return -EIO;
-               }
+                       retval = -EIO;
+               } else
+                       retval = driver->add_card(driver, &cparams);
 
-               retval = driver->add_card(driver, &cparams);
-               read_unlock_irqrestore(&capi_drivers_list_lock, flags);
+               mutex_unlock(&capi_drivers_lock);
                return retval;
 
        case AVMB1_LOAD:
@@ -962,27 +1078,30 @@ static int old_capi_manufacturer(unsigned int cmd, void __user *data)
                                           sizeof(avmb1_loadandconfigdef)))
                                return -EFAULT;
                }
-               card = get_capi_ctr_by_nr(ldef.contr);
-               if (!card)
-                       return -EINVAL;
-               card = capi_ctr_get(card);
-               if (!card)
-                       return -ESRCH;
-               if (card->load_firmware == NULL) {
+
+               mutex_lock(&capi_controller_lock);
+
+               ctr = get_capi_ctr_by_nr(ldef.contr);
+               if (!ctr) {
+                       retval = -EINVAL;
+                       goto load_unlock_out;
+               }
+
+               if (ctr->load_firmware == NULL) {
                        printk(KERN_DEBUG "kcapi: load: no load function\n");
-                       capi_ctr_put(card);
-                       return -ESRCH;
+                       retval = -ESRCH;
+                       goto load_unlock_out;
                }
 
                if (ldef.t4file.len <= 0) {
                        printk(KERN_DEBUG "kcapi: load: invalid parameter: length of t4file is %d ?\n", ldef.t4file.len);
-                       capi_ctr_put(card);
-                       return -EINVAL;
+                       retval = -EINVAL;
+                       goto load_unlock_out;
                }
                if (ldef.t4file.data == NULL) {
                        printk(KERN_DEBUG "kcapi: load: invalid parameter: dataptr is 0\n");
-                       capi_ctr_put(card);
-                       return -EINVAL;
+                       retval = -EINVAL;
+                       goto load_unlock_out;
                }
 
                ldata.firmware.user = 1;
@@ -992,54 +1111,49 @@ static int old_capi_manufacturer(unsigned int cmd, void __user *data)
                ldata.configuration.data = ldef.t4config.data;
                ldata.configuration.len = ldef.t4config.len;
 
-               if (card->cardstate != CARD_DETECTED) {
+               if (ctr->state != CAPI_CTR_DETECTED) {
                        printk(KERN_INFO "kcapi: load: contr=%d not in detect state\n", ldef.contr);
-                       capi_ctr_put(card);
-                       return -EBUSY;
+                       retval = -EBUSY;
+                       goto load_unlock_out;
                }
-               card->cardstate = CARD_LOADING;
-
-               retval = card->load_firmware(card, &ldata);
+               ctr->state = CAPI_CTR_LOADING;
 
+               retval = ctr->load_firmware(ctr, &ldata);
                if (retval) {
-                       card->cardstate = CARD_DETECTED;
-                       capi_ctr_put(card);
-                       return retval;
+                       ctr->state = CAPI_CTR_DETECTED;
+                       goto load_unlock_out;
                }
 
-               while (card->cardstate != CARD_RUNNING) {
-
-                       msleep_interruptible(100);      /* 0.1 sec */
+               retval = wait_on_ctr_state(ctr, CAPI_CTR_RUNNING);
 
-                       if (signal_pending(current)) {
-                               capi_ctr_put(card);
-                               return -EINTR;
-                       }
-               }
-               capi_ctr_put(card);
-               return 0;
+load_unlock_out:
+               mutex_unlock(&capi_controller_lock);
+               return retval;
 
        case AVMB1_RESETCARD:
                if (copy_from_user(&rdef, data, sizeof(avmb1_resetdef)))
                        return -EFAULT;
-               card = get_capi_ctr_by_nr(rdef.contr);
-               if (!card)
-                       return -ESRCH;
 
-               if (card->cardstate == CARD_DETECTED)
-                       return 0;
+               retval = 0;
 
-               card->reset_ctr(card);
+               mutex_lock(&capi_controller_lock);
 
-               while (card->cardstate > CARD_DETECTED) {
+               ctr = get_capi_ctr_by_nr(rdef.contr);
+               if (!ctr) {
+                       retval = -ESRCH;
+                       goto reset_unlock_out;
+               }
 
-                       msleep_interruptible(100);      /* 0.1 sec */
+               if (ctr->state == CAPI_CTR_DETECTED)
+                       goto reset_unlock_out;
 
-                       if (signal_pending(current))
-                               return -EINTR;
-               }
-               return 0;
+               ctr->reset_ctr(ctr);
 
+               retval = wait_on_ctr_state(ctr, CAPI_CTR_DETECTED);
+
+reset_unlock_out:
+               mutex_unlock(&capi_controller_lock);
+               return retval;
        }
        return -EINVAL;
 }
@@ -1056,7 +1170,8 @@ static int old_capi_manufacturer(unsigned int cmd, void __user *data)
 
 int capi20_manufacturer(unsigned int cmd, void __user *data)
 {
-        struct capi_ctr *card;
+       struct capi_ctr *ctr;
+       int retval;
 
        switch (cmd) {
 #ifdef AVMB1_COMPAT
@@ -1074,14 +1189,20 @@ int capi20_manufacturer(unsigned int cmd, void __user *data)
                if (copy_from_user(&fdef, data, sizeof(kcapi_flagdef)))
                        return -EFAULT;
 
-               card = get_capi_ctr_by_nr(fdef.contr);
-               if (!card)
-                       return -ESRCH;
+               mutex_lock(&capi_controller_lock);
+
+               ctr = get_capi_ctr_by_nr(fdef.contr);
+               if (ctr) {
+                       ctr->traceflag = fdef.flag;
+                       printk(KERN_INFO "kcapi: contr [%03d] set trace=%d\n",
+                              ctr->cnr, ctr->traceflag);
+                       retval = 0;
+               } else
+                       retval = -ESRCH;
 
-               card->traceflag = fdef.flag;
-               printk(KERN_INFO "kcapi: contr [%03d] set trace=%d\n",
-                       card->cnr, card->traceflag);
-               return 0;
+               mutex_unlock(&capi_controller_lock);
+
+               return retval;
        }
        case KCAPI_CMD_ADDCARD:
        {
@@ -1089,7 +1210,6 @@ int capi20_manufacturer(unsigned int cmd, void __user *data)
                struct capi_driver *driver = NULL;
                capicardparams cparams;
                kcapi_carddef cdef;
-               int retval;
 
                if ((retval = copy_from_user(&cdef, data, sizeof(cdef))))
                        return retval;
@@ -1101,6 +1221,8 @@ int capi20_manufacturer(unsigned int cmd, void __user *data)
                cparams.cardtype = 0;
                cdef.driver[sizeof(cdef.driver)-1] = 0;
 
+               mutex_lock(&capi_drivers_lock);
+
                list_for_each(l, &capi_drivers) {
                        driver = list_entry(l, struct capi_driver, list);
                        if (strcmp(driver->name, cdef.driver) == 0)
@@ -1109,15 +1231,15 @@ int capi20_manufacturer(unsigned int cmd, void __user *data)
                if (driver == NULL) {
                        printk(KERN_ERR "kcapi: driver \"%s\" not loaded.\n",
                                        cdef.driver);
-                       return -ESRCH;
-               }
-
-               if (!driver->add_card) {
+                       retval = -ESRCH;
+               } else if (!driver->add_card) {
                        printk(KERN_ERR "kcapi: driver \"%s\" has no add card function.\n", cdef.driver);
-                       return -EIO;
-               }
+                       retval = -EIO;
+               } else
+                       retval = driver->add_card(driver, &cparams);
 
-               return driver->add_card(driver, &cparams);
+               mutex_unlock(&capi_drivers_lock);
+               return retval;
        }
 
        default:
@@ -1131,30 +1253,6 @@ int capi20_manufacturer(unsigned int cmd, void __user *data)
 
 EXPORT_SYMBOL(capi20_manufacturer);
 
-/* temporary hack */
-
-/**
- * capi20_set_callback() - set CAPI application notification callback function
- * @ap:                CAPI application descriptor structure.
- * @callback:  callback function (NULL to remove).
- *
- * If not NULL, the callback function will be called to notify the
- * application of the addition or removal of a controller.
- * The first argument (cmd) will tell whether the controller was added
- * (KCI_CONTRUP) or removed (KCI_CONTRDOWN).
- * The second argument (contr) will be the controller number.
- * For cmd==KCI_CONTRUP the third argument (data) will be a pointer to the
- * new controller's capability profile structure.
- */
-
-void capi20_set_callback(struct capi20_appl *ap,
-                        void (*callback) (unsigned int cmd, __u32 contr, void *data))
-{
-       ap->callback = callback;
-}
-
-EXPORT_SYMBOL(capi20_set_callback);
-
 /* ------------------------------------------------------------- */
 /* -------- Init & Cleanup ------------------------------------- */
 /* ------------------------------------------------------------- */
@@ -1163,27 +1261,21 @@ EXPORT_SYMBOL(capi20_set_callback);
  * init / exit functions
  */
 
+static struct notifier_block capictr_nb = {
+       .notifier_call = notify_handler,
+       .priority = INT_MAX,
+};
+
 static int __init kcapi_init(void)
 {
-       char *p;
-       char rev[32];
-       int ret;
-
-       ret = cdebug_init();
-       if (ret)
-               return ret;
-        kcapi_proc_init();
-
-       if ((p = strchr(revision, ':')) != NULL && p[1]) {
-               strlcpy(rev, p + 2, sizeof(rev));
-               if ((p = strchr(rev, '$')) != NULL && p > rev)
-                  *(p-1) = 0;
-       } else
-               strcpy(rev, "1.0");
+       int err;
 
-        printk(KERN_NOTICE "CAPI Subsystem Rev %s\n", rev);
+       register_capictr_notifier(&capictr_nb);
 
-       return 0;
+       err = cdebug_init();
+       if (!err)
+               kcapi_proc_init();
+       return err;
 }
 
 static void __exit kcapi_exit(void)