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: * @(#)dhv.c 2.4 (2.11BSD 2.11BSD) 1997/5/31 7: */ 8: 9: /* 10: * Rewritten to implement hardware flowcontrol. A lot of clean up was done 11: * and the formatting style change to aid in debugging. 1997/4/25 - sms 12: * 13: * ported to 2.11BSD (uio logic added) 12/22/91 - SMS 14: * based on dh.c 6.3 84/03/15 15: * and on dmf.c 6.2 84/02/16 16: * 17: * REAL(tm) dhv driver derived from dhu.c by 18: * Steve Nuchia at Baylor 22 November 1987 19: * steve@eyeball.bcm.tmc.edu 20: * 21: * Dave Johnson, Brown University Computer Science 22: * ddj%brown@csnet-relay 23: */ 24: 25: #include "dhv.h" 26: #if NDHV > 0 27: /* 28: * DHV-11 driver 29: */ 30: #include "param.h" 31: #include "dhvreg.h" 32: #include "conf.h" 33: #include "user.h" 34: #include "file.h" 35: #include "proc.h" 36: #include "ioctl.h" 37: #include "tty.h" 38: #include "ttychars.h" 39: #include "clist.h" 40: #include "map.h" 41: #include "uba.h" 42: #include "ubavar.h" 43: #include "systm.h" 44: #include "syslog.h" 45: #include <sys/kernel.h> 46: 47: struct uba_device dhvinfo[NDHV]; 48: 49: #define NDHVLINE (NDHV*8) 50: 51: /* 52: * The minor device number is used as follows: 53: * 54: * bits meaning 55: * 0-2 unit number within board 56: * 3-5 board number (max of 8) 57: * 6 RTS/CTS flow control enabled 58: * 7 softcarrier (hardwired line) 59: */ 60: #define UNIT(x) (minor(x) & 077) 61: #define SOFTCAR 0x80 62: #define HWFLOW 0x40 63: 64: #define IFLAGS (EVENP|ODDP|ECHO) 65: 66: /* 67: * DHV's don't have a very good interrupt facility - you get an 68: * interrupt when the first character is put into the silo 69: * and nothing after that. Previously an attempt was made to 70: * delay a couple of clock ticks with receive interrupts disabled. 71: * 72: * Unfortunately the code was ineffective because the number of ticks 73: * to delay was decremented if a full (90%) or overrun silo was encountered. 74: * After two such events the driver was back in interrupt per character 75: * mode thus wasting/negating the whole effort. 76: */ 77: 78: char dhv_hwxon[NDHVLINE]; /* hardware xon/xoff enabled, per line */ 79: 80: /* 81: * Baud rates: no 50, 200, or 38400 baud; all other rates are from "Group B". 82: * EXTA => 19200 baud 83: * EXTB => 2000 baud 84: */ 85: char dhv_speeds[] = 86: { 0, 0, 1, 2, 3, 4, 0, 5, 6, 7, 8, 10, 11, 13, 14, 9 }; 87: 88: struct tty dhv_tty[NDHVLINE]; 89: int ndhv = NDHVLINE; 90: int dhvact; /* mask of active dhv's */ 91: int dhv_overrun[NDHVLINE]; 92: int dhvstart(); 93: long dhvmctl(),dmtodhv(); 94: extern int wakeup(); 95: 96: #if defined(UCB_CLIST) 97: extern ubadr_t clstaddr; 98: #define cpaddr(x) (clstaddr + (ubadr_t)((x) - (char *)cfree)) 99: #else 100: #define cpaddr(x) ((u_short)(x)) 101: #endif 102: 103: /* 104: * Routine called to attach a dhv. 105: */ 106: dhvattach(addr,unit) 107: register caddr_t addr; 108: register u_int unit; 109: { 110: if (addr && unit < NDHV && !dhvinfo[unit].ui_addr) 111: { 112: dhvinfo[unit].ui_unit = unit; 113: dhvinfo[unit].ui_addr = addr; 114: dhvinfo[unit].ui_alive = 1; 115: return (1); 116: } 117: return (0); 118: } 119: 120: /* 121: * Open a DHV11 line, mapping the clist onto the uba if this 122: * is the first dhv on this uba. Turn on this dhv if this is 123: * the first use of it. 124: */ 125: /*ARGSUSED*/ 126: dhvopen(dev, flag) 127: dev_t dev; 128: int flag; 129: { 130: register struct tty *tp; 131: register int unit; 132: int dhv, error, s; 133: register struct dhvdevice *addr; 134: struct uba_device *ui; 135: 136: unit = UNIT(dev); 137: dhv = unit >> 3; 138: if (unit >= NDHVLINE || (ui = &dhvinfo[dhv])->ui_alive == 0) 139: return(ENXIO); 140: tp = &dhv_tty[unit]; 141: addr = (struct dhvdevice *)ui->ui_addr; 142: tp->t_addr = (caddr_t)addr; 143: tp->t_oproc = dhvstart; 144: 145: if ((dhvact & (1<<dhv)) == 0) 146: { 147: addr->dhvcsr = DHV_SELECT(0) | DHV_IE; 148: dhvact |= (1<<dhv); 149: /* anything else to configure whole board */ 150: } 151: 152: s = spltty(); 153: if ((tp->t_state & TS_ISOPEN) == 0) 154: { 155: tp->t_state |= TS_WOPEN; 156: if (tp->t_ispeed == 0) 157: { 158: tp->t_state |= TS_HUPCLS; 159: tp->t_ispeed = B9600; 160: tp->t_ospeed = B9600; 161: tp->t_flags = IFLAGS; 162: } 163: ttychars(tp); 164: tp->t_dev = dev; 165: if (dev & HWFLOW) 166: tp->t_flags |= RTSCTS; 167: else 168: tp->t_flags &= ~RTSCTS; 169: dhvparam(unit); 170: } 171: else if ((tp->t_state & TS_XCLUDE) && u.u_uid) 172: { 173: error = EBUSY; 174: goto out; 175: } 176: dhvmctl(dev, (long)DHV_ON, DMSET); 177: addr->dhvcsr = DHV_SELECT(dev) | DHV_IE; 178: if ((addr->dhvstat & DHV_ST_DCD) || (dev & SOFTCAR)) 179: tp->t_state |= TS_CARR_ON; 180: while ((tp->t_state & TS_CARR_ON) == 0 && 181: (flag & O_NONBLOCK) == 0) 182: { 183: tp->t_state |= TS_WOPEN; 184: sleep((caddr_t)&tp->t_rawq, TTIPRI); 185: } 186: error = (*linesw[tp->t_line].l_open)(dev, tp); 187: out: 188: splx(s); 189: return(error); 190: } 191: 192: /* 193: * Close a DHV11 line, turning off the modem control. 194: */ 195: /*ARGSUSED*/ 196: dhvclose(dev, flag) 197: dev_t dev; 198: int flag; 199: { 200: register struct tty *tp; 201: register int unit; 202: 203: unit = UNIT(dev); 204: tp = &dhv_tty[unit]; 205: if (!(tp->t_state & (TS_WOPEN|TS_ISOPEN))) 206: return(0); 207: (*linesw[tp->t_line].l_close)(tp, flag); 208: (void) dhvmctl(unit, (long)DHV_BRK, DMBIC); 209: (void) dhvmctl(unit, (long)DHV_OFF, DMSET); 210: ttyclose(tp); 211: if (dhv_overrun[unit]) 212: { 213: log(LOG_NOTICE,"dhv%d %d overruns\n",unit,dhv_overrun[unit]); 214: dhv_overrun[unit] = 0; 215: } 216: return(0); 217: } 218: 219: dhvselect(dev, rw) 220: dev_t dev; 221: int rw; 222: { 223: struct tty *tp = &dhv_tty[UNIT(dev)]; 224: 225: return(ttyselect(tp, rw)); 226: } 227: 228: dhvread(dev, uio, flag) 229: dev_t dev; 230: struct uio *uio; 231: int flag; 232: { 233: register struct tty *tp = &dhv_tty[UNIT(dev)]; 234: 235: return((*linesw[tp->t_line].l_read) (tp, uio, flag)); 236: } 237: 238: dhvwrite(dev, uio, flag) 239: dev_t dev; 240: struct uio *uio; 241: int flag; 242: { 243: register struct tty *tp = &dhv_tty[UNIT(dev)]; 244: 245: return((*linesw[tp->t_line].l_write) (tp, uio, flag)); 246: } 247: 248: /* 249: * DHV11 receiver interrupt. 250: */ 251: 252: dhvrint(dhv) 253: int dhv; 254: { 255: register struct tty *tp; 256: register int c; 257: register struct dhvdevice *addr; 258: struct tty *tp0; 259: struct uba_device *ui; 260: int line, p; 261: 262: ui = &dhvinfo[dhv]; 263: addr = (struct dhvdevice *)ui->ui_addr; 264: if (!addr) 265: return; 266: tp0 = &dhv_tty[dhv<<3]; 267: 268: /* 269: * Loop fetching characters from the silo for this 270: * dhv until there are no more in the silo. 271: */ 272: while ((c = addr->dhvrbuf) & DHV_RB_VALID) 273: { 274: line = DHV_RX_LINE(c); 275: tp = tp0 + line; 276: if ((c & DHV_RB_STAT) == DHV_RB_STAT) 277: { 278: /* 279: * modem changed or diag info 280: */ 281: if (c & DHV_RB_DIAG) 282: { 283: if ((c & 0xff) > 0201) 284: log(LOG_NOTICE,"dhv%d diag %o\n",dhv, c&0xff); 285: continue; 286: } 287: if (!(tp->t_dev & SOFTCAR) || 288: (tp->t_flags & MDMBUF)) 289: (*linesw[tp->t_line].l_modem)(tp, 290: (c & DHV_ST_DCD) != 0); 291: if (tp->t_flags & RTSCTS) 292: { 293: if (c & DHV_ST_CTS) 294: { 295: tp->t_state &= ~TS_TTSTOP; 296: ttstart(tp); 297: } 298: else 299: { 300: tp->t_state |= TS_TTSTOP; 301: dhvstop(tp, 0); 302: } 303: } 304: continue; 305: } 306: if ((tp->t_state&TS_ISOPEN) == 0) 307: { 308: wakeup((caddr_t)&tp->t_rawq); 309: continue; 310: } 311: if (c & (DHV_RB_PE|DHV_RB_DO|DHV_RB_FE)) 312: { 313: if (c & DHV_RB_PE) 314: { 315: p = tp->t_flags & (EVENP|ODDP); 316: if (p == EVENP || p == ODDP) 317: continue; 318: } 319: if (c & DHV_RB_DO) 320: { 321: dhv_overrun[(dhv << 3) + line]++; 322: /* bit-bucket the silo to free the cpu */ 323: while (addr->dhvrbuf & DHV_RB_VALID) 324: ; 325: break; 326: } 327: if (c & DHV_RB_FE) 328: { 329: /* 330: * At framing error (break) generate an interrupt in cooked/cbreak mode. 331: * Let the char through in RAW mode for autobauding getty's. The 332: * DH driver * has been using the 'brkc' character for years - see 333: * the comment in dh.c 334: */ 335: if (!(tp->t_flags&RAW)) 336: #ifdef OLDWAY 337: c = tp->t_intrc; 338: #else 339: c = tp->t_brkc; 340: } 341: #endif 342: } 343: #if NBK > 0 344: if (tp->t_line == NETLDISC) 345: { 346: c &= 0x7f; 347: BKINPUT(c, tp); 348: } 349: else 350: #endif 351: { 352: if (!(c & DHV_RB_PE) && dhv_hwxon[(dhv<<3)+line] && 353: ((c & 0x7f) == CSTOP || (c & 0x7f) == CSTART)) 354: continue; 355: (*linesw[tp->t_line].l_rint)(c, tp); 356: } 357: } 358: } 359: 360: /* 361: * Ioctl for DHV11. 362: */ 363: /*ARGSUSED*/ 364: dhvioctl(dev, cmd, data, flag) 365: register dev_t dev; 366: u_int cmd; 367: caddr_t data; 368: { 369: register struct tty *tp; 370: register int unit = UNIT(dev); 371: int error; 372: 373: tp = &dhv_tty[unit]; 374: error = (*linesw[tp->t_line].l_ioctl)(tp, cmd, data, flag); 375: if (error >= 0) 376: return(error); 377: error = ttioctl(tp, cmd, data, flag); 378: if (error >= 0) 379: { 380: if (cmd == TIOCSETP || cmd == TIOCSETN || 381: cmd == TIOCLSET || cmd == TIOCLBIC || 382: cmd == TIOCLBIS || cmd == TIOCSETD || cmd == TIOCSETC) 383: dhvparam(unit); 384: return(error); 385: } 386: 387: switch (cmd) 388: { 389: case TIOCSBRK: 390: (void) dhvmctl(unit, (long)DHV_BRK, DMBIS); 391: break; 392: case TIOCCBRK: 393: (void) dhvmctl(unit, (long)DHV_BRK, DMBIC); 394: break; 395: case TIOCSDTR: 396: (void) dhvmctl(unit, (long)DHV_DTR|DHV_RTS, DMBIS); 397: break; 398: case TIOCCDTR: 399: (void) dhvmctl(unit, (long)DHV_DTR|DHV_RTS, DMBIC); 400: break; 401: case TIOCMSET: 402: (void) dhvmctl(dev, dmtodhv(*(int *)data), DMSET); 403: break; 404: case TIOCMBIS: 405: (void) dhvmctl(dev, dmtodhv(*(int *)data), DMBIS); 406: break; 407: case TIOCMBIC: 408: (void) dhvmctl(dev, dmtodhv(*(int *)data), DMBIC); 409: break; 410: case TIOCMGET: 411: *(int *)data = dhvtodm(dhvmctl(dev, 0L, DMGET)); 412: break; 413: default: 414: return(ENOTTY); 415: } 416: return(0); 417: } 418: 419: static long 420: dmtodhv(bits) 421: register int bits; 422: { 423: long b = 0; 424: 425: if (bits & TIOCM_RTS) b |= DHV_RTS; 426: if (bits & TIOCM_DTR) b |= DHV_DTR; 427: if (bits & TIOCM_LE) b |= DHV_LE; 428: return(b); 429: } 430: 431: static 432: dhvtodm(bits) 433: long bits; 434: { 435: register int b = 0; 436: 437: if (bits & DHV_DSR) b |= TIOCM_DSR; 438: if (bits & DHV_RNG) b |= TIOCM_RNG; 439: if (bits & DHV_CAR) b |= TIOCM_CAR; 440: if (bits & DHV_CTS) b |= TIOCM_CTS; 441: if (bits & DHV_RTS) b |= TIOCM_RTS; 442: if (bits & DHV_DTR) b |= TIOCM_DTR; 443: if (bits & DHV_LE) b |= TIOCM_LE; 444: return(b); 445: } 446: 447: /* 448: * Set parameters from open or stty into the DHV hardware 449: * registers. 450: */ 451: static 452: dhvparam(unit) 453: int unit; 454: { 455: register struct tty *tp; 456: register struct dhvdevice *addr; 457: register int lpar; 458: int s; 459: 460: tp = &dhv_tty[unit]; 461: addr = (struct dhvdevice *)tp->t_addr; 462: /* 463: * Block interrupts so parameters will be set 464: * before the line interrupts. 465: */ 466: s = spltty(); 467: if ((tp->t_ispeed) == 0) 468: { 469: tp->t_state |= TS_HUPCLS; 470: (void)dhvmctl(unit, (long)DHV_OFF, DMSET); 471: goto out; 472: } 473: lpar = (dhv_speeds[tp->t_ospeed]<<12) | (dhv_speeds[tp->t_ispeed]<<8); 474: if (tp->t_ispeed == B134) 475: lpar |= DHV_LP_BITS6|DHV_LP_PENABLE; 476: else if (tp->t_flags & (RAW|LITOUT|PASS8)) 477: lpar |= DHV_LP_BITS8; 478: else 479: lpar |= DHV_LP_BITS7|DHV_LP_PENABLE; 480: if (tp->t_flags&EVENP) 481: lpar |= DHV_LP_EPAR; 482: if ((tp->t_flags & EVENP) && (tp->t_flags & ODDP)) 483: { 484: /* hack alert. assume "allow both" means don't care */ 485: /* trying to make xon/xoff work with evenp+oddp */ 486: lpar |= DHV_LP_BITS8; 487: lpar &= ~DHV_LP_PENABLE; 488: } 489: if ((tp->t_ospeed) == B110) 490: lpar |= DHV_LP_TWOSB; 491: addr->dhvcsr = DHV_SELECT(unit) | DHV_IE; 492: addr->dhvlpr = lpar; 493: dhv_hwxon[unit] = !(tp->t_flags & RAW) && 494: (tp->t_line == OTTYDISC || tp->t_line == NTTYDISC) && 495: tp->t_stopc == CSTOP && tp->t_startc == CSTART; 496: 497: if (dhv_hwxon[unit]) 498: addr->dhvlcr |= DHV_LC_OAUTOF; 499: else 500: { 501: addr->dhvlcr &= ~DHV_LC_OAUTOF; 502: delay(25L); /* see the dhv manual, sec 3.3.6 */ 503: addr->dhvlcr2 |= DHV_LC2_TXEN; 504: } 505: out: 506: splx(s); 507: return; 508: } 509: 510: /* 511: * DHV11 transmitter interrupt. 512: * Restart each line which used to be active but has 513: * terminated transmission since the last interrupt. 514: */ 515: dhvxint(dhv) 516: int dhv; 517: { 518: register struct tty *tp; 519: register struct dhvdevice *addr; 520: struct tty *tp0; 521: struct uba_device *ui; 522: register int line, t; 523: u_short cntr; 524: ubadr_t base; 525: 526: ui = &dhvinfo[dhv]; 527: tp0 = &dhv_tty[dhv<<4]; 528: addr = (struct dhvdevice *)ui->ui_addr; 529: while ((t = addr->dhvcsrh) & DHV_CSH_TI) 530: { 531: line = DHV_TX_LINE(t); 532: tp = tp0 + line; 533: tp->t_state &= ~TS_BUSY; 534: if (t & DHV_CSH_NXM) 535: { 536: log(LOG_NOTICE, "dhv%d,%d NXM\n", dhv, line); 537: /* SHOULD RESTART OR SOMETHING... */ 538: } 539: if (tp->t_state&TS_FLUSH) 540: tp->t_state &= ~TS_FLUSH; 541: else 542: { 543: addr->dhvcsrl = DHV_SELECT(line) | DHV_IE; 544: base = (ubadr_t) addr->dhvbar1; 545: /* 546: * Clists are either: 547: * 1) in kernel virtual space, 548: * which in turn lies in the 549: * first 64K of physical memory or 550: * 2) at UNIBUS virtual address 0. 551: * 552: * In either case, the extension bits are 0. 553: */ 554: if (!ubmap) 555: base |= (ubadr_t)((addr->dhvbar2 & 037) << 16); 556: cntr = base - cpaddr(tp->t_outq.c_cf); 557: ndflush(&tp->t_outq,cntr); 558: } 559: if (tp->t_line) 560: (*linesw[tp->t_line].l_start)(tp); 561: else 562: dhvstart(tp); 563: } 564: } 565: 566: /* 567: * Start (restart) transmission on the given DHV11 line. 568: */ 569: dhvstart(tp) 570: register struct tty *tp; 571: { 572: register struct dhvdevice *addr; 573: register int unit, nch; 574: ubadr_t car; 575: int s; 576: 577: unit = UNIT(tp->t_dev); 578: addr = (struct dhvdevice *)tp->t_addr; 579: 580: /* 581: * Must hold interrupts in following code to prevent 582: * state of the tp from changing. 583: */ 584: s = spltty(); 585: /* 586: * If it's currently active, or delaying, no need to do anything. 587: */ 588: if (tp->t_state&(TS_TIMEOUT|TS_BUSY|TS_TTSTOP)) 589: goto out; 590: /* 591: * If there are sleepers, and output has drained below low 592: * water mark, wake up the sleepers.. 593: */ 594: ttyowake(tp); 595: 596: /* 597: * Now restart transmission unless the output queue is 598: * empty. 599: */ 600: if (tp->t_outq.c_cc == 0) 601: goto out; 602: addr->dhvcsrl = DHV_SELECT(unit) | DHV_IE; 603: /* 604: * If CTS is off and we're doing hardware flow control then mark the output 605: * as stopped and do not transmit anything. 606: */ 607: if ((addr->dhvstat & DHV_ST_CTS) == 0 && (tp->t_flags & RTSCTS)) 608: { 609: tp->t_state |= TS_TTSTOP; 610: goto out; 611: } 612: /* 613: * This is where any per character delay handling for special characters 614: * would go if ever implemented again. The call to ndqb would be replaced 615: * with a scan for special characters and then the appropriate sleep/wakeup 616: * done. 617: */ 618: nch = ndqb(&tp->t_outq, 0); 619: /* 620: * If characters to transmit, restart transmission. 621: */ 622: if (nch) 623: { 624: car = cpaddr(tp->t_outq.c_cf); 625: addr->dhvcsrl = DHV_SELECT(unit) | DHV_IE; 626: addr->dhvlcr &= ~DHV_LC_TXABORT; 627: addr->dhvbcr = nch; 628: addr->dhvbar1 = loint(car); 629: if (ubmap) 630: addr->dhvbar2 = (hiint(car) & DHV_BA2_XBA) | DHV_BA2_DMAGO; 631: else 632: addr->dhvbar2 = (hiint(car) & 037) | DHV_BA2_DMAGO; 633: tp->t_state |= TS_BUSY; 634: } 635: out: 636: splx(s); 637: } 638: 639: /* 640: * Stop output on a line, e.g. for ^S/^Q or output flush. 641: */ 642: /*ARGSUSED*/ 643: dhvstop(tp, flag) 644: register struct tty *tp; 645: { 646: register struct dhvdevice *addr; 647: register int unit, s; 648: 649: addr = (struct dhvdevice *)tp->t_addr; 650: /* 651: * Block input/output interrupts while messing with state. 652: */ 653: s = spltty(); 654: if (tp->t_state & TS_BUSY) 655: { 656: /* 657: * Device is transmitting; stop output 658: * by selecting the line and setting the 659: * abort xmit bit. We will get an xmit interrupt, 660: * where we will figure out where to continue the 661: * next time the transmitter is enabled. If 662: * TS_FLUSH is set, the outq will be flushed. 663: * In either case, dhvstart will clear the TXABORT bit. 664: */ 665: unit = UNIT(tp->t_dev); 666: addr->dhvcsrl = DHV_SELECT(unit) | DHV_IE; 667: addr->dhvlcr |= DHV_LC_TXABORT; 668: delay(25L); /* see the dhv manual, sec 3.3.6 */ 669: addr->dhvlcr2 |= DHV_LC2_TXEN; 670: if ((tp->t_state&TS_TTSTOP)==0) 671: tp->t_state |= TS_FLUSH; 672: } 673: (void) splx(s); 674: } 675: 676: /* 677: * DHV11 modem control 678: */ 679: static long 680: dhvmctl(dev, bits, how) 681: dev_t dev; 682: long bits; 683: int how; 684: { 685: register struct dhvdevice *dhvaddr; 686: register int unit; 687: register struct tty *tp; 688: long mbits; 689: int s; 690: 691: unit = UNIT(dev); 692: tp = dhv_tty + unit; 693: dhvaddr = (struct dhvdevice *) tp->t_addr; 694: s = spltty(); 695: dhvaddr->dhvcsr = DHV_SELECT(unit) | DHV_IE; 696: /* 697: * combine byte from stat register (read only, bits 16..23) 698: * with lcr register (read write, bits 0..15). 699: */ 700: mbits = (u_short)dhvaddr->dhvlcr | ((long)dhvaddr->dhvstat << 16); 701: switch (how) 702: { 703: case DMSET: 704: mbits = (mbits & 0xff0000L) | bits; 705: break; 706: case DMBIS: 707: mbits |= bits; 708: break; 709: case DMBIC: 710: mbits &= ~bits; 711: break; 712: case DMGET: 713: (void) splx(s); 714: return(mbits); 715: } 716: dhvaddr->dhvlcr = (mbits & 0xffff) | DHV_LC_RXEN; 717: if (dhv_hwxon[unit]) 718: dhvaddr->dhvlcr |= DHV_LC_OAUTOF; 719: dhvaddr->dhvlcr2 = DHV_LC2_TXEN; 720: (void) splx(s); 721: return(mbits); 722: } 723: #endif