xref: /aosp_15_r20/external/ltp/testcases/kernel/syscalls/ioctl/ioctl02.c (revision 49cdfc7efb34551c7342be41a7384b9c40d7cab7)
1 // SPDX-License-Identifier: GPL-2.0-or-later
2 /*
3  * Copyright (c) International Business Machines  Corp., 2001
4  * Copyright (c) 2020 Petr Vorel <[email protected]>
5  * Copyright (c) 2023 Marius Kittler <[email protected]>
6  */
7 
8 /*\
9  * [Description]
10  *
11  * Test TCGETA/TCGETS and TCSETA/TCSETS ioctl implementations for tty driver.
12  *
13  * In this test, the parent and child open the parentty and the childtty
14  * respectively.  After opening the childtty the child flushes the stream
15  * and wakes the parent (thereby asking it to continue its testing). The
16  * parent, then starts the testing. It issues a TCGETA/TCGETS ioctl to
17  * get all the tty parameters. It then changes them to known values by
18  * issuing a TCSETA/TCSETS ioctl. Then the parent issues a TCSETA/TCGETS
19  * ioctl again and compares the received values with what it had set
20  * earlier. The test fails if TCGETA/TCGETS or TCSETA/TCSETS fails, or if
21  * the received values don't match those that were set. The parent does
22  * all the testing, the requirement of the child process is to moniter
23  * the testing done by the parent, and hence the child just waits for the
24  * parent.
25  */
26 
27 #include <stdio.h>
28 #include <stdlib.h>
29 #include <asm/termbits.h>
30 
31 #include "lapi/ioctl.h"
32 #include "tst_test.h"
33 
34 static struct termio termio, termio_exp;
35 static struct termios termios, termios_exp, termios_bak;
36 
37 static char *parenttty, *childtty;
38 static int parentfd = -1;
39 static int parentpid, childpid;
40 
41 static void do_child(void);
42 static void prepare_termio(void);
43 static void run_ptest(void);
44 static void chk_tty_parms_termio(void);
45 static void chk_tty_parms_termios(void);
46 static void setup(void);
47 static void cleanup(void);
48 
49 static char *device;
50 
51 static struct variant {
52 	const char *name;
53 	void *termio, *termio_exp, *termio_bak;
54 	size_t termio_size;
55 	void (*check)(void);
56 	int tcget, tcset;
57 } variants[] = {
58 	{
59 		.name = "termio",
60 		.termio = &termio,
61 		.termio_exp = &termio_exp,
62 		.termio_size = sizeof(termio),
63 		.check = &chk_tty_parms_termio,
64 		.tcget = TCGETA,
65 		.tcset = TCSETA,
66 	},
67 	{
68 		.name = "termios",
69 		.termio = &termios,
70 		.termio_exp = &termios_exp,
71 		.termio_size = sizeof(termios),
72 		.check = &chk_tty_parms_termios,
73 		.tcget = TCGETS,
74 		.tcset = TCSETS,
75 	},
76 };
77 
verify_ioctl(void)78 static void verify_ioctl(void)
79 {
80 	tst_res(TINFO, "Testing %s variant", variants[tst_variant].name);
81 
82 	parenttty = device;
83 	childtty = device;
84 
85 	parentpid = getpid();
86 	childpid = SAFE_FORK();
87 	if (!childpid) {
88 		do_child();
89 		exit(EXIT_SUCCESS);
90 	}
91 
92 	TST_CHECKPOINT_WAIT(0);
93 
94 	parentfd = SAFE_OPEN(parenttty, O_RDWR, 0777);
95 	SAFE_IOCTL(parentfd, TCFLSH, TCIOFLUSH);
96 
97 	run_ptest();
98 
99 	TST_CHECKPOINT_WAKE(0);
100 	SAFE_CLOSE(parentfd);
101 }
102 
prepare_termio(void)103 static void prepare_termio(void)
104 {
105 	/* Use "old" line discipline */
106 	termios_exp.c_line = termio_exp.c_line = 0;
107 
108 	/* Set control modes */
109 	termios_exp.c_cflag = termio_exp.c_cflag = B50 | CS7 | CREAD | PARENB | PARODD | CLOCAL;
110 
111 	/* Set control chars. */
112 	for (int i = 0; i < NCC; i++)
113 		termio_exp.c_cc[i] = CSTART;
114 	for (int i = 0; i < VEOL2; i++)
115 		termios_exp.c_cc[i] = CSTART;
116 
117 	/* Set local modes. */
118 	termios_exp.c_lflag = termio_exp.c_lflag =
119 	    ((unsigned short)(ISIG | ICANON | XCASE | ECHO | ECHOE | NOFLSH));
120 
121 	/* Set input modes. */
122 	termios_exp.c_iflag = termio_exp.c_iflag =
123 	    BRKINT | IGNPAR | INPCK | ISTRIP | ICRNL | IUCLC | IXON | IXANY |
124 	    IXOFF;
125 
126 	/* Set output modes. */
127 	termios_exp.c_oflag = termio_exp.c_oflag = OPOST | OLCUC | ONLCR | ONOCR;
128 }
129 
130 /*
131  * run_ptest() - setup the various termio/termios structure values and issue
132  * the TCSETA/TCSETS ioctl call with the TEST macro.
133  */
run_ptest(void)134 static void run_ptest(void)
135 {
136 	struct variant *v = &variants[tst_variant];
137 
138 	/* Init termio/termios structures used to check if all params got set */
139 	memset(v->termio, 0, v->termio_size);
140 
141 	SAFE_IOCTL(parentfd, v->tcset, v->termio_exp);
142 
143 	/* Get termio and see if all parameters actually got set */
144 	SAFE_IOCTL(parentfd, v->tcget, v->termio);
145 	v->check();
146 }
147 
cmp_attr(unsigned long long exp,unsigned long long act,const char * attr)148 static int cmp_attr(unsigned long long exp, unsigned long long act, const char *attr)
149 {
150 	if (act == exp)
151 		return 0;
152 	tst_res(TFAIL, "%s has incorrect value %llu, expected %llu",
153 		attr, act, exp);
154 	return 1;
155 }
156 
cmp_c_cc(unsigned char * exp_c_cc,unsigned char * act_c_cc,int ncc)157 static int cmp_c_cc(unsigned char *exp_c_cc, unsigned char *act_c_cc, int ncc)
158 {
159 	int i, fails = 0;
160 	char what[32];
161 
162 	for (i = 0; i < ncc; ++i) {
163 		sprintf(what, "control char %d", i);
164 		fails += cmp_attr(exp_c_cc[i], act_c_cc[i], what);
165 	}
166 	return fails;
167 }
168 
169 #define CMP_ATTR(term_exp, term, attr, flag)				\
170 ({															\
171 	flag += cmp_attr((term_exp).attr, (term).attr, #attr);	\
172 	flag;                                                   \
173 })
174 
175 #define CMP_C_CC(term_exp, term, flag)								\
176 ({																	\
177 	flag += cmp_c_cc(term_exp.c_cc, term.c_cc, sizeof(term.c_cc));	\
178 	flag;															\
179 })
180 
chk_tty_parms_termio(void)181 static void chk_tty_parms_termio(void)
182 {
183 	int flag = 0;
184 
185 	flag = CMP_ATTR(termio_exp, termio, c_line, flag);
186 	flag = CMP_C_CC(termio_exp, termio, flag);
187 	flag = CMP_ATTR(termio_exp, termio, c_lflag, flag);
188 	flag = CMP_ATTR(termio_exp, termio, c_iflag, flag);
189 	flag = CMP_ATTR(termio_exp, termio, c_oflag, flag);
190 
191 	if (!flag)
192 		tst_res(TPASS, "TCGETA/TCSETA tests");
193 }
194 
chk_tty_parms_termios(void)195 static void chk_tty_parms_termios(void)
196 {
197 	int flag = 0;
198 
199 	flag = CMP_ATTR(termios_exp, termios, c_line, flag);
200 	flag = CMP_C_CC(termios_exp, termios, flag);
201 	flag = CMP_ATTR(termios_exp, termios, c_lflag, flag);
202 	flag = CMP_ATTR(termios_exp, termios, c_iflag, flag);
203 	flag = CMP_ATTR(termios_exp, termios, c_oflag, flag);
204 
205 	if (!flag)
206 		tst_res(TPASS, "TCGETS/TCSETS tests");
207 }
208 
do_child(void)209 static void do_child(void)
210 {
211 	int cfd = SAFE_OPEN(childtty, O_RDWR, 0777);
212 
213 	SAFE_IOCTL(cfd, TCFLSH, TCIOFLUSH);
214 
215 	/* tell the parent that we're done */
216 	TST_CHECKPOINT_WAKE(0);
217 
218 	TST_CHECKPOINT_WAIT(0);
219 	tst_res(TINFO, "child: parent has finished testing");
220 	SAFE_CLOSE(cfd);
221 }
222 
setup(void)223 static void setup(void)
224 {
225 	if (!device)
226 		tst_brk(TBROK, "You must specify a tty device with -d option");
227 
228 	int fd = SAFE_OPEN(device, O_RDWR, 0777);
229 
230 	SAFE_IOCTL(fd, TCGETS, &termios_bak);
231 	SAFE_CLOSE(fd);
232 
233 	prepare_termio();
234 }
235 
cleanup(void)236 static void cleanup(void)
237 {
238 	if (parentfd >= 0) {
239 		SAFE_IOCTL(parentfd, TCSETS, &termios_bak);
240 		SAFE_CLOSE(parentfd);
241 	}
242 }
243 
244 static struct tst_test test = {
245 	.needs_root = 1,
246 	.needs_checkpoints = 1,
247 	.forks_child = 1,
248 	.setup = setup,
249 	.cleanup = cleanup,
250 	.test_all = verify_ioctl,
251 	.test_variants = 2,
252 	.options = (struct tst_option[]) {
253 		{"d:", &device, "Tty device. For example, /dev/tty[0-9]"},
254 		{}
255 	}
256 };
257