1 /* $NetBSD: linux_termios.c,v 1.36 2008/07/04 10:13:52 matthias 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.36 2008/07/04 10:13:52 matthias 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 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 error = EINVAL; 263 goto out; 264 } 265 break; 266 case LINUX_TIOCGWINSZ: 267 SCARG(&ia, com) = TIOCGWINSZ; 268 break; 269 case LINUX_TIOCSWINSZ: 270 SCARG(&ia, com) = TIOCSWINSZ; 271 break; 272 case LINUX_TIOCGPGRP: 273 SCARG(&ia, com) = TIOCGPGRP; 274 break; 275 case LINUX_TIOCSPGRP: 276 SCARG(&ia, com) = TIOCSPGRP; 277 break; 278 case LINUX_FIONREAD: 279 SCARG(&ia, com) = FIONREAD; 280 break; 281 case LINUX_FIONBIO: 282 SCARG(&ia, com) = FIONBIO; 283 break; 284 case LINUX_FIOASYNC: 285 SCARG(&ia, com) = FIOASYNC; 286 break; 287 case LINUX_TIOCEXCL: 288 SCARG(&ia, com) = TIOCEXCL; 289 break; 290 case LINUX_TIOCNXCL: 291 SCARG(&ia, com) = TIOCNXCL; 292 break; 293 case LINUX_TIOCCONS: 294 SCARG(&ia, com) = TIOCCONS; 295 break; 296 case LINUX_TIOCNOTTY: 297 SCARG(&ia, com) = TIOCNOTTY; 298 break; 299 case LINUX_TCSBRK: 300 idat = (u_long)SCARG(uap, data); 301 if (idat != 0) 302 SCARG(&ia, com) = TIOCDRAIN; 303 else { 304 if ((error = (*bsdioctl)(fp, TIOCSBRK, NULL)) != 0) 305 goto out; 306 error = tsleep(&idat, PZERO | PCATCH, "linux_tcsbrk", hz / 4); 307 if (error == EINTR || error == ERESTART) { 308 (void)(*bsdioctl)(fp, TIOCCBRK, NULL); 309 error = EINTR; 310 } else 311 error = (*bsdioctl)(fp, TIOCCBRK, NULL); 312 goto out; 313 } 314 break; 315 case LINUX_TIOCMGET: 316 SCARG(&ia, com) = TIOCMGET; 317 break; 318 case LINUX_TIOCMSET: 319 SCARG(&ia, com) = TIOCMSET; 320 break; 321 case LINUX_TIOCMBIC: 322 SCARG(&ia, com) = TIOCMBIC; 323 break; 324 case LINUX_TIOCMBIS: 325 SCARG(&ia, com) = TIOCMBIS; 326 break; 327 #ifdef LINUX_TIOCGPTN 328 case LINUX_TIOCGPTN: 329 #ifndef NO_DEV_PTM 330 { 331 struct ptmget ptm; 332 333 error = (*bsdioctl)(fp, TIOCPTSNAME, &ptm); 334 if (error != 0) 335 goto out; 336 error = copyout(&ptm.sfd, SCARG(uap, data), 337 sizeof(ptm.sfd)); 338 goto out; 339 } 340 #endif /* NO_DEV_PTM */ 341 #endif /* LINUX_TIOCGPTN */ 342 #ifdef LINUX_TIOCSPTLCK 343 case LINUX_TIOCSPTLCK: 344 fd_putfile(SCARG(uap, fd)); 345 error = copyin(SCARG(uap, data), &idat, sizeof(idat)); 346 if (error) 347 return error; 348 DPRINTF(("TIOCSPTLCK %d\n", idat)); 349 return 0; 350 #endif 351 default: 352 error = EINVAL; 353 goto out; 354 } 355 356 SCARG(&ia, fd) = SCARG(uap, fd); 357 SCARG(&ia, data) = SCARG(uap, data); 358 error = sys_ioctl(curlwp, &ia, retval); 359 out: 360 fd_putfile(SCARG(uap, fd)); 361 return error; 362 } 363