Skip to content

Commit

Permalink
Extend ioctl02 to test termio and termios
Browse files Browse the repository at this point in the history
Testing the behavior of both sets of ioctl commands at the same time is
challenging because they use different structures. This change tries to
minimize the amount of code duplication (it is not fully possible) and to
minimize the amount of macro code (it is not fully avoidable).

To ease this, this change simplifies the checks:
* Create a separate function and macro for checking attributes
* Remove useless nested condition in loop for checking the control
  characters
    * The loop condition is `i < NCC` and the nested condition was
      `i == VEOL2`. The nested condition is completely useless because it
      is never reached because `VEOL2 > NCC`.
    * The same applies to the loop for initializing the structs.
    * This change introduces use of termios (where `VEOL2 < NCCS`) but
      extra handling for `VEOL2` can still be avoided.
* Implement the check for control characters in terms of the normal
  attribute check

Link: https://lore.kernel.org/ltp/[email protected]/
Reviewed-by: Cyril Hrubis <[email protected]>
Reviewed-by: Petr Vorel <[email protected]>
Signed-off-by: Marius Kittler <[email protected]>
[ pvorel: modify macros to pass and return flag ]
Signed-off-by: Petr Vorel <[email protected]>
  • Loading branch information
Martchus authored and pevik committed Dec 1, 2023
1 parent 4b75b18 commit 0ec2164
Showing 1 changed file with 125 additions and 82 deletions.
207 changes: 125 additions & 82 deletions testcases/kernel/syscalls/ioctl/ioctl02.c
Original file line number Diff line number Diff line change
Expand Up @@ -8,53 +8,77 @@
/*\
* [Description]
*
* Testcase to test the TCGETA, and TCSETA ioctl implementations for
* the tty driver
* Test TCGETA/TCGETS and TCSETA/TCSETS ioctl implementations for tty driver.
*
* In this test, the parent and child open the parentty and the childtty
* respectively. After opening the childtty the child flushes the stream
* and wakes the parent (thereby asking it to continue its testing). The
* parent, then starts the testing. It issues a TCGETA ioctl to get all
* the tty parameters. It then changes them to known values by issuing a
* TCSETA ioctl. Then the parent issues a TCGETA ioctl again and compares
* the received values with what it had set earlier. The test fails if
* TCGETA or TCSETA fails, or if the received values don't match those
* that were set. The parent does all the testing, the requirement of the
* child process is to moniter the testing done by the parent, and hence
* the child just waits for the parent.
* parent, then starts the testing. It issues a TCGETA/TCGETS ioctl to
* get all the tty parameters. It then changes them to known values by
* issuing a TCSETA/TCSETS ioctl. Then the parent issues a TCSETA/TCGETS
* ioctl again and compares the received values with what it had set
* earlier. The test fails if TCGETA/TCGETS or TCSETA/TCSETS fails, or if
* the received values don't match those that were set. The parent does
* all the testing, the requirement of the child process is to moniter
* the testing done by the parent, and hence the child just waits for the
* parent.
*/

#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <errno.h>
#include <sys/wait.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <termios.h>

#include "lapi/ioctl.h"

#include "tst_checkpoint.h"
#include "tst_test.h"
#include "tst_safe_macros.h"

static struct termio termio, save_io;
static struct termio termio, termio_exp;
static struct termios termios, termios_exp, termios_bak;

static char *parenttty, *childtty;
static int parentfd = -1;
static int parentpid, childpid;

static void do_child(void);
static void prepare_termio(void);
static void run_ptest(void);
static void chk_tty_parms(void);
static void chk_tty_parms_termio(void);
static void chk_tty_parms_termios(void);
static void setup(void);
static void cleanup(void);

static char *device;

static struct variant {
const char *name;
void *termio, *termio_exp, *termio_bak;
size_t termio_size;
void (*check)(void);
int tcget, tcset;
} variants[] = {
{
.name = "termio",
.termio = &termio,
.termio_exp = &termio_exp,
.termio_size = sizeof(termio),
.check = &chk_tty_parms_termio,
.tcget = TCGETA,
.tcset = TCSETA,
},
{
.name = "termios",
.termio = &termios,
.termio_exp = &termios_exp,
.termio_size = sizeof(termios),
.check = &chk_tty_parms_termios,
.tcget = TCGETS,
.tcset = TCSETS,
},
};

static void verify_ioctl(void)
{
tst_res(TINFO, "Testing %s variant", variants[tst_variant].name);

parenttty = device;
childtty = device;

Expand All @@ -73,99 +97,115 @@ static void verify_ioctl(void)
run_ptest();

TST_CHECKPOINT_WAKE(0);
SAFE_CLOSE(parentfd);
}

/*
* run_ptest() - setup the various termio structure values and issue
* the TCSETA ioctl call with the TEST macro.
*/
static void run_ptest(void)
static void prepare_termio(void)
{
/* Use "old" line discipline */
termio.c_line = 0;
termios_exp.c_line = termio_exp.c_line = 0;

/* Set control modes */
termio.c_cflag = B50 | CS7 | CREAD | PARENB | PARODD | CLOCAL;
termios_exp.c_cflag = termio_exp.c_cflag = B50 | CS7 | CREAD | PARENB | PARODD | CLOCAL;

/* Set control chars. */
for (int i = 0; i < NCC; i++) {
if (i == VEOL2)
continue;
termio.c_cc[i] = CSTART;
}
for (int i = 0; i < NCC; i++)
termio_exp.c_cc[i] = CSTART;
for (int i = 0; i < VEOL2; i++)
termios_exp.c_cc[i] = CSTART;

/* Set local modes. */
termio.c_lflag =
termios_exp.c_lflag = termio_exp.c_lflag =
((unsigned short)(ISIG | ICANON | XCASE | ECHO | ECHOE | NOFLSH));

/* Set input modes. */
termio.c_iflag =
termios_exp.c_iflag = termio_exp.c_iflag =
BRKINT | IGNPAR | INPCK | ISTRIP | ICRNL | IUCLC | IXON | IXANY |
IXOFF;

/* Set output modes. */
termio.c_oflag = OPOST | OLCUC | ONLCR | ONOCR;
termios_exp.c_oflag = termio_exp.c_oflag = OPOST | OLCUC | ONLCR | ONOCR;
}

