Skip to content

Commit 72206cc

Browse files
dstarke-siemensgregkh
authored andcommitted
tty: n_gsm: add keep alive support
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. Chapters 5.4.6.3.4 and 5.1.8.1.3 describe the test command which can be used to test the mux connection between both sides. Currently, no algorithm is implemented to make use of this command. This requires that each multiplexed upper layer protocol supervises the underlying muxer connection to handle possible connection losses. Introduce ioctl commands and functions to optionally enable keep alive handling via the test command as described in chapter 5.4.6.3.4. A single incrementing octet "ka_num" is being used for unique identification of each single keep alive packet. Retries will use the same "ka_num" value as the original packet. Retry count and interval are taken from the general parameters N2 and T2. Add usage description and basic example for the new ioctl to the n_gsm documentation. Note that support for the test command is mandatory and already present in the muxer implementation since the very first version. Also note that the previous ioctl structure gsm_config cannot be extended due to missing checks against zero of the field "unused". Signed-off-by: Daniel Starke <[email protected]> Link: https://lore.kernel.org/r/[email protected] Signed-off-by: Greg Kroah-Hartman <[email protected]>
1 parent 3f92730 commit 72206cc

File tree

3 files changed

+133
-2
lines changed

3 files changed

+133
-2
lines changed

Documentation/driver-api/tty/n_gsm.rst

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,8 @@ Config Initiator
2525
#. Switch the serial line to using the n_gsm line discipline by using
2626
``TIOCSETD`` ioctl.
2727

28+
#. Configure the mux using ``GSMIOC_GETCONF_EXT``/``GSMIOC_SETCONF_EXT`` ioctl if needed.
29+
2830
#. Configure the mux using ``GSMIOC_GETCONF``/``GSMIOC_SETCONF`` ioctl.
2931

3032
#. Obtain base gsmtty number for the used serial port.
@@ -42,6 +44,7 @@ Config Initiator
4244

4345
int ldisc = N_GSM0710;
4446
struct gsm_config c;
47+
struct gsm_config_ext ce;
4548
struct termios configuration;
4649
uint32_t first;
4750

@@ -62,6 +65,12 @@ Config Initiator
6265
/* use n_gsm line discipline */
6366
ioctl(fd, TIOCSETD, &ldisc);
6467

68+
/* get n_gsm extended configuration */
69+
ioctl(fd, GSMIOC_GETCONF_EXT, &ce);
70+
/* use keep-alive once every 5s for modem connection supervision */
71+
ce.keep_alive = 500;
72+
/* set the new extended configuration */
73+
ioctl(fd, GSMIOC_SETCONF_EXT, &ce);
6574
/* get n_gsm configuration */
6675
ioctl(fd, GSMIOC_GETCONF, &c);
6776
/* we are initiator and need encoding 0 (basic) */
@@ -106,6 +115,9 @@ Config Requester
106115
#. Switch the serial line to using the *n_gsm* line discipline by using
107116
``TIOCSETD`` ioctl.
108117

118+
#. Configure the mux using ``GSMIOC_GETCONF_EXT``/``GSMIOC_SETCONF_EXT``
119+
ioctl if needed.
120+
109121
#. Configure the mux using ``GSMIOC_GETCONF``/``GSMIOC_SETCONF`` ioctl.
110122

111123
#. Obtain base gsmtty number for the used serial port::
@@ -119,6 +131,7 @@ Config Requester
119131

120132
int ldisc = N_GSM0710;
121133
struct gsm_config c;
134+
struct gsm_config_ext ce;
122135
struct termios configuration;
123136
uint32_t first;
124137

@@ -132,6 +145,12 @@ Config Requester
132145
/* use n_gsm line discipline */
133146
ioctl(fd, TIOCSETD, &ldisc);
134147

