1 /*- 2 * SPDX-License-Identifier: BSD-3-Clause 3 * 4 * Copyright (c) 1982, 1986, 1991, 1993 5 * The Regents of the University of California. All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 3. Neither the name of the University nor the names of its contributors 16 * may be used to endorse or promote products derived from this software 17 * without specific prior written permission. 18 * 19 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 20 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 21 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 22 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 23 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 24 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 25 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 26 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 27 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 28 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 29 * SUCH DAMAGE. 30 * 31 * @(#)tty_compat.c 8.1 (Berkeley) 6/10/93 32 */ 33 34 #include <sys/cdefs.h> 35 __FBSDID("$FreeBSD$"); 36 37 #include "opt_compat.h" 38 39 /* 40 * mapping routines for old line discipline (yuck) 41 */ 42 43 #include <sys/param.h> 44 #include <sys/systm.h> 45 #include <sys/ioctl_compat.h> 46 #include <sys/tty.h> 47 #include <sys/kernel.h> 48 #include <sys/sysctl.h> 49 50 struct speedtab { 51 int sp_speed; /* Speed. */ 52 int sp_code; /* Code. */ 53 }; 54 55 static int ttcompatgetflags(struct tty *tp); 56 static void ttcompatsetflags(struct tty *tp, struct termios *t); 57 static void ttcompatsetlflags(struct tty *tp, struct termios *t); 58 static int ttcompatspeedtab(int speed, struct speedtab *table); 59 60 static int ttydebug = 0; 61 SYSCTL_INT(_debug, OID_AUTO, ttydebug, CTLFLAG_RW, &ttydebug, 0, ""); 62 63 static struct speedtab compatspeeds[] = { 64 #define MAX_SPEED 17 65 { 115200, 17 }, 66 { 57600, 16 }, 67 { 38400, 15 }, 68 { 19200, 14 }, 69 { 9600, 13 }, 70 { 4800, 12 }, 71 { 2400, 11 }, 72 { 1800, 10 }, 73 { 1200, 9 }, 74 { 600, 8 }, 75 { 300, 7 }, 76 { 200, 6 }, 77 { 150, 5 }, 78 { 134, 4 }, 79 { 110, 3 }, 80 { 75, 2 }, 81 { 50, 1 }, 82 { 0, 0 }, 83 { -1, -1 }, 84 }; 85 static int compatspcodes[] = { 86 0, 50, 75, 110, 134, 150, 200, 300, 600, 1200, 87 1800, 2400, 4800, 9600, 19200, 38400, 57600, 115200, 88 }; 89 90 static int 91 ttcompatspeedtab(int speed, struct speedtab *table) 92 { 93 if (speed == 0) 94 return (0); /* hangup */ 95 for ( ; table->sp_speed > 0; table++) 96 if (table->sp_speed <= speed) /* nearest one, rounded down */ 97 return (table->sp_code); 98 return (1); /* 50, min and not hangup */ 99 } 100 101 static int 102 ttsetcompat(struct tty *tp, u_long *com, caddr_t data, struct termios *term) 103 { 104 switch (*com) { 105 case TIOCSETP: 106 case TIOCSETN: { 107 struct sgttyb *sg = (struct sgttyb *)data; 108 int speed; 109 110 if ((speed = sg->sg_ispeed) > MAX_SPEED || speed < 0) 111 return(EINVAL); 112 else if (speed != ttcompatspeedtab(tp->t_termios.c_ispeed, 113 compatspeeds)) 114 term->c_ispeed = compatspcodes[speed]; 115 else 116 term->c_ispeed = tp->t_termios.c_ispeed; 117 if ((speed = sg->sg_ospeed) > MAX_SPEED || speed < 0) 118 return(EINVAL); 119 else if (speed != ttcompatspeedtab(tp->t_termios.c_ospeed, 120 compatspeeds)) 121 term->c_ospeed = compatspcodes[speed]; 122 else 123 term->c_ospeed = tp->t_termios.c_ospeed; 124 term->c_cc[VERASE] = sg->sg_erase; 125 term->c_cc[VKILL] = sg->sg_kill; 126 tp->t_compatflags = (tp->t_compatflags&0xffff0000) | 127 (sg->sg_flags&0xffff); 128 ttcompatsetflags(tp, term); 129 *com = (*com == TIOCSETP) ? TIOCSETAF : TIOCSETA; 130 break; 131 } 132 case TIOCSETC: { 133 struct tchars *tc = (struct tchars *)data; 134 cc_t *cc; 135 136 cc = term->c_cc; 137 cc[VINTR] = tc->t_intrc; 138 cc[VQUIT] = tc->t_quitc; 139 cc[VSTART] = tc->t_startc; 140 cc[VSTOP] = tc->t_stopc; 141 cc[VEOF] = tc->t_eofc; 142 cc[VEOL] = tc->t_brkc; 143 if (tc->t_brkc == (char)_POSIX_VDISABLE) 144 cc[VEOL2] = _POSIX_VDISABLE; 145 *com = TIOCSETA; 146 break; 147 } 148 case TIOCSLTC: { 149 struct ltchars *ltc = (struct ltchars *)data; 150 cc_t *cc; 151 152 cc = term->c_cc; 153 cc[VSUSP] = ltc->t_suspc; 154 cc[VDSUSP] = ltc->t_dsuspc; 155 cc[VREPRINT] = ltc->t_rprntc; 156 cc[VDISCARD] = ltc->t_flushc; 157 cc[VWERASE] = ltc->t_werasc; 158 cc[VLNEXT] = ltc->t_lnextc; 159 *com = TIOCSETA; 160 break; 161 } 162 case TIOCLBIS: 163 case TIOCLBIC: 164 case TIOCLSET: 165 if (*com == TIOCLSET) 166 tp->t_compatflags = (tp->t_compatflags&0xffff) | 167 *(int *)data<<16; 168 else { 169 tp->t_compatflags = (ttcompatgetflags(tp)&0xffff0000) | 170 (tp->t_compatflags&0xffff); 171 if (*com == TIOCLBIS) 172 tp->t_compatflags |= *(int *)data<<16; 173 else 174 tp->t_compatflags &= ~(*(int *)data<<16); 175 } 176 ttcompatsetlflags(tp, term); 177 *com = TIOCSETA; 178 break; 179 } 180 return 0; 181 } 182 183 /*ARGSUSED*/ 184 int 185 tty_ioctl_compat(struct tty *tp, u_long com, caddr_t data, int fflag, 186 struct thread *td) 187 { 188 switch (com) { 189 case TIOCSETP: 190 case TIOCSETN: 191 case TIOCSETC: 192 case TIOCSLTC: 193 case TIOCLBIS: 194 case TIOCLBIC: 195 case TIOCLSET: { 196 struct termios term; 197 int error; 198 199 term = tp->t_termios; 200 if ((error = ttsetcompat(tp, &com, data, &term)) != 0) 201 return error; 202 return tty_ioctl(tp, com, &term, fflag, td); 203 } 204 case TIOCGETP: { 205 struct sgttyb *sg = (struct sgttyb *)data; 206 cc_t *cc = tp->t_termios.c_cc; 207 208 sg->sg_ospeed = ttcompatspeedtab(tp->t_termios.c_ospeed, 209 compatspeeds); 210 if (tp->t_termios.c_ispeed == 0) 211 sg->sg_ispeed = sg->sg_ospeed; 212 else 213 sg->sg_ispeed = ttcompatspeedtab(tp->t_termios.c_ispeed, 214 compatspeeds); 215 sg->sg_erase = cc[VERASE]; 216 sg->sg_kill = cc[VKILL]; 217 sg->sg_flags = tp->t_compatflags = ttcompatgetflags(tp); 218 break; 219 } 220 case TIOCGETC: { 221 struct tchars *tc = (struct tchars *)data; 222 cc_t *cc = tp->t_termios.c_cc; 223 224 tc->t_intrc = cc[VINTR]; 225 tc->t_quitc = cc[VQUIT]; 226 tc->t_startc = cc[VSTART]; 227 tc->t_stopc = cc[VSTOP]; 228 tc->t_eofc = cc[VEOF]; 229 tc->t_brkc = cc[VEOL]; 230 break; 231 } 232 case TIOCGLTC: { 233 struct ltchars *ltc = (struct ltchars *)data; 234 cc_t *cc = tp->t_termios.c_cc; 235 236 ltc->t_suspc = cc[VSUSP]; 237 ltc->t_dsuspc = cc[VDSUSP]; 238 ltc->t_rprntc = cc[VREPRINT]; 239 ltc->t_flushc = cc[VDISCARD]; 240 ltc->t_werasc = cc[VWERASE]; 241 ltc->t_lnextc = cc[VLNEXT]; 242 break; 243 } 244 case TIOCLGET: 245 tp->t_compatflags = 246 (ttcompatgetflags(tp) & 0xffff0000UL) 247 | (tp->t_compatflags & 0xffff); 248 *(int *)data = tp->t_compatflags>>16; 249 if (ttydebug) 250 printf("CLGET: returning %x\n", *(int *)data); 251 break; 252 253 case OTIOCGETD: 254 *(int *)data = 2; 255 break; 256 257 case OTIOCSETD: { 258 int ldisczero = 0; 259 260 return (tty_ioctl(tp, TIOCSETD, 261 *(int *)data == 2 ? (caddr_t)&ldisczero : data, 262 fflag, td)); 263 } 264 265 case OTIOCCONS: { 266 int one = 1; 267 268 return (tty_ioctl(tp, TIOCCONS, (caddr_t)&one, fflag, td)); 269 } 270 271 default: 272 return (ENOIOCTL); 273 } 274 return (0); 275 } 276 277 static int 278 ttcompatgetflags(struct tty *tp) 279 { 280 tcflag_t iflag = tp->t_termios.c_iflag; 281 tcflag_t lflag = tp->t_termios.c_lflag; 282 tcflag_t oflag = tp->t_termios.c_oflag; 283 tcflag_t cflag = tp->t_termios.c_cflag; 284 int flags = 0; 285 286 if (iflag&IXOFF) 287 flags |= TANDEM; 288 if (iflag&ICRNL || oflag&ONLCR) 289 flags |= CRMOD; 290 if ((cflag&CSIZE) == CS8) { 291 flags |= PASS8; 292 if (iflag&ISTRIP) 293 flags |= ANYP; 294 } 295 else if (cflag&PARENB) { 296 if (iflag&INPCK) { 297 if (cflag&PARODD) 298 flags |= ODDP; 299 else 300 flags |= EVENP; 301 } else 302 flags |= EVENP | ODDP; 303 } 304 305 if ((lflag&ICANON) == 0) { 306 /* fudge */ 307 if (iflag&(INPCK|ISTRIP|IXON) || lflag&(IEXTEN|ISIG) 308 || (cflag&(CSIZE|PARENB)) != CS8) 309 flags |= CBREAK; 310 else 311 flags |= RAW; 312 } 313 if (!(flags&RAW) && !(oflag&OPOST) && (cflag&(CSIZE|PARENB)) == CS8) 314 flags |= LITOUT; 315 if (cflag&MDMBUF) 316 flags |= MDMBUF; 317 if ((cflag&HUPCL) == 0) 318 flags |= NOHANG; 319 if (oflag&TAB3) 320 flags |= XTABS; 321 if (lflag&ECHOE) 322 flags |= CRTERA|CRTBS; 323 if (lflag&ECHOKE) 324 flags |= CRTKIL|CRTBS; 325 if (lflag&ECHOPRT) 326 flags |= PRTERA; 327 if (lflag&ECHOCTL) 328 flags |= CTLECH; 329 if ((iflag&IXANY) == 0) 330 flags |= DECCTQ; 331 flags |= lflag&(ECHO|TOSTOP|FLUSHO|PENDIN|NOFLSH); 332 if (ttydebug) 333 printf("getflags: %x\n", flags); 334 return (flags); 335 } 336 337 static void 338 ttcompatsetflags(struct tty *tp, struct termios *t) 339 { 340 int flags = tp->t_compatflags; 341 tcflag_t iflag = t->c_iflag; 342 tcflag_t oflag = t->c_oflag; 343 tcflag_t lflag = t->c_lflag; 344 tcflag_t cflag = t->c_cflag; 345 346 if (flags & RAW) { 347 iflag = IGNBRK; 348 lflag &= ~(ECHOCTL|ISIG|ICANON|IEXTEN); 349 } else { 350 iflag &= ~(PARMRK|IGNPAR|IGNCR|INLCR); 351 iflag |= BRKINT|IXON|IMAXBEL; 352 lflag |= ISIG|IEXTEN|ECHOCTL; /* XXX was echoctl on ? */ 353 if (flags & XTABS) 354 oflag |= TAB3; 355 else 356 oflag &= ~TAB3; 357 if (flags & CBREAK) 358 lflag &= ~ICANON; 359 else 360 lflag |= ICANON; 361 if (flags&CRMOD) { 362 iflag |= ICRNL; 363 oflag |= ONLCR; 364 } else { 365 iflag &= ~ICRNL; 366 oflag &= ~ONLCR; 367 } 368 } 369 if (flags&ECHO) 370 lflag |= ECHO; 371 else 372 lflag &= ~ECHO; 373 374 cflag &= ~(CSIZE|PARENB); 375 if (flags&(RAW|LITOUT|PASS8)) { 376 cflag |= CS8; 377 if (!(flags&(RAW|PASS8)) 378 || (flags&(RAW|PASS8|ANYP)) == (PASS8|ANYP)) 379 iflag |= ISTRIP; 380 else 381 iflag &= ~ISTRIP; 382 if (flags&(RAW|LITOUT)) 383 oflag &= ~OPOST; 384 else 385 oflag |= OPOST; 386 } else { 387 cflag |= CS7|PARENB; 388 iflag |= ISTRIP; 389 oflag |= OPOST; 390 } 391 /* XXX don't set INPCK if RAW or PASS8? */ 392 if ((flags&(EVENP|ODDP)) == EVENP) { 393 iflag |= INPCK; 394 cflag &= ~PARODD; 395 } else if ((flags&(EVENP|ODDP)) == ODDP) { 396 iflag |= INPCK; 397 cflag |= PARODD; 398 } else 399 iflag &= ~INPCK; 400 if (flags&TANDEM) 401 iflag |= IXOFF; 402 else 403 iflag &= ~IXOFF; 404 if ((flags&DECCTQ) == 0) 405 iflag |= IXANY; 406 else 407 iflag &= ~IXANY; 408 t->c_iflag = iflag; 409 t->c_oflag = oflag; 410 t->c_lflag = lflag; 411 t->c_cflag = cflag; 412 } 413 414 static void 415 ttcompatsetlflags(struct tty *tp, struct termios *t) 416 { 417 int flags = tp->t_compatflags; 418 tcflag_t iflag = t->c_iflag; 419 tcflag_t oflag = t->c_oflag; 420 tcflag_t lflag = t->c_lflag; 421 tcflag_t cflag = t->c_cflag; 422 423 iflag &= ~(PARMRK|IGNPAR|IGNCR|INLCR); 424 if (flags&CRTERA) 425 lflag |= ECHOE; 426 else 427 lflag &= ~ECHOE; 428 if (flags&CRTKIL) 429 lflag |= ECHOKE; 430 else 431 lflag &= ~ECHOKE; 432 if (flags&PRTERA) 433 lflag |= ECHOPRT; 434 else 435 lflag &= ~ECHOPRT; 436 if (flags&CTLECH) 437 lflag |= ECHOCTL; 438 else 439 lflag &= ~ECHOCTL; 440 if (flags&TANDEM) 441 iflag |= IXOFF; 442 else 443 iflag &= ~IXOFF; 444 if ((flags&DECCTQ) == 0) 445 iflag |= IXANY; 446 else 447 iflag &= ~IXANY; 448 if (flags & MDMBUF) 449 cflag |= MDMBUF; 450 else 451 cflag &= ~MDMBUF; 452 if (flags&NOHANG) 453 cflag &= ~HUPCL; 454 else 455 cflag |= HUPCL; 456 lflag &= ~(TOSTOP|FLUSHO|PENDIN|NOFLSH); 457 lflag |= flags&(TOSTOP|FLUSHO|PENDIN|NOFLSH); 458 459 /* 460 * The next if-else statement is copied from above so don't bother 461 * checking it separately. We could avoid fiddlling with the 462 * character size if the mode is already RAW or if neither the 463 * LITOUT bit or the PASS8 bit is being changed, but the delta of 464 * the change is not available here and skipping the RAW case would 465 * make the code different from above. 466 */ 467 cflag &= ~(CSIZE|PARENB); 468 if (flags&(RAW|LITOUT|PASS8)) { 469 cflag |= CS8; 470 if (!(flags&(RAW|PASS8)) 471 || (flags&(RAW|PASS8|ANYP)) == (PASS8|ANYP)) 472 iflag |= ISTRIP; 473 else 474 iflag &= ~ISTRIP; 475 if (flags&(RAW|LITOUT)) 476 oflag &= ~OPOST; 477 else 478 oflag |= OPOST; 479 } else { 480 cflag |= CS7|PARENB; 481 iflag |= ISTRIP; 482 oflag |= OPOST; 483 } 484 t->c_iflag = iflag; 485 t->c_oflag = oflag; 486 t->c_lflag = lflag; 487 t->c_cflag = cflag; 488 } 489