Merge 5.18-rc5 into tty-next
[linux-2.6-microblaze.git] / drivers / tty / n_gsm.c
index fa92f72..eaa12b4 100644 (file)
@@ -73,6 +73,8 @@ module_param(debug, int, 0600);
  */
 #define MAX_MRU 1500
 #define MAX_MTU 1500
+/* SOF, ADDR, CTRL, LEN1, LEN2, ..., FCS, EOF */
+#define PROT_OVERHEAD 7
 #define        GSM_NET_TX_TIMEOUT (HZ*10)
 
 /*
@@ -219,7 +221,6 @@ struct gsm_mux {
        int encoding;
        u8 control;
        u8 fcs;
-       u8 received_fcs;
        u8 *txframe;                    /* TX framing buffer */
 
        /* Method for the receiver side */
@@ -231,6 +232,7 @@ struct gsm_mux {
        int initiator;                  /* Did we initiate connection */
        bool dead;                      /* Has the mux been shut down */
        struct gsm_dlci *dlci[NUM_DLCI];
+       int old_c_iflag;                /* termios c_iflag value before attach */
        bool constipated;               /* Asked by remote to shut up */
 
        spinlock_t tx_lock;
@@ -271,10 +273,6 @@ static DEFINE_SPINLOCK(gsm_mux_lock);
 
 static struct tty_driver *gsm_tty_driver;
 
-/* Save dlci open address */
-static int addr_open[256] = { 0 };
-/* Save dlci open count */
-static int addr_cnt;
 /*
  *     This section of the driver logic implements the GSM encodings
  *     both the basic and the 'advanced'. Reliable transport is not
@@ -369,6 +367,7 @@ static const u8 gsm_fcs8[256] = {
 #define GOOD_FCS       0xCF
 
 static int gsmld_output(struct gsm_mux *gsm, u8 *data, int len);
+static int gsm_modem_update(struct gsm_dlci *dlci, u8 brk);
 
 /**
  *     gsm_fcs_add     -       update FCS
@@ -749,7 +748,7 @@ static void __gsm_data_queue(struct gsm_dlci *dlci, struct gsm_msg *msg)
 
        *--dp = msg->ctrl;
        if (gsm->initiator)
-               *--dp = (msg->addr << 2) | 2 | EA;
+               *--dp = (msg->addr << 2) | CR | EA;
        else
                *--dp = (msg->addr << 2) | EA;
        *fcs = gsm_fcs_add_block(INIT_FCS, dp , msg->data - dp);
@@ -832,7 +831,7 @@ static int gsm_dlci_data_output(struct gsm_mux *gsm, struct gsm_dlci *dlci)
                        break;
                case 2: /* Unstructed with modem bits.
                Always one byte as we never send inline break data */
-                       *dp++ = gsm_encode_modem(dlci);
+                       *dp++ = (gsm_encode_modem(dlci) << 1) | EA;
                        break;
                }
                WARN_ON(kfifo_out_locked(&dlci->fifo, dp , len, &dlci->lock) != len);
@@ -916,6 +915,66 @@ static int gsm_dlci_data_output_framed(struct gsm_mux *gsm,
        return size;
 }
 