148+
/* get n_gsm extended configuration */
149+
ioctl(fd, GSMIOC_GETCONF_EXT, &ce);
150+
/* use keep-alive once every 5s for peer connection supervision */
151+
ce.keep_alive = 500;
152+
/* set the new extended configuration */
153+
ioctl(fd, GSMIOC_SETCONF_EXT, &ce);
135154
/* get n_gsm configuration */
136155
ioctl(fd, GSMIOC_GETCONF, &c);
137156
/* we are requester and need encoding 0 (basic) */

drivers/tty/n_gsm.c

Lines changed: 104 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -318,13 +318,19 @@ struct gsm_mux {
318318
struct gsm_control *pending_cmd;/* Our current pending command */
319319
spinlock_t control_lock; /* Protects the pending command */
320320

321+
/* Keep-alive */
322+
struct timer_list ka_timer; /* Keep-alive response timer */
323+
u8 ka_num; /* Keep-alive match pattern */
324+
signed int ka_retries; /* Keep-alive retry counter, -1 if not yet initialized */
325+
321326
/* Configuration */
322327
int adaption; /* 1 or 2 supported */
323328
u8 ftype; /* UI or UIH */
324329
int t1, t2; /* Timers in 1/100th of a sec */
325330
unsigned int t3; /* Power wake-up timer in seconds. */
326331
int n2; /* Retry count */
327332
u8 k; /* Window size */
333+
u32 keep_alive; /* Control channel keep-alive in 10ms */
328334

329335
/* Statistics (not currently exposed) */
330336
unsigned long bad_fcs;
@@ -1903,11 +1909,13 @@ static void gsm_control_response(struct gsm_mux *gsm, unsigned int command,
19031909
const u8 *data, int clen)
19041910
{
19051911
struct gsm_control *ctrl;
1912+
struct gsm_dlci *dlci;
19061913
unsigned long flags;
19071914

19081915
spin_lock_irqsave(&gsm->control_lock, flags);
19091916

19101917
ctrl = gsm->pending_cmd;
1918+
dlci = gsm->dlci[0];
19111919
command |= 1;
19121920
/* Does the reply match our command */
19131921
if (ctrl != NULL && (command == ctrl->cmd || command == CMD_NSC)) {
@@ -1922,6 +1930,53 @@ static void gsm_control_response(struct gsm_mux *gsm, unsigned int command,
19221930
/* Or did we receive the PN response to our PN command */
19231931
} else if (command == CMD_PN) {
19241932
gsm_control_negotiation(gsm, 0, data, clen);
1933+
/* Or did we receive the TEST response to our TEST command */
1934+
} else if (command == CMD_TEST && clen == 1 && *data == gsm->ka_num) {
1935+
gsm->ka_retries = -1; /* trigger new keep-alive message */
1936+
if (dlci && !dlci->dead)
1937+
mod_timer(&gsm->ka_timer, jiffies + gsm->keep_alive * HZ / 100);
1938+
}
1939+
spin_unlock_irqrestore(&gsm->control_lock, flags);
1940+
}
1941+
1942+
/**
1943+
* gsm_control_keep_alive - check timeout or start keep-alive
1944+
* @t: timer contained in our gsm object
1945+
*
1946+
* Called off the keep-alive timer expiry signaling that our link
1947+
* partner is not responding anymore. Link will be closed.
1948+
* This is also called to startup our timer.
1949+
*/
1950+
1951+
static void gsm_control_keep_alive(struct timer_list *t)
1952+
{
1953+
struct gsm_mux *gsm = from_timer(gsm, t, ka_timer);
1954+
unsigned long flags;
1955+
1956+
spin_lock_irqsave(&gsm->control_lock, flags);
1957+
if (gsm->ka_num && gsm->ka_retries == 0) {
1958+
/* Keep-alive expired -> close the link */
1959+
if (debug & DBG_ERRORS)
1960+
pr_debug("%s keep-alive timed out\n", __func__);
1961+
spin_unlock_irqrestore(&gsm->control_lock, flags);
1962+
if (gsm->dlci[0])
1963+
gsm_dlci_begin_close(gsm->dlci[0]);
1964+
return;
1965+
} else if (gsm->keep_alive && gsm->dlci[0] && !gsm->dlci[0]->dead) {
1966+
if (gsm->ka_retries > 0) {
1967+
/* T2 expired for keep-alive -> resend */
1968+
gsm->ka_retries--;
1969+
} else {
1970+
/* Start keep-alive timer */
1971+
gsm->ka_num++;
1972+
if (!gsm->ka_num)
1973+
gsm->ka_num++;
1974+
gsm->ka_retries = (signed int)gsm->n2;
1975+
}
1976+
gsm_control_command(gsm, CMD_TEST, &gsm->ka_num,
1977+
sizeof(gsm->ka_num));
1978+
mod_timer(&gsm->ka_timer,
1979+
jiffies + gsm->t2 * HZ / 100);
19251980
}
19261981
spin_unlock_irqrestore(&gsm->control_lock, flags);
19271982
}
@@ -2067,8 +2122,10 @@ static void gsm_dlci_close(struct gsm_dlci *dlci)
20672122
/* Ensure that gsmtty_open() can return. */
20682123
tty_port_set_initialized(&dlci->port, false);
20692124
wake_up_interruptible(&dlci->port.open_wait);
2070-
} else
2125+
} else {
2126+
del_timer(&dlci->gsm->ka_timer);
20712127
dlci->gsm->dead = true;
2128+
}
20722129
/* A DLCI 0 close is a MUX termination so we need to kick that
20732130
back to userspace somehow */
20742131
gsm_dlci_data_kick(dlci);
@@ -2084,6 +2141,8 @@ static void gsm_dlci_close(struct gsm_dlci *dlci)
20842141

