tty: n_gsm: fix wrong modem processing in convergence layer type 2
authordaniel.starke@siemens.com <daniel.starke@siemens.com>
Fri, 18 Feb 2022 07:31:22 +0000 (23:31 -0800)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Wed, 2 Mar 2022 10:48:09 +0000 (11:48 +0100)
commit 687f9ad43c52501f46164758e908a5dd181a87fc upstream.

The function gsm_process_modem() exists to handle modem status bits of
incoming frames. This includes incoming MSC (modem status command) frames
and convergence layer type 2 data frames. The function, however, was only
designed to handle MSC frames as it expects the command length. Within
gsm_dlci_data() it is wrongly assumed that this is the same as the data
frame length. This is only true if the data frame contains only 1 byte of
payload.

This patch names the length parameter of gsm_process_modem() in a generic
manner to reflect its association. It also corrects all calls to the
function to handle the variable number of modem status octets correctly in
both cases.

Fixes: 7263287af93d ("tty: n_gsm: Fixed logic to decode break signal from modem status")
Cc: stable@vger.kernel.org
Signed-off-by: Daniel Starke <daniel.starke@siemens.com>
Link: https://lore.kernel.org/r/20220218073123.2121-6-daniel.starke@siemens.com
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
drivers/tty/n_gsm.c

index 34ab05e..a37cc34 100644 (file)
@@ -1009,25 +1009,25 @@ static void gsm_control_reply(struct gsm_mux *gsm, int cmd, const u8 *data,
  *     @tty: virtual tty bound to the DLCI
  *     @dlci: DLCI to affect
  *     @modem: modem bits (full EA)
- *     @clen: command length
+ *     @slen: number of signal octets
  *
  *     Used when a modem control message or line state inline in adaption
  *     layer 2 is processed. Sort out the local modem state and throttles
  */
 
 static void gsm_process_modem(struct tty_struct *tty, struct gsm_dlci *dlci,
-                                                       u32 modem, int clen)
+                                                       u32 modem, int slen)
 {
        int  mlines = 0;
        u8 brk = 0;
        int fc;
 
-       /* The modem status command can either contain one octet (v.24 signals)
-          or two octets (v.24 signals + break signals). The length field will
-          either be 2 or 3 respectively. This is specified in section
-          5.4.6.3.7 of the  27.010 mux spec. */
+       /* The modem status command can either contain one octet (V.24 signals)
+        * or two octets (V.24 signals + break signals). This is specified in
+        * section 5.4.6.3.7 of the 07.10 mux spec.
+        */
 
-       if (clen == 2)
+       if (slen == 1)
                modem = modem & 0x7f;
        else {
                brk = modem & 0x7f;
@@ -1084,6 +1084,7 @@ static void gsm_control_modem(struct gsm_mux *gsm, const u8 *data, int clen)
        unsigned int brk = 0;
        struct gsm_dlci *dlci;
        int len = clen;
+       int slen;
        const u8 *dp = data;
        struct tty_struct *tty;
 
@@ -1103,6 +1104,7 @@ static void gsm_control_modem(struct gsm_mux *gsm, const u8 *data, int clen)
                return;
        dlci = gsm->dlci[addr];
 
+       slen = len;
        while (gsm_read_ea(&modem, *dp++) == 0) {
                len--;
                if (len == 0)
@@ -1119,7 +1121,7 @@ static void gsm_control_modem(struct gsm_mux *gsm, const u8 *data, int clen)
                modem |= (brk & 0x7f);
        }
        tty = tty_port_tty_get(&dlci->port);
-       gsm_process_modem(tty, dlci, modem, clen);
+       gsm_process_modem(tty, dlci, modem, slen);
        if (tty) {
                tty_wakeup(tty);
                tty_kref_put(tty);
@@ -1567,6 +1569,7 @@ static void gsm_dlci_data(struct gsm_dlci *dlci, const u8 *data, int clen)
        struct tty_struct *tty;
        unsigned int modem = 0;
        int len = clen;
+       int slen = 0;
 
        if (debug & 16)
                pr_debug("%d bytes for tty\n", len);
@@ -1579,12 +1582,14 @@ static void gsm_dlci_data(struct gsm_dlci *dlci, const u8 *data, int clen)
        case 2:         /* Asynchronous serial with line state in each frame */
                while (gsm_read_ea(&modem, *data++) == 0) {
                        len--;
+                       slen++;
                        if (len == 0)
                                return;
                }
+               slen++;
                tty = tty_port_tty_get(port);
                if (tty) {
-                       gsm_process_modem(tty, dlci, modem, clen);
+                       gsm_process_modem(tty, dlci, modem, slen);
                        tty_kref_put(tty);
                }
                fallthrough;