@@ -318,13 +318,19 @@ struct gsm_mux {
struct gsm_control *pending_cmd;/* Our current pending command */
spinlock_t control_lock; /* Protects the pending command */
+ /* Keep-alive */
+ struct timer_list ka_timer; /* Keep-alive response timer */
+ u8 ka_num; /* Keep-alive match pattern */
+ int ka_retries; /* Keep-alive retry counter */
+
/* Configuration */
- int adaption; /* 1 or 2 supported */
- u8 ftype; /* UI or UIH */
- int t1, t2; /* Timers in 1/100th of a sec */
- unsigned int t3; /* Power wake-up timer in seconds. */
- int n2; /* Retry count */
- u8 k; /* Window size */
+ int adaption; /* 1 or 2 supported */
+ u8 ftype; /* UI or UIH */
+ int t1, t2; /* Timers in 1/100th of a sec */
+ unsigned int t3; /* Power wake-up timer in seconds. */
+ int n2; /* Retry count */
+ u8 k; /* Window size */
+ unsigned int keep_alive; /* Control channel keep-alive in ms */
/* Statistics (not currently exposed) */
unsigned long bad_fcs;
@@ -1897,11 +1903,13 @@ static void gsm_control_response(struct gsm_mux *gsm, unsigned int command,
const u8 *data, int clen)
{
struct gsm_control *ctrl;
+ struct gsm_dlci *dlci;
unsigned long flags;
spin_lock_irqsave(&gsm->control_lock, flags);
ctrl = gsm->pending_cmd;
+ dlci = gsm->dlci[0];
command |= 1;
/* Does the reply match our command */
if (ctrl != NULL && (command == ctrl->cmd || command == CMD_NSC)) {
@@ -1916,10 +1924,57 @@ static void gsm_control_response(struct gsm_mux *gsm, unsigned int command,
/* Or did we receive the PN response to our PN command */
} else if (command == CMD_PN) {
gsm_control_negotiation(gsm, 0, data, clen);
+ /* Or did we receive the TEST response to our TEST command */
+ } else if (command == CMD_TEST && clen == 1 && *data == gsm->ka_num) {
+ gsm->ka_retries = -1; /* trigger new keep-alive message */
+ if (dlci && !dlci->dead)
+ mod_timer(&gsm->ka_timer,
+ jiffies + gsm->keep_alive * HZ / 100);
}
spin_unlock_irqrestore(&gsm->control_lock, flags);
}
+/**
+ * gsm_control_keep_alive - check timeout or start keep-alive
+ * @t: timer contained in our gsm object
+ *
+ * Called off the keep-alive timer expiry signaling that our link
+ * partner is not responding anymore. Link will be closed.
+ * This is also called to startup our timer.
+ */
+
+static void gsm_control_keep_alive(struct timer_list *t)
+{
+ struct gsm_mux *gsm = from_timer(gsm, t, ka_timer);
+ unsigned long flags;
+
+ spin_lock_irqsave(&gsm->control_lock, flags);
+ if (gsm->ka_num && gsm->ka_retries == 0) {
+ /* Keep-alive expired -> close the link */
+ if (debug & DBG_ERRORS)
+ pr_info("%s keep-alive timed out\n", __func__);
+ spin_unlock_irqrestore(&gsm->control_lock, flags);
+ if (gsm->dlci[0])
+ gsm_dlci_begin_close(gsm->dlci[0]);
+ } else if (gsm->keep_alive && gsm->dlci[0] && !gsm->dlci[0]->dead) {
+ if (gsm->ka_retries > 0) {
+ /* T2 expired for keep-alive -> resend */
+ gsm->ka_retries--;
+ } else {
+ /* Start keep-alive timer */
+ gsm->ka_num++;
+ if (!gsm->ka_num)
+ gsm->ka_num++;
+ gsm->ka_retries = gsm->n2;
+ }
+ gsm_control_command(gsm, CMD_TEST, &gsm->ka_num,
+ sizeof(gsm->ka_num));
+ mod_timer(&gsm->ka_timer,
+ jiffies + gsm->t2 * HZ / 100);
+ spin_unlock_irqrestore(&gsm->control_lock, flags);
+ }
+}
+
/**
* gsm_control_transmit - send control packet
* @gsm: gsm mux
@@ -2061,8 +2116,10 @@ static void gsm_dlci_close(struct gsm_dlci *dlci)
/* Ensure that gsmtty_open() can return. */
tty_port_set_initialized(&dlci->port, false);
wake_up_interruptible(&dlci->port.open_wait);
- } else
+ } else {
+ del_timer(&dlci->gsm->ka_timer);
dlci->gsm->dead = true;
+ }
/* A DLCI 0 close is a MUX termination so we need to kick that
back to userspace somehow */
gsm_dlci_data_kick(dlci);
@@ -2078,6 +2135,8 @@ static void gsm_dlci_close(struct gsm_dlci *dlci)
static void gsm_dlci_open(struct gsm_dlci *dlci)
{
+ struct gsm_mux *gsm = dlci->gsm;
+
/* Note that SABM UA .. SABM UA first UA lost can mean that we go
open -> open */
del_timer(&dlci->t1);
@@ -2087,8 +2146,15 @@ static void gsm_dlci_open(struct gsm_dlci *dlci)
if (debug & DBG_ERRORS)
pr_debug("DLCI %d goes open.\n", dlci->addr);
/* Send current modem state */
- if (dlci->addr)
+ if (dlci->addr) {
gsm_modem_update(dlci, 0);
+ } else {
+ /* Start keep-alive control */
+ gsm->ka_num = 0;
+ gsm->ka_retries = -1;
+ mod_timer(&gsm->ka_timer,
+ jiffies + gsm->keep_alive * HZ / 100);
+ }
gsm_dlci_data_kick(dlci);
wake_up(&dlci->gsm->event);
}
@@ -2840,6 +2906,7 @@ static void gsm_cleanup_mux(struct gsm_mux *gsm, bool disc)
/* Finish outstanding timers, making sure they are done */
del_timer_sync(&gsm->kick_timer);
del_timer_sync(&gsm->t2_timer);
+ del_timer_sync(&gsm->ka_timer);
/* Finish writing to ldisc */
flush_work(&gsm->tx_work);
@@ -2987,6 +3054,7 @@ static struct gsm_mux *gsm_alloc_mux(void)
INIT_LIST_HEAD(&gsm->tx_data_list);
timer_setup(&gsm->kick_timer, gsm_kick_timer, 0);
timer_setup(&gsm->t2_timer, gsm_control_retransmit, 0);
+ timer_setup(&gsm->ka_timer, gsm_control_keep_alive, 0);
INIT_WORK(&gsm->tx_work, gsmld_write_task);
init_waitqueue_head(&gsm->event);
spin_lock_init(&gsm->control_lock);
@@ -3003,6 +3071,7 @@ static struct gsm_mux *gsm_alloc_mux(void)
gsm->mru = 64; /* Default to encoding 1 so these should be 64 */
gsm->mtu = 64;
gsm->dead = true; /* Avoid early tty opens */
+ gsm->keep_alive = 0; /* Disabled */
/* Store the instance to the mux array or abort if no space is
* available.
@@ -3046,6 +3115,7 @@ static void gsm_copy_config_values(struct gsm_mux *gsm,
c->mru = gsm->mru;
c->mtu = gsm->mtu;
c->k = gsm->k;
+ c->keep_alive = gsm->keep_alive;
}
static int gsm_config(struct gsm_mux *gsm, struct gsm_config *c)
@@ -3094,6 +3164,8 @@ static int gsm_config(struct gsm_mux *gsm, struct gsm_config *c)
need_restart = 1;
if (c->mtu != gsm->mtu)
need_restart = 1;
+ if (c->keep_alive != gsm->keep_alive)
+ need_restart = true;
/*
* Close down what is needed, restart and initiate the new
@@ -3109,6 +3181,7 @@ static int gsm_config(struct gsm_mux *gsm, struct gsm_config *c)
gsm->encoding = c->encapsulation ? GSM_ADV_OPT : GSM_BASIC_OPT;
gsm->adaption = c->adaption;
gsm->n2 = c->n2;
+ gsm->keep_alive = c->keep_alive;
if (c->i == 1)
gsm->ftype = UIH;
@@ -19,7 +19,8 @@ struct gsm_config
unsigned int mtu;
unsigned int k;
unsigned int i;
- unsigned int unused[8]; /* Padding for expansion without
+ unsigned int keep_alive;
+ unsigned int unused[7]; /* Padding for expansion without
breaking stuff */
};