Merge branch 'topic/core-cleanup' into for-linus
[safe/jmp/linux-2.6] / drivers / isdn / gigaset / common.c
index 1163d31..f6f45f2 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * Stuff used by all variants of the driver
  *
- * Copyright (c) 2001 by Stefan Eilers <Eilers.Stefan@epost.de>,
+ * Copyright (c) 2001 by Stefan Eilers,
  *                       Hansjoerg Lipp <hjlipp@web.de>,
  *                       Tilman Schmidt <tilman@imap.cc>.
  *
  */
 
 #include "gigaset.h"
-#include <linux/ctype.h>
 #include <linux/module.h>
 #include <linux/moduleparam.h>
 
 /* Version Information */
-#define DRIVER_AUTHOR "Hansjoerg Lipp <hjlipp@web.de>, Tilman Schmidt <tilman@imap.cc>, Stefan Eilers <Eilers.Stefan@epost.de>"
+#define DRIVER_AUTHOR "Hansjoerg Lipp <hjlipp@web.de>, Tilman Schmidt <tilman@imap.cc>, Stefan Eilers"
 #define DRIVER_DESC "Driver for Gigaset 307x"
 
+#ifdef CONFIG_GIGASET_DEBUG
+#define DRIVER_DESC_DEBUG " (debug build)"
+#else
+#define DRIVER_DESC_DEBUG ""
+#endif
+
 /* Module parameters */
-int gigaset_debuglevel = DEBUG_DEFAULT;
+int gigaset_debuglevel;
 EXPORT_SYMBOL_GPL(gigaset_debuglevel);
 module_param_named(debug, gigaset_debuglevel, int, S_IRUGO|S_IWUSR);
 MODULE_PARM_DESC(debug, "debug level");
 
-/*======================================================================
-  Prototypes of internal functions
- */
-
-//static void gigaset_process_response(int resp_code, int parameter,
-//                                     struct at_state_t *at_state,
-//                                     unsigned char ** pstring);
-static struct cardstate *alloc_cs(struct gigaset_driver *drv);
-static void free_cs(struct cardstate *cs);
-static void make_valid(struct cardstate *cs, unsigned mask);
-static void make_invalid(struct cardstate *cs, unsigned mask);
-
-#define VALID_MINOR       0x01
-#define VALID_ID          0x02
-#define ASSIGNED          0x04
-
-/* bitwise byte inversion table */
-__u8 gigaset_invtab[256] = {
-       0x00, 0x80, 0x40, 0xc0, 0x20, 0xa0, 0x60, 0xe0,
-       0x10, 0x90, 0x50, 0xd0, 0x30, 0xb0, 0x70, 0xf0,
-       0x08, 0x88, 0x48, 0xc8, 0x28, 0xa8, 0x68, 0xe8,
-       0x18, 0x98, 0x58, 0xd8, 0x38, 0xb8, 0x78, 0xf8,
-       0x04, 0x84, 0x44, 0xc4, 0x24, 0xa4, 0x64, 0xe4,
-       0x14, 0x94, 0x54, 0xd4, 0x34, 0xb4, 0x74, 0xf4,
-       0x0c, 0x8c, 0x4c, 0xcc, 0x2c, 0xac, 0x6c, 0xec,
-       0x1c, 0x9c, 0x5c, 0xdc, 0x3c, 0xbc, 0x7c, 0xfc,
-       0x02, 0x82, 0x42, 0xc2, 0x22, 0xa2, 0x62, 0xe2,
-       0x12, 0x92, 0x52, 0xd2, 0x32, 0xb2, 0x72, 0xf2,
-       0x0a, 0x8a, 0x4a, 0xca, 0x2a, 0xaa, 0x6a, 0xea,
-       0x1a, 0x9a, 0x5a, 0xda, 0x3a, 0xba, 0x7a, 0xfa,
-       0x06, 0x86, 0x46, 0xc6, 0x26, 0xa6, 0x66, 0xe6,
-       0x16, 0x96, 0x56, 0xd6, 0x36, 0xb6, 0x76, 0xf6,
-       0x0e, 0x8e, 0x4e, 0xce, 0x2e, 0xae, 0x6e, 0xee,
-       0x1e, 0x9e, 0x5e, 0xde, 0x3e, 0xbe, 0x7e, 0xfe,
-       0x01, 0x81, 0x41, 0xc1, 0x21, 0xa1, 0x61, 0xe1,
-       0x11, 0x91, 0x51, 0xd1, 0x31, 0xb1, 0x71, 0xf1,
-       0x09, 0x89, 0x49, 0xc9, 0x29, 0xa9, 0x69, 0xe9,
-       0x19, 0x99, 0x59, 0xd9, 0x39, 0xb9, 0x79, 0xf9,
-       0x05, 0x85, 0x45, 0xc5, 0x25, 0xa5, 0x65, 0xe5,
-       0x15, 0x95, 0x55, 0xd5, 0x35, 0xb5, 0x75, 0xf5,
-       0x0d, 0x8d, 0x4d, 0xcd, 0x2d, 0xad, 0x6d, 0xed,
-       0x1d, 0x9d, 0x5d, 0xdd, 0x3d, 0xbd, 0x7d, 0xfd,
-       0x03, 0x83, 0x43, 0xc3, 0x23, 0xa3, 0x63, 0xe3,
-       0x13, 0x93, 0x53, 0xd3, 0x33, 0xb3, 0x73, 0xf3,
-       0x0b, 0x8b, 0x4b, 0xcb, 0x2b, 0xab, 0x6b, 0xeb,
-       0x1b, 0x9b, 0x5b, 0xdb, 0x3b, 0xbb, 0x7b, 0xfb,
-       0x07, 0x87, 0x47, 0xc7, 0x27, 0xa7, 0x67, 0xe7,
-       0x17, 0x97, 0x57, 0xd7, 0x37, 0xb7, 0x77, 0xf7,
-       0x0f, 0x8f, 0x4f, 0xcf, 0x2f, 0xaf, 0x6f, 0xef,
-       0x1f, 0x9f, 0x5f, 0xdf, 0x3f, 0xbf, 0x7f, 0xff
-};
-EXPORT_SYMBOL_GPL(gigaset_invtab);
+/* driver state flags */
+#define VALID_MINOR    0x01
+#define VALID_ID       0x02
 
