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