20852142
static void gsm_dlci_open(struct gsm_dlci *dlci)
20862143
{
2144+
struct gsm_mux *gsm = dlci->gsm;
2145+
20872146
/* Note that SABM UA .. SABM UA first UA lost can mean that we go
20882147
open -> open */
20892148
del_timer(&dlci->t1);
@@ -2093,8 +2152,15 @@ static void gsm_dlci_open(struct gsm_dlci *dlci)
20932152
if (debug & DBG_ERRORS)
20942153
pr_debug("DLCI %d goes open.\n", dlci->addr);
20952154
/* Send current modem state */
2096-
if (dlci->addr)
2155+
if (dlci->addr) {
20972156
gsm_modem_update(dlci, 0);
2157+
} else {
2158+
/* Start keep-alive control */
2159+
gsm->ka_num = 0;
2160+
gsm->ka_retries = -1;
2161+
mod_timer(&gsm->ka_timer,
2162+
jiffies + gsm->keep_alive * HZ / 100);
2163+
}
20982164
gsm_dlci_data_kick(dlci);
20992165
wake_up(&dlci->gsm->event);
21002166
}
@@ -2847,6 +2913,7 @@ static void gsm_cleanup_mux(struct gsm_mux *gsm, bool disc)
28472913
/* Finish outstanding timers, making sure they are done */
28482914
del_timer_sync(&gsm->kick_timer);
28492915
del_timer_sync(&gsm->t2_timer);
2916+
del_timer_sync(&gsm->ka_timer);
28502917