+/**
+ *     gsm_dlci_modem_output   -       try and push modem status out of a DLCI
+ *     @gsm: mux
+ *     @dlci: the DLCI to pull modem status from
+ *     @brk: break signal
+ *
+ *     Push an empty frame in to the transmit queue to update the modem status
+ *     bits and to transmit an optional break.
+ *
+ *     Caller must hold the tx_lock of the mux.
+ */
+
+static int gsm_dlci_modem_output(struct gsm_mux *gsm, struct gsm_dlci *dlci,
+                                u8 brk)
+{
+       u8 *dp = NULL;
+       struct gsm_msg *msg;
+       int size = 0;
+
+       /* for modem bits without break data */
+       switch (dlci->adaption) {
+       case 1: /* Unstructured */
+               break;
+       case 2: /* Unstructured with modem bits. */
+               size++;
+               if (brk > 0)
+                       size++;
+               break;
+       default:
+               pr_err("%s: unsupported adaption %d\n", __func__,
+                      dlci->adaption);
+               return -EINVAL;
+       }
+
+       msg = gsm_data_alloc(gsm, dlci->addr, size, gsm->ftype);
+       if (!msg) {
+               pr_err("%s: gsm_data_alloc error", __func__);
+               return -ENOMEM;
+       }
+       dp = msg->data;
+       switch (dlci->adaption) {
+       case 1: /* Unstructured */
+               break;
+       case 2: /* Unstructured with modem bits. */
+               if (brk == 0) {
+                       *dp++ = (gsm_encode_modem(dlci) << 1) | EA;
+               } else {
+                       *dp++ = gsm_encode_modem(dlci) << 1;
+                       *dp++ = (brk << 4) | 2 | EA; /* Length, Break, EA */
+               }
+               break;
+       default:
+               /* Handled above */
+               break;
+       }
+
+       __gsm_data_queue(dlci, msg);
+       return size;
+}
+
 /**
  *     gsm_dlci_data_sweep             -       look for data to send
  *     @gsm: the GSM mux
@@ -1093,7 +1152,6 @@ static void gsm_control_modem(struct gsm_mux *gsm, const u8 *data, int clen)
 {
        unsigned int addr = 0;
        unsigned int modem = 0;
-       unsigned int brk = 0;
        struct gsm_dlci *dlci;
        int len = clen;
        int slen;
@@ -1123,17 +1181,8 @@ static void gsm_control_modem(struct gsm_mux *gsm, const u8 *data, int clen)
                        return;
        }
        len--;
-       if (len > 0) {
-               while (gsm_read_ea(&brk, *dp++) == 0) {
-                       len--;
-                       if (len == 0)
-                               return;
-               }
-               modem <<= 7;
-               modem |= (brk & 0x7f);
-       }
        tty = tty_port_tty_get(&dlci->port);
-       gsm_process_modem(tty, dlci, modem, slen);
+       gsm_process_modem(tty, dlci, modem, slen - len);
        if (tty) {
                tty_wakeup(tty);
                tty_kref_put(tty);
@@ -1193,7 +1242,6 @@ static void gsm_control_rls(struct gsm_mux *gsm, const u8 *data, int clen)
 }
 
 static void gsm_dlci_begin_close(struct gsm_dlci *dlci);
-static void gsm_dlci_close(struct gsm_dlci *dlci);
 
 /**
  *     gsm_control_message     -       DLCI 0 control processing
@@ -1212,28 +1260,15 @@ static void gsm_control_message(struct gsm_mux *gsm, unsigned int command,
 {
        u8 buf[1];
        unsigned long flags;
-       struct gsm_dlci *dlci;
-       int i;
-       int address;
 
        switch (command) {
        case CMD_CLD: {
-               if (addr_cnt > 0) {
-                       for (i = 0; i < addr_cnt; i++) {
-                               address = addr_open[i];
-                               dlci = gsm->dlci[address];
-                               gsm_dlci_close(dlci);
-                               addr_open[i] = 0;
-                       }
-               }
+               struct gsm_dlci *dlci = gsm->dlci[0];
                /* Modem wishes to close down */
-               dlci = gsm->dlci[0];
                if (dlci) {
                        dlci->dead = true;
                        gsm->dead = true;
-                       gsm_dlci_close(dlci);
-                       addr_cnt = 0;
-                       gsm_response(gsm, 0, UA|PF);
+                       gsm_dlci_begin_close(dlci);
                }
                }
                break;
