1: /* 2: * Copyright (c) 1986 Regents of the University of California. 3: * All rights reserved. The Berkeley software License Agreement 4: * specifies the terms and conditions for redistribution. 5: * 6: * @(#)dhu.c 2.3 (2.11BSD GTE) 1997/5/9 7: */ 8: 9: /* 10: * Rewritten for hardware flow control - sms 1997/5/9 11: * 12: * based on dh.c 6.3 84/03/15 13: * and on dmf.c 6.2 84/02/16 14: * 15: * Dave Johnson, Brown University Computer Science 16: * ddj%brown@csnet-relay 17: */ 18: 19: #include "dhu.h" 20: #if NDHU > 0 21: /* 22: * DHU-11 driver 23: */ 24: #include "param.h" 25: #include "dhureg.h" 26: #include "conf.h" 27: #include "user.h" 28: #include "file.h" 29: #include "ioctl.h" 30: #include "tty.h" 31: #include "clist.h" 32: #include "map.h" 33: #include "proc.h" 34: #include "uba.h" 35: #include "ubavar.h" 36: #include "systm.h" 37: #include "syslog.h" 38: #include <sys/kernel.h> 39: 40: struct uba_device dhuinfo[NDHU]; 41: 42: #define NDHULINE (NDHU*16) 43: 44: #define UNIT(x) (minor(x) & 077) 45: #define SOFTCAR 0x80 46: #define HWFLOW 0x40 47: 48: #define ISPEED B9600 49: #define IFLAGS (EVENP|ODDP|ECHO) 50: 51: /* 52: * default receive silo timeout value -- valid values are 2..255 53: * number of ms. to delay between first char received and receive interrupt 54: * 55: * A value of 20 gives same response as ABLE dh/dm with silo alarm = 0 56: */ 57: int dhu_def_timo = 20; 58: 59: /* 60: * Baud rates: no 50, 200, or 38400 baud; all other rates are from "Group B". 61: * EXTA => 19200 baud 62: * EXTB => 2000 baud 63: */ 64: char dhu_speeds[] = 65: { 0, 0, 1, 2, 3, 4, 0, 5, 6, 7, 8, 10, 11, 13, 14, 9 }; 66: 67: struct tty dhu_tty[NDHULINE]; 68: int ndhu = NDHULINE; 69: int dhuact; /* mask of active dhu's */ 70: int dhu_overrun[NDHULINE]; 71: int dhustart(); 72: long dhumctl(),dmtodhu(); 73: extern int wakeup(); 74: 75: #if defined(UCB_CLIST) 76: extern ubadr_t clstaddr; 77: #define cpaddr(x) (clstaddr + (ubadr_t)((x) - (char *)cfree)) 78: #else 79: #define cpaddr(x) ((u_short)(x)) 80: #endif 81: 82: /* 83: * Routine called to attach a dhu. 84: * Called duattached for autoconfig. 85: */ 86: duattach(addr,unit) 87: register caddr_t addr; 88: register u_int unit; 89: { 90: register struct uba_device *ui; 91: 92: if (addr && unit < NDHU && !dhuinfo[unit].ui_addr) 93: { 94: ui = &dhuinfo[unit]; 95: ui->ui_unit = unit; 96: ui->ui_addr = addr; 97: ui->ui_alive = 1; 98: return(1); 99: } 100: return(0); 101: } 102: 103: /* 104: * Open a DHU11 line, mapping the clist onto the uba if this 105: * is the first dhu on this uba. Turn on this dhu if this is 106: * the first use of it. 107: */ 108: /*ARGSUSED*/ 109: dhuopen(dev, flag) 110: dev_t dev; 111: int flag; 112: { 113: register struct tty *tp; 114: int unit, dhu; 115: register struct dhudevice *addr; 116: register struct uba_device *ui; 117: int s, error; 118: 119: unit = UNIT(dev); 120: dhu = unit >> 4; 121: if (unit >= NDHULINE || (ui = &dhuinfo[dhu])->ui_alive == 0) 122: return (ENXIO); 123: tp = &dhu_tty[unit]; 124: addr = (struct dhudevice *)ui->ui_addr; 125: tp->t_addr = (caddr_t)addr; 126: tp->t_oproc = dhustart; 127: 128: if ((dhuact&(1<<dhu)) == 0) 129: { 130: addr->dhucsr = DHU_SELECT(0) | DHU_IE; 131: addr->dhutimo = dhu_def_timo; 132: dhuact |= (1<<dhu); 133: /* anything else to configure whole board */ 134: } 135: /* 136: * If this is first open, initialize tty state to default. 137: */ 138: s = spltty(); 139: if ((tp->t_state&TS_ISOPEN) == 0) 140: { 141: tp->t_state |= TS_WOPEN; 142: if (tp->t_ispeed == 0) 143: { 144: tp->t_state |= TS_HUPCLS; 145: tp->t_ispeed = ISPEED; 146: tp->t_ospeed = ISPEED; 147: tp->t_flags = IFLAGS; 148: } 149: ttychars(tp); 150: tp->t_dev = dev; 151: if (dev & HWFLOW) 152: tp->t_flags |= RTSCTS; 153: else 154: tp->t_flags &= ~RTSCTS; 155: dhuparam(unit); 156: } 157: else if (tp->t_state & TS_XCLUDE && u.u_uid) 158: { 159: error = EBUSY; 160: goto out; 161: } 162: /* 163: * Turn the device on. Wait for carrier (the wait is short if this is a 164: * softcarrier/hardwired line ;-)). Then do the line discipline specific open. 165: */ 166: dhumctl(dev, (long)DHU_ON, DMSET); 167: addr->dhucsr = DHU_SELECT(unit) | DHU_IE; 168: if ((addr->dhustat & DHU_ST_DCD) || (dev & SOFTCAR)) 169: tp->t_state |= TS_CARR_ON; 170: while ((tp->t_state & TS_CARR_ON) == 0 && 171: (flag & O_NONBLOCK) == 0) 172: { 173: tp->t_state |= TS_WOPEN; 174: sleep((caddr_t)&tp->t_rawq, TTIPRI); 175: } 176: error = (*linesw[tp->t_line].l_open)(dev, tp); 177: out: 178: splx(s); 179: return(error); 180: } 181: 182: /* 183: * Close a DHU11 line. Clear the 'break' state in case it's asserted and 184: * then drop DTR+RTS. 185: */ 186: /*ARGSUSED*/ 187: dhuclose(dev, flag) 188: dev_t dev; 189: int flag; 190: { 191: register struct tty *tp; 192: register int unit; 193: 194: unit = UNIT(dev); 195: tp = &dhu_tty[unit]; 196: /* 197: * Do we need to do this? Perhaps this should be ifdef'd. I can't see how 198: * this can happen... 199: */ 200: if (!(tp->t_state & TS_ISOPEN)) 201: return(EBADF); 202: (*linesw[tp->t_line].l_close)(tp, flag); 203: (void) dhumctl(unit, (long)DHU_BRK, DMBIC); 204: (void) dhumctl(unit, DHU_OFF, DMSET); 205: ttyclose(tp); 206: if (dhu_overrun[unit]) 207: { 208: log(LOG_NOTICE, "dhu%d %d overruns\n", unit, dhu_overrun[unit]); 209: dhu_overrun[unit] = 0; 210: } 211: return(0); 212: } 213: 214: dhuselect(dev, rw) 215: dev_t dev; 216: int rw; 217: { 218: 219: return(ttyselect(&dhu_tty[UNIT(dev)], rw)); 220: } 221: 222: dhuread(dev, uio, flag) 223: dev_t dev; 224: struct uio *uio; 225: { 226: register struct tty *tp = &dhu_tty[UNIT(dev)]; 227: 228: return ((*linesw[tp->t_line].l_read)(tp, uio, flag)); 229: } 230: 231: dhuwrite(dev, uio, flag) 232: dev_t dev; 233: struct uio *uio; 234: { 235: register struct tty *tp = &dhu_tty[UNIT(dev)]; 236: 237: return ((*linesw[tp->t_line].l_write)(tp, uio, flag)); 238: } 239: 240: /* 241: * DHU11 receiver interrupt. 242: */ 243: dhurint(dhu) 244: int dhu; 245: { 246: register struct tty *tp; 247: register int c; 248: register struct dhudevice *addr; 249: struct tty *tp0; 250: struct uba_device *ui; 251: int line; 252: 253: ui = &dhuinfo[dhu]; 254: addr = (struct dhudevice *)ui->ui_addr; 255: if (!addr) 256: return; 257: tp0 = &dhu_tty[dhu<<4]; 258: /* 259: * Loop fetching characters from the silo for this 260: * dhu until there are no more in the silo. 261: */ 262: while ((c = addr->dhurbuf) < 0) 263: { /* (c & DHU_RB_VALID) == on */ 264: line = DHU_RX_LINE(c); 265: tp = tp0 + line; 266: if ((c & DHU_RB_STAT) == DHU_RB_STAT) 267: { 268: /* 269: * modem changed or diag info 270: */ 271: if (c & DHU_RB_DIAG) 272: { 273: if ((c & 0xff) > 0201) 274: log(LOG_NOTICE,"dhu%d diag %o\n", 275: dhu, c); 276: continue; 277: } 278: if (!(tp->t_dev & SOFTCAR) || 279: (tp->t_flags & MDMBUF)) 280: (*linesw[tp->t_line].l_modem)(tp, 281: (c & DHU_ST_DCD) != 0); 282: if (tp->t_flags & RTSCTS) 283: { 284: if (c & DHU_ST_CTS) 285: { 286: tp->t_state &= ~TS_TTSTOP; 287: ttstart(tp); 288: } 289: else 290: { 291: tp->t_state |= TS_TTSTOP; 292: dhustop(tp, 0); 293: } 294: } 295: continue; 296: } 297: if ((tp->t_state&TS_ISOPEN) == 0) 298: { 299: wakeup((caddr_t)&tp->t_rawq); 300: continue; 301: } 302: if (c & DHU_RB_PE) 303: if ((tp->t_flags&(EVENP|ODDP)) == EVENP || 304: (tp->t_flags&(EVENP|ODDP)) == ODDP) 305: continue; 306: if (c & DHU_RB_DO) 307: { 308: dhu_overrun[(dhu << 4) + line]++; 309: /* bit bucket the silo to free the cpu */ 310: while (addr->dhurbuf & DHU_RB_VALID) 311: ; 312: break; 313: } 314: if (c & DHU_RB_FE) 315: { 316: /* 317: * At framing error (break) generate 318: * a null (in raw mode, for getty), or a 319: * interrupt (in cooked/cbreak mode). 320: */ 321: if (tp->t_flags&RAW) 322: c = 0; 323: else 324: #ifdef OLDWAY 325: c = tp->t_intrc; 326: #else 327: c = tp->t_brkc; 328: #endif 329: } 330: #if NBK > 0 331: if (tp->t_line == NETLDISC) 332: { 333: c &= 0x7f; 334: BKINPUT(c, tp); 335: } 336: else 337: #endif 338: (*linesw[tp->t_line].l_rint)(c, tp); 339: } 340: } 341: 342: /* 343: * Ioctl for DHU11. 344: */ 345: /*ARGSUSED*/ 346: dhuioctl(dev, cmd, data, flag) 347: u_int cmd; 348: caddr_t data; 349: { 350: register struct tty *tp; 351: register int unit = UNIT(dev); 352: int error; 353: 354: tp = &dhu_tty[unit]; 355: error = (*linesw[tp->t_line].l_ioctl)(tp, cmd, data, flag); 356: if (error >= 0) 357: return(error); 358: error = ttioctl(tp, cmd, data, flag); 359: if (error >= 0) 360: { 361: if (cmd == TIOCSETP || cmd == TIOCSETN || 362: cmd == TIOCLSET || cmd == TIOCLBIC || cmd == TIOCLBIS) 363: dhuparam(unit); 364: return(error); 365: } 366: 367: switch (cmd) 368: { 369: case TIOCSBRK: 370: (void) dhumctl(unit, (long)DHU_BRK, DMBIS); 371: break; 372: case TIOCCBRK: 373: (void) dhumctl(unit, (long)DHU_BRK, DMBIC); 374: break; 375: case TIOCSDTR: 376: (void) dhumctl(unit, (long)DHU_DTR|DHU_RTS, DMBIS); 377: break; 378: case TIOCCDTR: 379: (void) dhumctl(unit, (long)DHU_DTR|DHU_RTS, DMBIC); 380: break; 381: case TIOCMSET: 382: (void) dhumctl(dev, dmtodhu(*(int *)data), DMSET); 383: break; 384: case TIOCMBIS: 385: (void) dhumctl(dev, dmtodhu(*(int *)data), DMBIS); 386: break; 387: case TIOCMBIC: 388: (void) dhumctl(dev, dmtodhu(*(int *)data), DMBIC); 389: break; 390: case TIOCMGET: 391: *(int *)data = dhutodm(dhumctl(dev, 0L, DMGET)); 392: break; 393: default: 394: return(ENOTTY); 395: } 396: return(0); 397: } 398: 399: static long 400: dmtodhu(bits) 401: register int bits; 402: { 403: long b = 0; 404: 405: if (bits & TIOCM_RTS) b |= DHU_RTS; 406: if (bits & TIOCM_DTR) b |= DHU_DTR; 407: if (bits & TIOCM_LE) b |= DHU_LE; 408: return(b); 409: } 410: 411: static 412: dhutodm(bits) 413: long bits; 414: { 415: register int b = 0; 416: 417: if (bits & DHU_DSR) b |= TIOCM_DSR; 418: if (bits & DHU_RNG) b |= TIOCM_RNG; 419: if (bits & DHU_CAR) b |= TIOCM_CAR; 420: if (bits & DHU_CTS) b |= TIOCM_CTS; 421: if (bits & DHU_RTS) b |= TIOCM_RTS; 422: if (bits & DHU_DTR) b |= TIOCM_DTR; 423: if (bits & DHU_LE) b |= TIOCM_LE; 424: return(b); 425: } 426: 427: /* 428: * Set parameters from open or stty into the DHU hardware 429: * registers. 430: */ 431: static 432: dhuparam(unit) 433: register int unit; 434: { 435: register struct tty *tp; 436: register struct dhudevice *addr; 437: register int lpar; 438: int s; 439: 440: tp = &dhu_tty[unit]; 441: addr = (struct dhudevice *)tp->t_addr; 442: /* 443: * Block interrupts so parameters will be set 444: * before the line interrupts. 445: */ 446: s = spltty(); 447: if (tp->t_ispeed == 0) 448: { 449: tp->t_state |= TS_HUPCLS; 450: (void)dhumctl(unit, (long)DHU_OFF, DMSET); 451: goto out; 452: } 453: lpar = (dhu_speeds[tp->t_ospeed]<<12) | (dhu_speeds[tp->t_ispeed]<<8); 454: if ((tp->t_ispeed) == B134) 455: lpar |= DHU_LP_BITS6|DHU_LP_PENABLE; 456: else if (tp->t_flags & (RAW|LITOUT|PASS8)) 457: lpar |= DHU_LP_BITS8; 458: else 459: lpar |= DHU_LP_BITS7|DHU_LP_PENABLE; 460: if (tp->t_flags&EVENP) 461: lpar |= DHU_LP_EPAR; 462: if ((tp->t_ospeed) == B110) 463: lpar |= DHU_LP_TWOSB; 464: addr->dhucsr = DHU_SELECT(unit) | DHU_IE; 465: addr->dhulpr = lpar; 466: out: 467: splx(s); 468: return; 469: } 470: 471: /* 472: * DHU11 transmitter interrupt. 473: * Restart each line which used to be active but has 474: * terminated transmission since the last interrupt. 475: */ 476: dhuxint(dhu) 477: int dhu; 478: { 479: register struct tty *tp; 480: register struct dhudevice *addr; 481: struct tty *tp0; 482: struct uba_device *ui; 483: register int line, t; 484: u_short cntr; 485: ubadr_t base; 486: 487: ui = &dhuinfo[dhu]; 488: tp0 = &dhu_tty[dhu<<4]; 489: addr = (struct dhudevice *)ui->ui_addr; 490: while ((t = addr->dhucsrh) & DHU_CSH_TI) 491: { 492: line = DHU_TX_LINE(t); 493: tp = tp0 + line; 494: tp->t_state &= ~TS_BUSY; 495: if (t & DHU_CSH_NXM) 496: { 497: log(LOG_NOTICE, "dhu%d,%d NXM\n", dhu, line); 498: /* SHOULD RESTART OR SOMETHING... */ 499: } 500: if (tp->t_state&TS_FLUSH) 501: tp->t_state &= ~TS_FLUSH; 502: else 503: { 504: addr->dhucsrl = DHU_SELECT(line) | DHU_IE; 505: base = (ubadr_t) addr->dhubar1; 506: /* 507: * Clists are either: 508: * 1) in kernel virtual space, 509: * which in turn lies in the 510: * first 64K of physical memory or 511: * 2) at UNIBUS virtual address 0. 512: * 513: * In either case, the extension bits are 0. 514: */ 515: if (!ubmap) 516: base |= (ubadr_t)((addr->dhubar2 & 037) << 16); 517: cntr = base - cpaddr(tp->t_outq.c_cf); 518: ndflush(&tp->t_outq, cntr); 519: } 520: if (tp->t_line) 521: (*linesw[tp->t_line].l_start)(tp); 522: else 523: dhustart(tp); 524: } 525: } 526: 527: /* 528: * Start (restart) transmission on the given DHU11 line. 529: */ 530: dhustart(tp) 531: register struct tty *tp; 532: { 533: register struct dhudevice *addr; 534: register int unit, nch; 535: ubadr_t car; 536: int s; 537: 538: unit = UNIT(tp->t_dev); 539: addr = (struct dhudevice *)tp->t_addr; 540: 541: s = spltty(); 542: /* 543: * If it's currently active, or delaying, no need to do anything. 544: */ 545: if (tp->t_state&(TS_TIMEOUT|TS_BUSY|TS_TTSTOP)) 546: goto out; 547: /* 548: * If there are sleepers, and output has drained below low 549: * water mark, wake up the sleepers.. 550: */ 551: ttyowake(tp); 552: 553: /* 554: * Now restart transmission unless the output queue is 555: * empty. 556: */ 557: if (tp->t_outq.c_cc == 0) 558: goto out; 559: addr->dhucsrl = DHU_SELECT(unit) | DHU_IE; 560: /* 561: * If CTS is off and we're doing hardware flow control then mark the output 562: * as stopped and do not transmit anything. 563: */ 564: if ((addr->dhustat & DHU_ST_CTS) == 0 && (tp->t_flags & RTSCTS)) 565: { 566: tp->t_state |= TS_TTSTOP; 567: goto out; 568: } 569: /* 570: * This is where any per character delay handling for special characters 571: * would go if ever implemented again. The call to ndqb would be replaced 572: * with a scan for special characters and then the appropriate sleep/wakeup 573: * done. 574: */ 575: nch = ndqb(&tp->t_outq, 0); 576: /* 577: * If characters to transmit, restart transmission. 578: */ 579: if (nch) 580: { 581: car = cpaddr(tp->t_outq.c_cf); 582: addr->dhucsrl = DHU_SELECT(unit) | DHU_IE; 583: addr->dhulcr &= ~DHU_LC_TXABORT; 584: addr->dhubcr = nch; 585: addr->dhubar1 = loint(car); 586: if (ubmap) 587: addr->dhubar2 = (hiint(car) & DHU_BA2_XBA) | DHU_BA2_DMAGO; 588: else 589: addr->dhubar2 = (hiint(car) & 037) | DHU_BA2_DMAGO; 590: tp->t_state |= TS_BUSY; 591: } 592: out: 593: splx(s); 594: } 595: 596: /* 597: * Stop output on a line, e.g. for ^S/^Q or output flush. 598: */ 599: /*ARGSUSED*/ 600: dhustop(tp, flag) 601: register struct tty *tp; 602: { 603: register struct dhudevice *addr; 604: register int unit, s; 605: 606: addr = (struct dhudevice *)tp->t_addr; 607: /* 608: * Block input/output interrupts while messing with state. 609: */ 610: s = spltty(); 611: if (tp->t_state & TS_BUSY) 612: { 613: /* 614: * Device is transmitting; stop output 615: * by selecting the line and setting the 616: * abort xmit bit. We will get an xmit interrupt, 617: * where we will figure out where to continue the 618: * next time the transmitter is enabled. If 619: * TS_FLUSH is set, the outq will be flushed. 620: * In either case, dhustart will clear the TXABORT bit. 621: */ 622: unit = UNIT(tp->t_dev); 623: addr->dhucsrl = DHU_SELECT(unit) | DHU_IE; 624: addr->dhulcr |= DHU_LC_TXABORT; 625: if ((tp->t_state&TS_TTSTOP)==0) 626: tp->t_state |= TS_FLUSH; 627: } 628: (void) splx(s); 629: } 630: 631: /* 632: * DHU11 modem control 633: */ 634: static long 635: dhumctl(dev, bits, how) 636: dev_t dev; 637: long bits; 638: int how; 639: { 640: register struct dhudevice *dhuaddr; 641: register int unit, line; 642: long mbits; 643: int s; 644: 645: unit = UNIT(dev); 646: dhuaddr = (struct dhudevice *)(dhu_tty[unit].t_addr); 647: line = unit & 0xf; 648: s = spltty(); 649: dhuaddr->dhucsr = DHU_SELECT(line) | DHU_IE; 650: /* 651: * combine byte from stat register (read only, bits 16..23) 652: * with lcr register (read write, bits 0..15). 653: */ 654: mbits = (u_short)dhuaddr->dhulcr | ((long)dhuaddr->dhustat << 16); 655: switch (how) 656: { 657: case DMSET: 658: mbits = (mbits & 0xff0000L) | bits; 659: break; 660: case DMBIS: 661: mbits |= bits; 662: break; 663: case DMBIC: 664: mbits &= ~bits; 665: break; 666: case DMGET: 667: goto out; 668: } 669: dhuaddr->dhulcr = (mbits & 0xffffL) | DHU_LC_RXEN; 670: dhuaddr->dhulcr2 = DHU_LC2_TXEN; 671: out: 672: (void) splx(s); 673: return(mbits); 674: } 675: #endif