+/**
+ * gigaset_dbg_buffer() - dump data in ASCII and hex for debugging
+ * @level:     debugging level.
+ * @msg:       message prefix.
+ * @len:       number of bytes to dump.
+ * @buf:       data to dump.
+ *
+ * If the current debugging level includes one of the bits set in @level,
+ * @len bytes starting at @buf are logged to dmesg at KERN_DEBUG prio,
+ * prefixed by the text @msg.
+ */
 void gigaset_dbg_buffer(enum debuglevel level, const unsigned char *msg,
-                        size_t len, const unsigned char *buf, int from_user)
+                       size_t len, const unsigned char *buf)
 {
        unsigned char outbuf[80];
-       unsigned char inbuf[80 - 1];
-       size_t numin;
-       const unsigned char *in;
+       unsigned char c;
        size_t space = sizeof outbuf - 1;
        unsigned char *out = outbuf;
+       size_t numin = len;
 
-       if (!from_user) {
-               in = buf;
-               numin = len;
-       } else {
-               numin = len < sizeof inbuf ? len : sizeof inbuf;
-               in = inbuf;
-               if (copy_from_user(inbuf, (const unsigned char __user *) buf,
-                                  numin)) {
-                       strncpy(inbuf, "<FAULT>", sizeof inbuf);
-                       numin = sizeof "<FAULT>" - 1;
+       while (numin--) {
+               c = *buf++;
+               if (c == '~' || c == '^' || c == '\\') {
+                       if (!space--)
+                               break;
+                       *out++ = '\\';
                }
-       }
-
-       for (; numin && space; --numin, ++in) {
-               --space;
-               if (*in >= 32)
-                       *out++ = *in;
-               else {
+               if (c & 0x80) {
+                       if (!space--)
+                               break;
+                       *out++ = '~';
+                       c ^= 0x80;
+               }
+               if (c < 0x20 || c == 0x7f) {
+                       if (!space--)
+                               break;
                        *out++ = '^';
-                       if (space) {
-                               *out++ = '@' + *in;
-                               --space;
-                       }
+                       c ^= 0x40;
                }
+               if (!space--)
+                       break;
+               *out++ = c;
        }
        *out = 0;
 
-       dbg(level, "%s (%u bytes): %s", msg, (unsigned) len, outbuf);
+       gig_dbg(level, "%s (%u bytes): %s", msg, (unsigned) len, outbuf);
 }
 EXPORT_SYMBOL_GPL(gigaset_dbg_buffer);
 
@@ -143,12 +107,7 @@ int gigaset_enterconfigmode(struct cardstate *cs)
 {
        int i, r;
 
-       if (!atomic_read(&cs->connected)) {
-               err("not connected!");
-               return -1;
-       }
-
-       cs->control_state = TIOCM_RTS; //FIXME
+       cs->control_state = TIOCM_RTS;
 
        r = setflags(cs, TIOCM_DTR, 200);
        if (r < 0)
@@ -171,11 +130,11 @@ int gigaset_enterconfigmode(struct cardstate *cs)
        return 0;
 
 error:
-       err("error %d on setuartbits!\n", -r);
-       cs->control_state = TIOCM_RTS|TIOCM_DTR; // FIXME is this a good value?
+       dev_err(cs->dev, "error %d on setuartbits\n", -r);
+       cs->control_state = TIOCM_RTS|TIOCM_DTR;
        cs->ops->set_modem_ctrl(cs, 0, TIOCM_RTS|TIOCM_DTR);
 
-       return -1; //r
+       return -1;
 }
 
 static int test_timeout(struct at_state_t *at_state)
@@ -184,16 +143,13 @@ static int test_timeout(struct at_state_t *at_state)
                return 0;
 
        if (--at_state->timer_expires) {
-               dbg(DEBUG_MCMD, "decreased timer of %p to %lu",
-                   at_state, at_state->timer_expires);
+               gig_dbg(DEBUG_MCMD, "decreased timer of %p to %lu",
+                       at_state, at_state->timer_expires);
                return 0;
        }
 
-       if (!gigaset_add_event(at_state->cs, at_state, EV_TIMEOUT, NULL,
-                              atomic_read(&at_state->timer_index), NULL)) {
-               //FIXME what should we do?
-       }
-
+       gigaset_add_event(at_state->cs, at_state, EV_TIMEOUT, NULL,
+                         at_state->timer_index, NULL);
        return 1;
 }
 
@@ -218,10 +174,10 @@ static void timer_tick(unsigned long data)
                if (test_timeout(at_state))
                        timeout = 1;
 
-       if (atomic_read(&cs->running)) {
-               mod_timer(&cs->timer, jiffies + GIG_TICK);
+       if (cs->running) {
+               mod_timer(&cs->timer, jiffies + msecs_to_jiffies(GIG_TICK));
                if (timeout) {
-                       dbg(DEBUG_CMD, "scheduling timeout");
+                       gig_dbg(DEBUG_EVENT, "scheduling timeout");
                        tasklet_schedule(&cs->event_tasklet);
                }
        }
@@ -234,31 +190,60 @@ int gigaset_get_channel(struct bc_state *bcs)
        unsigned long flags;
 
        spin_lock_irqsave(&bcs->cs->lock, flags);
-       if (bcs->use_count) {
-               dbg(DEBUG_ANY, "could not allocate channel %d", bcs->channel);
+       if (bcs->use_count || !try_module_get(bcs->cs->driver->owner)) {
+               gig_dbg(DEBUG_CHANNEL, "could not allocate channel %d",
+                       bcs->channel);
                spin_unlock_irqrestore(&bcs->cs->lock, flags);
                return 0;
        }
        ++bcs->use_count;
        bcs->busy = 1;
-       dbg(DEBUG_ANY, "allocated channel %d", bcs->channel);
+       gig_dbg(DEBUG_CHANNEL, "allocated channel %d", bcs->channel);
        spin_unlock_irqrestore(&bcs->cs->lock, flags);
        return 1;
 }
 
+struct bc_state *gigaset_get_free_channel(struct cardstate *cs)
+{
+       unsigned long flags;
+       int i;
+
+       spin_lock_irqsave(&cs->lock, flags);
+       if (!try_module_get(cs->driver->owner)) {
+               gig_dbg(DEBUG_CHANNEL,
+                       "could not get module for allocating channel");
+               spin_unlock_irqrestore(&cs->lock, flags);
+               return NULL;
+       }
+       for (i = 0; i < cs->channels; ++i)
+               if (!cs->bcs[i].use_count) {
+                       ++cs->bcs[i].use_count;
+                       cs->bcs[i].busy = 1;
+                       spin_unlock_irqrestore(&cs->lock, flags);
+                       gig_dbg(DEBUG_CHANNEL, "allocated channel %d", i);
+                       return cs->bcs + i;
+               }
+       module_put(cs->driver->owner);
+       spin_unlock_irqrestore(&cs->lock, flags);
+       gig_dbg(DEBUG_CHANNEL, "no free channel");
+       return NULL;
+}
+
 void gigaset_free_channel(struct bc_state *bcs)
 {
        unsigned long flags;
 
        spin_lock_irqsave(&bcs->cs->lock, flags);
        if (!bcs->busy) {
-               dbg(DEBUG_ANY, "could not free channel %d", bcs->channel);
+               gig_dbg(DEBUG_CHANNEL, "could not free channel %d",
+                       bcs->channel);
                spin_unlock_irqrestore(&bcs->cs->lock, flags);
                return;
        }
        --bcs->use_count;
        bcs->busy = 0;
-       dbg(DEBUG_ANY, "freed channel %d", bcs->channel);
+       module_put(bcs->cs->driver->owner);
+       gig_dbg(DEBUG_CHANNEL, "freed channel %d", bcs->channel);
        spin_unlock_irqrestore(&bcs->cs->lock, flags);
 }
 
@@ -271,14 +256,15 @@ int gigaset_get_channels(struct cardstate *cs)
        for (i = 0; i < cs->channels; ++i)
                if (cs->bcs[i].use_count) {
                        spin_unlock_irqrestore(&cs->lock, flags);
-                       dbg(DEBUG_ANY, "could not allocated all channels");
+                       gig_dbg(DEBUG_CHANNEL,
+                               "could not allocate all channels");
                        return 0;
                }
        for (i = 0; i < cs->channels; ++i)
                ++cs->bcs[i].use_count;
        spin_unlock_irqrestore(&cs->lock, flags);
 
-       dbg(DEBUG_ANY, "allocated all channels");
+       gig_dbg(DEBUG_CHANNEL, "allocated all channels");
 
        return 1;
 }
@@ -288,7 +274,7 @@ void gigaset_free_channels(struct cardstate *cs)
        unsigned long flags;
        int i;
 
-       dbg(DEBUG_ANY, "unblocking all channels");
+       gig_dbg(DEBUG_CHANNEL, "unblocking all channels");
        spin_lock_irqsave(&cs->lock, flags);
        for (i = 0; i < cs->channels; ++i)
                --cs->bcs[i].use_count;
@@ -300,7 +286,7 @@ void gigaset_block_channels(struct cardstate *cs)
        unsigned long flags;
        int i;
 
-       dbg(DEBUG_ANY, "blocking all channels");
+       gig_dbg(DEBUG_CHANNEL, "blocking all channels");
        spin_lock_irqsave(&cs->lock, flags);
        for (i = 0; i < cs->channels; ++i)
                ++cs->bcs[i].use_count;
@@ -311,36 +297,54 @@ static void clear_events(struct cardstate *cs)
 {
        struct event_t *ev;
        unsigned head, tail;
+       unsigned long flags;
 
-       /* no locking needed (no reader/writer allowed) */
+       spin_lock_irqsave(&cs->ev_lock, flags);
 
-       head = atomic_read(&cs->ev_head);
-       tail = atomic_read(&cs->ev_tail);
+       head = cs->ev_head;
+       tail = cs->ev_tail;
 
        while (tail != head) {
                ev = cs->events + head;
                kfree(ev->ptr);
-
                head = (head + 1) % MAX_EVENTS;
        }
 
-       atomic_set(&cs->ev_head, tail);
+       cs->ev_head = tail;
+
+       spin_unlock_irqrestore(&cs->ev_lock, flags);
 }
 
+/**
+ * gigaset_add_event() - add event to device event queue
+ * @cs:                device descriptor structure.
+ * @at_state:  connection state structure.
+ * @type:      event type.
+ * @ptr:       pointer parameter for event.
+ * @parameter: integer parameter for event.
+ * @arg:       pointer parameter for event.
+ *
+ * Allocate an event queue entry from the device's event queue, and set it up
+ * with the parameters given.
+ *
+ * Return value: added event
+ */
 struct event_t *gigaset_add_event(struct cardstate *cs,
-                                  struct at_state_t *at_state, int type,
-                                  void *ptr, int parameter, void *arg)
+                                 struct at_state_t *at_state, int type,
+                                 void *ptr, int parameter, void *arg)
 {
        unsigned long flags;
        unsigned next, tail;
        struct event_t *event = NULL;
 
+       gig_dbg(DEBUG_EVENT, "queueing event %d", type);
+
        spin_lock_irqsave(&cs->ev_lock, flags);
 
-       tail = atomic_read(&cs->ev_tail);
+       tail = cs->ev_tail;
        next = (tail + 1) % MAX_EVENTS;
-       if (unlikely(next == atomic_read(&cs->ev_head)))
-               err("event queue full");
+       if (unlikely(next == cs->ev_head))
+               dev_err(cs->dev, "event queue full\n");
        else {
                event = cs->events + tail;
                event->type = type;
@@ -349,7 +353,7 @@ struct event_t *gigaset_add_event(struct cardstate *cs,
                event->ptr = ptr;
                event->arg = arg;
                event->parameter = parameter;
-               atomic_set(&cs->ev_tail, next);
+               cs->ev_tail = next;
        }
 
        spin_unlock_irqrestore(&cs->ev_lock, flags);
@@ -388,23 +392,77 @@ static void gigaset_freebcs(struct bc_state *bcs)
 {
        int i;
 
-       dbg(DEBUG_INIT, "freeing bcs[%d]->hw", bcs->channel);
-       if (!bcs->cs->ops->freebcshw(bcs)) {
-               dbg(DEBUG_INIT, "failed");
-       }
+       gig_dbg(DEBUG_INIT, "freeing bcs[%d]->hw", bcs->channel);
+       if (!bcs->cs->ops->freebcshw(bcs))
+               gig_dbg(DEBUG_INIT, "failed");
 
-       dbg(DEBUG_INIT, "clearing bcs[%d]->at_state", bcs->channel);
+       gig_dbg(DEBUG_INIT, "clearing bcs[%d]->at_state", bcs->channel);
        clear_at_state(&bcs->at_state);
-       dbg(DEBUG_INIT, "freeing bcs[%d]->skb", bcs->channel);
+       gig_dbg(DEBUG_INIT, "freeing bcs[%d]->skb", bcs->channel);
+       dev_kfree_skb(bcs->skb);
+       bcs->skb = NULL;
 
-       if (bcs->skb)
-               dev_kfree_skb(bcs->skb);
        for (i = 0; i < AT_NUM; ++i) {
                kfree(bcs->commands[i]);
                bcs->commands[i] = NULL;
        }
 }
 
+static struct cardstate *alloc_cs(struct gigaset_driver *drv)
+{
+       unsigned long flags;
+       unsigned i;
+       struct cardstate *cs;
+       struct cardstate *ret = NULL;
+
+       spin_lock_irqsave(&drv->lock, flags);
+       if (drv->blocked)
+               goto exit;
+       for (i = 0; i < drv->minors; ++i) {
+               cs = drv->cs + i;
+               if (!(cs->flags & VALID_MINOR)) {
+                       cs->flags = VALID_MINOR;
+                       ret = cs;
+                       break;
+               }
+       }
+exit:
+       spin_unlock_irqrestore(&drv->lock, flags);
+       return ret;
+}
+
+static void free_cs(struct cardstate *cs)
+{
+       cs->flags = 0;
+}
+
+static void make_valid(struct cardstate *cs, unsigned mask)
+{
+       unsigned long flags;
+       struct gigaset_driver *drv = cs->driver;
+       spin_lock_irqsave(&drv->lock, flags);
+       cs->flags |= mask;
+       spin_unlock_irqrestore(&drv->lock, flags);
+}
+
+static void make_invalid(struct cardstate *cs, unsigned mask)
+{
+       unsigned long flags;
+       struct gigaset_driver *drv = cs->driver;
+       spin_lock_irqsave(&drv->lock, flags);
+       cs->flags &= ~mask;
+       spin_unlock_irqrestore(&drv->lock, flags);
+}
+
+/**
+ * gigaset_freecs() - free all associated ressources of a device
+ * @cs:                device descriptor structure.
+ *
+ * Stops all tasklets and timers, unregisters the device from all
+ * subsystems it was registered to, deallocates the device structure
+ * @cs and all structures referenced from it.
+ * Operations on the device should be stopped before calling this.
+ */
 void gigaset_freecs(struct cardstate *cs)
 {
        int i;
@@ -413,7 +471,7 @@ void gigaset_freecs(struct cardstate *cs)
        if (!cs)
                return;
 
-       down(&cs->sem);
+       mutex_lock(&cs->mutex);
 
        if (!cs->bcs)
                goto f_cs;
@@ -421,7 +479,7 @@ void gigaset_freecs(struct cardstate *cs)
                goto f_bcs;
 
        spin_lock_irqsave(&cs->lock, flags);
-       atomic_set(&cs->running, 0);
+       cs->running = 0;
        spin_unlock_irqrestore(&cs->lock, flags); /* event handler and timer are
                                                     not rescheduled below */
 
@@ -430,47 +488,48 @@ void gigaset_freecs(struct cardstate *cs)
 
        switch (cs->cs_init) {
        default:
+               /* clear B channel structures */
+               for (i = 0; i < cs->channels; ++i) {
+                       gig_dbg(DEBUG_INIT, "clearing bcs[%d]", i);
+                       gigaset_freebcs(cs->bcs + i);
+               }
+
+               /* clear device sysfs */
+               gigaset_free_dev_sysfs(cs);
+
                gigaset_if_free(cs);
 
-               dbg(DEBUG_INIT, "clearing hw");
+               gig_dbg(DEBUG_INIT, "clearing hw");
                cs->ops->freecshw(cs);
 
-               //FIXME cmdbuf
-
                /* fall through */
        case 2: /* error in initcshw */
                /* Deregister from LL */
                make_invalid(cs, VALID_ID);
-               dbg(DEBUG_INIT, "clearing iif");
-               gigaset_i4l_cmd(cs, ISDN_STAT_UNLOAD);
+               gigaset_isdn_unregdev(cs);
 
                /* fall through */
-       case 1: /* error when regestering to LL */
-               dbg(DEBUG_INIT, "clearing at_state");
+       case 1: /* error when registering to LL */
+               gig_dbg(DEBUG_INIT, "clearing at_state");
                clear_at_state(&cs->at_state);
                dealloc_at_states(cs);
 
                /* fall through */
-       case 0: /* error in one call to initbcs */
-               for (i = 0; i < cs->channels; ++i) {
-                       dbg(DEBUG_INIT, "clearing bcs[%d]", i);
-                       gigaset_freebcs(cs->bcs + i);
-               }
-
+       case 0: /* error in basic setup */
                clear_events(cs);
-               dbg(DEBUG_INIT, "freeing inbuf");
+               gig_dbg(DEBUG_INIT, "freeing inbuf");
                kfree(cs->inbuf);
        }
-f_bcs: dbg(DEBUG_INIT, "freeing bcs[]");
+f_bcs: gig_dbg(DEBUG_INIT, "freeing bcs[]");
        kfree(cs->bcs);
-f_cs:  dbg(DEBUG_INIT, "freeing cs");
-       up(&cs->sem);
+f_cs:  gig_dbg(DEBUG_INIT, "freeing cs");
+       mutex_unlock(&cs->mutex);
        free_cs(cs);
 }
 EXPORT_SYMBOL_GPL(gigaset_freecs);
 
 void gigaset_at_init(struct at_state_t *at_state, struct bc_state *bcs,
-                     struct cardstate *cs, int cid)
+                    struct cardstate *cs, int cid)
 {
        int i;
 
@@ -480,8 +539,8 @@ void gigaset_at_init(struct at_state_t *at_state, struct bc_state *bcs,
        at_state->pending_commands = 0;
        at_state->timer_expires = 0;
        at_state->timer_active = 0;
-       atomic_set(&at_state->timer_index, 0);
-       atomic_set(&at_state->seq_index, 0);
+       at_state->timer_index = 0;
+       at_state->seq_index = 0;
        at_state->ConState = 0;
        for (i = 0; i < STR_NUM; ++i)
                at_state->str_var[i] = NULL;
@@ -498,25 +557,69 @@ void gigaset_at_init(struct at_state_t *at_state, struct bc_state *bcs,
 }
 
 
-static void gigaset_inbuf_init(struct inbuf_t *inbuf, struct bc_state *bcs,
-                               struct cardstate *cs, int inputstate)
+static void gigaset_inbuf_init(struct inbuf_t *inbuf, struct cardstate *cs)
 /* inbuf->read must be allocated before! */
 {
-       atomic_set(&inbuf->head, 0);
-       atomic_set(&inbuf->tail, 0);
+       inbuf->head = 0;
+       inbuf->tail = 0;
        inbuf->cs = cs;
-       inbuf->bcs = bcs; /*base driver: NULL*/
-       inbuf->rcvbuf = NULL; //FIXME
-       inbuf->inputstate = inputstate;
+       inbuf->inputstate = INS_command;
 }
 
+/**
+ * gigaset_fill_inbuf() - append received data to input buffer
+ * @inbuf:     buffer structure.
+ * @src:       received data.
+ * @numbytes:  number of bytes received.
+ */
+int gigaset_fill_inbuf(struct inbuf_t *inbuf, const unsigned char *src,
+                      unsigned numbytes)
+{
+       unsigned n, head, tail, bytesleft;
+
+       gig_dbg(DEBUG_INTR, "received %u bytes", numbytes);
+
+       if (!numbytes)
+               return 0;
+
+       bytesleft = numbytes;
+       tail = inbuf->tail;
+       head = inbuf->head;
+       gig_dbg(DEBUG_INTR, "buffer state: %u -> %u", head, tail);
+
+       while (bytesleft) {
+               if (head > tail)
+                       n = head - 1 - tail;
+               else if (head == 0)
+                       n = (RBUFSIZE-1) - tail;
+               else
+                       n = RBUFSIZE - tail;
+               if (!n) {
+                       dev_err(inbuf->cs->dev,
+                               "buffer overflow (%u bytes lost)\n",
+                               bytesleft);
+                       break;
+               }
+               if (n > bytesleft)
+                       n = bytesleft;
+               memcpy(inbuf->data + tail, src, n);
+               bytesleft -= n;
+               tail = (tail + n) % RBUFSIZE;
+               src += n;
+       }
+       gig_dbg(DEBUG_INTR, "setting tail to %u", tail);
+       inbuf->tail = tail;
+       return numbytes != bytesleft;
+}
+EXPORT_SYMBOL_GPL(gigaset_fill_inbuf);
+
 /* Initialize the b-channel structure */
 static struct bc_state *gigaset_initbcs(struct bc_state *bcs,
-                                        struct cardstate *cs, int channel)
+                                       struct cardstate *cs, int channel)
 {
        int i;
 
-       bcs->tx_skb = NULL; //FIXME -> hw part
+       bcs->tx_skb = NULL;
 
        skb_queue_head_init(&bcs->squeue);
 
@@ -524,26 +627,24 @@ static struct bc_state *gigaset_initbcs(struct bc_state *bcs,
        bcs->trans_down = 0;
        bcs->trans_up = 0;
 
-       dbg(DEBUG_INIT, "setting up bcs[%d]->at_state", channel);
+       gig_dbg(DEBUG_INIT, "setting up bcs[%d]->at_state", channel);
        gigaset_at_init(&bcs->at_state, bcs, cs, -1);
 
-       bcs->rcvbytes = 0;
-
 #ifdef CONFIG_GIGASET_DEBUG
        bcs->emptycount = 0;
 #endif
 
-       dbg(DEBUG_INIT, "allocating bcs[%d]->skb", channel);
+       gig_dbg(DEBUG_INIT, "allocating bcs[%d]->skb", channel);
        bcs->fcs = PPP_INITFCS;
        bcs->inputstate = 0;
        if (cs->ignoreframes) {
-               bcs->inputstate |= INS_skip_frame;
                bcs->skb = NULL;
-       } else if ((bcs->skb = dev_alloc_skb(SBUFSIZE + HW_HDR_LEN)) != NULL)
-               skb_reserve(bcs->skb, HW_HDR_LEN);
-       else {
-               warn("could not allocate skb");
-               bcs->inputstate |= INS_skip_frame;
+       } 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;
@@ -557,155 +658,164 @@ static struct bc_state *gigaset_initbcs(struct bc_state *bcs,
        for (i = 0; i < AT_NUM; ++i)
                bcs->commands[i] = NULL;
 
-       dbg(DEBUG_INIT, "  setting up bcs[%d]->hw", channel);
+       gig_dbg(DEBUG_INIT, "  setting up bcs[%d]->hw", channel);
        if (cs->ops->initbcshw(bcs))
                return bcs;
 
-       dbg(DEBUG_INIT, "  failed");
+       gig_dbg(DEBUG_INIT, "  failed");
 
-       dbg(DEBUG_INIT, "  freeing bcs[%d]->skb", channel);
-       if (bcs->skb)
-               dev_kfree_skb(bcs->skb);
+       gig_dbg(DEBUG_INIT, "  freeing bcs[%d]->skb", channel);
+       dev_kfree_skb(bcs->skb);
+       bcs->skb = NULL;
 
        return NULL;
 }
 
-/* gigaset_initcs
+/**
+ * gigaset_initcs() - initialize device structure
+ * @drv:       hardware driver the device belongs to
+ * @channels:  number of B channels supported by device
+ * @onechannel:        !=0 if B channel data and AT commands share one
+ *                 communication channel (M10x),
+ *             ==0 if B channels have separate communication channels (base)
+ * @ignoreframes:      number of frames to ignore after setting up B channel
+ * @cidmode:   !=0: start in CallID mode
+ * @modulename:        name of driver module for LL registration
+ *
  * Allocate and initialize cardstate structure for Gigaset driver
  * Calls hardware dependent gigaset_initcshw() function
  * Calls B channel initialization function gigaset_initbcs() for each B channel
- * parameters:
- *      drv            hardware driver the device belongs to
- *     channels        number of B channels supported by device
- *     onechannel      !=0: B channel data and AT commands share one
- *                          communication channel
- *                     ==0: B channels have separate communication channels
- *     ignoreframes    number of frames to ignore after setting up B channel
- *     cidmode         !=0: start in CallID mode
- *     modulename      name of driver module (used for I4L registration)
- * return value:
+ *
+ * Return value:
  *     pointer to cardstate structure
  */
 struct cardstate *gigaset_initcs(struct gigaset_driver *drv, int channels,
                                 int onechannel, int ignoreframes,
                                 int cidmode, const char *modulename)
 {
-       struct cardstate *cs = NULL;
+       struct cardstate *cs;
+       unsigned long flags;
        int i;
 
-       dbg(DEBUG_INIT, "allocating cs");
+       gig_dbg(DEBUG_INIT, "allocating cs");
        cs = alloc_cs(drv);
-       if (!cs)
-               goto error;
-       dbg(DEBUG_INIT, "allocating bcs[0..%d]", channels - 1);
+       if (!cs) {
+               pr_err("maximum number of devices exceeded\n");
+               return NULL;
+       }
+
+       gig_dbg(DEBUG_INIT, "allocating bcs[0..%d]", channels - 1);
        cs->bcs = kmalloc(channels * sizeof(struct bc_state), GFP_KERNEL);
-       if (!cs->bcs)
+       if (!cs->bcs) {
+               pr_err("out of memory\n");
                goto error;
-       dbg(DEBUG_INIT, "allocating inbuf");
+       }
+       gig_dbg(DEBUG_INIT, "allocating inbuf");
        cs->inbuf = kmalloc(sizeof(struct inbuf_t), GFP_KERNEL);
-       if (!cs->inbuf)
+       if (!cs->inbuf) {
+               pr_err("out of memory\n");
                goto error;
+       }
 
        cs->cs_init = 0;
        cs->channels = channels;
        cs->onechannel = onechannel;
        cs->ignoreframes = ignoreframes;
        INIT_LIST_HEAD(&cs->temp_at_states);
-       atomic_set(&cs->running, 0);
+       cs->running = 0;
        init_timer(&cs->timer); /* clear next & prev */
        spin_lock_init(&cs->ev_lock);
-       atomic_set(&cs->ev_tail, 0);
-       atomic_set(&cs->ev_head, 0);
-       init_MUTEX_LOCKED(&cs->sem);
-       tasklet_init(&cs->event_tasklet, &gigaset_handle_event,
+       cs->ev_tail = 0;
+       cs->ev_head = 0;
+
+       tasklet_init(&cs->event_tasklet, gigaset_handle_event,
                     (unsigned long) cs);
-       atomic_set(&cs->commands_pending, 0);
+       cs->commands_pending = 0;
        cs->cur_at_seq = 0;
        cs->gotfwver = -1;
        cs->open_count = 0;
+       cs->dev = NULL;
        cs->tty = NULL;
-       atomic_set(&cs->cidmode, cidmode != 0);
-
-       //if(onechannel) { //FIXME
-               cs->tabnocid = gigaset_tab_nocid_m10x;
-               cs->tabcid = gigaset_tab_cid_m10x;
-       //} else {
-       //      cs->tabnocid = gigaset_tab_nocid;
-       //      cs->tabcid = gigaset_tab_cid;
-       //}
+       cs->tty_dev = NULL;
+       cs->cidmode = cidmode != 0;
+       cs->tabnocid = gigaset_tab_nocid;
+       cs->tabcid = gigaset_tab_cid;
 
        init_waitqueue_head(&cs->waitqueue);
        cs->waiting = 0;
 
-       atomic_set(&cs->mode, M_UNKNOWN);
-       atomic_set(&cs->mstate, MS_UNINITIALIZED);
-
-       for (i = 0; i < channels; ++i) {
-               dbg(DEBUG_INIT, "setting up bcs[%d].read", i);
-               if (!gigaset_initbcs(cs->bcs + i, cs, i))
-                       goto error;
-       }
+       cs->mode = M_UNKNOWN;
+       cs->mstate = MS_UNINITIALIZED;
 
        ++cs->cs_init;
 
-       dbg(DEBUG_INIT, "setting up at_state");
+       gig_dbg(DEBUG_INIT, "setting up at_state");
        spin_lock_init(&cs->lock);
        gigaset_at_init(&cs->at_state, NULL, cs, 0);
        cs->dle = 0;
        cs->cbytes = 0;
 
-       dbg(DEBUG_INIT, "setting up inbuf");
-       if (onechannel) {                       //FIXME distinction necessary?
-               gigaset_inbuf_init(cs->inbuf, cs->bcs, cs, INS_command);
-       } else
-               gigaset_inbuf_init(cs->inbuf, NULL,    cs, INS_command);
+       gig_dbg(DEBUG_INIT, "setting up inbuf");
+       gigaset_inbuf_init(cs->inbuf, cs);
 
-       atomic_set(&cs->connected, 0);
+       cs->connected = 0;
+       cs->isdn_up = 0;
 
-       dbg(DEBUG_INIT, "setting up cmdbuf");
+       gig_dbg(DEBUG_INIT, "setting up cmdbuf");
        cs->cmdbuf = cs->lastcmdbuf = NULL;
        spin_lock_init(&cs->cmdlock);
        cs->curlen = 0;
        cs->cmdbytes = 0;
 
-       dbg(DEBUG_INIT, "setting up iif");
-       if (!gigaset_register_to_LL(cs, modulename)) {
-               err("register_isdn=>error");
+       gig_dbg(DEBUG_INIT, "setting up iif");
+       if (!gigaset_isdn_regdev(cs, modulename)) {
+               pr_err("error registering ISDN device\n");
                goto error;
        }
 
        make_valid(cs, VALID_ID);
        ++cs->cs_init;
-       dbg(DEBUG_INIT, "setting up hw");
+       gig_dbg(DEBUG_INIT, "setting up hw");
        if (!cs->ops->initcshw(cs))
                goto error;
 
        ++cs->cs_init;
 
+       /* set up character device */
        gigaset_if_init(cs);
 
-       atomic_set(&cs->running, 1);
-       cs->timer.data = (unsigned long) cs;
-       cs->timer.function = timer_tick;
-       cs->timer.expires = jiffies + GIG_TICK;
+       /* set up device sysfs */
+       gigaset_init_dev_sysfs(cs);
+
+       /* set up channel data structures */
+       for (i = 0; i < channels; ++i) {
+               gig_dbg(DEBUG_INIT, "setting up bcs[%d]", i);
+               if (!gigaset_initbcs(cs->bcs + i, cs, i)) {
+                       pr_err("could not allocate channel %d data\n", i);
+                       goto error;
+               }
+       }
+
+       spin_lock_irqsave(&cs->lock, flags);
+       cs->running = 1;
+       spin_unlock_irqrestore(&cs->lock, flags);
+       setup_timer(&cs->timer, timer_tick, (unsigned long) cs);
+       cs->timer.expires = jiffies + msecs_to_jiffies(GIG_TICK);
        /* FIXME: can jiffies increase too much until the timer is added?
         * Same problem(?) with mod_timer() in timer_tick(). */
        add_timer(&cs->timer);
 
-       dbg(DEBUG_INIT, "cs initialized!");
-       up(&cs->sem);
+       gig_dbg(DEBUG_INIT, "cs initialized");
        return cs;
 
-error: if (cs)
-               up(&cs->sem);
-       dbg(DEBUG_INIT, "failed");
+error:
+       gig_dbg(DEBUG_INIT, "failed");
        gigaset_freecs(cs);
        return NULL;
 }
 EXPORT_SYMBOL_GPL(gigaset_initcs);
 
-/* ReInitialize the b-channel structure */
-/* e.g. called on hangup, disconnect */
+/* ReInitialize the b-channel structure on hangup */
 void gigaset_bcs_reinit(struct bc_state *bcs)
 {
        struct sk_buff *skb;
@@ -720,7 +830,7 @@ void gigaset_bcs_reinit(struct bc_state *bcs)
        bcs->at_state.ConState = 0;
        bcs->at_state.timer_active = 0;
        bcs->at_state.timer_expires = 0;
-       bcs->at_state.cid = -1;                     /* No CID defined */
+       bcs->at_state.cid = -1;                 /* No CID defined */
        spin_unlock_irqrestore(&cs->lock, flags);
 
        bcs->inputstate = 0;
@@ -733,9 +843,10 @@ void gigaset_bcs_reinit(struct bc_state *bcs)
        bcs->chstate = 0;
 
        bcs->ignore = cs->ignoreframes;
-       if (bcs->ignore)
-               bcs->inputstate |= INS_skip_frame;
-
+       if (bcs->ignore) {
+               dev_kfree_skb(bcs->skb);
+               bcs->skb = NULL;
+       }
 
        cs->ops->reinitbcshw(bcs);
 }
@@ -748,19 +859,17 @@ static void cleanup_cs(struct cardstate *cs)
 
        spin_lock_irqsave(&cs->lock, flags);
 
-       atomic_set(&cs->mode, M_UNKNOWN);
-       atomic_set(&cs->mstate, MS_UNINITIALIZED);
+       cs->mode = M_UNKNOWN;
+       cs->mstate = MS_UNINITIALIZED;
 
        clear_at_state(&cs->at_state);
        dealloc_at_states(cs);
        free_strings(&cs->at_state);
        gigaset_at_init(&cs->at_state, NULL, cs, 0);
 
-       kfree(cs->inbuf->rcvbuf);
-       cs->inbuf->rcvbuf = NULL;
        cs->inbuf->inputstate = INS_command;
-       atomic_set(&cs->inbuf->head, 0);
-       atomic_set(&cs->inbuf->tail, 0);
+       cs->inbuf->head = 0;
+       cs->inbuf->tail = 0;
 
        cb = cs->cmdbuf;
        while (cb) {
@@ -774,7 +883,7 @@ static void cleanup_cs(struct cardstate *cs)
        cs->gotfwver = -1;
        cs->dle = 0;
        cs->cur_at_seq = 0;
-       atomic_set(&cs->commands_pending, 0);
+       cs->commands_pending = 0;
        cs->cbytes = 0;
 
        spin_unlock_irqrestore(&cs->lock, flags);
@@ -782,7 +891,7 @@ static void cleanup_cs(struct cardstate *cs)
        for (i = 0; i < cs->channels; ++i) {
                gigaset_freebcs(cs->bcs + i);
                if (!gigaset_initbcs(cs->bcs + i, cs, i))
-                       break;                  //FIXME error handling
+                       pr_err("could not allocate channel %d data\n", i);
        }
 
        if (cs->waiting) {
@@ -793,119 +902,123 @@ static void cleanup_cs(struct cardstate *cs)
 }
 
 
+/**
+ * gigaset_start() - start device operations
+ * @cs:                device descriptor structure.
+ *
+ * Prepares the device for use by setting up communication parameters,
+ * scheduling an EV_START event to initiate device initialization, and
+ * waiting for completion of the initialization.
+ *
+ * Return value:
+ *     1 - success, 0 - error
+ */
 int gigaset_start(struct cardstate *cs)
 {
-       if (down_interruptible(&cs->sem))
+       unsigned long flags;
+
+       if (mutex_lock_interruptible(&cs->mutex))
                return 0;
 
-       atomic_set(&cs->connected, 1);
+       spin_lock_irqsave(&cs->lock, flags);
+       cs->connected = 1;
+       spin_unlock_irqrestore(&cs->lock, flags);
 
-       if (atomic_read(&cs->mstate) != MS_LOCKED) {
+       if (cs->mstate != MS_LOCKED) {
                cs->ops->set_modem_ctrl(cs, 0, TIOCM_DTR|TIOCM_RTS);
                cs->ops->baud_rate(cs, B115200);
                cs->ops->set_line_ctrl(cs, CS8);
                cs->control_state = TIOCM_DTR|TIOCM_RTS;
-       } else {
-               //FIXME use some saved values?
        }
 
        cs->waiting = 1;
 
        if (!gigaset_add_event(cs, &cs->at_state, EV_START, NULL, 0, NULL)) {
                cs->waiting = 0;
-               //FIXME what should we do?
                goto error;
        }
-
-       dbg(DEBUG_CMD, "scheduling START");
        gigaset_schedule_event(cs);
 
        wait_event(cs->waitqueue, !cs->waiting);
 
-       up(&cs->sem);
+       mutex_unlock(&cs->mutex);
        return 1;
 
 error:
-       up(&cs->sem);
+       mutex_unlock(&cs->mutex);
        return 0;
 }
 EXPORT_SYMBOL_GPL(gigaset_start);
 
-void gigaset_shutdown(struct cardstate *cs)
+/**
+ * gigaset_shutdown() - shut down device operations
+ * @cs:                device descriptor structure.
+ *
+ * Deactivates the device by scheduling an EV_SHUTDOWN event and
+ * waiting for completion of the shutdown.
+ *
+ * Return value:
+ *     0 - success, -1 - error (no device associated)
+ */
+int gigaset_shutdown(struct cardstate *cs)
 {
-       down(&cs->sem);
+       mutex_lock(&cs->mutex);
+
+       if (!(cs->flags & VALID_MINOR)) {
+               mutex_unlock(&cs->mutex);
+               return -1;
+       }
 
        cs->waiting = 1;
 
-       if (!gigaset_add_event(cs, &cs->at_state, EV_SHUTDOWN, NULL, 0, NULL)) {
-               //FIXME what should we do?
+       if (!gigaset_add_event(cs, &cs->at_state, EV_SHUTDOWN, NULL, 0, NULL))
                goto exit;
-       }
-
-       dbg(DEBUG_CMD, "scheduling SHUTDOWN");
        gigaset_schedule_event(cs);
 
-       if (wait_event_interruptible(cs->waitqueue, !cs->waiting)) {
-               warn("aborted");
-               //FIXME
-       }
-
-       if (atomic_read(&cs->mstate) != MS_LOCKED) {
-               //FIXME?
-               //gigaset_baud_rate(cs, B115200);
-               //gigaset_set_line_ctrl(cs, CS8);
-               //gigaset_set_modem_ctrl(cs, TIOCM_DTR|TIOCM_RTS, 0);
-               //cs->control_state = 0;
-       } else {
-               //FIXME use some saved values?
-       }
+       wait_event(cs->waitqueue, !cs->waiting);
 
        cleanup_cs(cs);
 
 exit:
-       up(&cs->sem);
+       mutex_unlock(&cs->mutex);
+       return 0;
 }
 EXPORT_SYMBOL_GPL(gigaset_shutdown);
 
+/**
+ * gigaset_stop() - stop device operations
+ * @cs:                device descriptor structure.
+ *
+ * Stops operations on the device by scheduling an EV_STOP event and
+ * waiting for completion of the shutdown.
+ */
 void gigaset_stop(struct cardstate *cs)
 {
-       down(&cs->sem);
-
-       atomic_set(&cs->connected, 0);
+       mutex_lock(&cs->mutex);
 
        cs->waiting = 1;
 
-       if (!gigaset_add_event(cs, &cs->at_state, EV_STOP, NULL, 0, NULL)) {
-               //FIXME what should we do?
+       if (!gigaset_add_event(cs, &cs->at_state, EV_STOP, NULL, 0, NULL))
                goto exit;
-       }
-
-       dbg(DEBUG_CMD, "scheduling STOP");
        gigaset_schedule_event(cs);
 
-       if (wait_event_interruptible(cs->waitqueue, !cs->waiting)) {
-               warn("aborted");
-               //FIXME
-       }
-
-       /* Tell the LL that the device is not available .. */
-       gigaset_i4l_cmd(cs, ISDN_STAT_STOP); // FIXME move to event layer?
+       wait_event(cs->waitqueue, !cs->waiting);
 
        cleanup_cs(cs);
 
 exit:
-       up(&cs->sem);
+       mutex_unlock(&cs->mutex);
 }
 EXPORT_SYMBOL_GPL(gigaset_stop);
 
 static LIST_HEAD(drivers);
-static spinlock_t driver_lock = SPIN_LOCK_UNLOCKED;
+static DEFINE_SPINLOCK(driver_lock);
 
 struct cardstate *gigaset_get_cs_by_id(int id)
 {
        unsigned long flags;
-       static struct cardstate *ret = NULL;
-       static struct cardstate *cs;
+       struct cardstate *ret = NULL;
+       struct cardstate *cs;
        struct gigaset_driver *drv;
        unsigned i;
 
@@ -913,13 +1026,11 @@ struct cardstate *gigaset_get_cs_by_id(int id)
        list_for_each_entry(drv, &drivers, list) {
                spin_lock(&drv->lock);
                for (i = 0; i < drv->minors; ++i) {
-                       if (drv->flags[i] & VALID_ID) {
-                               cs = drv->cs + i;
-                               if (cs->myid == id)
-                                       ret = cs;
-                       }
-                       if (ret)
+                       cs = drv->cs + i;
+                       if ((cs->flags & VALID_ID) && cs->myid == id) {
+                               ret = cs;
                                break;
+                       }
                }
                spin_unlock(&drv->lock);
                if (ret)
@@ -938,35 +1049,27 @@ void gigaset_debugdrivers(void)
 
        spin_lock_irqsave(&driver_lock, flags);
        list_for_each_entry(drv, &drivers, list) {
-               dbg(DEBUG_DRIVER, "driver %p", drv);
+               gig_dbg(DEBUG_DRIVER, "driver %p", drv);
                spin_lock(&drv->lock);
                for (i = 0; i < drv->minors; ++i) {
-                       dbg(DEBUG_DRIVER, "  index %u", i);
-                       dbg(DEBUG_DRIVER, "    flags 0x%02x", drv->flags[i]);
+                       gig_dbg(DEBUG_DRIVER, "  index %u", i);
                        cs = drv->cs + i;
-                       dbg(DEBUG_DRIVER, "    cardstate %p", cs);
-                       dbg(DEBUG_DRIVER, "    minor_index %u",
-                           cs->minor_index);
-                       dbg(DEBUG_DRIVER, "    driver %p", cs->driver);
-                       dbg(DEBUG_DRIVER, "    i4l id %d", cs->myid);
+                       gig_dbg(DEBUG_DRIVER, "    cardstate %p", cs);
+                       gig_dbg(DEBUG_DRIVER, "    flags 0x%02x", cs->flags);
+                       gig_dbg(DEBUG_DRIVER, "    minor_index %u",
+                               cs->minor_index);
+                       gig_dbg(DEBUG_DRIVER, "    driver %p", cs->driver);
+                       gig_dbg(DEBUG_DRIVER, "    i4l id %d", cs->myid);
                }
                spin_unlock(&drv->lock);
        }
        spin_unlock_irqrestore(&driver_lock, flags);
 }
-EXPORT_SYMBOL_GPL(gigaset_debugdrivers);
 
-struct cardstate *gigaset_get_cs_by_tty(struct tty_struct *tty)
-{
-       if (tty->index < 0 || tty->index >= tty->driver->num)
-               return NULL;
-       return gigaset_get_cs_by_minor(tty->index + tty->driver->minor_start);
-}
-
-struct cardstate *gigaset_get_cs_by_minor(unsigned minor)
+static struct cardstate *gigaset_get_cs_by_minor(unsigned minor)
 {
        unsigned long flags;
-       static struct cardstate *ret = NULL;
+       struct cardstate *ret = NULL;
        struct gigaset_driver *drv;
        unsigned index;
 
@@ -976,7 +1079,7 @@ struct cardstate *gigaset_get_cs_by_minor(unsigned minor)
                        continue;
                index = minor - drv->minor;
                spin_lock(&drv->lock);
-               if (drv->flags[index] & VALID_MINOR)
+               if (drv->cs[index].flags & VALID_MINOR)
                        ret = drv->cs + index;
                spin_unlock(&drv->lock);
                if (ret)
@@ -986,6 +1089,21 @@ struct cardstate *gigaset_get_cs_by_minor(unsigned minor)
        return ret;
 }
 
+struct cardstate *gigaset_get_cs_by_tty(struct tty_struct *tty)
+{
+       if (tty->index < 0 || tty->index >= tty->driver->num)
+               return NULL;
+       return gigaset_get_cs_by_minor(tty->index + tty->driver->minor_start);
+}
+
+/**
+ * gigaset_freedriver() - free all associated ressources of a driver
+ * @drv:       driver descriptor structure.
+ *
+ * Unregisters the driver from the system and deallocates the driver
+ * structure @drv and all structures referenced from it.
+ * All devices should be shut down before calling this.
+ */
 void gigaset_freedriver(struct gigaset_driver *drv)
 {
        unsigned long flags;
@@ -995,31 +1113,29 @@ void gigaset_freedriver(struct gigaset_driver *drv)
        spin_unlock_irqrestore(&driver_lock, flags);
 
        gigaset_if_freedriver(drv);
-       module_put(drv->owner);
 
        kfree(drv->cs);
-       kfree(drv->flags);
        kfree(drv);
 }
 EXPORT_SYMBOL_GPL(gigaset_freedriver);
 
-/* gigaset_initdriver
+/**
+ * gigaset_initdriver() - initialize driver structure
+ * @minor:     First minor number
+ * @minors:    Number of minors this driver can handle
+ * @procname:  Name of the driver
+ * @devname:   Name of the device files (prefix without minor number)
+ *
  * Allocate and initialize gigaset_driver structure. Initialize interface.
- * parameters:
- *      minor           First minor number
- *      minors          Number of minors this driver can handle
- *      procname        Name of the driver
- *      devname         Name of the device files (prefix without minor number)
- *      devfsname       Devfs name of the device files without %d
- * return value:
- *      Pointer to the gigaset_driver structure on success, NULL on failure.
+ *
+ * Return value:
+ *     Pointer to the gigaset_driver structure on success, NULL on failure.
  */
 struct gigaset_driver *gigaset_initdriver(unsigned minor, unsigned minors,
-                                          const char *procname,
-                                          const char *devname,
-                                          const char *devfsname,
-                                          const struct gigaset_ops *ops,
-                                          struct module *owner)
+                                         const char *procname,
+                                         const char *devname,
+                                         const struct gigaset_ops *ops,
+                                         struct module *owner)
 {
        struct gigaset_driver *drv;
        unsigned long flags;
@@ -1028,10 +1144,7 @@ struct gigaset_driver *gigaset_initdriver(unsigned minor, unsigned minors,
        drv = kmalloc(sizeof *drv, GFP_KERNEL);
        if (!drv)
                return NULL;
-       if (!try_module_get(owner))
-               return NULL;
 
-       drv->cs = NULL;
        drv->have_tty = 0;
        drv->minor = minor;
        drv->minors = minors;
@@ -1043,19 +1156,17 @@ struct gigaset_driver *gigaset_initdriver(unsigned minor, unsigned minors,
 
        drv->cs = kmalloc(minors * sizeof *drv->cs, GFP_KERNEL);
        if (!drv->cs)
-               goto out1;
-       drv->flags = kmalloc(minors * sizeof *drv->flags, GFP_KERNEL);
-       if (!drv->flags)
-               goto out2;
+               goto error;
 
        for (i = 0; i < minors; ++i) {
-               drv->flags[i] = 0;
+               drv->cs[i].flags = 0;
                drv->cs[i].driver = drv;
                drv->cs[i].ops = drv->ops;
                drv->cs[i].minor_index = i;
+               mutex_init(&drv->cs[i].mutex);
        }
 
-       gigaset_if_initdriver(drv, procname, devname, devfsname);
+       gigaset_if_initdriver(drv, procname, devname);
 
        spin_lock_irqsave(&driver_lock, flags);
        list_add(&drv->list, &drivers);
@@ -1063,108 +1174,23 @@ struct gigaset_driver *gigaset_initdriver(unsigned minor, unsigned minors,
 
        return drv;
 
-out2:
+error:
        kfree(drv->cs);
-out1:
        kfree(drv);
-       module_put(owner);
        return NULL;
 }
 EXPORT_SYMBOL_GPL(gigaset_initdriver);
 
-static struct cardstate *alloc_cs(struct gigaset_driver *drv)
-{
-       unsigned long flags;
-       unsigned i;
-       static struct cardstate *ret = NULL;
-
-       spin_lock_irqsave(&drv->lock, flags);
-       for (i = 0; i < drv->minors; ++i) {
-               if (!(drv->flags[i] & VALID_MINOR)) {
-                       drv->flags[i] = VALID_MINOR;
-                       ret = drv->cs + i;
-               }
-               if (ret)
-                       break;
-       }
-       spin_unlock_irqrestore(&drv->lock, flags);
-       return ret;
-}
-
-static void free_cs(struct cardstate *cs)
-{
-       unsigned long flags;
-       struct gigaset_driver *drv = cs->driver;
-       spin_lock_irqsave(&drv->lock, flags);
-       drv->flags[cs->minor_index] = 0;
-       spin_unlock_irqrestore(&drv->lock, flags);
-}
-
-static void make_valid(struct cardstate *cs, unsigned mask)
-{
-       unsigned long flags;
-       struct gigaset_driver *drv = cs->driver;
-       spin_lock_irqsave(&drv->lock, flags);
-       drv->flags[cs->minor_index] |= mask;
-       spin_unlock_irqrestore(&drv->lock, flags);
-}
-
-static void make_invalid(struct cardstate *cs, unsigned mask)
-{
-       unsigned long flags;
-       struct gigaset_driver *drv = cs->driver;
-       spin_lock_irqsave(&drv->lock, flags);
-       drv->flags[cs->minor_index] &= ~mask;
-       spin_unlock_irqrestore(&drv->lock, flags);
-}
-
-/* For drivers without fixed assignment device<->cardstate (usb) */
-struct cardstate *gigaset_getunassignedcs(struct gigaset_driver *drv)
-{
-       unsigned long flags;
-       struct cardstate *cs = NULL;
-       unsigned i;
-
-       spin_lock_irqsave(&drv->lock, flags);
-       if (drv->blocked)
-               goto exit;
-       for (i = 0; i < drv->minors; ++i) {
-               if ((drv->flags[i] & VALID_MINOR) &&
-                   !(drv->flags[i] & ASSIGNED)) {
-                       drv->flags[i] |= ASSIGNED;
-                       cs = drv->cs + i;
-                       break;
-               }
-       }
-exit:
-       spin_unlock_irqrestore(&drv->lock, flags);
-       return cs;
-}
-EXPORT_SYMBOL_GPL(gigaset_getunassignedcs);
-
-void gigaset_unassign(struct cardstate *cs)
-{
-       unsigned long flags;
-       unsigned *minor_flags;
-       struct gigaset_driver *drv;
-
-       if (!cs)
-               return;
-       drv = cs->driver;
-       spin_lock_irqsave(&drv->lock, flags);
-       minor_flags = drv->flags + cs->minor_index;
-       if (*minor_flags & VALID_MINOR)
-               *minor_flags &= ~ASSIGNED;
-       spin_unlock_irqrestore(&drv->lock, flags);
-}
-EXPORT_SYMBOL_GPL(gigaset_unassign);
-
+/**
+ * gigaset_blockdriver() - block driver
+ * @drv:       driver descriptor structure.
+ *
+ * Prevents the driver from attaching new devices, in preparation for
+ * deregistration.
+ */
 void gigaset_blockdriver(struct gigaset_driver *drv)
 {
-       unsigned long flags;
-       spin_lock_irqsave(&drv->lock, flags);
        drv->blocked = 1;
-       spin_unlock_irqrestore(&drv->lock, flags);
 }
 EXPORT_SYMBOL_GPL(gigaset_blockdriver);
 
@@ -1177,13 +1203,14 @@ static int __init gigaset_init_module(void)
        if (gigaset_debuglevel == 1)
                gigaset_debuglevel = DEBUG_DEFAULT;
 
-       info(DRIVER_AUTHOR);
-       info(DRIVER_DESC);
+       pr_info(DRIVER_DESC DRIVER_DESC_DEBUG "\n");
+       gigaset_isdn_regdrv();
        return 0;
 }
 
 static void __exit gigaset_exit_module(void)
 {
+       gigaset_isdn_unregdrv();
 }
 
 module_init(gigaset_init_module);