@@ -1326,11 +1361,12 @@ static void gsm_control_response(struct gsm_mux *gsm, unsigned int command,
 
 static void gsm_control_transmit(struct gsm_mux *gsm, struct gsm_control *ctrl)
 {
-       struct gsm_msg *msg = gsm_data_alloc(gsm, 0, ctrl->len + 1, gsm->ftype);
+       struct gsm_msg *msg = gsm_data_alloc(gsm, 0, ctrl->len + 2, gsm->ftype);
        if (msg == NULL)
                return;
-       msg->data[0] = (ctrl->cmd << 1) | 2 | EA;       /* command */
-       memcpy(msg->data + 1, ctrl->data, ctrl->len);
+       msg->data[0] = (ctrl->cmd << 1) | CR | EA;      /* command */
+       msg->data[1] = (ctrl->len << 1) | EA;
+       memcpy(msg->data + 2, ctrl->data, ctrl->len);
        gsm_data_queue(gsm->dlci[0], msg);
 }
 
@@ -1353,7 +1389,6 @@ static void gsm_control_retransmit(struct timer_list *t)
        spin_lock_irqsave(&gsm->control_lock, flags);
        ctrl = gsm->pending_cmd;
        if (ctrl) {
-               gsm->cretries--;
                if (gsm->cretries == 0) {
                        gsm->pending_cmd = NULL;
                        ctrl->error = -ETIMEDOUT;
@@ -1362,6 +1397,7 @@ static void gsm_control_retransmit(struct timer_list *t)
                        wake_up(&gsm->event);
                        return;
                }
+               gsm->cretries--;
                gsm_control_transmit(gsm, ctrl);
                mod_timer(&gsm->t2_timer, jiffies + gsm->t2 * HZ / 100);
        }
@@ -1402,7 +1438,7 @@ retry:
 
        /* If DLCI0 is in ADM mode skip retries, it won't respond */
        if (gsm->dlci[0]->mode == DLCI_MODE_ADM)
-               gsm->cretries = 1;
+               gsm->cretries = 0;
        else
                gsm->cretries = gsm->n2;
 
@@ -1450,20 +1486,22 @@ static int gsm_control_wait(struct gsm_mux *gsm, struct gsm_control *control)
 
 static void gsm_dlci_close(struct gsm_dlci *dlci)
 {
+       unsigned long flags;
+
        del_timer(&dlci->t1);
        if (debug & 8)
                pr_debug("DLCI %d goes closed.\n", dlci->addr);
        dlci->state = DLCI_CLOSED;
        if (dlci->addr != 0) {
                tty_port_tty_hangup(&dlci->port, false);
+               spin_lock_irqsave(&dlci->lock, flags);
                kfifo_reset(&dlci->fifo);
+               spin_unlock_irqrestore(&dlci->lock, flags);
                /* Ensure that gsmtty_open() can return. */
                tty_port_set_initialized(&dlci->port, 0);
                wake_up_interruptible(&dlci->port.open_wait);
        } else
                dlci->gsm->dead = true;
-       /* Unregister gsmtty driver,report gsmtty dev remove uevent for user */
-       tty_unregister_device(gsm_tty_driver, dlci->addr);
        wake_up(&dlci->gsm->event);
        /* A DLCI 0 close is a MUX termination so we need to kick that
           back to userspace somehow */
@@ -1485,8 +1523,9 @@ static void gsm_dlci_open(struct gsm_dlci *dlci)
        dlci->state = DLCI_OPEN;
        if (debug & 8)
                pr_debug("DLCI %d goes open.\n", dlci->addr);
-       /* Register gsmtty driver,report gsmtty dev add uevent for user */
-       tty_register_device(gsm_tty_driver, dlci->addr, NULL);
+       /* Send current modem state */
+       if (dlci->addr)
+               gsm_modem_update(dlci, 0);
        wake_up(&dlci->gsm->event);
 }
 
@@ -1623,6 +1662,7 @@ static void gsm_dlci_data(struct gsm_dlci *dlci, const u8 *data, int clen)
                tty = tty_port_tty_get(port);
                if (tty) {
                        gsm_process_modem(tty, dlci, modem, slen);
+                       tty_wakeup(tty);
                        tty_kref_put(tty);
                }
                fallthrough;
@@ -1793,19 +1833,7 @@ static void gsm_queue(struct gsm_mux *gsm)
        struct gsm_dlci *dlci;
        u8 cr;
        int address;
-       int i, j, k, address_tmp;
-       /* We have to sneak a look at the packet body to do the FCS.
-          A somewhat layering violation in the spec */
 
-       if ((gsm->control & ~PF) == UI)
-               gsm->fcs = gsm_fcs_add_block(gsm->fcs, gsm->buf, gsm->len);
-       if (gsm->encoding == 0) {
-               /* WARNING: gsm->received_fcs is used for
-               gsm->encoding = 0 only.
-               In this case it contain the last piece of data
-               required to generate final CRC */
-               gsm->fcs = gsm_fcs_add(gsm->fcs, gsm->received_fcs);
-       }
        if (gsm->fcs != GOOD_FCS) {
                gsm->bad_fcs++;
                if (debug & 4)
@@ -1836,11 +1864,6 @@ static void gsm_queue(struct gsm_mux *gsm)
                else {
                        gsm_response(gsm, address, UA|PF);
                        gsm_dlci_open(dlci);
-                       /* Save dlci open address */
-                       if (address) {
-                               addr_open[addr_cnt] = address;
-                               addr_cnt++;
-                       }
                }
                break;
        case DISC|PF:
@@ -1851,35 +1874,9 @@ static void gsm_queue(struct gsm_mux *gsm)
                        return;
                }
                /* Real close complete */
-               if (!address) {
-                       if (addr_cnt > 0) {
-                               for (i = 0; i < addr_cnt; i++) {
-                                       address = addr_open[i];
-                                       dlci = gsm->dlci[address];
-                                       gsm_dlci_close(dlci);
-                                       addr_open[i] = 0;
-                               }
-                       }
-                       dlci = gsm->dlci[0];
-                       gsm_dlci_close(dlci);
-                       addr_cnt = 0;
-                       gsm_response(gsm, 0, UA|PF);
-               } else {
-                       gsm_response(gsm, address, UA|PF);
-                       gsm_dlci_close(dlci);
-                       /* clear dlci address */
-                       for (j = 0; j < addr_cnt; j++) {
-                               address_tmp = addr_open[j];
-                               if (address_tmp == address) {
-                                       for (k = j; k < addr_cnt; k++)
-                                               addr_open[k] = addr_open[k+1];
-                                       addr_cnt--;
-                                       break;
-                               }
-                       }
-               }
+               gsm_response(gsm, address, UA|PF);
+               gsm_dlci_close(dlci);
                break;
-       case UA:
        case UA|PF:
                if (cr == 0 || dlci == NULL)
                        break;
@@ -1908,10 +1905,6 @@ static void gsm_queue(struct gsm_mux *gsm)
        case UI|PF:
        case UIH:
        case UIH|PF:
-#if 0
-               if (cr)
-                       goto invalid;
-#endif
                if (dlci == NULL || dlci->state != DLCI_OPEN) {
                        gsm_command(gsm, address, DM|PF);
                        return;
@@ -1993,19 +1986,25 @@ static void gsm0_receive(struct gsm_mux *gsm, unsigned char c)
                break;
        case GSM_DATA:          /* Data */
                gsm->buf[gsm->count++] = c;
-               if (gsm->count == gsm->len)
+               if (gsm->count == gsm->len) {
+                       /* Calculate final FCS for UI frames over all data */
+                       if ((gsm->control & ~PF) != UIH) {
+                               gsm->fcs = gsm_fcs_add_block(gsm->fcs, gsm->buf,
+                                                            gsm->count);
+                       }
                        gsm->state = GSM_FCS;
+               }
                break;
        case GSM_FCS:           /* FCS follows the packet */
-               gsm->received_fcs = c;
-               gsm_queue(gsm);
+               gsm->fcs = gsm_fcs_add(gsm->fcs, c);
                gsm->state = GSM_SSOF;
                break;
        case GSM_SSOF:
-               if (c == GSM0_SOF) {
-                       gsm->state = GSM_SEARCH;
-                       break;
-               }
+               gsm->state = GSM_SEARCH;
+               if (c == GSM0_SOF)
+                       gsm_queue(gsm);
+               else
+                       gsm->bad_size++;
                break;
        default:
                pr_debug("%s: unhandled state: %d\n", __func__, gsm->state);
@@ -2023,12 +2022,35 @@ static void gsm0_receive(struct gsm_mux *gsm, unsigned char c)
 
 static void gsm1_receive(struct gsm_mux *gsm, unsigned char c)
 {
+       /* handle XON/XOFF */
+       if ((c & ISO_IEC_646_MASK) == XON) {
+               gsm->constipated = true;
+               return;
+       } else if ((c & ISO_IEC_646_MASK) == XOFF) {
+               gsm->constipated = false;
+               /* Kick the link in case it is idling */
+               gsm_data_kick(gsm, NULL);
+               return;
+       }
        if (c == GSM1_SOF) {
-               /* EOF is only valid in frame if we have got to the data state
-                  and received at least one byte (the FCS) */
-               if (gsm->state == GSM_DATA && gsm->count) {
-                       /* Extract the FCS */
+               /* EOF is only valid in frame if we have got to the data state */
+               if (gsm->state == GSM_DATA) {
+                       if (gsm->count < 1) {
+                               /* Missing FSC */
+                               gsm->malformed++;
+                               gsm->state = GSM_START;
+                               return;
+                       }
+                       /* Remove the FCS from data */
                        gsm->count--;
+                       if ((gsm->control & ~PF) != UIH) {
+                               /* Calculate final FCS for UI frames over all
+                                * data but FCS
+                                */
+                               gsm->fcs = gsm_fcs_add_block(gsm->fcs, gsm->buf,
+                                                            gsm->count);
+                       }
+                       /* Add the FCS itself to test against GOOD_FCS */
                        gsm->fcs = gsm_fcs_add(gsm->fcs, gsm->buf[gsm->count]);
                        gsm->len = gsm->count;
                        gsm_queue(gsm);
@@ -2037,7 +2059,8 @@ static void gsm1_receive(struct gsm_mux *gsm, unsigned char c)
                }
                /* Any partial frame was a runt so go back to start */
                if (gsm->state != GSM_START) {
-                       gsm->malformed++;
+                       if (gsm->state != GSM_SEARCH)
+                               gsm->malformed++;
                        gsm->state = GSM_START;
                }
                /* A SOF in GSM_START means we are still reading idling or
@@ -2106,74 +2129,43 @@ static void gsm_error(struct gsm_mux *gsm)
        gsm->io_error++;
 }
 
-static int gsm_disconnect(struct gsm_mux *gsm)
-{
-       struct gsm_dlci *dlci = gsm->dlci[0];
-       struct gsm_control *gc;
-
-       if (!dlci)
-               return 0;
-
-       /* In theory disconnecting DLCI 0 is sufficient but for some
-          modems this is apparently not the case. */
-       gc = gsm_control_send(gsm, CMD_CLD, NULL, 0);
-       if (gc)
-               gsm_control_wait(gsm, gc);
-
-       del_timer_sync(&gsm->t2_timer);
-       /* Now we are sure T2 has stopped */
-
-       gsm_dlci_begin_close(dlci);
-       wait_event_interruptible(gsm->event,
-                               dlci->state == DLCI_CLOSED);
-
-       if (signal_pending(current))
-               return -EINTR;
-
-       return 0;
-}
-
 /**
  *     gsm_cleanup_mux         -       generic GSM protocol cleanup
  *     @gsm: our mux
+ *     @disc: disconnect link?
  *
  *     Clean up the bits of the mux which are the same for all framing
  *     protocols. Remove the mux from the mux table, stop all the timers
  *     and then shut down each device hanging up the channels as we go.
  */
 
-static void gsm_cleanup_mux(struct gsm_mux *gsm)
+static void gsm_cleanup_mux(struct gsm_mux *gsm, bool disc)
 {
        int i;
        struct gsm_dlci *dlci = gsm->dlci[0];
        struct gsm_msg *txq, *ntxq;
 
        gsm->dead = true;
+       mutex_lock(&gsm->mutex);
 
-       spin_lock(&gsm_mux_lock);
-       for (i = 0; i < MAX_MUX; i++) {
-               if (gsm_mux[i] == gsm) {
-                       gsm_mux[i] = NULL;
-                       break;
+       if (dlci) {
+               if (disc && dlci->state != DLCI_CLOSED) {
+                       gsm_dlci_begin_close(dlci);
+                       wait_event(gsm->event, dlci->state == DLCI_CLOSED);
                }
+               dlci->dead = true;
        }
-       spin_unlock(&gsm_mux_lock);
-       /* open failed before registering => nothing to do */
-       if (i == MAX_MUX)
-               return;
 
+       /* Finish outstanding timers, making sure they are done */
        del_timer_sync(&gsm->t2_timer);
-       /* Now we are sure T2 has stopped */
-       if (dlci)
-               dlci->dead = true;
 
-       /* Free up any link layer users */
-       mutex_lock(&gsm->mutex);
-       for (i = 0; i < NUM_DLCI; i++)
+       /* Free up any link layer users and finally the control channel */
+       for (i = NUM_DLCI - 1; i >= 0; i--)
                if (gsm->dlci[i])
                        gsm_dlci_release(gsm->dlci[i]);
        mutex_unlock(&gsm->mutex);
        /* Now wipe the queues */
+       tty_ldisc_flush(gsm->tty);
        list_for_each_entry_safe(txq, ntxq, &gsm->tx_list, list)
                kfree(txq);
        INIT_LIST_HEAD(&gsm->tx_list);
@@ -2191,7 +2183,6 @@ static void gsm_cleanup_mux(struct gsm_mux *gsm)
 static int gsm_activate_mux(struct gsm_mux *gsm)
 {
        struct gsm_dlci *dlci;
-       int i = 0;
 
        timer_setup(&gsm->t2_timer, gsm_control_retransmit, 0);
        init_waitqueue_head(&gsm->event);
@@ -2203,18 +2194,6 @@ static int gsm_activate_mux(struct gsm_mux *gsm)
        else
                gsm->receive = gsm1_receive;
 
-       spin_lock(&gsm_mux_lock);
-       for (i = 0; i < MAX_MUX; i++) {
-               if (gsm_mux[i] == NULL) {
-                       gsm->num = i;
-                       gsm_mux[i] = gsm;
-                       break;
-               }
-       }
-       spin_unlock(&gsm_mux_lock);
-       if (i == MAX_MUX)
-               return -EBUSY;
-
        dlci = gsm_dlci_alloc(gsm, 0);
        if (dlci == NULL)
                return -ENOMEM;
@@ -2230,6 +2209,15 @@ static int gsm_activate_mux(struct gsm_mux *gsm)
  */
 static void gsm_free_mux(struct gsm_mux *gsm)
 {
+       int i;
+
+       for (i = 0; i < MAX_MUX; i++) {
+               if (gsm == gsm_mux[i]) {
+                       gsm_mux[i] = NULL;
+                       break;
+               }
+       }
+       mutex_destroy(&gsm->mutex);
        kfree(gsm->txframe);
        kfree(gsm->buf);
        kfree(gsm);
@@ -2249,12 +2237,20 @@ static void gsm_free_muxr(struct kref *ref)
 
 static inline void mux_get(struct gsm_mux *gsm)
 {
+       unsigned long flags;
+
+       spin_lock_irqsave(&gsm_mux_lock, flags);
        kref_get(&gsm->ref);
+       spin_unlock_irqrestore(&gsm_mux_lock, flags);
 }
 
 static inline void mux_put(struct gsm_mux *gsm)
 {
+       unsigned long flags;
+
+       spin_lock_irqsave(&gsm_mux_lock, flags);
        kref_put(&gsm->ref, gsm_free_muxr);
+       spin_unlock_irqrestore(&gsm_mux_lock, flags);
 }
 
 static inline unsigned int mux_num_to_base(struct gsm_mux *gsm)
@@ -2275,6 +2271,7 @@ static inline unsigned int mux_line_to_num(unsigned int line)
 
 static struct gsm_mux *gsm_alloc_mux(void)
 {
+       int i;
        struct gsm_mux *gsm = kzalloc(sizeof(struct gsm_mux), GFP_KERNEL);
        if (gsm == NULL)
                return NULL;
@@ -2283,7 +2280,7 @@ static struct gsm_mux *gsm_alloc_mux(void)
                kfree(gsm);
                return NULL;
        }
-       gsm->txframe = kmalloc(2 * MAX_MRU + 2, GFP_KERNEL);
+       gsm->txframe = kmalloc(2 * (MAX_MTU + PROT_OVERHEAD - 1), GFP_KERNEL);
        if (gsm->txframe == NULL) {
                kfree(gsm->buf);
                kfree(gsm);
@@ -2304,6 +2301,26 @@ static struct gsm_mux *gsm_alloc_mux(void)
        gsm->mtu = 64;
        gsm->dead = true;       /* Avoid early tty opens */
 
+       /* Store the instance to the mux array or abort if no space is
+        * available.
+        */
+       spin_lock(&gsm_mux_lock);
+       for (i = 0; i < MAX_MUX; i++) {
+               if (!gsm_mux[i]) {
+                       gsm_mux[i] = gsm;
+                       gsm->num = i;
+                       break;
+               }
+       }
+       spin_unlock(&gsm_mux_lock);
+       if (i == MAX_MUX) {
+               mutex_destroy(&gsm->mutex);
+               kfree(gsm->txframe);
+               kfree(gsm->buf);
+               kfree(gsm);
+               return NULL;
+       }
+
        return gsm;
 }
 
@@ -2339,7 +2356,7 @@ static int gsm_config(struct gsm_mux *gsm, struct gsm_config *c)
        /* Check the MRU/MTU range looks sane */
        if (c->mru > MAX_MRU || c->mtu > MAX_MTU || c->mru < 8 || c->mtu < 8)
                return -EINVAL;
-       if (c->n2 < 3)
+       if (c->n2 > 255)
                return -EINVAL;
        if (c->encapsulation > 1)       /* Basic, advanced, no I */
                return -EINVAL;
@@ -2370,19 +2387,11 @@ static int gsm_config(struct gsm_mux *gsm, struct gsm_config *c)
 
        /*
         * Close down what is needed, restart and initiate the new
-        * configuration
+        * configuration. On the first time there is no DLCI[0]
+        * and closing or cleaning up is not necessary.
         */
-
-       if (gsm->initiator && (need_close || need_restart)) {
-               int ret;
-
-               ret = gsm_disconnect(gsm);
-
-               if (ret)
-                       return ret;
-       }
-       if (need_restart)
-               gsm_cleanup_mux(gsm);
+       if (need_close || need_restart)
+               gsm_cleanup_mux(gsm, true);
 
        gsm->initiator = c->initiator;
        gsm->mru = c->mru;
@@ -2450,25 +2459,26 @@ static int gsmld_attach_gsm(struct tty_struct *tty, struct gsm_mux *gsm)
        int ret, i;
 
        gsm->tty = tty_kref_get(tty);
+       /* Turn off tty XON/XOFF handling to handle it explicitly. */
+       gsm->old_c_iflag = tty->termios.c_iflag;
+       tty->termios.c_iflag &= (IXON | IXOFF);
        ret =  gsm_activate_mux(gsm);
        if (ret != 0)
                tty_kref_put(gsm->tty);
        else {
                /* Don't register device 0 - this is the control channel and not
                   a usable tty interface */
-               if (gsm->initiator) {
-                       base = mux_num_to_base(gsm); /* Base for this MUX */
-                       for (i = 1; i < NUM_DLCI; i++) {
-                               struct device *dev;
+               base = mux_num_to_base(gsm); /* Base for this MUX */
+               for (i = 1; i < NUM_DLCI; i++) {
+                       struct device *dev;
 
-                               dev = tty_register_device(gsm_tty_driver,
+                       dev = tty_register_device(gsm_tty_driver,
                                                        base + i, NULL);
-                               if (IS_ERR(dev)) {
-                                       for (i--; i >= 1; i--)
-                                               tty_unregister_device(gsm_tty_driver,
-                                                                       base + i);
-                                       return PTR_ERR(dev);
-                               }
+                       if (IS_ERR(dev)) {
+                               for (i--; i >= 1; i--)
+                                       tty_unregister_device(gsm_tty_driver,
+                                                               base + i);
+                               return PTR_ERR(dev);
                        }
                }
        }
@@ -2490,11 +2500,10 @@ static void gsmld_detach_gsm(struct tty_struct *tty, struct gsm_mux *gsm)
        int i;
 
        WARN_ON(tty != gsm->tty);
-       if (gsm->initiator) {
-               for (i = 1; i < NUM_DLCI; i++)
-                       tty_unregister_device(gsm_tty_driver, base + i);
-       }
-       gsm_cleanup_mux(gsm);
+       for (i = 1; i < NUM_DLCI; i++)
+               tty_unregister_device(gsm_tty_driver, base + i);
+       /* Restore tty XON/XOFF handling. */
+       gsm->tty->termios.c_iflag = gsm->old_c_iflag;
        tty_kref_put(gsm->tty);
        gsm->tty = NULL;
 }
@@ -2559,6 +2568,12 @@ static void gsmld_close(struct tty_struct *tty)
 {
        struct gsm_mux *gsm = tty->disc_data;
 
+       /* The ldisc locks and closes the port before calling our close. This
+        * means we have no way to do a proper disconnect. We will not bother
+        * to do one.
+        */
+       gsm_cleanup_mux(gsm, false);
+
        gsmld_detach_gsm(tty, gsm);
 
        gsmld_flush_buffer(tty);
@@ -2597,7 +2612,7 @@ static int gsmld_open(struct tty_struct *tty)
 
        ret = gsmld_attach_gsm(tty, gsm);
        if (ret != 0) {
-               gsm_cleanup_mux(gsm);
+               gsm_cleanup_mux(gsm, false);
                mux_put(gsm);
        }
        return ret;
@@ -2954,26 +2969,78 @@ static struct tty_ldisc_ops tty_ldisc_packet = {
 
 #define TX_SIZE                512
 
-static int gsmtty_modem_update(struct gsm_dlci *dlci, u8 brk)
+/**
+ *     gsm_modem_upd_via_data  -       send modem bits via convergence layer
+ *     @dlci: channel
+ *     @brk: break signal
+ *
+ *     Send an empty frame to signal mobile state changes and to transmit the
+ *     break signal for adaption 2.
+ */
+
+static void gsm_modem_upd_via_data(struct gsm_dlci *dlci, u8 brk)
+{
+       struct gsm_mux *gsm = dlci->gsm;
+       unsigned long flags;
+
+       if (dlci->state != DLCI_OPEN || dlci->adaption != 2)
+               return;
+
+       spin_lock_irqsave(&gsm->tx_lock, flags);
+       gsm_dlci_modem_output(gsm, dlci, brk);
+       spin_unlock_irqrestore(&gsm->tx_lock, flags);
+}
+
+/**
+ *     gsm_modem_upd_via_msc   -       send modem bits via control frame
+ *     @dlci: channel
+ *     @brk: break signal
+ */
+
+static int gsm_modem_upd_via_msc(struct gsm_dlci *dlci, u8 brk)
 {
-       u8 modembits[5];
+       u8 modembits[3];
        struct gsm_control *ctrl;
        int len = 2;
 
-       if (brk)
-               len++;
+       if (dlci->gsm->encoding != 0)
+               return 0;
 
-       modembits[0] = len << 1 | EA;           /* Data bytes */
-       modembits[1] = dlci->addr << 2 | 3;     /* DLCI, EA, 1 */
-       modembits[2] = gsm_encode_modem(dlci) << 1 | EA;
-       if (brk)
-               modembits[3] = brk << 4 | 2 | EA;       /* Valid, EA */
-       ctrl = gsm_control_send(dlci->gsm, CMD_MSC, modembits, len + 1);
+       modembits[0] = (dlci->addr << 2) | 2 | EA;  /* DLCI, Valid, EA */
+       if (!brk) {
+               modembits[1] = (gsm_encode_modem(dlci) << 1) | EA;
+       } else {
+               modembits[1] = gsm_encode_modem(dlci) << 1;
+               modembits[2] = (brk << 4) | 2 | EA; /* Length, Break, EA */
+               len++;
+       }
+       ctrl = gsm_control_send(dlci->gsm, CMD_MSC, modembits, len);
        if (ctrl == NULL)
                return -ENOMEM;
        return gsm_control_wait(dlci->gsm, ctrl);
 }
 
+/**
+ *     gsm_modem_update        -       send modem status line state
+ *     @dlci: channel
+ *     @brk: break signal
+ */
+
+static int gsm_modem_update(struct gsm_dlci *dlci, u8 brk)
+{
+       if (dlci->adaption == 2) {
+               /* Send convergence layer type 2 empty data frame. */
+               gsm_modem_upd_via_data(dlci, brk);
+               return 0;
+       } else if (dlci->gsm->encoding == 0) {
+               /* Send as MSC control message. */
+               return gsm_modem_upd_via_msc(dlci, brk);
+       }
+
+       /* Modem status lines are not supported. */
+       return -EPROTONOSUPPORT;
+}
+
 static int gsm_carrier_raised(struct tty_port *port)
 {
        struct gsm_dlci *dlci = container_of(port, struct gsm_dlci, port);
@@ -3006,7 +3073,7 @@ static void gsm_dtr_rts(struct tty_port *port, int onoff)
                modem_tx &= ~(TIOCM_DTR | TIOCM_RTS);
        if (modem_tx != dlci->modem_tx) {
                dlci->modem_tx = modem_tx;
-               gsmtty_modem_update(dlci, 0);
+               gsm_modem_update(dlci, 0);
        }
 }
 
@@ -3155,13 +3222,17 @@ static unsigned int gsmtty_chars_in_buffer(struct tty_struct *tty)
 static void gsmtty_flush_buffer(struct tty_struct *tty)
 {
        struct gsm_dlci *dlci = tty->driver_data;
+       unsigned long flags;
+
        if (dlci->state == DLCI_CLOSED)
                return;
        /* Caution needed: If we implement reliable transport classes
           then the data being transmitted can't simply be junked once
           it has first hit the stack. Until then we can just blow it
           away */
+       spin_lock_irqsave(&dlci->lock, flags);
        kfifo_reset(&dlci->fifo);
+       spin_unlock_irqrestore(&dlci->lock, flags);
        /* Need to unhook this DLCI from the transmit queue logic */
 }
 
@@ -3193,7 +3264,7 @@ static int gsmtty_tiocmset(struct tty_struct *tty,
 
        if (modem_tx != dlci->modem_tx) {
                dlci->modem_tx = modem_tx;
-               return gsmtty_modem_update(dlci, 0);
+               return gsm_modem_update(dlci, 0);
        }
        return 0;
 }
@@ -3254,7 +3325,7 @@ static void gsmtty_throttle(struct tty_struct *tty)
                dlci->modem_tx &= ~TIOCM_RTS;
        dlci->throttled = true;
        /* Send an MSC with RTS cleared */
-       gsmtty_modem_update(dlci, 0);
+       gsm_modem_update(dlci, 0);
 }
 
 static void gsmtty_unthrottle(struct tty_struct *tty)
@@ -3266,7 +3337,7 @@ static void gsmtty_unthrottle(struct tty_struct *tty)
                dlci->modem_tx |= TIOCM_RTS;
        dlci->throttled = false;
        /* Send an MSC with RTS set */
-       gsmtty_modem_update(dlci, 0);
+       gsm_modem_update(dlci, 0);
 }
 
 static int gsmtty_break_ctl(struct tty_struct *tty, int state)
@@ -3284,7 +3355,7 @@ static int gsmtty_break_ctl(struct tty_struct *tty, int state)
                if (encode > 0x0F)
                        encode = 0x0F;  /* Best effort */
        }
-       return gsmtty_modem_update(dlci, encode);
+       return gsm_modem_update(dlci, encode);
 }
 
 static void gsmtty_cleanup(struct tty_struct *tty)