1 /* $NetBSD: linux_termios.c,v 1.39 2021/11/23 17:54:08 pho Exp $ */
2
3 /*-
4 * Copyright (c) 1995, 1998, 2008 The NetBSD Foundation, Inc.
5 * All rights reserved.
6 *
7 * This code is derived from software contributed to The NetBSD Foundation
8 * by Frank van der Linden and Eric Haszlakiewicz.
9 *
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
12 * are met:
13 * 1. Redistributions of source code must retain the above copyright
14 * notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 * notice, this list of conditions and the following disclaimer in the
17 * documentation and/or other materials provided with the distribution.
18 *
19 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
20 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
21 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
23 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29 * POSSIBILITY OF SUCH DAMAGE.
30 */
31
32 #include <sys/cdefs.h>
33 __KERNEL_RCSID(0, "$NetBSD: linux_termios.c,v 1.39 2021/11/23 17:54:08 pho Exp $");
34
35 #if defined(_KERNEL_OPT)
36 #include "opt_ptm.h"
37 #endif
38
39 #include <sys/param.h>
40 #include <sys/proc.h>
41 #include <sys/systm.h>
42 #include <sys/file.h>
43 #include <sys/filedesc.h>
44 #include <sys/ioctl.h>
45 #include <sys/mount.h>
46 #include <sys/termios.h>
47 #include <sys/kernel.h>
48
49 #include <sys/syscallargs.h>
50
51 #include <compat/linux/common/linux_types.h>
52 #include <compat/linux/common/linux_ioctl.h>
53 #include <compat/linux/common/linux_signal.h>
54 #include <compat/linux/common/linux_util.h>
55 #include <compat/linux/common/linux_termios.h>
56 #include <compat/linux/common/linux_ipc.h>
57 #include <compat/linux/common/linux_sem.h>
58
59 #include <compat/linux/linux_syscallargs.h>
60
61 #ifdef DEBUG_LINUX
62 #define DPRINTF(a) uprintf a
63 #else
64 #define DPRINTF(a)
65 #endif
66
67 int
linux_ioctl_termios(struct lwp * l,const struct linux_sys_ioctl_args * uap,register_t * retval)68 linux_ioctl_termios(struct lwp *l, const struct linux_sys_ioctl_args *uap, register_t *retval)
69 {
70 /* {
71 syscallarg(int) fd;
72 syscallarg(u_long) com;
73 syscallarg(void *) data;
74 } */
75 file_t *fp;
76 u_long com;
77 struct linux_termio tmplt;
78 struct linux_termios tmplts;
79 struct termios tmpbts;
80 int idat;
81 struct sys_ioctl_args ia;
82 int error;
83 char tioclinux;
84 int (*bsdioctl)(file_t *, u_long, void *);
85
86 if ((fp = fd_getfile(SCARG(uap, fd))) == NULL)
87 return (EBADF);
88
89 if ((fp->f_flag & (FREAD | FWRITE)) == 0) {
90 error = EBADF;
91 goto out;
92 }
93
94 bsdioctl = fp->f_ops->fo_ioctl;
95 com = SCARG(uap, com);
96 retval[0] = 0;
97
98 switch (com) {
99 case LINUX_TCGETS:
100 error = (*bsdioctl)(fp, TIOCGETA, &tmpbts);
101 if (error)
102 goto out;
103 bsd_termios_to_linux_termios(&tmpbts, &tmplts);
104 error = copyout(&tmplts, SCARG(uap, data), sizeof tmplts);
105 goto out;
106 case LINUX_TCSETS:
107 case LINUX_TCSETSW:
108 case LINUX_TCSETSF:
109 /*
110 * First fill in all fields, so that we keep the current
111 * values for fields that Linux doesn't know about.
112 */
113 error = (*bsdioctl)(fp, TIOCGETA, &tmpbts);
114 if (error)
115 goto out;
116 error = copyin(SCARG(uap, data), &tmplts, sizeof tmplts);
117 if (error)
118 goto out;
119 linux_termios_to_bsd_termios(&tmplts, &tmpbts);
120 switch (com) {
121 case LINUX_TCSETS:
122 com = TIOCSETA;
123 break;
124 case LINUX_TCSETSW:
125 com = TIOCSETAW;
126 break;
127 case LINUX_TCSETSF:
128 com = TIOCSETAF;
129 break;
130 }
131 error = (*bsdioctl)(fp, com, &tmpbts);
132 goto out;
133 case LINUX_TCGETA:
134 error = (*bsdioctl)(fp, TIOCGETA, &tmpbts);
135 if (error)
136 goto out;
137 bsd_termios_to_linux_termio(&tmpbts, &tmplt);
138 error = copyout(&tmplt, SCARG(uap, data), sizeof tmplt);
139 goto out;
140 case LINUX_TCSETA:
141 case LINUX_TCSETAW:
142 case LINUX_TCSETAF:
143 /*
144 * First fill in all fields, so that we keep the current
145 * values for fields that Linux doesn't know about.
146 */
147 error = (*bsdioctl)(fp, TIOCGETA, &tmpbts);
148 if (error)
149 goto out;
150 error = copyin(SCARG(uap, data), &tmplt, sizeof tmplt);
151 if (error)
152 goto out;
153 linux_termio_to_bsd_termios(&tmplt, &tmpbts);
154 switch (com) {
155 case LINUX_TCSETA:
156 com = TIOCSETA;
157 break;
158 case LINUX_TCSETAW:
159 com = TIOCSETAW;
160 break;
161 case LINUX_TCSETAF:
162 com = TIOCSETAF;
163 break;
164 }
165 error = (*bsdioctl)(fp, com, &tmpbts);
166 goto out;
167 case LINUX_TCFLSH:
168 switch((u_long)SCARG(uap, data)) {
169 case 0:
170 idat = FREAD;
171 break;
172 case 1:
173 idat = FWRITE;
174 break;
175 case 2:
176 idat = 0;
177 break;
178 default:
179 error = EINVAL;
180 goto out;
181 }
182 error = (*bsdioctl)(fp, TIOCFLUSH, &idat);
183 goto out;
184 case LINUX_TIOCGETD:
185 error = (*bsdioctl)(fp, TIOCGETD, &idat);
186 if (error)
187 goto out;
188 switch (idat) {
189 case TTYDISC:
190 idat = LINUX_N_TTY;
191 break;
192 case SLIPDISC:
193 idat = LINUX_N_SLIP;
194 break;
195 case PPPDISC:
196 idat = LINUX_N_PPP;
197 break;
198 case STRIPDISC:
199 idat = LINUX_N_STRIP;
200 break;
201 /*
202 * Linux does not have the tablet line discipline.
203 */
204 case TABLDISC:
205 default:
206 idat = -1; /* XXX What should this be? */
207 break;
208 }
209 error = copyout(&idat, SCARG(uap, data), sizeof idat);
210 goto out;
211 case LINUX_TIOCSETD:
212 error = copyin(SCARG(uap, data), &idat, sizeof idat);
213 if (error)
214 goto out;
215 switch (idat) {
216 case LINUX_N_TTY:
217 idat = TTYDISC;
218 break;
219 case LINUX_N_SLIP:
220 idat = SLIPDISC;
221 break;
222 case LINUX_N_PPP:
223 idat = PPPDISC;
224 break;
225 case LINUX_N_STRIP:
226 idat = STRIPDISC;
227 break;
228 /*
229 * We can't handle the mouse line discipline Linux has.
230 */
231 case LINUX_N_MOUSE:
232 case LINUX_N_AX25:
233 case LINUX_N_X25:
234 case LINUX_N_6PACK:
235 default:
236 error = EINVAL;
237 goto out;
238 }
239 error = (*bsdioctl)(fp, TIOCSETD, &idat);
240 goto out;
241 case LINUX_TIOCLINUX:
242 error = copyin(SCARG(uap, data), &tioclinux, sizeof tioclinux);
243 if (error != 0)
244 goto out;
245 switch (tioclinux) {
246 case LINUX_TIOCLINUX_KERNMSG:
247 /*
248 * XXX needed to not fail for some things. Could
249 * try to use TIOCCONS, but the char argument
250 * specifies the VT #, not an fd.
251 */
252 error = 0;
253 goto out;
254 case LINUX_TIOCLINUX_COPY:
255 case LINUX_TIOCLINUX_PASTE:
256 case LINUX_TIOCLINUX_UNBLANK:
257 case LINUX_TIOCLINUX_LOADLUT:
258 case LINUX_TIOCLINUX_READSHIFT:
259 case LINUX_TIOCLINUX_READMOUSE:
260 case LINUX_TIOCLINUX_VESABLANK:
261 case LINUX_TIOCLINUX_CURCONS: /* could use VT_GETACTIVE */
262 default:
263 error = EINVAL;
264 goto out;
265 }
266 break;
267 case LINUX_TIOCGWINSZ:
268 SCARG(&ia, com) = TIOCGWINSZ;
269 break;
270 case LINUX_TIOCSWINSZ:
271 SCARG(&ia, com) = TIOCSWINSZ;
272 break;
273 case LINUX_TIOCGPGRP:
274 SCARG(&ia, com) = TIOCGPGRP;
275 break;
276 case LINUX_TIOCSPGRP:
277 SCARG(&ia, com) = TIOCSPGRP;
278 break;
279 case LINUX_FIOCLEX:
280 SCARG(&ia, com) = FIOCLEX;
281 break;
282 case LINUX_FIONCLEX:
283 SCARG(&ia, com) = FIONCLEX;
284 break;
285 case LINUX_FIONREAD:
286 SCARG(&ia, com) = FIONREAD;
287 break;
288 case LINUX_FIONBIO:
289 SCARG(&ia, com) = FIONBIO;
290 break;
291 case LINUX_FIOASYNC:
292 SCARG(&ia, com) = FIOASYNC;
293 break;
294 case LINUX_TIOCEXCL:
295 SCARG(&ia, com) = TIOCEXCL;
296 break;
297 case LINUX_TIOCNXCL:
298 SCARG(&ia, com) = TIOCNXCL;
299 break;
300 case LINUX_TIOCCONS:
301 SCARG(&ia, com) = TIOCCONS;
302 break;
303 case LINUX_TIOCNOTTY:
304 SCARG(&ia, com) = TIOCNOTTY;
305 break;
306 case LINUX_TCSBRK:
307 idat = (u_long)SCARG(uap, data);
308 if (idat != 0)
309 SCARG(&ia, com) = TIOCDRAIN;
310 else {
311 if ((error = (*bsdioctl)(fp, TIOCSBRK, NULL)) != 0)
312 goto out;
313 error = tsleep(&idat, PZERO | PCATCH, "linux_tcsbrk", hz / 4);
314 if (error == EINTR || error == ERESTART) {
315 (void)(*bsdioctl)(fp, TIOCCBRK, NULL);
316 error = EINTR;
317 } else
318 error = (*bsdioctl)(fp, TIOCCBRK, NULL);
319 goto out;
320 }
321 break;
322 case LINUX_TIOCMGET:
323 SCARG(&ia, com) = TIOCMGET;
324 break;
325 case LINUX_TIOCMSET:
326 SCARG(&ia, com) = TIOCMSET;
327 break;
328 case LINUX_TIOCMBIC:
329 SCARG(&ia, com) = TIOCMBIC;
330 break;
331 case LINUX_TIOCMBIS:
332 SCARG(&ia, com) = TIOCMBIS;
333 break;
334 #ifdef LINUX_TIOCGPTN
335 case LINUX_TIOCGPTN:
336 #ifndef NO_DEV_PTM
337 {
338 struct ptmget ptm;
339
340 error = (*bsdioctl)(fp, TIOCPTSNAME, &ptm);
341 if (error != 0)
342 goto out;
343 error = copyout(&ptm.sfd, SCARG(uap, data),
344 sizeof(ptm.sfd));
345 goto out;
346 }
347 #endif /* NO_DEV_PTM */
348 #endif /* LINUX_TIOCGPTN */
349 #ifdef LINUX_TIOCSPTLCK
350 case LINUX_TIOCSPTLCK:
351 fd_putfile(SCARG(uap, fd));
352 error = copyin(SCARG(uap, data), &idat, sizeof(idat));
353 if (error)
354 return error;
355 DPRINTF(("TIOCSPTLCK %d\n", idat));
356 return 0;
357 #endif
358 case LINUX_TCXONC:
359 idat = (u_long)SCARG(uap, data);
360 switch (idat) {
361 case LINUX_TCOOFF:
362 SCARG(&ia, com) = TIOCSTOP;
363 break;
364 case LINUX_TCOON:
365 SCARG(&ia, com) = TIOCSTART;
366 break;
367 case LINUX_TCIOFF:
368 case LINUX_TCION:
369 default:
370 error = EINVAL;
371 goto out;
372 }
373 break;
374 default:
375 error = EINVAL;
376 goto out;
377 }
378
379 SCARG(&ia, fd) = SCARG(uap, fd);
380 SCARG(&ia, data) = SCARG(uap, data);
381 error = sys_ioctl(curlwp, &ia, retval);
382 out:
383 fd_putfile(SCARG(uap, fd));
384 return error;
385 }
386