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 67int 68linux_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); 382out: 383 fd_putfile(SCARG(uap, fd)); 384 return error; 385} 386