1 /* 2 * Copyright (c) 1982, 1986, 1989, 1993 3 * The Regents of the University of California. All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 1. Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * 2. Redistributions in binary form must reproduce the above copyright 11 * notice, this list of conditions and the following disclaimer in the 12 * documentation and/or other materials provided with the distribution. 13 * 4. Neither the name of the University nor the names of its contributors 14 * may be used to endorse or promote products derived from this software 15 * without specific prior written permission. 16 * 17 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 18 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 21 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 22 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 23 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 24 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 25 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 26 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 27 * SUCH DAMAGE. 28 * 29 */ 30 31 #include <sys/cdefs.h> 32 __FBSDID("$FreeBSD$"); 33 34 /* 35 * Pseudo-nulmodem driver 36 * Mighty handy for use with serial console in Vmware 37 */ 38 39 #include "opt_compat.h" 40 #include "opt_tty.h" 41 42 #include <sys/param.h> 43 #include <sys/systm.h> 44 #if defined(COMPAT_43) || defined(COMPAT_SUNOS) 45 #include <sys/ioctl_compat.h> 46 #endif 47 #include <sys/proc.h> 48 #include <sys/tty.h> 49 #include <sys/conf.h> 50 #include <sys/fcntl.h> 51 #include <sys/poll.h> 52 #include <sys/kernel.h> 53 #include <sys/vnode.h> 54 #include <sys/signalvar.h> 55 #include <sys/malloc.h> 56 57 MALLOC_DEFINE(M_NLMDM, "nullmodem", "nullmodem data structures"); 58 59 static void nmdmstart(struct tty *tp); 60 static void nmdmstop(struct tty *tp, int rw); 61 static void wakeup_other(struct tty *tp, int flag); 62 static void nmdminit(dev_t dev); 63 64 static d_open_t nmdmopen; 65 static d_close_t nmdmclose; 66 static d_read_t nmdmread; 67 static d_write_t nmdmwrite; 68 static d_ioctl_t nmdmioctl; 69 70 static struct cdevsw nmdm_cdevsw = { 71 .d_version = D_VERSION, 72 .d_open = nmdmopen, 73 .d_close = nmdmclose, 74 .d_read = nmdmread, 75 .d_write = nmdmwrite, 76 .d_ioctl = nmdmioctl, 77 .d_name = "nmdn", 78 .d_flags = D_TTY | D_PSEUDO | D_NEEDGIANT, 79 }; 80 81 #define BUFSIZ 100 /* Chunk size iomoved to/from user */ 82 #define NMDM_MAX_NUM 128 /* Artificially limit # devices. */ 83 #define PF_STOPPED 0x10 /* user told stopped */ 84 #define BFLAG CLONE_FLAG0 85 86 struct softpart { 87 struct tty nm_tty; 88 dev_t dev; 89 int modemsignals; /* bits defined in sys/ttycom.h */ 90 int gotbreak; 91 }; 92 93 struct nm_softc { 94 TAILQ_ENTRY(nm_softc) pt_list; 95 int pt_flags; 96 struct softpart part1, part2; 97 struct prison *pt_prison; 98 }; 99 100 static struct clonedevs *nmdmclones; 101 static TAILQ_HEAD(,nm_softc) nmdmhead = TAILQ_HEAD_INITIALIZER(nmdmhead); 102 103 static void 104 nmdm_clone(void *arg, char *name, int nameen, dev_t *dev) 105 { 106 int i, unit; 107 char *p; 108 dev_t d1, d2; 109 110 if (*dev != NODEV) 111 return; 112 if (strcmp(name, "nmdm") == 0) { 113 p = NULL; 114 unit = -1; 115 } else { 116 i = dev_stdclone(name, &p, "nmdm", &unit); 117 if (i == 0) 118 return; 119 if (p[0] != '\0' && p[0] != 'A' && p[0] != 'B') 120 return; 121 else if (p[0] != '\0' && p[1] != '\0') 122 return; 123 } 124 i = clone_create(&nmdmclones, &nmdm_cdevsw, &unit, &d1, 0); 125 if (i) { 126 d1 = make_dev(&nmdm_cdevsw, unit2minor(unit), 127 0, 0, 0666, "nmdm%dA", unit); 128 if (d1 == NULL) 129 return; 130 d2 = make_dev(&nmdm_cdevsw, unit2minor(unit) | BFLAG, 131 0, 0, 0666, "nmdm%dB", unit); 132 if (d2 == NULL) { 133 destroy_dev(d1); 134 return; 135 } 136 d2->si_drv2 = d1; 137 d1->si_drv2 = d2; 138 dev_depends(d1, d2); 139 dev_depends(d2, d1); 140 d1->si_flags |= SI_CHEAPCLONE; 141 d2->si_flags |= SI_CHEAPCLONE; 142 } 143 if (p != NULL && p[0] == 'B') 144 *dev = d1->si_drv2; 145 else 146 *dev = d1; 147 } 148 149 static void 150 nmdm_crossover(struct nm_softc *pti, 151 struct softpart *ourpart, 152 struct softpart *otherpart); 153 154 #define GETPARTS(tp, ourpart, otherpart) \ 155 do { \ 156 struct nm_softc *pti = tp->t_dev->si_drv1; \ 157 if (tp == &pti->part1.nm_tty) { \ 158 ourpart = &pti->part1; \ 159 otherpart = &pti->part2; \ 160 } else { \ 161 ourpart = &pti->part2; \ 162 otherpart = &pti->part1; \ 163 } \ 164 } while (0) 165 166 /* 167 * This function creates and initializes a pair of ttys. 168 */ 169 static void 170 nmdminit(dev_t dev1) 171 { 172 dev_t dev2; 173 struct nm_softc *pt; 174 175 dev2 = dev1->si_drv2; 176 177 dev1->si_flags &= ~SI_CHEAPCLONE; 178 dev2->si_flags &= ~SI_CHEAPCLONE; 179 180 pt = malloc(sizeof(*pt), M_NLMDM, M_WAITOK | M_ZERO); 181 TAILQ_INSERT_TAIL(&nmdmhead, pt, pt_list); 182 dev1->si_drv1 = dev2->si_drv1 = pt; 183 184 pt->part1.dev = dev1; 185 pt->part2.dev = dev2; 186 dev1->si_tty = &pt->part1.nm_tty; 187 dev2->si_tty = &pt->part2.nm_tty; 188 ttyregister(&pt->part1.nm_tty); 189 ttyregister(&pt->part2.nm_tty); 190 pt->part1.nm_tty.t_oproc = nmdmstart; 191 pt->part2.nm_tty.t_oproc = nmdmstart; 192 pt->part1.nm_tty.t_stop = nmdmstop; 193 pt->part2.nm_tty.t_stop = nmdmstop; 194 pt->part2.nm_tty.t_dev = dev1; 195 pt->part1.nm_tty.t_dev = dev2; 196 } 197 198 /* 199 * Device opened from userland 200 */ 201 static int 202 nmdmopen(dev_t dev, int flag, int devtype, struct thread *td) 203 { 204 register struct tty *tp, *tp2; 205 int error; 206 struct nm_softc *pti; 207 struct softpart *ourpart, *otherpart; 208 209 if (dev->si_drv1 == NULL) 210 nmdminit(dev); 211 pti = dev->si_drv1; 212 213 if (minor(dev) & BFLAG) 214 tp = &pti->part2.nm_tty; 215 else 216 tp = &pti->part1.nm_tty; 217 GETPARTS(tp, ourpart, otherpart); 218 219 tp2 = &otherpart->nm_tty; 220 ourpart->modemsignals |= TIOCM_LE; 221 222 if ((tp->t_state & TS_ISOPEN) == 0) { 223 ttychars(tp); /* Set up default chars */ 224 tp->t_iflag = TTYDEF_IFLAG; 225 tp->t_oflag = TTYDEF_OFLAG; 226 tp->t_lflag = TTYDEF_LFLAG; 227 tp->t_cflag = TTYDEF_CFLAG; 228 tp->t_ispeed = tp->t_ospeed = TTYDEF_SPEED; 229 } else if (tp->t_state & TS_XCLUDE && suser(td)) { 230 return (EBUSY); 231 } else if (pti->pt_prison != td->td_ucred->cr_prison) { 232 return (EBUSY); 233 } 234 235 /* 236 * If the other side is open we have carrier 237 */ 238 if (tp2->t_state & TS_ISOPEN) { 239 (void)(*linesw[tp->t_line].l_modem)(tp, 1); 240 } 241 242 /* 243 * And the other side gets carrier as we are now open. 244 */ 245 (void)(*linesw[tp2->t_line].l_modem)(tp2, 1); 246 247 /* External processing makes no sense here */ 248 tp->t_lflag &= ~EXTPROC; 249 250 /* 251 * Wait here if we don't have carrier. 252 */ 253 #if 0 254 while ((tp->t_state & TS_CARR_ON) == 0) { 255 if (flag & FNONBLOCK) 256 break; 257 error = ttysleep(tp, TSA_CARR_ON(tp), TTIPRI | PCATCH, 258 "nmdopn", 0); 259 if (error) 260 return (error); 261 } 262 #endif 263 264 /* 265 * Give the line disciplin a chance to set this end up. 266 */ 267 error = (*linesw[tp->t_line].l_open)(dev, tp); 268 269 /* 270 * Wake up the other side. 271 * Theoretically not needed. 272 */ 273 ourpart->modemsignals |= TIOCM_DTR; 274 nmdm_crossover(pti, ourpart, otherpart); 275 if (error == 0) 276 wakeup_other(tp, FREAD|FWRITE); /* XXX */ 277 return (error); 278 } 279 280 /* 281 * Device closed again 282 */ 283 static int 284 nmdmclose(dev_t dev, int flag, int mode, struct thread *td) 285 { 286 register struct tty *tp, *tp2; 287 int err; 288 struct softpart *ourpart, *otherpart; 289 290 /* 291 * let the other end know that the game is up 292 */ 293 tp = dev->si_tty; 294 GETPARTS(tp, ourpart, otherpart); 295 tp2 = &otherpart->nm_tty; 296 (void)(*linesw[tp2->t_line].l_modem)(tp2, 0); 297 298 /* 299 * XXX MDMBUF makes no sense for nmdms but would inhibit the above 300 * l_modem(). CLOCAL makes sense but isn't supported. Special 301 * l_modem()s that ignore carrier drop make no sense for nmdms but 302 * may be in use because other parts of the line discipline make 303 * sense for nmdms. Recover by doing everything that a normal 304 * ttymodem() would have done except for sending a SIGHUP. 305 */ 306 if (tp2->t_state & TS_ISOPEN) { 307 tp2->t_state &= ~(TS_CARR_ON | TS_CONNECTED); 308 tp2->t_state |= TS_ZOMBIE; 309 ttyflush(tp2, FREAD | FWRITE); 310 } 311 312 err = (*linesw[tp->t_line].l_close)(tp, flag); 313 ourpart->modemsignals &= ~TIOCM_DTR; 314 nmdm_crossover(dev->si_drv1, ourpart, otherpart); 315 nmdmstop(tp, FREAD|FWRITE); 316 (void) ttyclose(tp); 317 return (err); 318 } 319 320 /* 321 * handle read(2) request from userland 322 */ 323 static int 324 nmdmread(dev_t dev, struct uio *uio, int flag) 325 { 326 int error = 0; 327 struct tty *tp, *tp2; 328 struct softpart *ourpart, *otherpart; 329 330 tp = dev->si_tty; 331 GETPARTS(tp, ourpart, otherpart); 332 tp2 = &otherpart->nm_tty; 333 334 #if 0 335 if (tp2->t_state & TS_ISOPEN) { 336 error = (*linesw[tp->t_line].l_read)(tp, uio, flag); 337 wakeup_other(tp, FWRITE); 338 } else { 339 if (flag & IO_NDELAY) { 340 return (EWOULDBLOCK); 341 } 342 error = tsleep(TSA_PTC_READ(tp), 343 TTIPRI | PCATCH, "nmdout", 0); 344 } 345 } 346 #else 347 if ((error = (*linesw[tp->t_line].l_read)(tp, uio, flag)) == 0) 348 wakeup_other(tp, FWRITE); 349 #endif 350 return (error); 351 } 352 353 /* 354 * Write to pseudo-tty. 355 * Wakeups of controlling tty will happen 356 * indirectly, when tty driver calls nmdmstart. 357 */ 358 static int 359 nmdmwrite(dev_t dev, struct uio *uio, int flag) 360 { 361 register u_char *cp = 0; 362 register int cc = 0; 363 u_char locbuf[BUFSIZ]; 364 int cnt = 0; 365 int error = 0; 366 struct tty *tp1, *tp; 367 struct softpart *ourpart, *otherpart; 368 369 tp1 = dev->si_tty; 370 /* 371 * Get the other tty struct. 372 * basically we are writing into the INPUT side of the other device. 373 */ 374 GETPARTS(tp1, ourpart, otherpart); 375 tp = &otherpart->nm_tty; 376 377 again: 378 if ((tp->t_state & TS_ISOPEN) == 0) 379 return (EIO); 380 while (uio->uio_resid > 0 || cc > 0) { 381 /* 382 * Fill up the buffer if it's empty 383 */ 384 if (cc == 0) { 385 cc = min(uio->uio_resid, BUFSIZ); 386 cp = locbuf; 387 error = uiomove((caddr_t)cp, cc, uio); 388 if (error) 389 return (error); 390 /* check again for safety */ 391 if ((tp->t_state & TS_ISOPEN) == 0) { 392 /* adjust for data copied in but not written */ 393 uio->uio_resid += cc; 394 return (EIO); 395 } 396 } 397 while (cc > 0) { 398 if (((tp->t_rawq.c_cc + tp->t_canq.c_cc) >= (TTYHOG-2)) 399 && ((tp->t_canq.c_cc > 0) || !(tp->t_iflag&ICANON))) { 400 /* 401 * Come here to wait for space in outq, 402 * or space in rawq, or an empty canq. 403 */ 404 wakeup(TSA_HUP_OR_INPUT(tp)); 405 if ((tp->t_state & TS_CONNECTED) == 0) { 406 /* 407 * Data piled up because not connected. 408 * Adjust for data copied in but 409 * not written. 410 */ 411 uio->uio_resid += cc; 412 return (EIO); 413 } 414 if (flag & IO_NDELAY) { 415 /* 416 * Don't wait if asked not to. 417 * Adjust for data copied in but 418 * not written. 419 */ 420 uio->uio_resid += cc; 421 if (cnt == 0) 422 return (EWOULDBLOCK); 423 return (0); 424 } 425 error = tsleep(TSA_PTC_WRITE(tp), 426 TTOPRI | PCATCH, "nmdout", 0); 427 if (error) { 428 /* 429 * Tsleep returned (signal?). 430 * Go find out what the user wants. 431 * adjust for data copied in but 432 * not written 433 */ 434 uio->uio_resid += cc; 435 return (error); 436 } 437 goto again; 438 } 439 (*linesw[tp->t_line].l_rint)(*cp++, tp); 440 cnt++; 441 cc--; 442 } 443 cc = 0; 444 } 445 return (0); 446 } 447 448 /* 449 * Start output on pseudo-tty. 450 * Wake up process selecting or sleeping for input from controlling tty. 451 */ 452 static void 453 nmdmstart(struct tty *tp) 454 { 455 register struct nm_softc *pti = tp->t_dev->si_drv1; 456 457 if (tp->t_state & TS_TTSTOP) 458 return; 459 pti->pt_flags &= ~PF_STOPPED; 460 wakeup_other(tp, FREAD); 461 } 462 463 /* Wakes up the OTHER tty;*/ 464 static void 465 wakeup_other(struct tty *tp, int flag) 466 { 467 struct softpart *ourpart, *otherpart; 468 469 GETPARTS(tp, ourpart, otherpart); 470 if (flag & FREAD) { 471 selwakeuppri(&otherpart->nm_tty.t_rsel, TTIPRI); 472 wakeup(TSA_PTC_READ((&otherpart->nm_tty))); 473 } 474 if (flag & FWRITE) { 475 selwakeuppri(&otherpart->nm_tty.t_wsel, TTOPRI); 476 wakeup(TSA_PTC_WRITE((&otherpart->nm_tty))); 477 } 478 } 479 480 /* 481 * stopped output on tty, called when device is closed 482 */ 483 static void 484 nmdmstop(register struct tty *tp, int flush) 485 { 486 struct nm_softc *pti = tp->t_dev->si_drv1; 487 int flag; 488 489 /* note: FLUSHREAD and FLUSHWRITE already ok */ 490 if (flush == 0) { 491 flush = TIOCPKT_STOP; 492 pti->pt_flags |= PF_STOPPED; 493 } else 494 pti->pt_flags &= ~PF_STOPPED; 495 /* change of perspective */ 496 flag = 0; 497 if (flush & FREAD) 498 flag |= FWRITE; 499 if (flush & FWRITE) 500 flag |= FREAD; 501 wakeup_other(tp, flag); 502 } 503 504 /* 505 * handle ioctl(2) request from userland 506 */ 507 static int 508 nmdmioctl(dev_t dev, u_long cmd, caddr_t data, int flag, struct thread *td) 509 { 510 register struct tty *tp = dev->si_tty; 511 struct nm_softc *pti = dev->si_drv1; 512 int error, s; 513 register struct tty *tp2; 514 struct softpart *ourpart, *otherpart; 515 516 s = spltty(); 517 GETPARTS(tp, ourpart, otherpart); 518 tp2 = &otherpart->nm_tty; 519 520 error = (*linesw[tp->t_line].l_ioctl)(tp, cmd, data, flag, td); 521 if (error == ENOIOCTL) 522 error = ttioctl(tp, cmd, data, flag); 523 if (error == ENOIOCTL) { 524 switch (cmd) { 525 case TIOCSBRK: 526 otherpart->gotbreak = 1; 527 break; 528 case TIOCCBRK: 529 break; 530 case TIOCSDTR: 531 ourpart->modemsignals |= TIOCM_DTR; 532 break; 533 case TIOCCDTR: 534 ourpart->modemsignals &= TIOCM_DTR; 535 break; 536 case TIOCMSET: 537 ourpart->modemsignals = *(int *)data; 538 otherpart->modemsignals = *(int *)data; 539 break; 540 case TIOCMBIS: 541 ourpart->modemsignals |= *(int *)data; 542 break; 543 case TIOCMBIC: 544 ourpart->modemsignals &= ~(*(int *)data); 545 otherpart->modemsignals &= ~(*(int *)data); 546 break; 547 case TIOCMGET: 548 *(int *)data = ourpart->modemsignals; 549 break; 550 case TIOCMSDTRWAIT: 551 break; 552 case TIOCMGDTRWAIT: 553 *(int *)data = 0; 554 break; 555 case TIOCTIMESTAMP: 556 /* FALLTHROUGH */ 557 case TIOCDCDTIMESTAMP: 558 default: 559 splx(s); 560 error = ENOTTY; 561 return (error); 562 } 563 error = 0; 564 nmdm_crossover(pti, ourpart, otherpart); 565 } 566 splx(s); 567 return (error); 568 } 569 570 static void 571 nmdm_crossover(struct nm_softc *pti, struct softpart *ourpart, 572 struct softpart *otherpart) 573 { 574 otherpart->modemsignals &= ~(TIOCM_CTS|TIOCM_CAR); 575 if (ourpart->modemsignals & TIOCM_RTS) 576 otherpart->modemsignals |= TIOCM_CTS; 577 if (ourpart->modemsignals & TIOCM_DTR) 578 otherpart->modemsignals |= TIOCM_CAR; 579 } 580 581 /* 582 * Module handling 583 */ 584 static int 585 nmdm_modevent(module_t mod, int type, void *data) 586 { 587 static eventhandler_tag tag; 588 struct nm_softc *pt, *tpt; 589 int error = 0; 590 591 switch(type) { 592 case MOD_LOAD: 593 clone_setup(&nmdmclones); 594 tag = EVENTHANDLER_REGISTER(dev_clone, nmdm_clone, 0, 1000); 595 if (tag == NULL) 596 return (ENOMEM); 597 break; 598 599 case MOD_SHUTDOWN: 600 /* FALLTHROUGH */ 601 case MOD_UNLOAD: 602 EVENTHANDLER_DEREGISTER(dev_clone, tag); 603 TAILQ_FOREACH_SAFE(pt, &nmdmhead, pt_list, tpt) { 604 destroy_dev(pt->part1.dev); 605 TAILQ_REMOVE(&nmdmhead, pt, pt_list); 606 free(pt, M_NLMDM); 607 } 608 clone_cleanup(&nmdmclones); 609 break; 610 default: 611 error = EOPNOTSUPP; 612 } 613 return (error); 614 } 615 616 DEV_MODULE(nmdm, nmdm_modevent, NULL); 617