28512918
/* Finish writing to ldisc */
28522919
flush_work(&gsm->tx_work);
@@ -2994,6 +3061,7 @@ static struct gsm_mux *gsm_alloc_mux(void)
29943061
INIT_LIST_HEAD(&gsm->tx_data_list);
29953062
timer_setup(&gsm->kick_timer, gsm_kick_timer, 0);
29963063
timer_setup(&gsm->t2_timer, gsm_control_retransmit, 0);
3064+
timer_setup(&gsm->ka_timer, gsm_control_keep_alive, 0);
29973065
INIT_WORK(&gsm->tx_work, gsmld_write_task);
29983066
init_waitqueue_head(&gsm->event);
29993067
spin_lock_init(&gsm->control_lock);
@@ -3010,6 +3078,7 @@ static struct gsm_mux *gsm_alloc_mux(void)
30103078
gsm->mru = 64; /* Default to encoding 1 so these should be 64 */
30113079
gsm->mtu = 64;
30123080
gsm->dead = true; /* Avoid early tty opens */
3081+
gsm->keep_alive = 0; /* Disabled */
30133082

30143083
/* Store the instance to the mux array or abort if no space is
30153084
* available.
@@ -3145,6 +3214,29 @@ static int gsm_config(struct gsm_mux *gsm, struct gsm_config *c)
31453214
return 0;
31463215
}
31473216

3217+
static void gsm_copy_config_ext_values(struct gsm_mux *gsm,
3218+
struct gsm_config_ext *ce)
3219+
{
3220+
memset(ce, 0, sizeof(*ce));
3221+
ce->keep_alive = gsm->keep_alive;
3222+
}
3223+
3224+
static int gsm_config_ext(struct gsm_mux *gsm, struct gsm_config_ext *ce)
3225+
{
3226+
unsigned int i;
3227+
3228+
/*
3229+
* Check that userspace doesn't put stuff in here to prevent breakages
3230+
* in the future.
3231+
*/
3232+
for (i = 0; i < ARRAY_SIZE(ce->reserved); i++)
3233+
if (ce->reserved[i])
3234+
return -EINVAL;
3235+
3236+
gsm->keep_alive = ce->keep_alive;
3237+
return 0;
3238+
}
3239+
31483240
/**
31493241
* gsmld_output - write to link
31503242
* @gsm: our mux
@@ -3463,6 +3555,7 @@ static int gsmld_ioctl(struct tty_struct *tty, unsigned int cmd,
34633555
unsigned long arg)
34643556
{
34653557
struct gsm_config c;
3558+
struct gsm_config_ext ce;
34663559
struct gsm_mux *gsm = tty->disc_data;
34673560
unsigned int base;
34683561

@@ -3479,6 +3572,15 @@ static int gsmld_ioctl(struct tty_struct *tty, unsigned int cmd,
34793572
case GSMIOC_GETFIRST:
34803573
base = mux_num_to_base(gsm);
34813574
return put_user(base + 1, (__u32 __user *)arg);
3575+
case GSMIOC_GETCONF_EXT:
3576+
gsm_copy_config_ext_values(gsm, &ce);
3577+
if (copy_to_user((void __user *)arg, &ce, sizeof(ce)))
3578+
return -EFAULT;
3579+
return 0;
3580+
case GSMIOC_SETCONF_EXT:
3581+
if (copy_from_user(&ce, (void __user *)arg, sizeof(ce)))
3582+
return -EFAULT;
3583+
return gsm_config_ext(gsm, &ce);
34823584
default:
34833585
return n_tty_ioctl_helper(tty, cmd, arg);
34843586
}

include/uapi/linux/gsmmux.h

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,4 +39,14 @@ struct gsm_netconfig {
3939
/* get the base tty number for a configured gsmmux tty */
4040
#define GSMIOC_GETFIRST _IOR('G', 4, __u32)
4141

42+
struct gsm_config_ext {
43+
__u32 keep_alive; /* Control channel keep-alive in 1/100th of a
44+
* second (0 to disable)
45+
*/
46+
__u32 reserved[7]; /* For future use, must be initialized to zero */
47+
};
48+
49+
#define GSMIOC_GETCONF_EXT _IOR('G', 5, struct gsm_config_ext)
50+
#define GSMIOC_SETCONF_EXT _IOW('G', 6, struct gsm_config_ext)
51+
4252
#endif

0 commit comments

Comments
 (0)