1 /* $NetBSD: linux_termios.c,v 1.40 2026/09/20 13:43:51 riastradh 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.40 2026/09/20 13:43:51 riastradh 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 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 memset(&ia, 0, sizeof(ia)); 99 100 switch (com) { 101 case LINUX_TCGETS: 102 error = (*bsdioctl)(fp, TIOCGETA, &tmpbts); 103 if (error) 104 goto out; 105 bsd_termios_to_linux_termios(&tmpbts, &tmplts); 106 error = copyout(&tmplts, SCARG(uap, data), sizeof tmplts); 107 goto out; 108 case LINUX_TCSETS: 109 case LINUX_TCSETSW: 110 case LINUX_TCSETSF: 111 /* 112 * First fill in all fields, so that we keep the current 113 * values for fields that Linux doesn't know about. 114 */ 115 error = (*bsdioctl)(fp, TIOCGETA, &tmpbts); 116 if (error) 117 goto out; 118 error = copyin(SCARG(uap, data), &tmplts, sizeof tmplts); 119 if (error) 120 goto out; 121 linux_termios_to_bsd_termios(&tmplts, &tmpbts); 122 switch (com) { 123 case LINUX_TCSETS: 124 com = TIOCSETA; 125 break; 126 case LINUX_TCSETSW: 127 com = TIOCSETAW; 128 break; 129 case LINUX_TCSETSF: 130 com = TIOCSETAF; 131 break; 132 } 133 error = (*bsdioctl)(fp, com, &tmpbts); 134 goto out; 135 case LINUX_TCGETA: 136 error = (*bsdioctl)(fp, TIOCGETA, &tmpbts); 137 if (error) 138 goto out; 139 bsd_termios_to_linux_termio(&tmpbts, &tmplt); 140 error = copyout(&tmplt, SCARG(uap, data), sizeof tmplt); 141 goto out; 142 case LINUX_TCSETA: 143 case LINUX_TCSETAW: 144 case LINUX_TCSETAF: 145 /* 146 * First fill in all fields, so that we keep the current 147 * values for fields that Linux doesn't know about. 148 */ 149 error = (*bsdioctl)(fp, TIOCGETA, &tmpbts); 150 if (error) 151 goto out; 152 error = copyin(SCARG(uap, data), &tmplt, sizeof tmplt); 153 if (error) 154 goto out; 155 linux_termio_to_bsd_termios(&tmplt, &tmpbts); 156 switch (com) { 157 case LINUX_TCSETA: 158 com = TIOCSETA; 159 break; 160 case LINUX_TCSETAW: 161 com = TIOCSETAW; 162 break; 163 case LINUX_TCSETAF: 164 com = TIOCSETAF; 165 break; 166 } 167 error = (*bsdioctl)(fp, com, &tmpbts); 168 goto out; 169 case LINUX_TCFLSH: 170 switch((u_long)SCARG(uap, data)) { 171 case 0: 172 idat = FREAD; 173 break; 174 case 1: 175 idat = FWRITE; 176 break; 177 case 2: 178 idat = 0; 179 break; 180 default: 181 error = EINVAL; 182 goto out; 183 } 184 error = (*bsdioctl)(fp, TIOCFLUSH, &idat); 185 goto out; 186 case LINUX_TIOCGETD: 187 error = (*bsdioctl)(fp, TIOCGETD, &idat); 188 if (error) 189 goto out; 190 switch (idat) { 191 case TTYDISC: 192 idat = LINUX_N_TTY; 193 break; 194 case SLIPDISC: 195 idat = LINUX_N_SLIP; 196 break; 197 case PPPDISC: 198 idat = LINUX_N_PPP; 199 break; 200 case STRIPDISC: 201 idat = LINUX_N_STRIP; 202 break; 203 /* 204 * Linux does not have the tablet line discipline. 205 */ 206 case TABLDISC: 207 default: 208 idat = -1; /* XXX What should this be? */ 209 break; 210 } 211 error = copyout(&idat, SCARG(uap, data), sizeof idat); 212 goto out; 213 case LINUX_TIOCSETD: 214 error = copyin(SCARG(uap, data), &idat, sizeof idat); 215 if (error) 216 goto out; 217 switch (idat) { 218 case LINUX_N_TTY: 219 idat = TTYDISC; 220 break; 221 case LINUX_N_SLIP: 222 idat = SLIPDISC; 223 break; 224 case LINUX_N_PPP: 225 idat = PPPDISC; 226 break; 227 case LINUX_N_STRIP: 228 idat = STRIPDISC; 229 break; 230 /* 231 * We can't handle the mouse line discipline Linux has. 232 */ 233 case LINUX_N_MOUSE: 234 case LINUX_N_AX25: 235 case LINUX_N_X25: 236 case LINUX_N_6PACK: 237 default: 238 error = EINVAL; 239 goto out; 240 } 241 error = (*bsdioctl)(fp, TIOCSETD, &idat); 242 goto out; 243 case LINUX_TIOCLINUX: 244 error = copyin(SCARG(uap, data), &tioclinux, sizeof tioclinux); 245 if (error != 0) 246 goto out; 247 switch (tioclinux) { 248 case LINUX_TIOCLINUX_KERNMSG: 249 /* 250 * XXX needed to not fail for some things. Could 251 * try to use TIOCCONS, but the char argument 252 * specifies the VT #, not an fd. 253 */ 254 error = 0; 255 goto out; 256 case LINUX_TIOCLINUX_COPY: 257 case LINUX_TIOCLINUX_PASTE: 258 case LINUX_TIOCLINUX_UNBLANK: 259 case LINUX_TIOCLINUX_LOADLUT: 260 case LINUX_TIOCLINUX_READSHIFT: 261 case LINUX_TIOCLINUX_READMOUSE: 262 case LINUX_TIOCLINUX_VESABLANK: 263 case LINUX_TIOCLINUX_CURCONS: /* could use VT_GETACTIVE */ 264 default: 265 error = EINVAL; 266 goto out; 267 } 268 break; 269 case LINUX_TIOCGWINSZ: 270 SCARG(&ia, com) = TIOCGWINSZ; 271 break; 272 case LINUX_TIOCSWINSZ: 273 SCARG(&ia, com) = TIOCSWINSZ; 274 break; 275 case LINUX_TIOCGPGRP: 276 SCARG(&ia, com) = TIOCGPGRP; 277 break; 278 case LINUX_TIOCSPGRP: 279 SCARG(&ia, com) = TIOCSPGRP; 280 break; 281 case LINUX_FIOCLEX: 282 SCARG(&ia, com) = FIOCLEX; 283 break; 284 case LINUX_FIONCLEX: 285 SCARG(&ia, com) = FIONCLEX; 286 break; 287 case LINUX_FIONREAD: 288 SCARG(&ia, com) = FIONREAD; 289 break; 290 case LINUX_FIONBIO: 291 SCARG(&ia, com) = FIONBIO; 292 break; 293 case LINUX_FIOASYNC: 294 SCARG(&ia, com) = FIOASYNC; 295 break; 296 case LINUX_TIOCEXCL: 297 SCARG(&ia, com) = TIOCEXCL; 298 break; 299 case LINUX_TIOCNXCL: 300 SCARG(&ia, com) = TIOCNXCL; 301 break; 302 case LINUX_TIOCCONS: 303 SCARG(&ia, com) = TIOCCONS; 304 break; 305 case LINUX_TIOCNOTTY: 306 SCARG(&ia, com) = TIOCNOTTY; 307 break; 308 case LINUX_TCSBRK: 309 idat = (u_long)SCARG(uap, data); 310 if (idat != 0) 311 SCARG(&ia, com) = TIOCDRAIN; 312 else { 313 if ((error = (*bsdioctl)(fp, TIOCSBRK, NULL)) != 0) 314 goto out; 315 error = tsleep(&idat, PZERO | PCATCH, "linux_tcsbrk", hz / 4); 316 if (error == EINTR || error == ERESTART) { 317 (void)(*bsdioctl)(fp, TIOCCBRK, NULL); 318 error = EINTR; 319 } else 320 error = (*bsdioctl)(fp, TIOCCBRK, NULL); 321 goto out; 322 } 323 break; 324 case LINUX_TIOCMGET: 325 SCARG(&ia, com) = TIOCMGET; 326 break; 327 case LINUX_TIOCMSET: 328 SCARG(&ia, com) = TIOCMSET; 329 break; 330 case LINUX_TIOCMBIC: 331 SCARG(&ia, com) = TIOCMBIC; 332 break; 333 case LINUX_TIOCMBIS: 334 SCARG(&ia, com) = TIOCMBIS; 335 break; 336 #ifdef LINUX_TIOCGPTN 337 case LINUX_TIOCGPTN: 338 #ifndef NO_DEV_PTM 339 { 340 struct ptmget ptm; 341 342 error = (*bsdioctl)(fp, TIOCPTSNAME, &ptm); 343 if (error != 0) 344 goto out; 345 error = copyout(&ptm.sfd, SCARG(uap, data), 346 sizeof(ptm.sfd)); 347 goto out; 348 } 349 #endif /* NO_DEV_PTM */ 350 #endif /* LINUX_TIOCGPTN */ 351 #ifdef LINUX_TIOCSPTLCK 352 case LINUX_TIOCSPTLCK: 353 fd_putfile(SCARG(uap, fd)); 354 error = copyin(SCARG(uap, data), &idat, sizeof(idat)); 355 if (error) 356 return error; 357 DPRINTF(("TIOCSPTLCK %d\n", idat)); 358 return 0; 359 #endif 360 case LINUX_TCXONC: 361 idat = (u_long)SCARG(uap, data); 362 switch (idat) { 363 case LINUX_TCOOFF: 364 SCARG(&ia, com) = TIOCSTOP; 365 break; 366 case LINUX_TCOON: 367 SCARG(&ia, com) = TIOCSTART; 368 break; 369 case LINUX_TCIOFF: 370 case LINUX_TCION: 371 default: 372 error = EINVAL; 373 goto out; 374 } 375 break; 376 default: 377 error = EINVAL; 378 goto out; 379 } 380 381 SCARG(&ia, fd) = SCARG(uap, fd); 382 SCARG(&ia, data) = SCARG(uap, data); 383 error = sys_ioctl(curlwp, &ia, retval); 384 out: 385 fd_putfile(SCARG(uap, fd)); 386 return error; 387 } 388