Merge branch 'sched-fixes-for-linus' of git://git.kernel.org/pub/scm/linux/kernel...
[pandora-kernel.git] / drivers / isdn / gigaset / bas-gigaset.c
index d60a651..3f11910 100644 (file)
@@ -73,6 +73,14 @@ static int gigaset_probe(struct usb_interface *interface,
 /* Function will be called if the device is unplugged */
 static void gigaset_disconnect(struct usb_interface *interface);
 
+/* functions called before/after suspend */
+static int gigaset_suspend(struct usb_interface *intf, pm_message_t message);
+static int gigaset_resume(struct usb_interface *intf);
+
+/* functions called before/after device reset */
+static int gigaset_pre_reset(struct usb_interface *intf);
+static int gigaset_post_reset(struct usb_interface *intf);
+
 static int atread_submit(struct cardstate *, int);
 static void stopurbs(struct bas_bc_state *);
 static int req_submit(struct bc_state *, int, int, int);
@@ -105,8 +113,9 @@ struct bas_cardstate {
        unsigned char           int_in_buf[3];
 
        spinlock_t              lock;           /* locks all following */
-       atomic_t                basstate;       /* bitmap (BS_*) */
+       int                     basstate;       /* bitmap (BS_*) */
        int                     pending;        /* uncompleted base request */
+       wait_queue_head_t       waitqueue;
        int                     rcvbuf_size;    /* size of AT receive buffer */
                                                /* 0: no receive in progress */
        int                     retry_cmd_in;   /* receive req retry count */
@@ -121,10 +130,10 @@ struct bas_cardstate {
 #define BS_ATTIMER     0x020   /* waiting for HD_READY_SEND_ATDATA */
 #define BS_ATRDPEND    0x040   /* urb_cmd_in in use */
 #define BS_ATWRPEND    0x080   /* urb_cmd_out in use */
+#define BS_SUSPEND     0x100   /* USB port suspended */
 
 
 static struct gigaset_driver *driver = NULL;
-static struct cardstate *cardstate = NULL;
 
 /* usb specific object needed to register this driver with the usb subsystem */
 static struct usb_driver gigaset_usb_driver = {
@@ -132,6 +141,11 @@ static struct usb_driver gigaset_usb_driver = {
        .probe =        gigaset_probe,
        .disconnect =   gigaset_disconnect,
        .id_table =     gigaset_table,
+       .suspend =      gigaset_suspend,
+       .resume =       gigaset_resume,
+       .reset_resume = gigaset_post_reset,
+       .pre_reset =    gigaset_pre_reset,
+       .post_reset =   gigaset_post_reset,
 };
 
 /* get message text for usb_submit_urb return code
@@ -355,27 +369,27 @@ static void check_pending(struct bas_cardstate *ucs)
        case 0:
                break;
        case HD_OPEN_ATCHANNEL:
-               if (atomic_read(&ucs->basstate) & BS_ATOPEN)
+               if (ucs->basstate & BS_ATOPEN)
                        ucs->pending = 0;
                break;
        case HD_OPEN_B1CHANNEL:
-               if (atomic_read(&ucs->basstate) & BS_B1OPEN)
+               if (ucs->basstate & BS_B1OPEN)
                        ucs->pending = 0;
                break;
        case HD_OPEN_B2CHANNEL:
-               if (atomic_read(&ucs->basstate) & BS_B2OPEN)
+               if (ucs->basstate & BS_B2OPEN)
                        ucs->pending = 0;
                break;
        case HD_CLOSE_ATCHANNEL:
-               if (!(atomic_read(&ucs->basstate) & BS_ATOPEN))
+               if (!(ucs->basstate & BS_ATOPEN))
                        ucs->pending = 0;
                break;
        case HD_CLOSE_B1CHANNEL:
-               if (!(atomic_read(&ucs->basstate) & BS_B1OPEN))
+               if (!(ucs->basstate & BS_B1OPEN))
                        ucs->pending = 0;
                break;
        case HD_CLOSE_B2CHANNEL:
-               if (!(atomic_read(&ucs->basstate) & BS_B2OPEN))
+               if (!(ucs->basstate & BS_B2OPEN))
                        ucs->pending = 0;
                break;
        case HD_DEVICE_INIT_ACK:                /* no reply expected */
@@ -441,8 +455,8 @@ inline static int update_basstate(struct bas_cardstate *ucs,
        int state;
 
        spin_lock_irqsave(&ucs->lock, flags);
-       state = atomic_read(&ucs->basstate);
-       atomic_set(&ucs->basstate, (state & ~clear) | set);
+       state = ucs->basstate;
+       ucs->basstate = (state & ~clear) | set;
        spin_unlock_irqrestore(&ucs->lock, flags);
        return state;
 }
@@ -465,6 +479,7 @@ static void read_ctrl_callback(struct urb *urb)
        int rc;
 
        update_basstate(ucs, 0, BS_ATRDPEND);
+       wake_up(&ucs->waitqueue);
 
        if (!ucs->rcvbuf_size) {
                dev_warn(cs->dev, "%s: no receive in progress\n", __func__);
@@ -551,17 +566,28 @@ static void read_ctrl_callback(struct urb *urb)
 static int atread_submit(struct cardstate *cs, int timeout)
 {
        struct bas_cardstate *ucs = cs->hw.bas;
+       int basstate;
        int ret;
 
        gig_dbg(DEBUG_USBREQ, "-------> HD_READ_ATMESSAGE (%d)",
                ucs->rcvbuf_size);
 
-       if (update_basstate(ucs, BS_ATRDPEND, 0) & BS_ATRDPEND) {
+       basstate = update_basstate(ucs, BS_ATRDPEND, 0);
+       if (basstate & BS_ATRDPEND) {
                dev_err(cs->dev,
                        "could not submit HD_READ_ATMESSAGE: URB busy\n");
                return -EBUSY;
        }
 
+       if (basstate & BS_SUSPEND) {
+               dev_notice(cs->dev,
+                          "HD_READ_ATMESSAGE not submitted, "
+                          "suspend in progress\n");
+               update_basstate(ucs, 0, BS_ATRDPEND);
+               /* treat like disconnect */
+               return -ENODEV;
+       }
+
        ucs->dr_cmd_in.bRequestType = IN_VENDOR_REQ;
        ucs->dr_cmd_in.bRequest = HD_READ_ATMESSAGE;
        ucs->dr_cmd_in.wValue = 0;
@@ -747,6 +773,7 @@ static void read_int_callback(struct urb *urb)
        }
 
        check_pending(ucs);
+       wake_up(&ucs->waitqueue);
 
 resubmit:
        rc = usb_submit_urb(urb, GFP_ATOMIC);
@@ -804,7 +831,7 @@ static void read_iso_callback(struct urb *urb)
                        urb->iso_frame_desc[i].status = 0;
                        urb->iso_frame_desc[i].actual_length = 0;
                }
-               if (likely(atomic_read(&ubc->running))) {
+               if (likely(ubc->running)) {
                        /* urb->dev is clobbered by USB subsystem */
                        urb->dev = bcs->cs->hw.bas->udev;
                        urb->transfer_flags = URB_ISO_ASAP;
@@ -881,7 +908,7 @@ static int starturbs(struct bc_state *bcs)
                bcs->inputstate |= INS_flag_hunt;
 
        /* submit all isochronous input URBs */
-       atomic_set(&ubc->running, 1);
+       ubc->running = 1;
        for (k = 0; k < BAS_INURBS; k++) {
                urb = ubc->isoinurbs[k];
                if (!urb) {
@@ -964,7 +991,7 @@ static void stopurbs(struct bas_bc_state *ubc)
 {
        int k, rc;
 
-       atomic_set(&ubc->running, 0);
+       ubc->running = 0;
 
        for (k = 0; k < BAS_INURBS; ++k) {
                rc = usb_unlink_urb(ubc->isoinurbs[k]);
@@ -1023,10 +1050,9 @@ static int submit_iso_write_urb(struct isow_urbctx_t *ucx)
                }
 
                /* retrieve block of data to send */
-               ifd->offset = gigaset_isowbuf_getbytes(ubc->isooutbuf,
-                                                      ifd->length);
-               if (ifd->offset < 0) {
-                       if (ifd->offset == -EBUSY) {
+               rc = gigaset_isowbuf_getbytes(ubc->isooutbuf, ifd->length);
+               if (rc < 0) {
+                       if (rc == -EBUSY) {
                                gig_dbg(DEBUG_ISO,
                                        "%s: buffer busy at frame %d",
                                        __func__, nframe);
@@ -1035,12 +1061,13 @@ static int submit_iso_write_urb(struct isow_urbctx_t *ucx)
                        } else {
                                dev_err(ucx->bcs->cs->dev,
                                        "%s: buffer error %d at frame %d\n",
-                                       __func__, ifd->offset, nframe);
-                               return ifd->offset;
+                                       __func__, rc, nframe);
+                               return rc;
                        }
                        break;
                }
-               ucx->limit = atomic_read(&ubc->isooutbuf->nextread);
+               ifd->offset = rc;
+               ucx->limit = ubc->isooutbuf->nextread;
                ifd->status = 0;
                ifd->actual_length = 0;
        }
@@ -1087,7 +1114,7 @@ static void write_iso_tasklet(unsigned long data)
 
        /* loop while completed URBs arrive in time */
        for (;;) {
-               if (unlikely(!(atomic_read(&ubc->running)))) {
+               if (unlikely(!(ubc->running))) {
                        gig_dbg(DEBUG_ISO, "%s: not running", __func__);
                        return;
                }
@@ -1192,7 +1219,7 @@ static void write_iso_tasklet(unsigned long data)
 
                /* mark the write buffer area covered by this URB as free */
                if (done->limit >= 0)
-                       atomic_set(&ubc->isooutbuf->read, done->limit);
+                       ubc->isooutbuf->read = done->limit;
 
                /* mark URB as free */
                spin_lock_irqsave(&ubc->isooutlock, flags);
@@ -1266,7 +1293,7 @@ static void read_iso_tasklet(unsigned long data)
                }
                spin_unlock_irqrestore(&ubc->isoinlock, flags);
 
-               if (unlikely(!(atomic_read(&ubc->running)))) {
+               if (unlikely(!(ubc->running))) {
                        gig_dbg(DEBUG_ISO,
                                "%s: channel not running, "
                                "dropped URB with status: %s",
@@ -1416,6 +1443,8 @@ static void req_timeout(unsigned long data)
                dev_warn(bcs->cs->dev, "request 0x%02x timed out, clearing\n",
                         pending);
        }
+
+       wake_up(&ucs->waitqueue);
 }
 
 /* write_ctrl_callback
@@ -1456,7 +1485,9 @@ static void write_ctrl_callback(struct urb *urb)
                break;
 
        default:                                /* any failure */
-               if (++ucs->retry_ctrl > BAS_RETRY) {
+               /* don't retry if suspend requested */
+               if (++ucs->retry_ctrl > BAS_RETRY ||
+                   (ucs->basstate & BS_SUSPEND)) {
                        dev_err(&ucs->interface->dev,
                                "control request 0x%02x failed: %s\n",
                                ucs->dr_ctrl.bRequest,
@@ -1485,6 +1516,7 @@ static void write_ctrl_callback(struct urb *urb)
        del_timer(&ucs->timer_ctrl);
        ucs->pending = 0;
        spin_unlock_irqrestore(&ucs->lock, flags);
+       wake_up(&ucs->waitqueue);
 }
 
 /* req_submit
@@ -1570,6 +1602,14 @@ static int gigaset_init_bchannel(struct bc_state *bcs)
                return -ENODEV;
        }
 
+       if (cs->hw.bas->basstate & BS_SUSPEND) {
+               dev_notice(cs->dev,
+                          "not starting isochronous I/O, "
+                          "suspend in progress\n");
+               spin_unlock_irqrestore(&cs->lock, flags);
+               return -EHOSTUNREACH;
+       }
+
        if ((ret = starturbs(bcs)) < 0) {
                dev_err(cs->dev,
                        "could not start isochronous I/O for channel B%d: %s\n",
@@ -1617,8 +1657,7 @@ static int gigaset_close_bchannel(struct bc_state *bcs)
                return -ENODEV;
        }
 
-       if (!(atomic_read(&cs->hw.bas->basstate) &
-             (bcs->channel ? BS_B2OPEN : BS_B1OPEN))) {
+       if (!(cs->hw.bas->basstate & (bcs->channel ? BS_B2OPEN : BS_B1OPEN))) {
                /* channel not running: just signal common.c */
                spin_unlock_irqrestore(&cs->lock, flags);
                gigaset_bchannel_down(bcs);
@@ -1682,6 +1721,7 @@ static void write_command_callback(struct urb *urb)
        unsigned long flags;
 
        update_basstate(ucs, 0, BS_ATWRPEND);
+       wake_up(&ucs->waitqueue);
 
        /* check status */
        switch (status) {
@@ -1705,6 +1745,13 @@ static void write_command_callback(struct urb *urb)
                                 ucs->retry_cmd_out);
                        break;
                }
+               if (ucs->basstate & BS_SUSPEND) {
+                       dev_warn(cs->dev,
+                                "command write: %s, "
+                                "won't retry - suspend requested\n",
+                                get_usb_statmsg(status));
+                       break;
+               }
                if (cs->cmdbuf == NULL) {
                        dev_warn(cs->dev,
                                 "command write: %s, "
@@ -1813,8 +1860,14 @@ static int start_cbsend(struct cardstate *cs)
        int rc;
        int retval = 0;
 
+       /* check if suspend requested */
+       if (ucs->basstate & BS_SUSPEND) {
+               gig_dbg(DEBUG_TRANSCMD|DEBUG_LOCKCMD, "suspending");
+               return -EHOSTUNREACH;
+       }
+
        /* check if AT channel is open */
-       if (!(atomic_read(&ucs->basstate) & BS_ATOPEN)) {
+       if (!(ucs->basstate & BS_ATOPEN)) {
                gig_dbg(DEBUG_TRANSCMD|DEBUG_LOCKCMD, "AT channel not open");
                rc = req_submit(cs->bcs, HD_OPEN_ATCHANNEL, 0, BAS_TIMEOUT);
                if (rc < 0) {
@@ -1830,8 +1883,7 @@ static int start_cbsend(struct cardstate *cs)
        /* try to send first command in queue */
        spin_lock_irqsave(&cs->cmdlock, flags);
 
-       while ((cb = cs->cmdbuf) != NULL &&
-              atomic_read(&ucs->basstate) & BS_ATREADY) {
+       while ((cb = cs->cmdbuf) != NULL && (ucs->basstate & BS_ATREADY)) {
                ucs->retry_cmd_out = 0;
                rc = atwrite_submit(cs, cb->buf, cb->len);
                if (unlikely(rc)) {
@@ -1869,7 +1921,7 @@ static int gigaset_write_cmd(struct cardstate *cs,
        unsigned long flags;
        int rc;
 
-       gigaset_dbg_buffer(atomic_read(&cs->mstate) != MS_LOCKED ?
+       gigaset_dbg_buffer(cs->mstate != MS_LOCKED ?
                             DEBUG_TRANSCMD : DEBUG_LOCKCMD,
                           "CMD Transmit", len, buf);
 
@@ -1984,7 +2036,7 @@ static int gigaset_freebcshw(struct bc_state *bcs)
                return 0;
 
        /* kill URBs and tasklets before freeing - better safe than sorry */
-       atomic_set(&ubc->running, 0);
+       ubc->running = 0;
        gig_dbg(DEBUG_INIT, "%s: killing iso URBs", __func__);
        for (i = 0; i < BAS_OUTURBS; ++i) {
                usb_kill_urb(ubc->isoouturbs[i].urb);
@@ -2019,7 +2071,7 @@ static int gigaset_initbcshw(struct bc_state *bcs)
                return 0;
        }
 
-       atomic_set(&ubc->running, 0);
+       ubc->running = 0;
        atomic_set(&ubc->corrbytes, 0);
        spin_lock_init(&ubc->isooutlock);
        for (i = 0; i < BAS_OUTURBS; ++i) {
@@ -2064,7 +2116,7 @@ static void gigaset_reinitbcshw(struct bc_state *bcs)
 {
        struct bas_bc_state *ubc = bcs->hw.bas;
 
-       atomic_set(&bcs->hw.bas->running, 0);
+       bcs->hw.bas->running = 0;
        atomic_set(&bcs->hw.bas->corrbytes, 0);
        bcs->hw.bas->numsub = 0;
        spin_lock_init(&ubc->isooutlock);
@@ -2095,10 +2147,11 @@ static int gigaset_initcshw(struct cardstate *cs)
        spin_lock_init(&ucs->lock);
        ucs->pending = 0;
 
-       atomic_set(&ucs->basstate, 0);
+       ucs->basstate = 0;
        init_timer(&ucs->timer_ctrl);
        init_timer(&ucs->timer_atrdy);
        init_timer(&ucs->timer_cmd_in);
+       init_waitqueue_head(&ucs->waitqueue);
 
        return 1;
 }
@@ -2193,11 +2246,11 @@ static int gigaset_probe(struct usb_interface *interface,
                 __func__, le16_to_cpu(udev->descriptor.idVendor),
                 le16_to_cpu(udev->descriptor.idProduct));
 
-       cs = gigaset_getunassignedcs(driver);
-       if (!cs) {
-               dev_err(&udev->dev, "no free cardstate\n");
+       /* allocate memory for our device state and intialize it */
+       cs = gigaset_initcs(driver, BAS_CHANNELS, 0, 0, cidmode,
+                           GIGASET_MODULENAME);
+       if (!cs)
                return -ENODEV;
-       }
        ucs = cs->hw.bas;
 
        /* save off device structure ptrs for later use */
@@ -2251,7 +2304,7 @@ static int gigaset_probe(struct usb_interface *interface,
 
        /* tell common part that the device is ready */
        if (startmode == SM_LOCKED)
-               atomic_set(&cs->mstate, MS_LOCKED);
+               cs->mstate = MS_LOCKED;
 
        /* save address of controller structure */
        usb_set_intfdata(interface, cs);
@@ -2266,7 +2319,7 @@ allocerr:
 error:
        freeurbs(cs);
        usb_set_intfdata(interface, NULL);
-       gigaset_unassign(cs);
+       gigaset_freecs(cs);
        return -ENODEV;
 }
 
@@ -2286,7 +2339,7 @@ static void gigaset_disconnect(struct usb_interface *interface)
        dev_info(cs->dev, "disconnecting Gigaset base\n");
 
        /* mark base as not ready, all channels disconnected */
-       atomic_set(&ucs->basstate, 0);
+       ucs->basstate = 0;
 
        /* tell LL all channels are down */
        for (j = 0; j < BAS_CHANNELS; ++j)
@@ -2308,9 +2361,113 @@ static void gigaset_disconnect(struct usb_interface *interface)
        ucs->interface = NULL;
        ucs->udev = NULL;
        cs->dev = NULL;
-       gigaset_unassign(cs);
+       gigaset_freecs(cs);
 }
 
+/* gigaset_suspend
+ * This function is called before the USB connection is suspended.
+ */
+static int gigaset_suspend(struct usb_interface *intf, pm_message_t message)
+{
+       struct cardstate *cs = usb_get_intfdata(intf);
+       struct bas_cardstate *ucs = cs->hw.bas;
+       int rc;
+
+       /* set suspend flag; this stops AT command/response traffic */
+       if (update_basstate(ucs, BS_SUSPEND, 0) & BS_SUSPEND) {
+               gig_dbg(DEBUG_SUSPEND, "already suspended");
+               return 0;
+       }
+
+       /* wait a bit for blocking conditions to go away */
+       rc = wait_event_timeout(ucs->waitqueue,
+                       !(ucs->basstate &
+                         (BS_B1OPEN|BS_B2OPEN|BS_ATRDPEND|BS_ATWRPEND)),
+                       BAS_TIMEOUT*HZ/10);
+       gig_dbg(DEBUG_SUSPEND, "wait_event_timeout() -> %d", rc);
+
+       /* check for conditions preventing suspend */
+       if (ucs->basstate & (BS_B1OPEN|BS_B2OPEN|BS_ATRDPEND|BS_ATWRPEND)) {
+               dev_warn(cs->dev, "cannot suspend:\n");
+               if (ucs->basstate & BS_B1OPEN)
+                       dev_warn(cs->dev, " B channel 1 open\n");
+               if (ucs->basstate & BS_B2OPEN)
+                       dev_warn(cs->dev, " B channel 2 open\n");
+               if (ucs->basstate & BS_ATRDPEND)
+                       dev_warn(cs->dev, " receiving AT reply\n");
+               if (ucs->basstate & BS_ATWRPEND)
+                       dev_warn(cs->dev, " sending AT command\n");
+               update_basstate(ucs, 0, BS_SUSPEND);
+               return -EBUSY;
+       }
+
+       /* close AT channel if open */
+       if (ucs->basstate & BS_ATOPEN) {
+               gig_dbg(DEBUG_SUSPEND, "closing AT channel");
+               rc = req_submit(cs->bcs, HD_CLOSE_ATCHANNEL, 0, 0);
+               if (rc) {
+                       update_basstate(ucs, 0, BS_SUSPEND);
+                       return rc;
+               }
+               wait_event_timeout(ucs->waitqueue, !ucs->pending,
+                                  BAS_TIMEOUT*HZ/10);
+               /* in case of timeout, proceed anyway */
+       }
+
+       /* kill all URBs and timers that might still be pending */
+       usb_kill_urb(ucs->urb_ctrl);
+       usb_kill_urb(ucs->urb_int_in);
+       del_timer_sync(&ucs->timer_ctrl);
+
+       gig_dbg(DEBUG_SUSPEND, "suspend complete");
+       return 0;
+}
+
+/* gigaset_resume
+ * This function is called after the USB connection has been resumed.
+ */
+static int gigaset_resume(struct usb_interface *intf)
+{
+       struct cardstate *cs = usb_get_intfdata(intf);
+       struct bas_cardstate *ucs = cs->hw.bas;
+       int rc;
+
+       /* resubmit interrupt URB for spontaneous messages from base */
+       rc = usb_submit_urb(ucs->urb_int_in, GFP_KERNEL);
+       if (rc) {
+               dev_err(cs->dev, "could not resubmit interrupt URB: %s\n",
+                       get_usb_rcmsg(rc));
+               return rc;
+       }
+
+       /* clear suspend flag to reallow activity */
+       update_basstate(ucs, 0, BS_SUSPEND);
+
+       gig_dbg(DEBUG_SUSPEND, "resume complete");
+       return 0;
+}
+
+/* gigaset_pre_reset
+ * This function is called before the USB connection is reset.
+ */
+static int gigaset_pre_reset(struct usb_interface *intf)
+{
+       /* handle just like suspend */
+       return gigaset_suspend(intf, PMSG_ON);
+}
+
+/* gigaset_post_reset
+ * This function is called after the USB connection has been reset.
+ */
+static int gigaset_post_reset(struct usb_interface *intf)
+{
+       /* FIXME: send HD_DEVICE_INIT_ACK? */
+
+       /* resume operations */
+       return gigaset_resume(intf);
+}
+
+
 static const struct gigaset_ops gigops = {
        gigaset_write_cmd,
        gigaset_write_room,
@@ -2343,12 +2500,6 @@ static int __init bas_gigaset_init(void)
                                       &gigops, THIS_MODULE)) == NULL)
                goto error;
 
-       /* allocate memory for our device state and intialize it */
-       cardstate = gigaset_initcs(driver, BAS_CHANNELS, 0, 0, cidmode,
-                                  GIGASET_MODULENAME);
-       if (!cardstate)
-               goto error;
-
        /* register this driver with the USB subsystem */
        result = usb_register(&gigaset_usb_driver);
        if (result < 0) {
@@ -2360,9 +2511,7 @@ static int __init bas_gigaset_init(void)
        info(DRIVER_DESC);
        return 0;
 
-error: if (cardstate)
-               gigaset_freecs(cardstate);
-       cardstate = NULL;
+error:
        if (driver)
                gigaset_freedriver(driver);
        driver = NULL;
@@ -2374,43 +2523,50 @@ error:  if (cardstate)
  */
 static void __exit bas_gigaset_exit(void)
 {
-       struct bas_cardstate *ucs = cardstate->hw.bas;
+       struct bas_cardstate *ucs;
+       int i;
 
        gigaset_blockdriver(driver); /* => probe will fail
                                      * => no gigaset_start any more
                                      */
 
-       gigaset_shutdown(cardstate);
-       /* from now on, no isdn callback should be possible */
-
-       /* close all still open channels */
-       if (atomic_read(&ucs->basstate) & BS_B1OPEN) {
-               gig_dbg(DEBUG_INIT, "closing B1 channel");
-               usb_control_msg(ucs->udev, usb_sndctrlpipe(ucs->udev, 0),
-                               HD_CLOSE_B1CHANNEL, OUT_VENDOR_REQ, 0, 0,
-                               NULL, 0, BAS_TIMEOUT);
-       }
-       if (atomic_read(&ucs->basstate) & BS_B2OPEN) {
-               gig_dbg(DEBUG_INIT, "closing B2 channel");
-               usb_control_msg(ucs->udev, usb_sndctrlpipe(ucs->udev, 0),
-                               HD_CLOSE_B2CHANNEL, OUT_VENDOR_REQ, 0, 0,
-                               NULL, 0, BAS_TIMEOUT);
-       }
-       if (atomic_read(&ucs->basstate) & BS_ATOPEN) {
-               gig_dbg(DEBUG_INIT, "closing AT channel");
-               usb_control_msg(ucs->udev, usb_sndctrlpipe(ucs->udev, 0),
-                               HD_CLOSE_ATCHANNEL, OUT_VENDOR_REQ, 0, 0,
-                               NULL, 0, BAS_TIMEOUT);
+       /* stop all connected devices */
+       for (i = 0; i < driver->minors; i++) {
+               if (gigaset_shutdown(driver->cs + i) < 0)
+                       continue;               /* no device */
+               /* from now on, no isdn callback should be possible */
+
+               /* close all still open channels */
+               ucs = driver->cs[i].hw.bas;
+               if (ucs->basstate & BS_B1OPEN) {
+                       gig_dbg(DEBUG_INIT, "closing B1 channel");
+                       usb_control_msg(ucs->udev,
+                                       usb_sndctrlpipe(ucs->udev, 0),
+                                       HD_CLOSE_B1CHANNEL, OUT_VENDOR_REQ,
+                                       0, 0, NULL, 0, BAS_TIMEOUT);
+               }
+               if (ucs->basstate & BS_B2OPEN) {
+                       gig_dbg(DEBUG_INIT, "closing B2 channel");
+                       usb_control_msg(ucs->udev,
+                                       usb_sndctrlpipe(ucs->udev, 0),
+                                       HD_CLOSE_B2CHANNEL, OUT_VENDOR_REQ,
+                                       0, 0, NULL, 0, BAS_TIMEOUT);
+               }
+               if (ucs->basstate & BS_ATOPEN) {
+                       gig_dbg(DEBUG_INIT, "closing AT channel");
+                       usb_control_msg(ucs->udev,
+                                       usb_sndctrlpipe(ucs->udev, 0),
+                                       HD_CLOSE_ATCHANNEL, OUT_VENDOR_REQ,
+                                       0, 0, NULL, 0, BAS_TIMEOUT);
+               }
+               ucs->basstate = 0;
        }
-       atomic_set(&ucs->basstate, 0);
 
        /* deregister this driver with the USB subsystem */
        usb_deregister(&gigaset_usb_driver);
        /* this will call the disconnect-callback */
        /* from now on, no disconnect/probe callback should be running */
 
-       gigaset_freecs(cardstate);
-       cardstate = NULL;
        gigaset_freedriver(driver);
        driver = NULL;
 }