tty-n_gsm-fix-frame-reception-handling.patch
tty-n_gsm-fix-malformed-counter-for-out-of-frame-data.patch
netfilter-nft_socket-only-do-sk-lookups-when-indev-is-available.patch
+tty-n_gsm-fix-insufficient-txframe-size.patch
+tty-n_gsm-fix-wrong-dlci-release-order.patch
+tty-n_gsm-fix-missing-explicit-ldisc-flush.patch
+tty-n_gsm-fix-wrong-command-retry-handling.patch
+tty-n_gsm-fix-wrong-command-frame-length-field-encoding.patch
+tty-n_gsm-fix-wrong-signal-octets-encoding-in-msc.patch
+tty-n_gsm-fix-missing-tty-wakeup-in-convergence-layer-type-2.patch
+tty-n_gsm-fix-reset-fifo-race-condition.patch
+tty-n_gsm-fix-incorrect-ua-handling.patch
+tty-n_gsm-fix-missing-update-of-modem-controls-after-dlci-open.patch
+tty-n_gsm-fix-broken-virtual-tty-handling.patch
+tty-n_gsm-fix-invalid-use-of-msc-in-advanced-option.patch
+tty-n_gsm-fix-software-flow-control-handling.patch
--- /dev/null
+From a8c5b8255f8a9acd58a4b15ff1c14cd6effd114b Mon Sep 17 00:00:00 2001
+From: Daniel Starke <daniel.starke@siemens.com>
+Date: Fri, 22 Apr 2022 00:10:23 -0700
+Subject: tty: n_gsm: fix broken virtual tty handling
+
+From: Daniel Starke <daniel.starke@siemens.com>
+
+commit a8c5b8255f8a9acd58a4b15ff1c14cd6effd114b upstream.
+
+Dynamic virtual tty registration was introduced to allow the user to handle
+these cases with uevent rules. The following commits relate to this:
+Commit 5b87686e3203 ("tty: n_gsm: Modify gsmtty driver register method when config requester")
+Commit 0b91b5332368 ("tty: n_gsm: Save dlci address open status when config requester")
+Commit 46292622ad73 ("tty: n_gsm: clean up indenting in gsm_queue()")
+
+However, the following behavior can be seen with this implementation:
+- n_gsm ldisc is activated via ioctl
+- all configuration parameters are set to their default value (initiator=0)
+- the mux gets activated and attached and gsmtty0 is being registered in
+ in gsm_dlci_open() after DLCI 0 was established (DLCI 0 is the control
+ channel)
+- the user configures n_gsm via ioctl GSMIOC_SETCONF as initiator
+- this re-attaches the n_gsm mux
+- no new gsmtty devices are registered in gsmld_attach_gsm() because the
+ mux is already active
+- the initiator side registered only the control channel as gsmtty0
+ (which should never happen) and no user channel tty
+
+The commits above make it impossible to operate the initiator side as no
+user channel tty is or will be available.
+On the other hand, this behavior will make it also impossible to allow DLCI
+parameter negotiation on responder side in the future. The responder side
+first needs to provide a device for the application before the application
+can set its parameters of the associated DLCI via ioctl.
+Note that the user application is still able to detect a link establishment
+without relaying to uevent by waiting for DTR open on responder side. This
+is the same behavior as on a physical serial interface. And on initiator
+side a tty hangup can be detected if a link establishment request failed.
+
+Revert the commits above completely to always register all user channels
+and no control channel after mux attachment. No other changes are made.
+
+Fixes: 5b87686e3203 ("tty: n_gsm: Modify gsmtty driver register method when config requester")
+Cc: stable@vger.kernel.org
+Signed-off-by: Daniel Starke <daniel.starke@siemens.com>
+Link: https://lore.kernel.org/r/20220422071025.5490-1-daniel.starke@siemens.com
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/tty/n_gsm.c | 87 ++++++++--------------------------------------------
+ 1 file changed, 15 insertions(+), 72 deletions(-)
+
+--- a/drivers/tty/n_gsm.c
++++ b/drivers/tty/n_gsm.c
+@@ -272,10 +272,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
+@@ -1185,7 +1181,6 @@ static void gsm_control_rls(struct gsm_m
+ }
+
+ 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
+@@ -1204,28 +1199,15 @@ static void gsm_control_message(struct g
+ {
+ 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;
+@@ -1459,8 +1441,6 @@ static void gsm_dlci_close(struct gsm_dl
+ 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 */
+@@ -1482,8 +1462,6 @@ static void gsm_dlci_open(struct gsm_dlc
+ 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)
+ gsmtty_modem_update(dlci, 0);
+@@ -1794,7 +1772,6 @@ static void gsm_queue(struct gsm_mux *gs
+ struct gsm_dlci *dlci;
+ u8 cr;
+ int address;
+- int i, j, k, address_tmp;
+
+ if (gsm->fcs != GOOD_FCS) {
+ gsm->bad_fcs++;
+@@ -1826,11 +1803,6 @@ static void gsm_queue(struct gsm_mux *gs
+ 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:
+@@ -1841,33 +1813,8 @@ static void gsm_queue(struct gsm_mux *gs
+ 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|PF:
+ if (cr == 0 || dlci == NULL)
+@@ -2451,19 +2398,17 @@ static int gsmld_attach_gsm(struct tty_s
+ 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);
+ }
+ }
+ }
+@@ -2485,10 +2430,8 @@ static void gsmld_detach_gsm(struct tty_
+ 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);
+- }
++ for (i = 1; i < NUM_DLCI; i++)
++ tty_unregister_device(gsm_tty_driver, base + i);
+ tty_kref_put(gsm->tty);
+ gsm->tty = NULL;
+ }
--- /dev/null
+From ff9166c623704337bd6fe66fce2838d9768a6634 Mon Sep 17 00:00:00 2001
+From: Daniel Starke <daniel.starke@siemens.com>
+Date: Thu, 14 Apr 2022 02:42:25 -0700
+Subject: tty: n_gsm: fix incorrect UA handling
+
+From: Daniel Starke <daniel.starke@siemens.com>
+
+commit ff9166c623704337bd6fe66fce2838d9768a6634 upstream.
+
+n_gsm is based on the 3GPP 07.010 and its newer version is the 3GPP 27.010.
+See https://portal.3gpp.org/desktopmodules/Specifications/SpecificationDetails.aspx?specificationId=1516
+The changes from 07.010 to 27.010 are non-functional. Therefore, I refer to
+the newer 27.010 here. Chapter 5.4.4.2 states that any received unnumbered
+acknowledgment (UA) with its poll/final (PF) bit set to 0 shall be
+discarded. Currently, all UA frame are handled in the same way regardless
+of the PF bit. This does not comply with the standard.
+Remove the UA case in gsm_queue() to process only UA frames with PF bit set
+to 1 to abide the standard.
+
+Fixes: e1eaea46bb40 ("tty: n_gsm line discipline")
+Cc: stable@vger.kernel.org
+Signed-off-by: Daniel Starke <daniel.starke@siemens.com>
+Link: https://lore.kernel.org/r/20220414094225.4527-20-daniel.starke@siemens.com
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/tty/n_gsm.c | 1 -
+ 1 file changed, 1 deletion(-)
+
+--- a/drivers/tty/n_gsm.c
++++ b/drivers/tty/n_gsm.c
+@@ -1865,7 +1865,6 @@ static void gsm_queue(struct gsm_mux *gs
+ }
+ }
+ break;
+- case UA:
+ case UA|PF:
+ if (cr == 0 || dlci == NULL)
+ break;
--- /dev/null
+From 535bf600de75a859698892ee873521a48d289ec1 Mon Sep 17 00:00:00 2001
+From: Daniel Starke <daniel.starke@siemens.com>
+Date: Thu, 14 Apr 2022 02:42:13 -0700
+Subject: tty: n_gsm: fix insufficient txframe size
+
+From: Daniel Starke <daniel.starke@siemens.com>
+
+commit 535bf600de75a859698892ee873521a48d289ec1 upstream.
+
+n_gsm is based on the 3GPP 07.010 and its newer version is the 3GPP 27.010.
+See https://portal.3gpp.org/desktopmodules/Specifications/SpecificationDetails.aspx?specificationId=1516
+The changes from 07.010 to 27.010 are non-functional. Therefore, I refer to
+the newer 27.010 here. Chapter 5.7.2 states that the maximum frame size
+(N1) refers to the length of the information field (i.e. user payload).
+However, 'txframe' stores the whole frame including frame header, checksum
+and start/end flags. We also need to consider the byte stuffing overhead.
+Define constant for the protocol overhead and adjust the 'txframe' size
+calculation accordingly to reserve enough space for a complete mux frame
+including byte stuffing for advanced option mode. Note that no byte
+stuffing is applied to the start and end flag.
+Also use MAX_MTU instead of MAX_MRU as this buffer is used for data
+transmission.
+
+Fixes: e1eaea46bb40 ("tty: n_gsm line discipline")
+Cc: stable@vger.kernel.org
+Signed-off-by: Daniel Starke <daniel.starke@siemens.com>
+Link: https://lore.kernel.org/r/20220414094225.4527-8-daniel.starke@siemens.com
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/tty/n_gsm.c | 4 +++-
+ 1 file changed, 3 insertions(+), 1 deletion(-)
+
+--- a/drivers/tty/n_gsm.c
++++ b/drivers/tty/n_gsm.c
+@@ -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)
+
+ /*
+@@ -2264,7 +2266,7 @@ static struct gsm_mux *gsm_alloc_mux(voi
+ 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);
--- /dev/null
+From c19ffe00fed6bb423d81406d2a7e5793074c7d83 Mon Sep 17 00:00:00 2001
+From: Daniel Starke <daniel.starke@siemens.com>
+Date: Fri, 22 Apr 2022 00:10:24 -0700
+Subject: tty: n_gsm: fix invalid use of MSC in advanced option
+
+From: Daniel Starke <daniel.starke@siemens.com>
+
+commit c19ffe00fed6bb423d81406d2a7e5793074c7d83 upstream.
+
+n_gsm is based on the 3GPP 07.010 and its newer version is the 3GPP 27.010.
+See https://portal.3gpp.org/desktopmodules/Specifications/SpecificationDetails.aspx?specificationId=1516
+The changes from 07.010 to 27.010 are non-functional. Therefore, I refer to
+the newer 27.010 here. Chapter 5.4.6.3.7 states that the Modem Status
+Command (MSC) shall only be used if the basic option was chosen.
+The current implementation uses MSC frames even if advanced option was
+chosen to inform the peer about modem line state updates. A standard
+conform peer may choose to discard these frames in advanced option mode.
+Furthermore, gsmtty_modem_update() is not part of the 'tty_operations'
+functions despite its name.
+Rename gsmtty_modem_update() to gsm_modem_update() to clarify this. Split
+its function into gsm_modem_upd_via_data() and gsm_modem_upd_via_msc()
+depending on the encoding and adaption. Introduce gsm_dlci_modem_output()
+as adaption of gsm_dlci_data_output() to encode and queue empty frames in
+advanced option mode. Use it in gsm_modem_upd_via_data().
+gsm_modem_upd_via_msc() is based on the initial gsmtty_modem_update()
+function which used only MSC frames to update modem states.
+
+Fixes: e1eaea46bb40 ("tty: n_gsm line discipline")
+Cc: stable@vger.kernel.org
+Signed-off-by: Daniel Starke <daniel.starke@siemens.com>
+Link: https://lore.kernel.org/r/20220422071025.5490-2-daniel.starke@siemens.com
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/tty/n_gsm.c | 125 ++++++++++++++++++++++++++++++++++++++++++++++++----
+ 1 file changed, 117 insertions(+), 8 deletions(-)
+
+--- a/drivers/tty/n_gsm.c
++++ b/drivers/tty/n_gsm.c
+@@ -366,7 +366,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 gsmtty_modem_update(struct gsm_dlci *dlci, u8 brk);
++static int gsm_modem_update(struct gsm_dlci *dlci, u8 brk);
+
+ /**
+ * gsm_fcs_add - update FCS
+@@ -915,6 +915,63 @@ static int gsm_dlci_data_output_framed(s
+ }
+
+ /**
++ * 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;
++
++ /* for modem bits without break data */
++ if (dlci->adaption == 1) {
++ size = 0;
++ } else if (dlci->adaption == 2) {
++ size = 1;
++ if (brk > 0)
++ size++;
++ } else {
++ pr_err("%s: unsupported adaption %d\n", __func__,
++ dlci->adaption);
++ }
++
++ 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
+ *
+@@ -1464,7 +1521,7 @@ static void gsm_dlci_open(struct gsm_dlc
+ pr_debug("DLCI %d goes open.\n", dlci->addr);
+ /* Send current modem state */
+ if (dlci->addr)
+- gsmtty_modem_update(dlci, 0);
++ gsm_modem_update(dlci, 0);
+ wake_up(&dlci->gsm->event);
+ }
+
+@@ -2897,12 +2954,43 @@ static struct tty_ldisc_ops tty_ldisc_pa
+
+ #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[3];
+ struct gsm_control *ctrl;
+ int len = 2;
+
++ if (dlci->gsm->encoding != 0)
++ return 0;
++
+ modembits[0] = (dlci->addr << 2) | 2 | EA; /* DLCI, Valid, EA */
+ if (!brk) {
+ modembits[1] = (gsm_encode_modem(dlci) << 1) | EA;
+@@ -2917,6 +3005,27 @@ static int gsmtty_modem_update(struct gs
+ 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);
+@@ -2949,7 +3058,7 @@ static void gsm_dtr_rts(struct tty_port
+ 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);
+ }
+ }
+
+@@ -3140,7 +3249,7 @@ static int gsmtty_tiocmset(struct tty_st
+
+ 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;
+ }
+@@ -3201,7 +3310,7 @@ static void gsmtty_throttle(struct tty_s
+ 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)
+@@ -3213,7 +3322,7 @@ static void gsmtty_unthrottle(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)
+@@ -3231,7 +3340,7 @@ static int gsmtty_break_ctl(struct tty_s
+ 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)
--- /dev/null
+From 17eac652028501df7ea296b1d9b9c134db262b7d Mon Sep 17 00:00:00 2001
+From: Daniel Starke <daniel.starke@siemens.com>
+Date: Thu, 14 Apr 2022 02:42:15 -0700
+Subject: tty: n_gsm: fix missing explicit ldisc flush
+
+From: Daniel Starke <daniel.starke@siemens.com>
+
+commit 17eac652028501df7ea296b1d9b9c134db262b7d upstream.
+
+In gsm_cleanup_mux() the muxer is closed down and all queues are removed.
+However, removing the queues is done without explicit control of the
+underlying buffers. Flush those before freeing up our queues to ensure
+that all outgoing queues are cleared consistently. Otherwise, a new mux
+connection establishment attempt may time out while the underlying tty is
+still busy sending out the remaining data from the previous connection.
+
+Fixes: e1eaea46bb40 ("tty: n_gsm line discipline")
+Cc: stable@vger.kernel.org
+Signed-off-by: Daniel Starke <daniel.starke@siemens.com>
+Link: https://lore.kernel.org/r/20220414094225.4527-10-daniel.starke@siemens.com
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/tty/n_gsm.c | 1 +
+ 1 file changed, 1 insertion(+)
+
+--- a/drivers/tty/n_gsm.c
++++ b/drivers/tty/n_gsm.c
+@@ -2152,6 +2152,7 @@ static void gsm_cleanup_mux(struct gsm_m
+ 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);
--- /dev/null
+From 1adf6fee58ca25fb6720b8d34c919dcf5425cc9c Mon Sep 17 00:00:00 2001
+From: Daniel Starke <daniel.starke@siemens.com>
+Date: Thu, 14 Apr 2022 02:42:19 -0700
+Subject: tty: n_gsm: fix missing tty wakeup in convergence layer type 2
+
+From: Daniel Starke <daniel.starke@siemens.com>
+
+commit 1adf6fee58ca25fb6720b8d34c919dcf5425cc9c upstream.
+
+gsm_control_modem() informs the virtual tty that more data can be written
+after receiving a control signal octet via modem status command (MSC).
+However, gsm_dlci_data() fails to do the same after receiving a control
+signal octet from the convergence layer type 2 header.
+Add tty_wakeup() in gsm_dlci_data() for convergence layer type 2 to fix
+this.
+
+Fixes: e1eaea46bb40 ("tty: n_gsm line discipline")
+Cc: stable@vger.kernel.org
+Signed-off-by: Daniel Starke <daniel.starke@siemens.com>
+Link: https://lore.kernel.org/r/20220414094225.4527-14-daniel.starke@siemens.com
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/tty/n_gsm.c | 1 +
+ 1 file changed, 1 insertion(+)
+
+--- a/drivers/tty/n_gsm.c
++++ b/drivers/tty/n_gsm.c
+@@ -1615,6 +1615,7 @@ static void gsm_dlci_data(struct gsm_dlc
+ tty = tty_port_tty_get(port);
+ if (tty) {
+ gsm_process_modem(tty, dlci, modem, slen);
++ tty_wakeup(tty);
+ tty_kref_put(tty);
+ }
+ fallthrough;
--- /dev/null
+From 48473802506d2d6151f59e0e764932b33b53cb3b Mon Sep 17 00:00:00 2001
+From: Daniel Starke <daniel.starke@siemens.com>
+Date: Wed, 20 Apr 2022 03:13:44 -0700
+Subject: tty: n_gsm: fix missing update of modem controls after DLCI open
+
+From: Daniel Starke <daniel.starke@siemens.com>
+
+commit 48473802506d2d6151f59e0e764932b33b53cb3b upstream.
+
+Currently the peer is not informed about the initial state of the modem
+control lines after a new DLCI has been opened.
+Fix this by sending the initial modem control line states after DLCI open.
+
+Fixes: e1eaea46bb40 ("tty: n_gsm line discipline")
+Cc: stable@vger.kernel.org
+Signed-off-by: Daniel Starke <daniel.starke@siemens.com>
+Link: https://lore.kernel.org/r/20220420101346.3315-1-daniel.starke@siemens.com
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/tty/n_gsm.c | 4 ++++
+ 1 file changed, 4 insertions(+)
+
+--- a/drivers/tty/n_gsm.c
++++ b/drivers/tty/n_gsm.c
+@@ -370,6 +370,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 gsmtty_modem_update(struct gsm_dlci *dlci, u8 brk);
+
+ /**
+ * gsm_fcs_add - update FCS
+@@ -1483,6 +1484,9 @@ static void gsm_dlci_open(struct gsm_dlc
+ 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)
++ gsmtty_modem_update(dlci, 0);
+ wake_up(&dlci->gsm->event);
+ }
+
--- /dev/null
+From 73029a4d7161f8b6c0934553145ef574d2d0c645 Mon Sep 17 00:00:00 2001
+From: Daniel Starke <daniel.starke@siemens.com>
+Date: Thu, 14 Apr 2022 02:42:22 -0700
+Subject: tty: n_gsm: fix reset fifo race condition
+
+From: Daniel Starke <daniel.starke@siemens.com>
+
+commit 73029a4d7161f8b6c0934553145ef574d2d0c645 upstream.
+
+gsmtty_write() and gsm_dlci_data_output() properly guard the fifo access.
+However, gsm_dlci_close() and gsmtty_flush_buffer() modifies the fifo but
+do not guard this.
+Add a guard here to prevent race conditions on parallel writes to the fifo.
+
+Fixes: e1eaea46bb40 ("tty: n_gsm line discipline")
+Cc: stable@vger.kernel.org
+Signed-off-by: Daniel Starke <daniel.starke@siemens.com>
+Link: https://lore.kernel.org/r/20220414094225.4527-17-daniel.starke@siemens.com
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/tty/n_gsm.c | 8 ++++++++
+ 1 file changed, 8 insertions(+)
+
+--- a/drivers/tty/n_gsm.c
++++ b/drivers/tty/n_gsm.c
+@@ -1442,13 +1442,17 @@ static int gsm_control_wait(struct gsm_m
+
+ 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);
+@@ -3148,13 +3152,17 @@ static unsigned int gsmtty_chars_in_buff
+ 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 */
+ }
+
--- /dev/null
+From f4f7d63287217ba25e5c80f5faae5e4f7118790e Mon Sep 17 00:00:00 2001
+From: Daniel Starke <daniel.starke@siemens.com>
+Date: Fri, 22 Apr 2022 00:10:25 -0700
+Subject: tty: n_gsm: fix software flow control handling
+
+From: Daniel Starke <daniel.starke@siemens.com>
+
+commit f4f7d63287217ba25e5c80f5faae5e4f7118790e upstream.
+
+n_gsm is based on the 3GPP 07.010 and its newer version is the 3GPP 27.010.
+See https://portal.3gpp.org/desktopmodules/Specifications/SpecificationDetails.aspx?specificationId=1516
+The changes from 07.010 to 27.010 are non-functional. Therefore, I refer to
+the newer 27.010 here. Chapter 5.4.8.1 states that XON/XOFF characters
+shall be used instead of Fcon/Fcoff command in advanced option mode to
+handle flow control. Chapter 5.4.8.2 describes how XON/XOFF characters
+shall be handled. Basic option mode only used Fcon/Fcoff commands and no
+XON/XOFF characters. These are treated as data bytes here.
+The current implementation uses the gsm_mux field 'constipated' to handle
+flow control from the remote peer and the gsm_dlci field 'constipated' to
+handle flow control from each DLCI. The later is unrelated to this patch.
+The gsm_mux field is correctly set for Fcon/Fcoff commands in
+gsm_control_message(). However, the same is not true for XON/XOFF
+characters in gsm1_receive().
+Disable software flow control handling in the tty to allow explicit
+handling by n_gsm.
+Add the missing handling in advanced option mode for gsm_mux in
+gsm1_receive() to comply with the standard.
+
+This patch depends on the following commit:
+Commit 8838b2af23ca ("tty: n_gsm: fix SW flow control encoding/handling")
+
+Fixes: e1eaea46bb40 ("tty: n_gsm line discipline")
+Cc: stable@vger.kernel.org
+Signed-off-by: Daniel Starke <daniel.starke@siemens.com>
+Link: https://lore.kernel.org/r/20220422071025.5490-3-daniel.starke@siemens.com
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/tty/n_gsm.c | 16 ++++++++++++++++
+ 1 file changed, 16 insertions(+)
+
+--- a/drivers/tty/n_gsm.c
++++ b/drivers/tty/n_gsm.c
+@@ -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;
+@@ -2022,6 +2023,16 @@ static void gsm0_receive(struct gsm_mux
+
+ 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 +2460,9 @@ static int gsmld_attach_gsm(struct tty_s
+ 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 +2503,8 @@ static void gsmld_detach_gsm(struct tty_
+ 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;
+ }
--- /dev/null
+From 398867f59f956985f4c324f173eff7b946e14bd8 Mon Sep 17 00:00:00 2001
+From: Daniel Starke <daniel.starke@siemens.com>
+Date: Thu, 14 Apr 2022 02:42:17 -0700
+Subject: tty: n_gsm: fix wrong command frame length field encoding
+
+From: Daniel Starke <daniel.starke@siemens.com>
+
+commit 398867f59f956985f4c324f173eff7b946e14bd8 upstream.
+
+n_gsm is based on the 3GPP 07.010 and its newer version is the 3GPP 27.010.
+See https://portal.3gpp.org/desktopmodules/Specifications/SpecificationDetails.aspx?specificationId=1516
+The changes from 07.010 to 27.010 are non-functional. Therefore, I refer to
+the newer 27.010 here. Chapter 5.4.6.1 states that each command frame shall
+be made up from type, length and value. Looking for example in chapter
+5.4.6.3.5 at the description for the encoding of a flow control on command
+it becomes obvious, that the type and length field is always present
+whereas the value may be zero bytes long. The current implementation omits
+the length field if the value is not present. This is wrong.
+Correct this by always sending the length in gsm_control_transmit().
+So far only the modem status command (MSC) has included a value and encoded
+its length directly. Therefore, also change gsmtty_modem_update().
+
+Fixes: e1eaea46bb40 ("tty: n_gsm line discipline")
+Cc: stable@vger.kernel.org
+Signed-off-by: Daniel Starke <daniel.starke@siemens.com>
+Link: https://lore.kernel.org/r/20220414094225.4527-12-daniel.starke@siemens.com
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/tty/n_gsm.c | 23 +++++++++++------------
+ 1 file changed, 11 insertions(+), 12 deletions(-)
+
+--- a/drivers/tty/n_gsm.c
++++ b/drivers/tty/n_gsm.c
+@@ -1327,11 +1327,12 @@ static void gsm_control_response(struct
+
+ 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);
+ }
+
+@@ -2957,19 +2958,17 @@ static struct tty_ldisc_ops tty_ldisc_pa
+
+ static int gsmtty_modem_update(struct gsm_dlci *dlci, u8 brk)
+ {
+- u8 modembits[5];
++ u8 modembits[3];
+ struct gsm_control *ctrl;
+ int len = 2;
+
+- if (brk)
++ modembits[0] = (dlci->addr << 2) | 2 | EA; /* DLCI, Valid, EA */
++ modembits[1] = (gsm_encode_modem(dlci) << 1) | EA;
++ if (brk) {
++ modembits[2] = (brk << 4) | 2 | EA; /* Length, Break, EA */
+ len++;
+-
+- 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);
++ }
++ ctrl = gsm_control_send(dlci->gsm, CMD_MSC, modembits, len);
+ if (ctrl == NULL)
+ return -ENOMEM;
+ return gsm_control_wait(dlci->gsm, ctrl);
--- /dev/null
+From d0bcdffcad5a22f202e3bf37190c0dd8c080ea92 Mon Sep 17 00:00:00 2001
+From: Daniel Starke <daniel.starke@siemens.com>
+Date: Thu, 14 Apr 2022 02:42:16 -0700
+Subject: tty: n_gsm: fix wrong command retry handling
+
+From: Daniel Starke <daniel.starke@siemens.com>
+
+commit d0bcdffcad5a22f202e3bf37190c0dd8c080ea92 upstream.
+
+n_gsm is based on the 3GPP 07.010 and its newer version is the 3GPP 27.010.
+See https://portal.3gpp.org/desktopmodules/Specifications/SpecificationDetails.aspx?specificationId=1516
+The changes from 07.010 to 27.010 are non-functional. Therefore, I refer to
+the newer 27.010 here. Chapter 5.7.3 states that the valid range for the
+maximum number of retransmissions (N2) is from 0 to 255 (both including).
+gsm_config() fails to limit this range correctly. Furthermore,
+gsm_control_retransmit() handles this number incorrectly by performing
+N2 - 1 retransmission attempts. Setting N2 to zero results in more than 255
+retransmission attempts.
+Fix the range check in gsm_config() and the value handling in
+gsm_control_send() and gsm_control_retransmit() to comply with 3GPP 27.010.
+
+Fixes: e1eaea46bb40 ("tty: n_gsm line discipline")
+Cc: stable@vger.kernel.org
+Signed-off-by: Daniel Starke <daniel.starke@siemens.com>
+Link: https://lore.kernel.org/r/20220414094225.4527-11-daniel.starke@siemens.com
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/tty/n_gsm.c | 6 +++---
+ 1 file changed, 3 insertions(+), 3 deletions(-)
+
+--- a/drivers/tty/n_gsm.c
++++ b/drivers/tty/n_gsm.c
+@@ -1354,7 +1354,6 @@ static void gsm_control_retransmit(struc
+ 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;
+@@ -1363,6 +1362,7 @@ static void gsm_control_retransmit(struc
+ wake_up(&gsm->event);
+ return;
+ }
++ gsm->cretries--;
+ gsm_control_transmit(gsm, ctrl);
+ mod_timer(&gsm->t2_timer, jiffies + gsm->t2 * HZ / 100);
+ }
+@@ -1403,7 +1403,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;
+
+@@ -2343,7 +2343,7 @@ static int gsm_config(struct gsm_mux *gs
+ /* 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;
--- /dev/null
+From deefc58bafb4841df7f0a0d85d89a1c819db9743 Mon Sep 17 00:00:00 2001
+From: Daniel Starke <daniel.starke@siemens.com>
+Date: Thu, 14 Apr 2022 02:42:14 -0700
+Subject: tty: n_gsm: fix wrong DLCI release order
+
+From: Daniel Starke <daniel.starke@siemens.com>
+
+commit deefc58bafb4841df7f0a0d85d89a1c819db9743 upstream.
+
+The current DLCI release order starts with the control channel followed by
+the user channels. Reverse this order to keep the control channel open
+until all user channels have been released.
+
+Fixes: e1eaea46bb40 ("tty: n_gsm line discipline")
+Cc: stable@vger.kernel.org
+Signed-off-by: Daniel Starke <daniel.starke@siemens.com>
+Link: https://lore.kernel.org/r/20220414094225.4527-9-daniel.starke@siemens.com
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/tty/n_gsm.c | 4 ++--
+ 1 file changed, 2 insertions(+), 2 deletions(-)
+
+--- a/drivers/tty/n_gsm.c
++++ b/drivers/tty/n_gsm.c
+@@ -2146,8 +2146,8 @@ static void gsm_cleanup_mux(struct gsm_m
+ /* Finish outstanding timers, making sure they are done */
+ del_timer_sync(&gsm->t2_timer);
+
+- /* Free up any link layer users */
+- 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);
--- /dev/null
+From 317f86af7f5d19f286ed2d181cbaef4a188c7f19 Mon Sep 17 00:00:00 2001
+From: Daniel Starke <daniel.starke@siemens.com>
+Date: Thu, 14 Apr 2022 02:42:18 -0700
+Subject: tty: n_gsm: fix wrong signal octets encoding in MSC
+
+From: Daniel Starke <daniel.starke@siemens.com>
+
+commit 317f86af7f5d19f286ed2d181cbaef4a188c7f19 upstream.
+
+n_gsm is based on the 3GPP 07.010 and its newer version is the 3GPP 27.010.
+See https://portal.3gpp.org/desktopmodules/Specifications/SpecificationDetails.aspx?specificationId=1516
+The changes from 07.010 to 27.010 are non-functional. Therefore, I refer to
+the newer 27.010 here. The value of the modem status command (MSC) frame
+contains an address field, control signal and optional break signal octet.
+The address field is encoded as described in chapter 5.2.1.2 with only one
+octet (may be extended to more in future versions of the standard). Whereas
+the control signal and break signal octet are always one byte each. This is
+strange at first glance as it makes the EA bit redundant. However, the same
+two octets are also encoded as header in convergence layer type 2 as
+described in chapter 5.5.2. No header length field is given and the only
+way to test if there is an optional break signal octet is via the EA flag
+which extends the control signal octet with a break signal octet. Now it
+becomes obvious how the EA bit for those two octets shall be encoded in the
+MSC frame. The current implementation treats the signal octet different for
+MSC frame and convergence layer type 2 header even though the standard
+describes it for both in the same way.
+Use the EA bit to encode the signal octets not only in the convergence
+layer type 2 header but also in the MSC frame in the same way with either
+1 or 2 bytes in case of an optional break signal. Adjust the receiving path
+accordingly in gsm_control_modem().
+
+Fixes: 3ac06b905655 ("tty: n_gsm: Fix for modems with brk in modem status control")
+Cc: stable@vger.kernel.org
+Signed-off-by: Daniel Starke <daniel.starke@siemens.com>
+Link: https://lore.kernel.org/r/20220414094225.4527-13-daniel.starke@siemens.com
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/tty/n_gsm.c | 18 +++++-------------
+ 1 file changed, 5 insertions(+), 13 deletions(-)
+
+--- a/drivers/tty/n_gsm.c
++++ b/drivers/tty/n_gsm.c
+@@ -1094,7 +1094,6 @@ static void gsm_control_modem(struct gsm
+ {
+ unsigned int addr = 0;
+ unsigned int modem = 0;
+- unsigned int brk = 0;
+ struct gsm_dlci *dlci;
+ int len = clen;
+ int slen;
+@@ -1124,17 +1123,8 @@ static void gsm_control_modem(struct gsm
+ 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);
+@@ -2963,8 +2953,10 @@ static int gsmtty_modem_update(struct gs
+ int len = 2;
+
+ modembits[0] = (dlci->addr << 2) | 2 | EA; /* DLCI, Valid, EA */
+- modembits[1] = (gsm_encode_modem(dlci) << 1) | EA;
+- if (brk) {
++ 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++;
+ }