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