SAFE_IOCTL(parentfd, TCSETA, &termio);
/*
* run_ptest() - setup the various termio/termios structure values and issue
* the TCSETA/TCSETS ioctl call with the TEST macro.
*/
static void run_ptest(void)
{
struct variant *v = &variants[tst_variant];

/* Init termio/termios structures used to check if all params got set */
memset(v->termio, 0, v->termio_size);

SAFE_IOCTL(parentfd, v->tcset, v->termio_exp);

/* Get termio and see if all parameters actually got set */
SAFE_IOCTL(parentfd, TCGETA, &termio);
chk_tty_parms();
SAFE_IOCTL(parentfd, v->tcget, v->termio);
v->check();
}

static int cmp_attr(unsigned long long exp, unsigned long long act, const char *attr)
{
if (act == exp)
return 0;
tst_res(TFAIL, "%s has incorrect value %llu, expected %llu",
attr, act, exp);
return 1;
}

static void chk_tty_parms(void)
static int cmp_c_cc(unsigned char *exp_c_cc, unsigned char *act_c_cc, int ncc)
{
int i, flag = 0;
int i, fails = 0;
char what[32];

if (termio.c_line != 0) {
tst_res(TFAIL, "line discipline has incorrect value %o",
termio.c_line);
flag++;
for (i = 0; i < ncc; ++i) {
sprintf(what, "control char %d", i);
fails += cmp_attr(exp_c_cc[i], act_c_cc[i], what);
}
return fails;
}

for (i = 0; i < NCC; i++) {
if (i == VEOL2) {
if (!termio.c_cc[i]) {
continue;
} else {
tst_res(TFAIL, "control char %d has "
"incorrect value %d", i, termio.c_cc[i]);
flag++;
continue;
}
}

if (termio.c_cc[i] != CSTART) {
tst_res(TFAIL, "control char %d has incorrect "
"value %d.", i, termio.c_cc[i]);
flag++;
}
}
#define CMP_ATTR(term_exp, term, attr, flag) \
({ \
flag += cmp_attr((term_exp).attr, (term).attr, #attr); \
flag; \
})

if (termio.c_lflag != (ISIG | ICANON | XCASE | ECHO | ECHOE
| NOFLSH)) {
tst_res(TFAIL, "lflag has incorrect value. %o",
termio.c_lflag);
flag++;
}
#define CMP_C_CC(term_exp, term, flag) \
({ \
flag += cmp_c_cc(term_exp.c_cc, term.c_cc, sizeof(term.c_cc)); \
flag; \
})

if (termio.c_iflag != (BRKINT | IGNPAR | INPCK | ISTRIP
| ICRNL | IUCLC | IXON | IXANY | IXOFF)) {
tst_res(TFAIL, "iflag has incorrect value. %o",
termio.c_iflag);
flag++;
}
static void chk_tty_parms_termio(void)
{
int flag = 0;

if (termio.c_oflag != (OPOST | OLCUC | ONLCR | ONOCR)) {
tst_res(TFAIL, "oflag has incorrect value. %o",
termio.c_oflag);
flag++;
}
flag = CMP_ATTR(termio_exp, termio, c_line, flag);
flag = CMP_C_CC(termio_exp, termio, flag);
flag = CMP_ATTR(termio_exp, termio, c_lflag, flag);
flag = CMP_ATTR(termio_exp, termio, c_iflag, flag);
flag = CMP_ATTR(termio_exp, termio, c_oflag, flag);

if (!flag)
tst_res(TPASS, "TCGETA/TCSETA tests");
}

static void chk_tty_parms_termios(void)
{
int flag = 0;

flag = CMP_ATTR(termios_exp, termios, c_line, flag);
flag = CMP_C_CC(termios_exp, termios, flag);
flag = CMP_ATTR(termios_exp, termios, c_lflag, flag);
flag = CMP_ATTR(termios_exp, termios, c_iflag, flag);
flag = CMP_ATTR(termios_exp, termios, c_oflag, flag);

if (!flag)
tst_res(TPASS, "TCGETS/TCSETS tests");
}

static void do_child(void)
{
int cfd = SAFE_OPEN(childtty, O_RDWR, 0777);
Expand All @@ -187,14 +227,16 @@ static void setup(void)

int fd = SAFE_OPEN(device, O_RDWR, 0777);

SAFE_IOCTL(fd, TCGETA, &save_io);
SAFE_IOCTL(fd, TCGETS, &termios_bak);
SAFE_CLOSE(fd);

prepare_termio();
}

static void cleanup(void)
{
if (parentfd >= 0) {
SAFE_IOCTL(parentfd, TCSETA, &save_io);
SAFE_IOCTL(parentfd, TCSETS, &termios_bak);
SAFE_CLOSE(parentfd);
}
}
Expand All @@ -206,8 +248,9 @@ static struct tst_test test = {
.setup = setup,
.cleanup = cleanup,
.test_all = verify_ioctl,
.test_variants = 2,
.options = (struct tst_option[]) {
{"D:", &device, "Tty device. For example, /dev/tty[0-9]"},
{}
}
};
};

0 comments on commit 0ec2164

Please sign in to comment.