|
|
1.1 ! root 1: /* ! 2: * File: asy.c ! 3: * ! 4: * Purpose: 8250-family async port device driver ! 5: * ! 6: * Devices are named /dev/asy[00..31]{fpl} ! 7: * Minor number bit assignments: ! 8: * x... .... 1 for NO modem control, "l" ! 9: * .x.. .... 1 for polled operation (no irq service), "p" ! 10: * ..x. .... 1 for RTS/CTS flow control, "f" ! 11: * ...x xxxx channel number - 0..31 ! 12: * ! 13: * $Log: asy.c,v $ ! 14: * Revision 1.7 92/07/16 16:34:44 hal ! 15: * Kernel #58 ! 16: * ! 17: * Revision 1.6 92/07/07 09:04:30 root ! 18: * Allow up to 16 slots per group. ! 19: * Support Digiboard as fourth group type. ! 20: * ! 21: * Revision 1.5 92/06/11 17:28:05 root ! 22: * Temporary fling with condev & asy_putchar. ! 23: * ! 24: * Revision 1.4 92/06/10 17:26:59 hal ! 25: * Conditional logging to screen of opens and closes. Ker #55. ! 26: * ! 27: * Revision 1.3 92/06/06 12:39:33 hal ! 28: * Last before adding termio field to tty struct. ! 29: * ! 30: */ ! 31: ! 32: /* ! 33: * ----------------------------------------------------------------- ! 34: * Includes. ! 35: */ ! 36: #include <sys/coherent.h> ! 37: #include <sys/stat.h> ! 38: #include <sys/proc.h> ! 39: #include <sys/tty.h> ! 40: #include <sys/con.h> ! 41: #include <sys/devices.h> ! 42: #include <errno.h> ! 43: #include <poll.h> ! 44: #include <sys/sched.h> /* CVTTOUT, IVTTOUT, SVTTOUT */ ! 45: #include <sys/asy.h> ! 46: #include <sys/ins8250.h> ! 47: #include <sys/poll_clk.h> ! 48: #ifdef _I386 ! 49: #include <termio.h> ! 50: #endif ! 51: ! 52: /* ! 53: * ----------------------------------------------------------------- ! 54: * Definitions. ! 55: * Constants. ! 56: * Macros with argument lists. ! 57: * Typedefs. ! 58: * Enums. ! 59: */ ! 60: #define IEN_USE_MSI (IE_RxI | IE_TxI | IE_LSI | IE_MSI) ! 61: #define IEN_NO_MSI (IE_RxI | IE_TxI | IE_LSI) ! 62: ! 63: #define CTLQ 0021 ! 64: #define CTLS 0023 ! 65: ! 66: #define NUM_IRQ 16 /* PC allows irq numbers 0..15 */ ! 67: #define BPB 8 /* 8 bits per byte */ ! 68: #define DTRTMOUT 3 /* DTR seconds for close */ ! 69: #define LOOP_LIMIT 100 /* safety valve on irq loops */ ! 70: ! 71: /* ! 72: * For rawin silo (see poll_clk.h), use last element of si_buf to count ! 73: * the number of characters in the silo. ! 74: */ ! 75: #define SILO_CHAR_COUNT si_buf[SI_BUFSIZ-1] ! 76: #define SILO_HIGH_MARK (SI_BUFSIZ-SI_BUFSIZ/4) ! 77: #define SILO_LOW_MARK (SI_BUFSIZ/4) ! 78: #define MAX_SILO_INDEX (SI_BUFSIZ-2) ! 79: #define MAX_SILO_CHARS (SI_BUFSIZ-1) ! 80: ! 81: #define RAWIN_FLUSH(in_silo) { \ ! 82: in_silo->si_ox = in_silo->si_ix; \ ! 83: in_silo->SILO_CHAR_COUNT = 0; } ! 84: #define RAWOUT_FLUSH(out_silo) { out_silo->si_ox = out_silo->si_ix; } ! 85: #define channel(dev) (dev & 0x1F) ! 86: ! 87: #define IEN ((a0->a_nms)?IEN_NO_MSI:IEN_USE_MSI) ! 88: #ifdef _I386 ! 89: #define EEBUSY EBUSY ! 90: #else ! 91: #define EEBUSY EDBUSY ! 92: #endif ! 93: ! 94: #define NW_OUTSILO 1 /* bits in need_wake[] entries */ ! 95: ! 96: typedef void (* VPTR)(); /* pointer to function returning void */ ! 97: typedef void (* FPTR)(); /* pointer to function returning int */ ! 98: ! 99: /* ! 100: * ----------------------------------------------------------------- ! 101: * Functions. ! 102: * Import Functions. ! 103: * Export Functions. ! 104: * Local Functions. ! 105: */ ! 106: int nulldev(); ! 107: ! 108: void asy_putchar(); ! 109: ! 110: /* ! 111: * Configuration functions (local). ! 112: */ ! 113: static void asyclose(); ! 114: static void asyioctl(); ! 115: static void asyioctl0(); ! 116: static void asyload(); ! 117: static void asyopen(); ! 118: static void asyread(); ! 119: static void asytimer(); ! 120: static void asyunload(); ! 121: static void asywrite(); ! 122: static int asypoll(); ! 123: static void cinit(); ! 124: ! 125: /* ! 126: * Support functions (local). ! 127: */ ! 128: static void add_irq(); ! 129: static void asy_irq(); ! 130: static int asy_send(); ! 131: static void asybreak(); ! 132: static void asyclock(); ! 133: static void asycycle(); ! 134: static void asydump(); ! 135: static int asyintr(); ! 136: static void asyparam(); ! 137: static void asysph(); ! 138: static void asyspr(); ! 139: static void asystart(); ! 140: static void irqdummy(); ! 141: static void upd_irq1(); ! 142: ! 143: static void i2(),i3(),i4(),i5(),i6(),i7(),i8(),i9(); ! 144: static void i10(),i11(),i12(),i13(),i14(),i15(); ! 145: static int p1(),p2(),p3(),p4(); ! 146: ! 147: /* ! 148: * ----------------------------------------------------------------- ! 149: * Global Data. ! 150: * Import Variables. ! 151: * Export Variables. ! 152: * Local Variables. ! 153: */ ! 154: extern int albaud[], alp_rate[]; ! 155: ! 156: /* ! 157: * When asypatch runs, it checks whether its internal value for ! 158: * ASY_VERSION matches this driver's value, so as to prevent the patch ! 159: * utility and the driver from getting out of phase. ! 160: */ ! 161: int ASY_VER = ASY_VERSION; ! 162: int ASY_HPCL = 1; ! 163: int ASY_NUM = 0; ! 164: int ASYGP_NUM = 0; ! 165: asy0_t asy0[MAX_ASY] = { ! 166: { 0 } ! 167: }; ! 168: asy_gp_t asy_gp[MAX_ASYGP] = { ! 169: { 0 } ! 170: }; ! 171: ! 172: static asy1_t * asy1; /* unused entries have type US_NONE */ ! 173: static short dummy_port; /* used only during driver startup */ ! 174: static int poll_divisor; /* set by asyspr(), read by asyclk() */ ! 175: static char pptbl[MAX_ASY]; /* channel numbers of polled ports */ ! 176: static int ppnum; /* number of channels in pptbl */ ! 177: ! 178: /* ! 179: * itbl keeps function pointers for irq service routines, for ease of setting ! 180: * and clearing vectors. ! 181: * ! 182: * irq0[x] and irq1[x] are lists for irq number x. ! 183: * irq0 has nodes that may possibly cause an irq. ! 184: * irq1 contains nodes for active devices. ! 185: * Whenever a device becomes active or inactive, irq1 is rebuilt from irq0. ! 186: * ! 187: * nodespace is an array of available nodes used in making the lists. ! 188: * nextnode points to the next free node. ! 189: * Nodes are taken from nodespace only during driver load. ! 190: */ ! 191: static VPTR itbl[NUM_IRQ] = { ! 192: 0,0,i2,i3,i4,i5,i6,i7,i8,i9,i10,i11,i12,i13,i14,i15 }; ! 193: static FPTR ptbl[PT_MAX] = { asyintr,p1,p2,p3,p4 }; ! 194: static struct irqnode *irq0[NUM_IRQ], *irq1[NUM_IRQ]; ! 195: static struct irqnode nodespace[MAX_ASY]; ! 196: static char need_wake[MAX_ASY]; ! 197: static char nextnode; ! 198: static int initialized; /* for asy_putchar() */ ! 199: ! 200: /* ! 201: * Configuration table (export data). ! 202: */ ! 203: CON asycon ={ ! 204: DFCHR|DFPOL, /* Flags */ ! 205: ASY_MAJOR, /* Major index */ ! 206: asyopen, /* Open */ ! 207: asyclose, /* Close */ ! 208: nulldev, /* Block */ ! 209: asyread, /* Read */ ! 210: asywrite, /* Write */ ! 211: #ifdef _I386 ! 212: asyioctl0, /* Ioctl */ ! 213: #else ! 214: asyioctl, /* Ioctl */ ! 215: #endif ! 216: nulldev, /* Powerfail */ ! 217: asytimer, /* Timeout */ ! 218: asyload, /* Load */ ! 219: asyunload, /* Unload */ ! 220: asypoll /* Poll */ ! 221: }; ! 222: ! 223: /* ! 224: * ----------------------------------------------------------------- ! 225: * Code. ! 226: */ ! 227: ! 228: /* ! 229: * asyload() ! 230: */ ! 231: static void ! 232: asyload() ! 233: { ! 234: int s, chan; ! 235: asy0_t *a0; ! 236: asy1_t *a1; ! 237: TTY *tp; ! 238: short port; ! 239: char irq; ! 240: char speed; ! 241: char g; ! 242: char sense_ct = 0; ! 243: ! 244: /* ! 245: * Allocate space for asy structs. Possible error return. ! 246: */ ! 247: asy1 = (asy1_t *)kalloc(ASY_NUM * sizeof(asy1_t)); ! 248: if (asy1 == 0) { ! 249: printf("asyload: can't allocate space for %d async devices\n", ! 250: ASY_NUM); ! 251: return; ! 252: } ! 253: kclear(asy1, ASY_NUM*sizeof(asy1_t)); ! 254: ! 255: /* ! 256: * For each non-null port: ! 257: * if port is uses irq ! 258: * set dummy routine in case uart_sense causes bogus irpts ! 259: * sense chip type ! 260: * write baud rate to sgtty/termio structs ! 261: * disable port interrupts ! 262: * hang up port ! 263: * set default baud rate (also resets UART) ! 264: * hook "start" function into line discipline module ! 265: * hook "param" function into line discipline module ! 266: * hook CS into line discipline module ! 267: * if port is uses irq ! 268: * release dummy routine ! 269: * if not in a port group ! 270: * add to irq list ! 271: */ ! 272: for (chan = 0; chan < ASY_NUM; chan++) { ! 273: a0 = asy0 + chan; ! 274: a1 = asy1 + chan; ! 275: tp = &a1->a_tty; ! 276: speed = a0->a_speed; ! 277: tp->t_sgttyb.sg_ispeed = tp->t_sgttyb.sg_ospeed = speed; ! 278: tp->t_dispeed = tp->t_dospeed = speed; ! 279: port = a0->a_port; ! 280: ! 281: /* ! 282: * A port address of zero means a skipped entry in the table. ! 283: * In this case a1->a_ut keeps its initial value of US_NONE. ! 284: */ ! 285: if (port) { ! 286: dummy_port = port; ! 287: if (a0->a_irqno) ! 288: setivec(a0->a_irqno, irqdummy); ! 289: /* ! 290: * uart_sense() prints port info. ! 291: * Do this four times per line. ! 292: */ ! 293: a1->a_ut = uart_sense(port); ! 294: sense_ct++; ! 295: if ((sense_ct & 1) == 0) ! 296: putchar('\n'); ! 297: else ! 298: putchar('\t'); ! 299: s = sphi(); ! 300: outb(port+MCR, 0); ! 301: outb(port+LCR, LC_DLAB); ! 302: outb(port+DLL, albaud[speed]); ! 303: outb(port+DLH, albaud[speed] >> 8); ! 304: outb(port+LCR, LC_CS8); ! 305: tp->t_start = asystart; ! 306: /* leave tp->t_param at 0 */ ! 307: tp->t_cs_sel = cs_sel(); ! 308: tp->t_ddp = (int *)chan; ! 309: spl(s); ! 310: if (a0->a_irqno) { ! 311: clrivec(a0->a_irqno); ! 312: if (a0->a_asy_gp == NO_ASYGP) ! 313: add_irq(a0->a_irqno, asyintr, chan); ! 314: } ! 315: } ! 316: } ! 317: if (sense_ct & 1) ! 318: putchar('\n'); ! 319: ! 320: /* ! 321: * for each port group ! 322: * add group to irq list ! 323: */ ! 324: for (g = 0; g < ASYGP_NUM; g++) { ! 325: add_irq(asy_gp[g].irq, ptbl[asy_gp[g].gp_type], g); ! 326: } ! 327: ! 328: /* ! 329: * Attach irq routines. ! 330: */ ! 331: for (irq = 0; irq < NUM_IRQ; irq++) ! 332: if (irq0[irq]) { ! 333: setivec(irq, itbl[irq]); ! 334: } ! 335: } ! 336: ! 337: /* ! 338: * asyunload() ! 339: */ ! 340: static void ! 341: asyunload() ! 342: { ! 343: char chan, irq; ! 344: ! 345: /* ! 346: * for each channel ! 347: * disable UART interrupts ! 348: * hangup port ! 349: * cancel timer ! 350: */ ! 351: for (chan = 0; chan < ASY_NUM; chan++) { ! 352: asy0_t * a0 = asy0 + chan; ! 353: asy1_t * a1 = asy1 + chan; ! 354: short port = a0->a_port; ! 355: TTY *tp = &a1->a_tty; ! 356: ! 357: outb(port+IER, 0); ! 358: outb(port+MCR, 0); ! 359: timeout(tp->t_rawtim, 0, NULL, 0); ! 360: } ! 361: ! 362: /* ! 363: * for each irq ! 364: * if irq routine was attached ! 365: * detach it ! 366: */ ! 367: for (irq = 0; irq < NUM_IRQ; irq++) ! 368: if (irq0[irq]) ! 369: clrivec(irq); ! 370: ! 371: /* ! 372: * Deallocate dynamic asy storage. ! 373: */ ! 374: if (asy1) ! 375: kfree(asy1); ! 376: } ! 377: ! 378: /* ! 379: * asyopen() ! 380: */ ! 381: static void ! 382: asyopen(dev, mode) ! 383: dev_t dev; ! 384: int mode; ! 385: { ! 386: int s; ! 387: char msr, mcr; ! 388: char chan = channel(dev); ! 389: asy0_t *a0 = asy0 + chan; ! 390: asy1_t *a1 = asy1 + chan; ! 391: TTY *tp = &a1->a_tty; ! 392: short port = a0->a_port; ! 393: ! 394: if (a1->a_ut == US_NONE) { /* chip not found */ ! 395: T_HAL(4, devmsg(dev, "no UART")); ! 396: u.u_error = ENXIO; ! 397: goto bad_open; ! 398: } ! 399: ! 400: if ((tp->t_flags & T_EXCL) && !super()) { ! 401: T_HAL(4, devmsg(dev, "exclusive use")); ! 402: u.u_error = ENODEV; ! 403: goto bad_open; ! 404: } ! 405: ! 406: #if 0 ! 407: if (drvl[major(dev)].d_time != 0) { /* Modem settling */ ! 408: T_HAL(4, devmsg(dev, "modem settling")); ! 409: u.u_error = EEBUSY; ! 410: goto bad_open; ! 411: } ! 412: #endif ! 413: ! 414: /* ! 415: * Can't open for hardware flow control if modem status ! 416: * interrupts are disallowed. ! 417: */ ! 418: if (a0->a_nms && (dev & CFLOW)) { ! 419: T_HAL(4, devmsg(dev, "no modem status irq's")); ! 420: u.u_error = ENXIO; ! 421: goto bad_open; ! 422: } ! 423: ! 424: /* ! 425: * Can't open a polled port if another driver is using polling. ! 426: */ ! 427: if (dev & CPOLL && poll_owner & ~ POLL_ASY) { ! 428: T_HAL(4, devmsg(dev, "polling unavailable")); ! 429: u.u_error = EEBUSY; ! 430: goto bad_open; ! 431: } ! 432: ! 433: /* ! 434: * Can't have both com[13] or both com[24] IRQ at once. ! 435: */ ! 436: if (!(dev & CPOLL) && a0->a_ixc) { ! 437: struct irqnode *np = irq1[a0->a_irqno]; ! 438: while (np) { ! 439: if (np->func != ptbl[0] || np->arg != chan) { ! 440: T_HAL(4, devmsg(dev, "irq conflict")); ! 441: u.u_error = EEBUSY; ! 442: goto bad_open; ! 443: } ! 444: np = np->next_actv; ! 445: } ! 446: } ! 447: ! 448: /* ! 449: * If port already in use, are new and old open modes compatible? ! 450: */ ! 451: if (a1->a_in_use) { ! 452: int oldmode = 0, newmode = 0; /* mctl:1 irq:2 flow:4 */ ! 453: ! 454: if (a1->a_modc) ! 455: oldmode += 1; ! 456: if (a1->a_irq) ! 457: oldmode += 2; ! 458: if (a1->a_flc) ! 459: oldmode += 4; ! 460: if ((dev & NMODC) == 0) ! 461: newmode += 1; ! 462: if ((dev & CPOLL) == 0) ! 463: newmode += 2; ! 464: if (dev & CFLOW) ! 465: newmode += 4; ! 466: if (oldmode != newmode) { ! 467: T_HAL(4, devmsg(dev, "conflicting open modes")); ! 468: u.u_error = EEBUSY; ! 469: goto bad_open; ! 470: } ! 471: } ! 472: ! 473: /* ! 474: * Sleep here if another process is opening or closing the port. ! 475: * This can happen if: ! 476: * another process is trying a first open and awaiting CD; ! 477: * another process is closing the port after losing CD; ! 478: * a remote process opened the port, spawned a daemon, ! 479: * and disconnected, and the daemon ignored SIGHUP and is ! 480: * improperly keeping the port open. ! 481: * Don't try to set tp->t_flags before this sleep! During ! 482: * the sleep, ttclose() may be called and clear the flags. ! 483: */ ! 484: while (a1->a_in_use && (a1->a_hcls || ! 485: ((dev & NMODC) == 0 && (inb(port+MSR) & MS_RLSD) == 0))) { ! 486: v_sleep((char *)(&tp->t_open), CVTTOUT, IVTTOUT, SVTTOUT, ! 487: "asyblk"); ! 488: if (SELF->p_ssig && nondsig()) { /* signal? */ ! 489: u.u_error = EINTR; ! 490: goto bad_open; ! 491: } ! 492: } ! 493: ! 494: /* ! 495: * If channel not in use, mark it as such. ! 496: */ ! 497: if (a1->a_in_use == 0) { ! 498: /* ! 499: * Save modes for this open attempt to avoid future conflicts. ! 500: * Then start asycycle() for this port. ! 501: */ ! 502: if (dev & NMODC) { ! 503: tp->t_flags &= ~T_MODC; ! 504: a1->a_modc = 0; ! 505: } else { ! 506: tp->t_flags |= T_MODC; ! 507: a1->a_modc = 1; ! 508: } ! 509: if (dev & CPOLL) ! 510: a1->a_irq = 0; ! 511: else ! 512: a1->a_irq = 1; ! 513: if (dev & CFLOW) { ! 514: tp->t_flags |= T_CFLOW; ! 515: a1->a_flc = 1; ! 516: } else { ! 517: tp->t_flags &= ~T_CFLOW; ! 518: a1->a_flc = 0; ! 519: } ! 520: } ! 521: a1->a_in_use++; ! 522: ! 523: /* ! 524: * From here, error exit is bad_open_u. ! 525: */ ! 526: ! 527: if (tp->t_open == 0) { /* not already open */ ! 528: silo_t * in_silo = &a1->a_in; ! 529: ! 530: if (!(dev & CPOLL)) { ! 531: upd_irq1(a0->a_irqno); ! 532: a1->a_has_irq = 1; ! 533: } ! 534: ! 535: /* ! 536: * Need to start cycling to scan for CD. ! 537: */ ! 538: asycycle(chan); ! 539: ! 540: s = sphi(); ! 541: /* ! 542: * Raise basic modem control lines even if modem ! 543: * control hasn't been specified. ! 544: * MC_OUT2 turns on NON-open-collector IRQ line from the UART. ! 545: * since we can't have two UART's on same IRQ with MC_OUT2 on ! 546: */ ! 547: mcr = MC_RTS | MC_DTR; ! 548: if (dev & CPOLL) { ! 549: outb(port+MCR, mcr); ! 550: } else { ! 551: outb(port+MCR, mcr | a0->a_outs); ! 552: outb(port+IER, IEN); ! 553: } ! 554: ! 555: if ((dev & NMODC) == 0) { /* want modem control? */ ! 556: tp->t_flags |= T_HOPEN | T_STOP; ! 557: for (;;) { /* wait for carrier */ ! 558: msr = inb(port+MSR); ! 559: /* ! 560: * If carrier detect present ! 561: * if port not already open ! 562: * break out of loop and finish first open ! 563: * else ! 564: * do second (or third, etc.) open ! 565: */ ! 566: if (msr & MS_RLSD) ! 567: break; ! 568: sleep((char *)(&tp->t_open), CVTTOUT, IVTTOUT, ! 569: SVTTOUT, "need CD"); /* wait for carrier */ ! 570: if (SELF->p_ssig && nondsig()) { /* signal? */ ! 571: outb(port+MCR, 0); ! 572: outb(port+IER, 0); ! 573: u.u_error = EINTR; ! 574: tp->t_flags &= ~(T_HOPEN | T_STOP); ! 575: spl(s); ! 576: goto bad_open_u; ! 577: } ! 578: } ! 579: ! 580: /* ! 581: * Mark that we are no longer hanging in open. ! 582: * Allow output over the port unless hardware flow ! 583: * control says not to. ! 584: */ ! 585: tp->t_flags &= ~T_HOPEN; ! 586: tp->t_flags &= ~T_STOP; ! 587: if (!(tp->t_flags & T_CFLOW) || (msr & MS_CTS)) ! 588: a1->a_ohlt = 0; ! 589: else ! 590: a1->a_ohlt = 1; ! 591: ! 592: /* ! 593: * Awaken any other opens on same device. ! 594: */ ! 595: wakeup((char *)(&tp->t_open)); ! 596: } ! 597: ttopen(tp); /* stty inits */ ! 598: tp->t_flags |= T_CARR; ! 599: if (ASY_HPCL) ! 600: tp->t_flags |= T_HPCL; ! 601: ! 602: asyparam(tp); /* gimmick: do this while t_open is zero */ ! 603: ! 604: /* ! 605: * TO DO: flush UART input register(s). ! 606: */ ! 607: ! 608: spl(s); ! 609: ! 610: /* ! 611: * Turn on polling for the port. ! 612: */ ! 613: if (dev & CPOLL) { ! 614: a1->a_poll = 1; ! 615: asyspr(); ! 616: } ! 617: } /* end of first-open case */ ! 618: ! 619: tp->t_open++; ! 620: T_HAL(0x400, printf("ch%d open + %d\n", chan, tp->t_open)); ! 621: ttsetgrp(tp, dev, mode); ! 622: ! 623: return; ! 624: ! 625: bad_open_u: ! 626: a1->a_in_use--; ! 627: wakeup((char *)(&tp->t_open)); ! 628: bad_open: ! 629: return; ! 630: } ! 631: ! 632: /* ! 633: * asyclose() ! 634: */ ! 635: static void ! 636: asyclose(dev, mode) ! 637: dev_t dev; ! 638: int mode; ! 639: { ! 640: int chan = channel(dev); ! 641: asy0_t *a0 = asy0 + chan; ! 642: asy1_t *a1 = asy1 + chan; ! 643: TTY *tp = &a1->a_tty; ! 644: silo_t * out_silo = &a1->a_out; ! 645: silo_t * in_silo = &a1->a_in; ! 646: int flags, maj; ! 647: int s; ! 648: short port = a0->a_port; ! 649: char lsr; ! 650: ! 651: if (--tp->t_open) ! 652: goto not_last_close; ! 653: T_HAL(0x400, printf("ch%d open - %d\n", chan, tp->t_open)); ! 654: s = sphi(); ! 655: ! 656: a1->a_hcls = 1; /* disallow reopen til done closing */ ! 657: flags = tp->t_flags; /* save flags - ttclose zeroes them */ ! 658: #if 1 ! 659: ttclose(tp); ! 660: #endif ! 661: ! 662: /* ! 663: * Wait for output silo and UART xmit buffer to empty. ! 664: * Allow signal to break the sleep. ! 665: */ ! 666: for (;;) { ! 667: lsr = inb(port + LSR); ! 668: if ((lsr & LS_TxIDLE) ! 669: && (out_silo->si_ix == out_silo->si_ox)) ! 670: break; ! 671: need_wake[chan] |= NW_OUTSILO; ! 672: v_sleep((char *)out_silo, CVTTOUT, IVTTOUT, SVTTOUT, ! 673: "asyclose"); ! 674: if (SELF->p_ssig && nondsig()) { /* signal? */ ! 675: RAWOUT_FLUSH(out_silo); ! 676: break; ! 677: } ! 678: } ! 679: need_wake[chan] &= ~NW_OUTSILO; ! 680: ! 681: /* ! 682: * If not hanging in open ! 683: */ ! 684: if ((flags & T_HOPEN) == 0) { ! 685: /* ! 686: * Disable interrupts. ! 687: */ ! 688: outb(port+IER, 0); ! 689: outb(port+MCR, inb(port+MCR) & ~MC_OUTS); ! 690: } ! 691: ! 692: /* ! 693: * If hupcls ! 694: */ ! 695: if (flags & T_HPCL) { ! 696: T_HAL(0x400, printf("ch%d drop DTR\n", chan)); ! 697: /* ! 698: * Hangup port - drop DTR and RTS. ! 699: */ ! 700: outb(port+MCR, inb(port+MCR) & MC_OUTS); ! 701: ! 702: /* ! 703: * Hold dtr low for timeout ! 704: */ ! 705: maj = major(dev); ! 706: drvl[maj].d_time = 1; ! 707: v_sleep((char *)&drvl[maj].d_time, CVTTOUT, IVTTOUT, SVTTOUT, ! 708: "drop DTR"); ! 709: drvl[maj].d_time = 0; ! 710: } ! 711: ! 712: a1->a_poll = 0; ! 713: asyspr(); ! 714: RAWIN_FLUSH(in_silo); ! 715: a1->a_hcls = 0; /* allow reopen - done closing */ ! 716: wakeup((char *)(&tp->t_open)); ! 717: spl(s); ! 718: a1->a_in_use--; ! 719: ! 720: if (!(dev & CPOLL)) ! 721: upd_irq1(a0->a_irqno); ! 722: return; ! 723: ! 724: not_last_close: ! 725: T_HAL(0x400, printf("ch%d open - %d\n", chan, tp->t_open)); ! 726: a1->a_in_use--; ! 727: wakeup((char *)(&tp->t_open)); ! 728: return; ! 729: } ! 730: ! 731: /* ! 732: * asyread() ! 733: */ ! 734: static void ! 735: asyread(dev, iop) ! 736: dev_t dev; ! 737: register IO * iop; ! 738: { ! 739: int chan = channel(dev); ! 740: asy1_t *a1 = asy1 + chan; ! 741: TTY *tp = &a1->a_tty; ! 742: ! 743: ttread(tp, iop); ! 744: } ! 745: ! 746: /* ! 747: * asytimer() ! 748: */ ! 749: static void ! 750: asytimer(dev) ! 751: dev_t dev; ! 752: { ! 753: if (++drvl[major(dev)].d_time > DTRTMOUT) ! 754: wakeup((char *)&drvl[major(dev)].d_time); ! 755: } ! 756: ! 757: /* ! 758: * asywrite() ! 759: */ ! 760: static void ! 761: asywrite(dev, iop) ! 762: dev_t dev; ! 763: register IO * iop; ! 764: { ! 765: int chan = channel(dev); ! 766: asy0_t *a0 = asy0 + chan; ! 767: asy1_t *a1 = asy1 + chan; ! 768: TTY *tp = &a1->a_tty; ! 769: short port = a0->a_port; ! 770: register int c; ! 771: ! 772: /* ! 773: * Treat user writes through tty driver. ! 774: */ ! 775: if (iop->io_seg != IOSYS) { ! 776: ttwrite(tp, iop); ! 777: return; ! 778: } ! 779: ! 780: /* ! 781: * Treat kernel writes by blocking on transmit buffer. ! 782: */ ! 783: while ((c = iogetc(iop)) >= 0) { ! 784: /* ! 785: * Wait until transmit buffer is empty. ! 786: * Check twice to prevent critical race with interrupt handler. ! 787: */ ! 788: for (;;) { ! 789: if (inb(port+LSR) & LS_TxRDY) ! 790: if (inb(port+LSR) & LS_TxRDY) ! 791: break; ! 792: } ! 793: ! 794: /* ! 795: * Output the next character. ! 796: */ ! 797: outb(port+DREG, c); ! 798: } ! 799: } ! 800: ! 801: /* ! 802: * asyioctl() ! 803: */ ! 804: #ifdef _I386 ! 805: static void ! 806: asyioctl0(dev, com, vec) ! 807: dev_t dev; ! 808: int com; ! 809: struct sgttyb *vec; ! 810: { ! 811: tioc286(dev, com, vec, asyioctl); ! 812: } ! 813: #endif ! 814: ! 815: static void ! 816: asyioctl(dev, com, vec) ! 817: dev_t dev; ! 818: int com; struct sgttyb *vec; ! 819: { ! 820: int chan = channel(dev); ! 821: asy0_t *a0 = asy0 + chan; ! 822: asy1_t *a1 = asy1 + chan; ! 823: TTY *tp = &a1->a_tty; ! 824: int s; ! 825: int stat1, stat2; ! 826: silo_t *out_silo = &a1->a_out; ! 827: silo_t *in_silo = &a1->a_in; ! 828: short port = a0->a_port; ! 829: char msr; ! 830: char ier_save; ! 831: char do_ttioctl = 0; ! 832: char do_asyparam = 0; ! 833: ! 834: s = sphi(); ! 835: ier_save = inb(port+IER); ! 836: stat1 = inb(port+MCR); /* get current MCR register status */ ! 837: stat2 = inb(port+LCR); /* get current LCR register status */ ! 838: ! 839: #if 0 ! 840: /* ! 841: * If command will drain input, do the drain now ! 842: * before calling ttioctl(). ! 843: */ ! 844: switch(com) { ! 845: case TCSETAW: ! 846: case TCSETAF: ! 847: case TIOCSETP: ! 848: /* ! 849: * Wait for output silo and UART xmit buffer to empty. ! 850: * Allow signal to break the sleep. ! 851: */ ! 852: for (;;) { ! 853: if (!ttoutp(tp) ! 854: && (out_silo->si_ix == out_silo->si_ox) ! 855: && (inb(port + LSR) & LS_TxIDLE)) ! 856: break; ! 857: need_wake[chan] |= NW_OUTSILO; ! 858: v_sleep((char *)out_silo, CVTTOUT, IVTTOUT, SVTTOUT, ! 859: "asydrain"); ! 860: if (SELF->p_ssig && nondsig()) { /* signal? */ ! 861: break; ! 862: } ! 863: } ! 864: need_wake[chan] &= ~NW_OUTSILO; ! 865: } ! 866: #endif ! 867: ! 868: switch(com) { ! 869: case TIOCSBRK: /* set BREAK */ ! 870: outb(port+LCR, stat2|LC_SBRK); ! 871: break; ! 872: case TIOCCBRK: /* clear BREAK */ ! 873: outb(port+LCR, stat2 & ~LC_SBRK); ! 874: break; ! 875: case TIOCSDTR: /* set DTR */ ! 876: outb(port+MCR, stat1|MC_DTR); ! 877: break; ! 878: case TIOCCDTR: /* clear DTR */ ! 879: outb(port+MCR, stat1 & ~MC_DTR); ! 880: break; ! 881: case TIOCSRTS: /* set RTS */ ! 882: outb(port+MCR, stat1|MC_RTS); ! 883: break; ! 884: case TIOCCRTS: /* clear RTS */ ! 885: outb(port+MCR, stat1 & ~MC_RTS); ! 886: break; ! 887: case TIOCRSPEED: /* set "raw" I/O speed divisor */ ! 888: outb(port+LCR, stat2|LC_DLAB); /* set speed latch bit */ ! 889: outb(port+DLL, (unsigned) vec); ! 890: outb(port+DLH, (unsigned) vec >> 8); ! 891: outb(port+LCR, stat2); /* reset latch bit */ ! 892: break; ! 893: case TIOCWORDL: /* set word length and stop bits */ ! 894: outb(port+LCR, ((stat2&~0x7) | ((unsigned) vec & 0x7))); ! 895: break; ! 896: case TIOCRMSR: /* get CTS/DSR/RI/RLSD (MSR) */ ! 897: msr = inb(port+MSR); ! 898: stat1 = msr >> 4; ! 899: kucopy(&stat1, (unsigned *) vec, sizeof(unsigned)); ! 900: break; ! 901: case TIOCFLUSH: /* Flush silos here, queues in tty.c */ ! 902: RAWIN_FLUSH(in_silo); ! 903: RAWOUT_FLUSH(out_silo); ! 904: do_ttioctl = 1; ! 905: break; ! 906: ! 907: /* ! 908: * If port parameters change, plan to call asyparam(). ! 909: * Need to check now before structs are updated. ! 910: */ ! 911: #ifdef _I386 ! 912: case TCSETA: ! 913: case TCSETAW: ! 914: case TCSETAF: ! 915: { ! 916: struct termio trm; ! 917: ! 918: ukcopy(vec, &trm, sizeof(struct termio)); ! 919: if (trm.c_cflag != tp->t_termio.c_cflag) ! 920: do_asyparam = 1; ! 921: } ! 922: do_ttioctl = 1; ! 923: break; ! 924: #endif ! 925: case TIOCSETP: ! 926: case TIOCSETN: ! 927: { ! 928: struct sgttyb sg; ! 929: ! 930: ukcopy(vec, &sg, sizeof(struct sgttyb)); ! 931: if (sg.sg_ispeed != tp->t_sgttyb.sg_ispeed ! 932: || ((sg.sg_flags ^ tp->t_sgttyb.sg_flags) & ANYP)) ! 933: do_asyparam = 1; ! 934: } ! 935: do_ttioctl = 1; ! 936: break; ! 937: default: ! 938: do_ttioctl = 1; ! 939: } ! 940: outb(port+IER, ier_save); ! 941: if (do_ttioctl) ! 942: ttioctl(tp, com, vec); ! 943: spl(s); ! 944: if (do_asyparam) ! 945: asyparam(tp); ! 946: } ! 947: ! 948: /* ! 949: * asyparam() ! 950: */ ! 951: static void ! 952: asyparam(tp) ! 953: TTY * tp; ! 954: { ! 955: int chan = (int)tp->t_ddp; ! 956: asy0_t *a0 = asy0 + chan; ! 957: asy1_t *a1 = asy1 + chan; ! 958: short port = a0->a_port; ! 959: int s; ! 960: int write_baud=1, write_lcr=1; ! 961: char newlcr, speed; ! 962: ! 963: #ifdef _I386 ! 964: unsigned short cflag = tp->t_termio.c_cflag; ! 965: ! 966: T_HAL(4, printf("ch%d asyparam cflag=%x\n", chan, cflag)); ! 967: speed = cflag & CBAUD; ! 968: switch (cflag & CSIZE) { ! 969: case CS5: newlcr = LC_CS5; break; ! 970: case CS6: newlcr = LC_CS6; break; ! 971: case CS7: newlcr = LC_CS7; break; ! 972: case CS8: newlcr = LC_CS8; break; ! 973: } ! 974: if (cflag & CSTOPB) ! 975: newlcr |= LC_STOPB; ! 976: if (cflag & PARENB) { ! 977: newlcr |= LC_PARENB; ! 978: if ((cflag & PARODD) == 0) ! 979: newlcr |= LC_PAREVEN; ! 980: } ! 981: #else ! 982: speed = tp->t_sgttyb.sg_ispeed; ! 983: switch (tp->t_sgttyb.sg_flags & (EVENP|ODDP|RAW)) { ! 984: case ODDP: ! 985: newlcr = LC_CS7|LC_PARENB; ! 986: break; ! 987: case EVENP: ! 988: newlcr = LC_CS7|LC_PARENB|LC_PAREVEN; ! 989: break; ! 990: default: ! 991: newlcr = LC_CS8; ! 992: break; ! 993: } ! 994: #endif ! 995: ! 996: /* ! 997: * Don't bang on the UART needlessly. ! 998: * Writing baud rate resets the port, which loses characters. ! 999: * You want this on first open, NOT on later opens. ! 1000: */ ! 1001: if (speed == a0->a_speed && tp->t_open) { ! 1002: write_baud = 0; ! 1003: if (newlcr == a1->a_lcr) { ! 1004: write_lcr = 0; ! 1005: } ! 1006: } ! 1007: a0->a_speed = speed; ! 1008: a1->a_lcr = newlcr; ! 1009: ! 1010: if (write_lcr) { ! 1011: char ier_save; ! 1012: s = sphi(); ! 1013: ier_save = inb(port+IER); ! 1014: if (write_baud) { ! 1015: short divisor = albaud[speed]; ! 1016: ! 1017: T_HAL(4, printf("CH%d speed=%x\n", chan, speed)); ! 1018: outb(port+LCR, LC_DLAB); ! 1019: outb(port+DLL, divisor); ! 1020: outb(port+DLH, divisor >> 8); ! 1021: } ! 1022: T_HAL(4, printf("CH%d newlcr=%x\n", chan, newlcr)); ! 1023: outb(port+LCR, newlcr); ! 1024: if (a1->a_ut == US_16550A) ! 1025: outb(port+FCR, FC_ENABLE | FC_Rx_RST | FC_Rx_08); ! 1026: outb(port+IER, ier_save); ! 1027: spl(s); ! 1028: } ! 1029: if (write_baud) ! 1030: asyspr(); ! 1031: } ! 1032: ! 1033: /* ! 1034: * asystart() ! 1035: */ ! 1036: static void ! 1037: asystart(tp) ! 1038: TTY * tp; ! 1039: { ! 1040: int chan = (int)tp->t_ddp; ! 1041: asy0_t *a0 = asy0 + chan; ! 1042: asy1_t *a1 = asy1 + chan; ! 1043: short port = a0->a_port; ! 1044: int s; ! 1045: int need_xmit = 1; /* True if should start sending data now. */ ! 1046: silo_t *out_silo = &a1->a_out; ! 1047: char lsr; ! 1048: ! 1049: /* ! 1050: * Read line status register AFTER disabling interrupts. ! 1051: */ ! 1052: s = sphi(); ! 1053: lsr = inb(port + LSR); ! 1054: ! 1055: /* ! 1056: * Process break indication. ! 1057: * NOTE: Break indication cleared when line status register was read. ! 1058: */ ! 1059: if (lsr & LS_BREAK) ! 1060: defer(asybreak, chan); ! 1061: ! 1062: /* ! 1063: * If no output data, it may be time to finish closing the port; ! 1064: * but won't need another xmit interrupt. ! 1065: */ ! 1066: if (out_silo->si_ix == out_silo->si_ox) { ! 1067: if (need_wake[chan] & NW_OUTSILO) { ! 1068: need_wake[chan] &= ~NW_OUTSILO; ! 1069: wakeup((char *)out_silo); ! 1070: } ! 1071: need_xmit = 0; ! 1072: } ! 1073: ! 1074: /* ! 1075: * Do nothing if output is stopped. ! 1076: */ ! 1077: if (tp->t_flags & T_STOP) ! 1078: need_xmit = 0; ! 1079: if (a1->a_ohlt) ! 1080: need_xmit = 0; ! 1081: ! 1082: /* ! 1083: * Start data transmission by writing to UART xmit reg. ! 1084: */ ! 1085: if ((lsr & LS_TxRDY) && need_xmit) { ! 1086: int xmit_count; ! 1087: xmit_count = (a1->a_ut == US_16550A)?16:1; ! 1088: asy_send(out_silo, port+DREG, xmit_count); ! 1089: } ! 1090: spl(s); ! 1091: } ! 1092: ! 1093: /* ! 1094: * asypoll() ! 1095: */ ! 1096: static int ! 1097: asypoll(dev, ev, msec) ! 1098: dev_t dev; ! 1099: int ev; ! 1100: int msec; ! 1101: { ! 1102: int chan = channel(dev); ! 1103: asy1_t *a1 = asy1 + chan; ! 1104: TTY *tp = &a1->a_tty; ! 1105: ! 1106: return ttpoll(tp, ev, msec); ! 1107: } ! 1108: ! 1109: /* ! 1110: * asycycle() ! 1111: * ! 1112: * Do a wakeup of any sleeping asy's at regular intervals. ! 1113: */ ! 1114: static void ! 1115: asycycle(chan) ! 1116: int chan; ! 1117: { ! 1118: asy0_t *a0 = asy0 + chan; ! 1119: asy1_t *a1 = asy1 + chan; ! 1120: TTY *tp = &a1->a_tty; ! 1121: short port = a0->a_port; ! 1122: int s; ! 1123: char msr, mcr; ! 1124: silo_t *out_silo = &a1->a_out; ! 1125: silo_t *in_silo = &a1->a_in; ! 1126: int n, ch; ! 1127: int do_start = 1; ! 1128: unsigned char iir; ! 1129: ! 1130: /* ! 1131: * Check Carrier Detect (RLSD). ! 1132: * ! 1133: * Modem status interrupts were not enabled due to 8250 hardware bug. ! 1134: * Enabling modem status and receive interrupts may cause lockup ! 1135: * on older parts. ! 1136: */ ! 1137: if (tp->t_flags & T_MODC) { ! 1138: ! 1139: /* ! 1140: * Get status ! 1141: */ ! 1142: msr = inb(port+MSR); ! 1143: ! 1144: /* ! 1145: * Carrier changed. ! 1146: */ ! 1147: if ((msr & MS_RLSD) && !(tp->t_flags & T_CARR)) { ! 1148: /* ! 1149: * Carrier is on - wakeup open. ! 1150: */ ! 1151: s = sphi(); ! 1152: tp->t_flags |= T_CARR; ! 1153: spl(s); ! 1154: wakeup((char *)(&tp->t_open)); ! 1155: } ! 1156: ! 1157: if (!(msr & MS_RLSD) && (tp->t_flags & T_CARR)) { ! 1158: s = sphi(); ! 1159: RAWIN_FLUSH(in_silo); ! 1160: RAWOUT_FLUSH(out_silo); ! 1161: tp->t_flags &= ~T_CARR; ! 1162: spl(s); ! 1163: tthup(tp); ! 1164: } ! 1165: } ! 1166: ! 1167: /* ! 1168: * Empty raw input buffer. ! 1169: * ! 1170: * The line discipline module (tty.c) will set T_ISTOP true when the ! 1171: * tt input queue is nearly full (tp->t_iq.cq_cc >= IHILIM), and make ! 1172: * T_ISTOP false when it's ready for more input. ! 1173: * ! 1174: * When T_ISTOP is true, ttin() simply discards the character passed. ! 1175: */ ! 1176: if (!(tp->t_flags & T_ISTOP)) { ! 1177: while (in_silo->SILO_CHAR_COUNT > 0) { ! 1178: s = sphi(); ! 1179: ttin(tp, in_silo->si_buf[in_silo->si_ox]); ! 1180: if (in_silo->si_ox < MAX_SILO_INDEX) ! 1181: in_silo->si_ox++; ! 1182: else ! 1183: in_silo->si_ox = 0; ! 1184: in_silo->SILO_CHAR_COUNT--; ! 1185: spl(s); ! 1186: } ! 1187: } ! 1188: ! 1189: /* ! 1190: * Hardware flow control. ! 1191: * Check CTS to see if we need to halt output. ! 1192: * (MS_INTR should have done this - repeat code here to be sure) ! 1193: * Check input silo to see if we need to raise RTS. ! 1194: */ ! 1195: if (tp->t_flags & T_CFLOW) { ! 1196: ! 1197: /* ! 1198: * Get status ! 1199: */ ! 1200: msr = inb(port+MSR); ! 1201: s = sphi(); ! 1202: if (msr & MS_CTS) ! 1203: a1->a_ohlt = 0; ! 1204: else ! 1205: a1->a_ohlt = 1; ! 1206: spl(s); ! 1207: T_HAL(4, {static cts = 0; if (!cts && (msr & MS_CTS)) { cts = 1; putchar('[');\ ! 1208: } else if (cts && !(msr & MS_CTS)) { cts = 0; putchar(']'); }}); ! 1209: ! 1210: /* ! 1211: * If using hardware flow control, see if we need to drop RTS. ! 1212: */ ! 1213: if ((tp->t_flags & T_CFLOW) ! 1214: && (in_silo->SILO_CHAR_COUNT > SILO_HIGH_MARK)) { ! 1215: s = sphi(); ! 1216: mcr = inb(port+MCR); ! 1217: if (mcr & MC_RTS) { ! 1218: outb(port+MCR, mcr & ~MC_RTS); ! 1219: T_HAL(4, putchar('-')); ! 1220: } ! 1221: spl(s); ! 1222: } ! 1223: ! 1224: /* ! 1225: * If input silo below low mark, assert RTS. ! 1226: */ ! 1227: if (in_silo->SILO_CHAR_COUNT <= SILO_LOW_MARK) { ! 1228: s = sphi(); ! 1229: mcr = inb(port+MCR); ! 1230: if ((mcr & MC_RTS) == 0) { ! 1231: outb(port+MCR, mcr | MC_RTS); ! 1232: T_HAL(4, putchar('+')); ! 1233: } ! 1234: spl(s); ! 1235: } ! 1236: } ! 1237: ! 1238: /* ! 1239: * Calculate free output slot count. ! 1240: */ ! 1241: n = sizeof(out_silo->si_buf) - 1; ! 1242: n += out_silo->si_ox - out_silo->si_ix; ! 1243: n %= sizeof(out_silo->si_buf); ! 1244: ! 1245: /* ! 1246: * Fill raw output buffer. ! 1247: */ ! 1248: for (;;) { ! 1249: if (--n < 0) ! 1250: break; ! 1251: s = sphi(); ! 1252: ch = ttout(tp); ! 1253: spl(s); ! 1254: if (ch < 0) ! 1255: break; ! 1256: ! 1257: s = sphi(); ! 1258: out_silo->si_buf[out_silo->si_ix] = ch; ! 1259: if (out_silo->si_ix >= sizeof(out_silo->si_buf) - 1) ! 1260: out_silo->si_ix = 0; ! 1261: else ! 1262: out_silo->si_ix++; ! 1263: spl(s); ! 1264: } ! 1265: ! 1266: #ifdef _I386 ! 1267: /* ! 1268: * if port has an interrupt pending (probably missed an irq) ! 1269: * the following two loops should not be merged ! 1270: * - need ALL port irq's inactive at once ! 1271: * for each port on this irq line (use irq1 for this) ! 1272: * disable interrupts (clear IER) ! 1273: * for each port on this irq line ! 1274: * restore interrupts ! 1275: */ ! 1276: if (a1->a_has_irq && ((iir=inb(port+IIR)) & 1) == 0) { ! 1277: struct irqnode *ip; ! 1278: asy_gp_t *gp; ! 1279: int s; ! 1280: short p; ! 1281: char c, slot; ! 1282: ! 1283: T_HAL(4, printf("CH%d missed iir:x\n", chan, iir)); ! 1284: ! 1285: do_start = 0; ! 1286: s = sphi(); ! 1287: ip = irq1[a0->a_irqno]; ! 1288: while(ip) { ! 1289: if (ip->func == asyintr) { ! 1290: p = ip->arg; ! 1291: outb(p + IER, 0); ! 1292: } else { ! 1293: gp = asy_gp + ip->arg; ! 1294: for (slot = 0; slot < MAX_SLOTS; slot++) { ! 1295: if ((c=gp->chan_list[slot]) < MAX_ASY){ ! 1296: p = asy0[c].a_port; ! 1297: outb(p + IER, 0); ! 1298: } ! 1299: } ! 1300: } ! 1301: ip = ip->next_actv; ! 1302: } ! 1303: /* ! 1304: * Now, all ports on the offending irq line have irq off. ! 1305: */ ! 1306: ip = irq1[a0->a_irqno]; ! 1307: while(ip) { ! 1308: if (ip->func == asyintr) { ! 1309: p = ip->arg; ! 1310: outb(p + IER, IEN); ! 1311: } else { ! 1312: gp = asy_gp + ip->arg; ! 1313: for (slot = 0; slot < MAX_SLOTS; slot++) { ! 1314: if ((c=gp->chan_list[slot]) < MAX_ASY){ ! 1315: p = asy0[c].a_port; ! 1316: outb(p + IER, IEN); ! 1317: } ! 1318: } ! 1319: } ! 1320: ip = ip->next_actv; ! 1321: } ! 1322: spl(s); ! 1323: } ! 1324: #endif ! 1325: ! 1326: if(do_start) ! 1327: ttstart(tp); ! 1328: ! 1329: /* ! 1330: * Schedule next cycle. ! 1331: */ ! 1332: if (a1->a_in_use) { ! 1333: timeout(&tp->t_rawtim, HZ/10, asycycle, chan); ! 1334: } ! 1335: } ! 1336: ! 1337: /* ! 1338: * irqdummy() ! 1339: * ! 1340: * Suppress interrupts that may occur during chip sensing. ! 1341: */ ! 1342: static void ! 1343: irqdummy() ! 1344: { ! 1345: /* ! 1346: * Try to clear all pending interrupts. ! 1347: */ ! 1348: inb(dummy_port+IIR); ! 1349: inb(dummy_port+LSR); ! 1350: inb(dummy_port+MSR); ! 1351: inb(dummy_port+DREG); ! 1352: } ! 1353: ! 1354: /* ! 1355: * add_irq() ! 1356: * ! 1357: * Given channel number, add port info to irq0 list. ! 1358: */ ! 1359: static void ! 1360: add_irq(irq, func, arg) ! 1361: int irq; ! 1362: void (*func)(); ! 1363: int arg; ! 1364: { ! 1365: struct irqnode * np; ! 1366: ! 1367: /* ! 1368: * Sanity check. ! 1369: */ ! 1370: if (irq <=0 || irq >= NUM_IRQ || itbl[irq] == 0) ! 1371: return; ! 1372: ! 1373: if (nextnode < MAX_ASY) { ! 1374: np = nodespace + nextnode++; ! 1375: np->func = func; ! 1376: np->arg = arg; ! 1377: np->next = irq0[irq]; ! 1378: irq0[irq] = np; ! 1379: } else { ! 1380: printf("asy: too many irq nodes (%d)\n", nextnode); ! 1381: } ! 1382: } ! 1383: ! 1384: /* ! 1385: * Interrupt handlers. ! 1386: */ ! 1387: static void i2() { asy_irq(irq1[2]); } ! 1388: static void i3() { asy_irq(irq1[3]); } ! 1389: static void i4() { asy_irq(irq1[4]); } ! 1390: static void i5() { asy_irq(irq1[5]); } ! 1391: static void i6() { asy_irq(irq1[6]); } ! 1392: static void i7() { asy_irq(irq1[7]); } ! 1393: static void i8() { asy_irq(irq1[8]); } ! 1394: static void i9() { asy_irq(irq1[9]); } ! 1395: static void i10() { asy_irq(irq1[10]); } ! 1396: static void i11() { asy_irq(irq1[11]); } ! 1397: static void i12() { asy_irq(irq1[12]); } ! 1398: static void i13() { asy_irq(irq1[13]); } ! 1399: static void i14() { asy_irq(irq1[14]); } ! 1400: static void i15() { asy_irq(irq1[15]); } ! 1401: ! 1402: /* ! 1403: * asy_irq() ! 1404: * ! 1405: * Given pointer to node list, service async interrupt. ! 1406: */ ! 1407: static void ! 1408: asy_irq(ip) ! 1409: struct irqnode * ip; ! 1410: { ! 1411: struct irqnode *here; ! 1412: int doit; ! 1413: ! 1414: do { ! 1415: doit = 0; ! 1416: here = ip; ! 1417: while(here) { ! 1418: doit |= (*(here->func))(here->arg); ! 1419: here = here->next_actv; ! 1420: } ! 1421: } while(doit); ! 1422: } ! 1423: ! 1424: /* ! 1425: * upd_irq1() ! 1426: * ! 1427: * Given an irq number, rebuild the links for active devices. ! 1428: */ ! 1429: static void ! 1430: upd_irq1(irq) ! 1431: int irq; ! 1432: { ! 1433: struct irqnode *np; ! 1434: asy1_t *a1; ! 1435: int chan; ! 1436: int s; ! 1437: ! 1438: /* ! 1439: * Sanity check. ! 1440: */ ! 1441: if (irq <=0 || irq >= NUM_IRQ || itbl[irq] == 0) ! 1442: return; ! 1443: ! 1444: /* ! 1445: * For each node in the irq0 list ! 1446: * if node is for irq status port ! 1447: * for each channel using the status port ! 1448: * if channel in use, in irq mode ! 1449: * add node to irq1 list ! 1450: * skip rest of channels for this node ! 1451: * else - node is for simple UART ! 1452: * if channel in use, in irq mode ! 1453: * add node to irq1 list ! 1454: */ ! 1455: s = sphi(); ! 1456: np = irq0[irq]; ! 1457: irq1[irq] = 0; ! 1458: while (np) { ! 1459: if (np->func != asyintr) { ! 1460: char ix, loop = 1; ! 1461: asy_gp_t *gp = asy_gp + np->arg; ! 1462: ! 1463: for (ix = 0; ix < MAX_SLOTS && loop; ix++) { ! 1464: if ((chan = gp->chan_list[ix]) < MAX_ASY) { ! 1465: a1 = asy1 + chan; ! 1466: if (a1->a_in_use && a1->a_irq) { ! 1467: np->next_actv = irq1[irq]; ! 1468: irq1[irq] = np; ! 1469: loop = 0; ! 1470: } ! 1471: } ! 1472: } ! 1473: } else { ! 1474: a1 = asy1 + np->arg; ! 1475: if (a1->a_in_use && a1->a_irq) { ! 1476: np->next_actv = irq1[irq]; ! 1477: irq1[irq] = np; ! 1478: } ! 1479: } ! 1480: np = np->next; ! 1481: } ! 1482: spl(s); ! 1483: } ! 1484: ! 1485: /* ! 1486: * asybreak() ! 1487: */ ! 1488: static void ! 1489: asybreak(chan) ! 1490: int chan; ! 1491: { ! 1492: int s; ! 1493: asy1_t *a1 = asy1 + chan; ! 1494: silo_t *out_silo = &a1->a_out; ! 1495: silo_t *in_silo = &a1->a_in; ! 1496: TTY *tp = &a1->a_tty; ! 1497: ! 1498: s = sphi(); ! 1499: RAWIN_FLUSH(in_silo); ! 1500: RAWOUT_FLUSH(out_silo); ! 1501: spl(s); ! 1502: ttsignal(tp, SIGINT); ! 1503: } ! 1504: ! 1505: /* ! 1506: * asyintr() ! 1507: * ! 1508: * Handle interrupt for a single channel. ! 1509: */ ! 1510: static int ! 1511: asyintr(chan) ! 1512: int chan; ! 1513: { ! 1514: asy0_t *a0 = asy0 + chan; ! 1515: asy1_t *a1 = asy1 + chan; ! 1516: TTY *tp = &a1->a_tty; ! 1517: silo_t *out_silo = &a1->a_out; ! 1518: silo_t *in_silo = &a1->a_in; ! 1519: int c, xmit_count; ! 1520: int ret = 0; ! 1521: short port = a0->a_port; ! 1522: unsigned char msr, lsr; ! 1523: ! 1524: if (chan >= MAX_ASY) { ! 1525: printf("asy: irq on channel %d\n", chan); ! 1526: return 0; ! 1527: } ! 1528: ! 1529: rescan: ! 1530: switch (inb(port+IIR) & 0x07) { ! 1531: ! 1532: case LS_INTR: ! 1533: ret = 1; ! 1534: lsr = inb(port + LSR); ! 1535: T_HAL(0x800, printf("[%d:L%x]", chan, lsr)); ! 1536: if (lsr & LS_BREAK) ! 1537: defer(asybreak, chan); ! 1538: goto rescan; ! 1539: ! 1540: case Rx_INTR: ! 1541: T_HAL(0x800, printf("[%d:R]", chan)); ! 1542: ret = 1; ! 1543: c = inb(port+DREG); ! 1544: if (tp->t_open == 0) ! 1545: goto rescan; ! 1546: /* ! 1547: * Must recognize XOFF quickly to avoid transmit overrun. ! 1548: * Recognize XON here as well to avoid race conditions. ! 1549: */ ! 1550: if (!ISRIN) { ! 1551: /* ! 1552: * XOFF. ! 1553: */ ! 1554: if (ISSTOP) { ! 1555: tp->t_flags |= T_STOP; ! 1556: goto rescan; ! 1557: } ! 1558: ! 1559: /* ! 1560: * XON. ! 1561: */ ! 1562: if (ISSTART) { ! 1563: tp->t_flags &= ~T_STOP; ! 1564: goto rescan; ! 1565: } ! 1566: } ! 1567: ! 1568: /* ! 1569: * Save char in raw input buffer. ! 1570: */ ! 1571: if (in_silo->SILO_CHAR_COUNT < MAX_SILO_CHARS) { ! 1572: in_silo->si_buf[in_silo->si_ix] = c; ! 1573: if (in_silo->si_ix < MAX_SILO_INDEX) ! 1574: in_silo->si_ix++; ! 1575: else ! 1576: in_silo->si_ix = 0; ! 1577: in_silo->SILO_CHAR_COUNT++; ! 1578: } ! 1579: ! 1580: /* ! 1581: * If using hardware flow control, see if we need to drop RTS. ! 1582: */ ! 1583: if ((tp->t_flags & T_CFLOW) ! 1584: && (in_silo->SILO_CHAR_COUNT > SILO_HIGH_MARK)) { ! 1585: unsigned char mcr = inb(port+MCR); ! 1586: if (mcr & MC_RTS) { ! 1587: outb(port+MCR, mcr & ~MC_RTS); ! 1588: } ! 1589: } ! 1590: ! 1591: goto rescan; ! 1592: ! 1593: case Tx_INTR: ! 1594: T_HAL(0x800, printf("[%d:T]", chan)); ! 1595: ret = 1; ! 1596: /* ! 1597: * Do nothing if output is stopped. ! 1598: */ ! 1599: if (tp->t_flags & T_STOP) { ! 1600: goto rescan; ! 1601: } ! 1602: if (a1->a_ohlt) ! 1603: goto rescan; ! 1604: ! 1605: /* ! 1606: * Transmit next char in raw output buffer. ! 1607: */ ! 1608: xmit_count = (a1->a_ut == US_16550A)?16:1; ! 1609: asy_send(out_silo, port+DREG, xmit_count); ! 1610: goto rescan; ! 1611: ! 1612: case MS_INTR: ! 1613: ret = 1; ! 1614: /* ! 1615: * Get status (and clear interrupt). ! 1616: */ ! 1617: msr = inb(port+MSR); ! 1618: T_HAL(0x800, printf("[%d:M%x]", chan, msr)); ! 1619: ! 1620: /* ! 1621: * Hardware flow control. ! 1622: * Check CTS to see if we need to halt output. ! 1623: */ ! 1624: if (tp->t_flags & T_CFLOW) { ! 1625: if (msr & MS_CTS) ! 1626: a1->a_ohlt = 0; ! 1627: else ! 1628: a1->a_ohlt = 1; ! 1629: } ! 1630: ! 1631: goto rescan; ! 1632: default: ! 1633: return ret; ! 1634: } /* endswitch */ ! 1635: } ! 1636: ! 1637: /* ! 1638: * asyclk() ! 1639: * ! 1640: * Called every time T0 interrupts.- if it returns 0, ! 1641: * the usual system timer interrupt stuff is done. ! 1642: * Poll all pollable ports. ! 1643: */ ! 1644: static int ! 1645: asyclk() ! 1646: { ! 1647: static int count; ! 1648: int ix; ! 1649: ! 1650: for (ix = 0; ix < ppnum; ix++) ! 1651: asysph(pptbl[ix]); ! 1652: ! 1653: count++; ! 1654: if (count >= poll_divisor) ! 1655: count = 0; ! 1656: return count; ! 1657: } ! 1658: ! 1659: /* ! 1660: * asyspr() ! 1661: * ! 1662: * asyspr is called when a port is opened or closed or changes speed ! 1663: * It sets the polling rate only as fast as needed, and shuts off polling ! 1664: * whenever possible. ! 1665: * It updates the links in irq1[0], which lists polled-mode ports. ! 1666: */ ! 1667: static void ! 1668: asyspr() ! 1669: { ! 1670: asy0_t *a0; ! 1671: asy1_t *a1; ! 1672: int chan; ! 1673: int s; ! 1674: int ix, max_rate, port_rate; ! 1675: ! 1676: /* ! 1677: * Rebuild table of pollable ports. ! 1678: */ ! 1679: s = sphi(); ! 1680: ppnum = 0; ! 1681: for (chan = 0; chan < ASY_NUM; chan++) { ! 1682: a1 = asy1 + chan; ! 1683: if (a1->a_poll) ! 1684: pptbl[ppnum++] = chan; ! 1685: } ! 1686: spl(s); ! 1687: ! 1688: /* ! 1689: * If another driver has the polling clock, do nothing. ! 1690: */ ! 1691: if (poll_owner & ~ POLL_ASY) ! 1692: return; ! 1693: ! 1694: /* ! 1695: * Find highest valid polling rate in units of HZ/10. ! 1696: * If using FIFO chip, can poll at 1/16 the usual rate. ! 1697: */ ! 1698: max_rate = 0; ! 1699: for (ix = 0; ix < ppnum; ix++) { ! 1700: chan = pptbl[ix]; ! 1701: a0 = asy0 + chan; ! 1702: a1 = asy1 + chan; ! 1703: port_rate = alp_rate[a0->a_speed]; ! 1704: if (a1->a_ut == US_16550A) { ! 1705: port_rate /= 16; ! 1706: if (port_rate % HZ) ! 1707: port_rate += HZ - (port_rate % HZ); ! 1708: } ! 1709: if (max_rate < port_rate) ! 1710: max_rate = port_rate; ! 1711: } ! 1712: ! 1713: /* ! 1714: * if max_rate is not current rate, adjust the system clock ! 1715: */ ! 1716: if (max_rate != poll_rate) { ! 1717: poll_rate = max_rate; ! 1718: poll_divisor = poll_rate/HZ; /* used in asyclk() */ ! 1719: altclk_out(); /* stop previous polling */ ! 1720: poll_owner &= ~ POLL_ASY; ! 1721: if (poll_rate) { /* resume polling at new rate if needed */ ! 1722: poll_owner |= POLL_ASY; ! 1723: altclk_in(poll_rate, asyclk); ! 1724: } ! 1725: } ! 1726: } ! 1727: ! 1728: /* ! 1729: * asysph() ! 1730: * ! 1731: * Serial polling handler. ! 1732: */ ! 1733: static void ! 1734: asysph(chan) ! 1735: int chan; ! 1736: { ! 1737: asy0_t *a0 = asy0 + chan; ! 1738: asy1_t *a1 = asy1 + chan; ! 1739: TTY *tp = &a1->a_tty; ! 1740: silo_t *out_silo = &a1->a_out; ! 1741: silo_t *in_silo = &a1->a_in; ! 1742: int c, xmit_count; ! 1743: short port = a0->a_port; ! 1744: char lsr; ! 1745: ! 1746: /* ! 1747: * Check for received break first. ! 1748: * This status is wiped out on reading the LSR. ! 1749: */ ! 1750: lsr = inb(port + LSR); ! 1751: if (lsr & LS_BREAK) ! 1752: defer(asybreak, chan); ! 1753: ! 1754: /* ! 1755: * Handle all incoming characters. ! 1756: */ ! 1757: for (;;) { ! 1758: lsr = inb(port + LSR); ! 1759: if ((lsr & LS_RxRDY) == 0) ! 1760: break; ! 1761: c = inb(port+DREG); ! 1762: if (tp->t_open == 0) ! 1763: continue; ! 1764: /* ! 1765: * Must recognize XOFF quickly to avoid transmit overrun. ! 1766: * Recognize XON here as well to avoid race conditions. ! 1767: */ ! 1768: if (!ISRIN) { ! 1769: /* ! 1770: * XOFF. ! 1771: */ ! 1772: if (ISSTOP) { ! 1773: tp->t_flags |= T_STOP; ! 1774: continue; ! 1775: } ! 1776: ! 1777: /* ! 1778: * XON. ! 1779: */ ! 1780: if (ISSTART) { ! 1781: tp->t_flags &= ~T_STOP; ! 1782: continue; ! 1783: } ! 1784: } ! 1785: ! 1786: /* ! 1787: * Save char in raw input buffer. ! 1788: */ ! 1789: if (in_silo->SILO_CHAR_COUNT < MAX_SILO_CHARS) { ! 1790: in_silo->si_buf[in_silo->si_ix] = c; ! 1791: if (in_silo->si_ix < MAX_SILO_INDEX) ! 1792: in_silo->si_ix++; ! 1793: else ! 1794: in_silo->si_ix = 0; ! 1795: in_silo->SILO_CHAR_COUNT++; ! 1796: } ! 1797: ! 1798: /* ! 1799: * If using hardware flow control, see if we need to drop RTS. ! 1800: */ ! 1801: if ((tp->t_flags & T_CFLOW) ! 1802: && (in_silo->SILO_CHAR_COUNT > SILO_HIGH_MARK)) { ! 1803: unsigned char mcr = inb(port+MCR); ! 1804: if (mcr & MC_RTS) { ! 1805: outb(port+MCR, mcr & ~MC_RTS); ! 1806: } ! 1807: } ! 1808: } ! 1809: ! 1810: /* ! 1811: * Handle outgoing characters. ! 1812: * Do nothing if output is stopped. ! 1813: */ ! 1814: lsr = inb(port + LSR); ! 1815: if ((lsr & LS_TxRDY) ! 1816: && !(tp->t_flags & T_STOP) ! 1817: && !(a1->a_ohlt)) { ! 1818: /* ! 1819: * Transmit next char in raw output buffer. ! 1820: */ ! 1821: xmit_count = (a1->a_ut == US_16550A)?16:1; ! 1822: asy_send(out_silo, port+DREG, xmit_count); ! 1823: } ! 1824: ! 1825: /* ! 1826: * Hardware flow control. ! 1827: * Check CTS to see if we need to halt output. ! 1828: */ ! 1829: if (tp->t_flags & T_CFLOW) { ! 1830: if (inb(port+MSR) & MS_CTS) ! 1831: a1->a_ohlt = 0; ! 1832: else ! 1833: a1->a_ohlt = 1; ! 1834: } ! 1835: } ! 1836: ! 1837: /* ! 1838: * asy_send() ! 1839: * ! 1840: * Write to xmit data register of the UART. ! 1841: * Assume all checking about whether it's time to send has been done already. ! 1842: * Called by time-critical IRQ and polling routines! ! 1843: * ! 1844: * "rawout" is the output silo for the TTY struct supplying data to the port. ! 1845: * "dreg" is the i/o address of the UART xmit data register. ! 1846: * "xmit_count" is the max number of chars we can write (16 for FIFO parts). ! 1847: */ ! 1848: static int ! 1849: asy_send(rawout, dreg, xmit_count) ! 1850: register silo_t * rawout; ! 1851: int dreg, xmit_count; ! 1852: { ! 1853: /* ! 1854: * Transmit next chars in raw output buffer. ! 1855: */ ! 1856: for (;(rawout->si_ix != rawout->si_ox) && xmit_count; xmit_count--) { ! 1857: outb(dreg, rawout->si_buf[rawout->si_ox]); ! 1858: /* ! 1859: * Adjust raw output buffer output index. ! 1860: */ ! 1861: if (++rawout->si_ox >= sizeof(rawout->si_buf)) ! 1862: rawout->si_ox = 0; ! 1863: } ! 1864: return xmit_count; ! 1865: } ! 1866: ! 1867: /* ! 1868: * p1() ! 1869: * ! 1870: * Interrupt handler for Comtrol-type port groups. ! 1871: * Status register has 1 in bit positions for interrupting ports. ! 1872: */ ! 1873: static int ! 1874: p1(g) ! 1875: int g; ! 1876: { ! 1877: asy_gp_t *gp = asy_gp + g; ! 1878: short port = gp->stat_port; ! 1879: unsigned char status, index, chan; ! 1880: int safety = LOOP_LIMIT; ! 1881: int ret = 0; ! 1882: ! 1883: #if 0 /* DEBUG */ ! 1884: static int pxstat[2][8]; ! 1885: int ci; ! 1886: int change_found=0; ! 1887: ! 1888: for (ci=0; ci<1; ci++) { ! 1889: index = inb(port+ci); ! 1890: outb(port+ci, 0); ! 1891: if (index != pxstat[g][ci]) { ! 1892: if (!change_found) { ! 1893: change_found = 1; ! 1894: printf("<%d:", g); ! 1895: } else ! 1896: putchar(' '); ! 1897: printf("%x:%x", port+ci, index); ! 1898: pxstat[g][ci] = index; ! 1899: } ! 1900: } ! 1901: if (change_found) ! 1902: putchar('>'); ! 1903: for (ci=0; ci<8; ci++) ! 1904: asyintr(4+ci); ! 1905: putchar('.'); ! 1906: return 0; ! 1907: #endif /* DEBUG */ ! 1908: ! 1909: /* ! 1910: * while any port is active ! 1911: * call simple interrupt handler for active channel ! 1912: */ ! 1913: while (status = inb(port)) { ! 1914: ret = 1; ! 1915: index = 0; ! 1916: if (status & 0xf0) { ! 1917: status &= 0xf0; ! 1918: index +=4; ! 1919: } else ! 1920: status &= 0x0f; ! 1921: if (status & 0xcc) { ! 1922: status &= 0xcc; ! 1923: index +=2; ! 1924: } else ! 1925: status &= 0x33; ! 1926: if (status & 0xaa) ! 1927: index++; ! 1928: chan = gp->chan_list[index]; ! 1929: asyintr(chan); ! 1930: if (safety-- == 0) { ! 1931: printf("asy: p1 runaway - status %x\n", status); ! 1932: break; ! 1933: } ! 1934: } ! 1935: ! 1936: return ret; ! 1937: } ! 1938: ! 1939: /* ! 1940: * p2() ! 1941: * ! 1942: * Interrupt handler for Arnet-type port groups. ! 1943: * Status register has 0 in bit positions for interrupting ports. ! 1944: */ ! 1945: static int ! 1946: p2(g) ! 1947: int g; ! 1948: { ! 1949: asy_gp_t *gp = asy_gp + g; ! 1950: short port = gp->stat_port; ! 1951: unsigned char status, index, chan; ! 1952: int safety = LOOP_LIMIT; ! 1953: int ret = 0; ! 1954: ! 1955: /* ! 1956: * while any port is active ! 1957: * call simple interrupt handler for active channel ! 1958: */ ! 1959: while (status = ~inb(port)) { ! 1960: ret = 1; ! 1961: index = 0; ! 1962: if (status & 0xf0) { ! 1963: status &= 0xf0; ! 1964: index +=4; ! 1965: } else ! 1966: status &= 0x0f; ! 1967: if (status & 0xcc) { ! 1968: status &= 0xcc; ! 1969: index +=2; ! 1970: } else ! 1971: status &= 0x33; ! 1972: if (status & 0xaa) ! 1973: index++; ! 1974: chan = gp->chan_list[index]; ! 1975: asyintr(chan); ! 1976: if (safety-- == 0) { ! 1977: printf("asy: p2 runaway - status %x\n", status); ! 1978: break; ! 1979: } ! 1980: } ! 1981: return ret; ! 1982: } ! 1983: ! 1984: /* ! 1985: * p3() ! 1986: * ! 1987: * Interrupt handler for GTEK-type port groups. ! 1988: */ ! 1989: static int ! 1990: p3(g) ! 1991: int g; ! 1992: { ! 1993: asy_gp_t *gp = asy_gp + g; ! 1994: short port = gp->stat_port; ! 1995: unsigned char index, chan; ! 1996: ! 1997: /* ! 1998: * Call simple interrupt handler for active channel. ! 1999: */ ! 2000: index = inb(port) & 7; ! 2001: chan = gp->chan_list[index]; ! 2002: return asyintr(chan); ! 2003: } ! 2004: ! 2005: /* ! 2006: * p4() ! 2007: * ! 2008: * Interrupt handler for DigiBoard-type port groups. ! 2009: */ ! 2010: static int ! 2011: p4(g) ! 2012: int g; ! 2013: { ! 2014: asy_gp_t *gp = asy_gp + g; ! 2015: short port = gp->stat_port; ! 2016: unsigned char index, chan; ! 2017: int ret = 0; ! 2018: int safety = LOOP_LIMIT; ! 2019: ! 2020: /* ! 2021: * Status register has slot number for active port, ! 2022: * or 0xFF if no port is active. ! 2023: */ ! 2024: ! 2025: for (;;) { ! 2026: index = inb(port); ! 2027: if (index == 0xFF) ! 2028: break; ! 2029: if (safety-- == 0) { ! 2030: printf("asy: p4 runaway - status %x\n", index); ! 2031: break; ! 2032: } ! 2033: ret = 1; ! 2034: chan = gp->chan_list[index&0xF]; ! 2035: asyintr(chan); ! 2036: } ! 2037: return ret; ! 2038: } ! 2039: ! 2040: #ifdef TRACER ! 2041: void ! 2042: asydump(chan, tag) ! 2043: int chan; ! 2044: char *tag; ! 2045: { ! 2046: asy0_t *a0 = asy0 + chan; ! 2047: asy1_t *a1 = asy1 + chan; ! 2048: TTY *tp = &a1->a_tty; ! 2049: ! 2050: printf("ch=%d %s\n", chan, tag); ! 2051: printf("port=%x irqno=%x speed=%d ", ! 2052: a0->a_port, a0->a_irqno, a0->a_speed); ! 2053: printf("outs=%x gp=%d xcl=%d\n", ! 2054: a0->a_outs, a0->a_asy_gp, a0->a_ixc); ! 2055: printf("in_use=%d lcr=%x irq=%d has_irq=%d ", ! 2056: a1->a_in_use, a1->a_lcr, a1->a_irq, a1->a_has_irq); ! 2057: printf("hop=%d hcl=%d ", a1->a_hopn, a1->a_hcls); ! 2058: printf("opn=%d ier=%x\n", tp->t_open, inb(a0->a_port+IER)); ! 2059: } ! 2060: #endif
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.