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