Merge 5.18-rc5 into tty-next
[linux-2.6-microblaze.git] / drivers / tty / n_gsm.c
index 570f0b8..eaa12b4 100644 (file)
@@ -232,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;
@@ -747,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);
@@ -931,18 +932,21 @@ static int gsm_dlci_modem_output(struct gsm_mux *gsm, struct gsm_dlci *dlci,
 {
        u8 *dp = NULL;
        struct gsm_msg *msg;
-       int size;
+       int size = 0;
 
        /* for modem bits without break data */
-       if (dlci->adaption == 1) {
-               size = 0;
-       } else if (dlci->adaption == 2) {
-               size = 1;
+       switch (dlci->adaption) {
+       case 1: /* Unstructured */
+               break;
+       case 2: /* Unstructured with modem bits. */
+               size++;
                if (brk > 0)
                        size++;
-       } else {
+               break;
+       default:
                pr_err("%s: unsupported adaption %d\n", __func__,
                       dlci->adaption);
+               return -EINVAL;
        }
 
        msg = gsm_data_alloc(gsm, dlci->addr, size, gsm->ftype);
@@ -1901,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;
@@ -2022,6 +2022,16 @@ 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 */
                if (gsm->state == GSM_DATA) {
@@ -2449,6 +2459,9 @@ 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);
@@ -2489,6 +2502,8 @@ static void gsmld_detach_gsm(struct tty_struct *tty, struct gsm_mux *gsm)
        WARN_ON(tty != gsm->tty);
        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;
 }