summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorIvaylo Dimitrov <ivo.g.dimitrov.75@gmail.com>2024-12-28 17:00:59 +0200
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>2025-01-07 11:50:07 +0100
commite2e4025bc5461258a48cb331107c5b55b3c787d3 (patch)
tree426ea28d2aaeced67124c66bed5cf1f8f2fd45f4
parentd78a89990986a4d7d34313d3f58f8596b8ae1222 (diff)
downloadlinux-e2e4025bc5461258a48cb331107c5b55b3c787d3.tar.gz
linux-e2e4025bc5461258a48cb331107c5b55b3c787d3.tar.bz2
linux-e2e4025bc5461258a48cb331107c5b55b3c787d3.zip
tty: n_gsm: wait until channel 0 is ready
Currently code does not wait for channel 0 open sequence to complete before pushing data to the other channels. Also, if userland opens tty, it will receive EL2NSYNC. Both issues result in hard to predict initialization sequence and possible userland failures. Fix that by waiting channel 0 open sequence to complete before attempting opening of the other channels. Also, if tty open() is attempted while channel 0 is opening, wait until sequence is complete before returning to userland. Signed-off-by: Ivaylo Dimitrov <ivo.g.dimitrov.75@gmail.com> Signed-off-by: Tony Lindgren <tony@atomide.com> Link: https://lore.kernel.org/r/20241228150100.100354-2-ivo.g.dimitrov.75@gmail.com Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
-rw-r--r--drivers/tty/n_gsm.c31
1 files changed, 23 insertions, 8 deletions
diff --git a/drivers/tty/n_gsm.c b/drivers/tty/n_gsm.c
index 252849910588..b92480051e3d 100644
--- a/drivers/tty/n_gsm.c
+++ b/drivers/tty/n_gsm.c
@@ -2244,13 +2244,16 @@ static void gsm_dlci_t1(struct timer_list *t)
break;
case DLCI_OPENING:
if (dlci->retries) {
- dlci->retries--;
- gsm_command(dlci->gsm, dlci->addr, SABM|PF);
+ if (!dlci->addr || !gsm->dlci[0] ||
+ gsm->dlci[0]->state != DLCI_OPENING) {
+ dlci->retries--;
+ gsm_command(dlci->gsm, dlci->addr, SABM|PF);
+ }
+
mod_timer(&dlci->t1, jiffies + gsm->t1 * HZ / 100);
} else if (!dlci->addr && gsm->control == (DM | PF)) {
if (debug & DBG_ERRORS)
- pr_info("DLCI %d opening in ADM mode.\n",
- dlci->addr);
+ pr_info("DLCI 0 opening in ADM mode.\n");
dlci->mode = DLCI_MODE_ADM;
gsm_dlci_open(dlci);
} else {
@@ -2308,7 +2311,9 @@ static void gsm_dlci_begin_open(struct gsm_dlci *dlci)
dlci->retries = gsm->n2;
if (!need_pn) {
dlci->state = DLCI_OPENING;
- gsm_command(gsm, dlci->addr, SABM|PF);
+ if (!dlci->addr || !gsm->dlci[0] ||
+ gsm->dlci[0]->state != DLCI_OPENING)
+ gsm_command(gsm, dlci->addr, SABM|PF);
} else {
/* Configure DLCI before setup */
dlci->state = DLCI_CONFIGURE;
@@ -4251,7 +4256,7 @@ static const struct tty_port_operations gsm_port_ops = {
static int gsmtty_install(struct tty_driver *driver, struct tty_struct *tty)
{
struct gsm_mux *gsm;
- struct gsm_dlci *dlci;
+ struct gsm_dlci *dlci, *dlci0;
unsigned int line = tty->index;
unsigned int mux = mux_line_to_num(line);
bool alloc = false;
@@ -4274,10 +4279,20 @@ static int gsmtty_install(struct tty_driver *driver, struct tty_struct *tty)
perspective as we don't have to worry about this
if DLCI0 is lost */
mutex_lock(&gsm->mutex);
- if (gsm->dlci[0] && gsm->dlci[0]->state != DLCI_OPEN) {
+
+ dlci0 = gsm->dlci[0];
+ if (dlci0 && dlci0->state != DLCI_OPEN) {
mutex_unlock(&gsm->mutex);
- return -EL2NSYNC;
+
+ if (dlci0->state == DLCI_OPENING)
+ wait_event(gsm->event, dlci0->state != DLCI_OPENING);
+
+ if (dlci0->state != DLCI_OPEN)
+ return -EL2NSYNC;
+
+ mutex_lock(&gsm->mutex);
}
+
dlci = gsm->dlci[line];
if (dlci == NULL) {
alloc = true;