|
|
1.1 ! root 1: /* ! 2: * Copyright (c) 1988 University of Utah. ! 3: * Copyright (c) 1982, 1986, 1990 The Regents of the University of California. ! 4: * All rights reserved. ! 5: * ! 6: * This code is derived from software contributed to Berkeley by ! 7: * the Systems Programming Group of the University of Utah Computer ! 8: * Science Department. ! 9: * ! 10: * Redistribution is only permitted until one year after the first shipment ! 11: * of 4.4BSD by the Regents. Otherwise, redistribution and use in source and ! 12: * binary forms are permitted provided that: (1) source distributions retain ! 13: * this entire copyright notice and comment, and (2) distributions including ! 14: * binaries display the following acknowledgement: This product includes ! 15: * software developed by the University of California, Berkeley and its ! 16: * contributors'' in the documentation or other materials provided with the ! 17: * distribution and in all advertising materials mentioning features or use ! 18: * of this software. Neither the name of the University nor the names of ! 19: * its contributors may be used to endorse or promote products derived from ! 20: * this software without specific prior written permission. ! 21: * THIS SOFTWARE IS PROVIDED AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED ! 22: * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF ! 23: * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ! 24: * ! 25: * from: $Hdr: dcm.c 1.17 89/10/01$ ! 26: * ! 27: * @(#)dcm.c 7.8 (Berkeley) 6/30/90 ! 28: */ ! 29: ! 30: /* ! 31: * TODO: ! 32: * Timeouts ! 33: * Test console/kgdb support. ! 34: */ ! 35: ! 36: #include "dcm.h" ! 37: #if NDCM > 0 ! 38: /* ! 39: * 98642/MUX ! 40: */ ! 41: #include "param.h" ! 42: #include "systm.h" ! 43: #include "ioctl.h" ! 44: #include "tty.h" ! 45: #include "user.h" ! 46: #include "conf.h" ! 47: #include "file.h" ! 48: #include "uio.h" ! 49: #include "kernel.h" ! 50: #include "syslog.h" ! 51: #include "time.h" ! 52: ! 53: #include "device.h" ! 54: #include "dcmreg.h" ! 55: #include "machine/cpu.h" ! 56: #include "machine/isr.h" ! 57: ! 58: #ifndef DEFAULT_BAUD_RATE ! 59: #define DEFAULT_BAUD_RATE 9600 ! 60: #endif ! 61: ! 62: int ttrstrt(); ! 63: int dcmprobe(), dcmstart(), dcmintr(), dcmparam(); ! 64: ! 65: struct driver dcmdriver = { ! 66: dcmprobe, "dcm", ! 67: }; ! 68: ! 69: #define NDCMLINE (NDCM*4) ! 70: ! 71: struct tty dcm_tty[NDCMLINE]; ! 72: char mcndlast[NDCMLINE]; /* XXX last modem status for line */ ! 73: int ndcm = NDCMLINE; ! 74: ! 75: int dcm_active; ! 76: int dcmsoftCAR[NDCM]; ! 77: struct dcmdevice *dcm_addr[NDCM]; ! 78: struct isr dcmisr[NDCM]; ! 79: ! 80: struct speedtab dcmspeedtab[] = { ! 81: 0, BR_0, ! 82: 50, BR_50, ! 83: 75, BR_75, ! 84: 110, BR_110, ! 85: 134, BR_134, ! 86: 150, BR_150, ! 87: 300, BR_300, ! 88: 600, BR_600, ! 89: 1200, BR_1200, ! 90: 1800, BR_1800, ! 91: 2400, BR_2400, ! 92: 4800, BR_4800, ! 93: 9600, BR_9600, ! 94: 19200, BR_19200, ! 95: 38400, BR_38400, ! 96: -1, -1 ! 97: }; ! 98: ! 99: /* u-sec per character based on baudrate (assumes 1 start/8 data/1 stop bit) */ ! 100: #define DCM_USPERCH(s) (10000000 / (s)) ! 101: ! 102: /* ! 103: * Per board interrupt scheme. 16.7ms is the polling interrupt rate ! 104: * (16.7ms is about 550 buad, 38.4k is 72 chars in 16.7ms). ! 105: */ ! 106: #define DIS_TIMER 0 ! 107: #define DIS_PERCHAR 1 ! 108: #define DIS_RESET 2 ! 109: ! 110: int dcmistype = -1; /* -1 == dynamic, 0 == timer, 1 == perchar */ ! 111: int dcminterval = 5; /* interval (secs) between checks */ ! 112: struct dcmischeme { ! 113: int dis_perchar; /* non-zero if interrupting per char */ ! 114: long dis_time; /* last time examined */ ! 115: int dis_intr; /* recv interrupts during last interval */ ! 116: int dis_char; /* characters read during last interval */ ! 117: } dcmischeme[NDCM]; ! 118: ! 119: /* ! 120: * Console support ! 121: */ ! 122: int dcmconsole = -1; ! 123: int dcmdefaultrate = DEFAULT_BAUD_RATE; ! 124: int dcmconbrdbusy = 0; ! 125: extern struct tty *constty; ! 126: ! 127: #ifdef KGDB ! 128: /* ! 129: * Kernel GDB support ! 130: */ ! 131: extern int kgdb_dev; ! 132: extern int kgdb_rate; ! 133: extern int kgdb_debug_init; ! 134: #endif ! 135: ! 136: /* #define IOSTATS */ ! 137: ! 138: #ifdef DEBUG ! 139: int dcmdebug = 0x00; ! 140: #define DDB_SIOERR 0x01 ! 141: #define DDB_PARAM 0x02 ! 142: #define DDB_INPUT 0x04 ! 143: #define DDB_OUTPUT 0x08 ! 144: #define DDB_INTR 0x10 ! 145: #define DDB_IOCTL 0x20 ! 146: #define DDB_INTSCHM 0x40 ! 147: #define DDB_MODEM 0x80 ! 148: #define DDB_OPENCLOSE 0x100 ! 149: #endif ! 150: ! 151: #ifdef IOSTATS ! 152: #define DCMRBSIZE 94 ! 153: #define DCMXBSIZE 24 ! 154: ! 155: struct dcmstats { ! 156: long xints; /* # of xmit ints */ ! 157: long xchars; /* # of xmit chars */ ! 158: long xempty; /* times outq is empty in dcmstart */ ! 159: long xrestarts; /* times completed while xmitting */ ! 160: long rints; /* # of recv ints */ ! 161: long rchars; /* # of recv chars */ ! 162: long xsilo[DCMXBSIZE+2]; /* times this many chars xmit on one int */ ! 163: long rsilo[DCMRBSIZE+2]; /* times this many chars read on one int */ ! 164: } dcmstats[NDCM]; ! 165: #endif ! 166: ! 167: #define UNIT(x) minor(x) ! 168: #define BOARD(x) (((x) >> 2) & 0x3f) ! 169: #define PORT(x) ((x) & 3) ! 170: #define MKUNIT(b,p) (((b) << 2) | (p)) ! 171: ! 172: dcmprobe(hd) ! 173: register struct hp_device *hd; ! 174: { ! 175: register struct dcmdevice *dcm; ! 176: register int i; ! 177: register int timo = 0; ! 178: int s, brd, isconsole; ! 179: ! 180: dcm = (struct dcmdevice *)hd->hp_addr; ! 181: if ((dcm->dcm_rsid & 0x1f) != DCMID) ! 182: return (0); ! 183: brd = hd->hp_unit; ! 184: isconsole = (brd == BOARD(dcmconsole)); ! 185: /* ! 186: * XXX selected console device (CONSUNIT) as determined by ! 187: * dcmcnprobe does not agree with logical numbering imposed ! 188: * by the config file (i.e. lowest address DCM is not unit ! 189: * CONSUNIT). Don't recognize this card. ! 190: */ ! 191: if (isconsole && dcm != dcm_addr[BOARD(dcmconsole)]) ! 192: return(0); ! 193: ! 194: /* ! 195: * Empirically derived self-test magic ! 196: */ ! 197: s = spltty(); ! 198: dcm->dcm_rsid = DCMRS; ! 199: DELAY(50000); /* 5000 is not long enough */ ! 200: dcm->dcm_rsid = 0; ! 201: dcm->dcm_ic = IC_IE; ! 202: dcm->dcm_cr = CR_SELFT; ! 203: while ((dcm->dcm_ic & IC_IR) == 0) ! 204: if (++timo == 20000) ! 205: return(0); ! 206: DELAY(50000) /* XXX why is this needed ???? */ ! 207: while ((dcm->dcm_iir & IIR_SELFT) == 0) ! 208: if (++timo == 400000) ! 209: return(0); ! 210: DELAY(50000) /* XXX why is this needed ???? */ ! 211: if (dcm->dcm_stcon != ST_OK) { ! 212: if (!isconsole) ! 213: printf("dcm%d: self test failed: %x\n", ! 214: brd, dcm->dcm_stcon); ! 215: return(0); ! 216: } ! 217: dcm->dcm_ic = IC_ID; ! 218: splx(s); ! 219: ! 220: hd->hp_ipl = DCMIPL(dcm->dcm_ic); ! 221: dcm_addr[brd] = dcm; ! 222: dcm_active |= 1 << brd; ! 223: dcmsoftCAR[brd] = hd->hp_flags; ! 224: dcmisr[brd].isr_ipl = hd->hp_ipl; ! 225: dcmisr[brd].isr_arg = brd; ! 226: dcmisr[brd].isr_intr = dcmintr; ! 227: isrlink(&dcmisr[brd]); ! 228: #ifdef KGDB ! 229: if (major(kgdb_dev) == 2 && BOARD(kgdb_dev) == brd) { ! 230: if (dcmconsole == UNIT(kgdb_dev)) ! 231: kgdb_dev = -1; /* can't debug over console port */ ! 232: else { ! 233: (void) dcminit(kgdb_dev, kgdb_rate); ! 234: if (kgdb_debug_init) { ! 235: printf("dcm%d: kgdb waiting...", ! 236: UNIT(kgdb_dev)); ! 237: /* trap into kgdb */ ! 238: asm("trap #15;"); ! 239: printf("connected.\n"); ! 240: } else ! 241: printf("dcm%d: kgdb enabled\n", ! 242: UNIT(kgdb_dev)); ! 243: } ! 244: } ! 245: #endif ! 246: if (dcmistype == DIS_TIMER) ! 247: dcmsetischeme(brd, DIS_RESET|DIS_TIMER); ! 248: else ! 249: dcmsetischeme(brd, DIS_RESET|DIS_PERCHAR); ! 250: dcm->dcm_mdmmsk = MI_CD|MI_CTS; /* DCD (modem) and CTS (flow ctrl) */ ! 251: dcm->dcm_ic = IC_IE; /* turn all interrupts on */ ! 252: /* ! 253: * Need to reset baud rate, etc. of next print so reset dcmconsole. ! 254: * Also make sure console is always "hardwired" ! 255: */ ! 256: if (isconsole) { ! 257: dcmconsole = -1; ! 258: dcmsoftCAR[brd] |= (1 << PORT(dcmconsole)); ! 259: } ! 260: return (1); ! 261: } ! 262: ! 263: dcmopen(dev, flag) ! 264: dev_t dev; ! 265: { ! 266: register struct tty *tp; ! 267: register int unit, brd; ! 268: int error = 0; ! 269: ! 270: unit = UNIT(dev); ! 271: brd = BOARD(unit); ! 272: if (unit >= NDCMLINE || (dcm_active & (1 << brd)) == 0) ! 273: return (ENXIO); ! 274: #ifdef KGDB ! 275: if (unit == UNIT(kgdb_dev)) ! 276: return (EBUSY); ! 277: #endif ! 278: tp = &dcm_tty[unit]; ! 279: tp->t_oproc = dcmstart; ! 280: tp->t_param = dcmparam; ! 281: tp->t_dev = dev; ! 282: if ((tp->t_state & TS_ISOPEN) == 0) { ! 283: tp->t_state |= TS_WOPEN; ! 284: ttychars(tp); ! 285: tp->t_iflag = TTYDEF_IFLAG; ! 286: tp->t_oflag = TTYDEF_OFLAG; ! 287: tp->t_cflag = TTYDEF_CFLAG; ! 288: tp->t_lflag = TTYDEF_LFLAG; ! 289: tp->t_ispeed = tp->t_ospeed = TTYDEF_SPEED; ! 290: (void) dcmparam(tp, &tp->t_termios); ! 291: ttsetwater(tp); ! 292: } else if (tp->t_state&TS_XCLUDE && u.u_uid != 0) ! 293: return (EBUSY); ! 294: if (PORT(unit) == 0) /* enable port 0 */ ! 295: (void) dcmmctl(dev, MO_ON, DMSET); ! 296: if (dcmsoftCAR[brd] & (1 << PORT(unit))) ! 297: tp->t_state |= TS_CARR_ON; ! 298: else if (PORT(unit)) /* Only port 0 has modem control */ ! 299: tp->t_state |= TS_CARR_ON; ! 300: else if (dcmmctl(dev, MO_OFF, DMGET) & MI_CD) ! 301: tp->t_state |= TS_CARR_ON; ! 302: (void) spltty(); ! 303: while ((flag&O_NONBLOCK) == 0 && (tp->t_cflag&CLOCAL) == 0 && ! 304: (tp->t_state & TS_CARR_ON) == 0) { ! 305: tp->t_state |= TS_WOPEN; ! 306: if (error = ttysleep(tp, (caddr_t)&tp->t_rawq, TTIPRI | PCATCH, ! 307: ttopen, 0)) ! 308: break; ! 309: } ! 310: (void) spl0(); ! 311: #ifdef DEBUG ! 312: if (dcmdebug & DDB_OPENCLOSE) ! 313: printf("dcmopen: u %x st %x fl %x\n", ! 314: unit, tp->t_state, tp->t_flags); ! 315: #endif ! 316: if (error == 0) ! 317: error = (*linesw[tp->t_line].l_open)(dev, tp); ! 318: return (error); ! 319: } ! 320: ! 321: /*ARGSUSED*/ ! 322: dcmclose(dev, flag) ! 323: dev_t dev; ! 324: { ! 325: register struct tty *tp; ! 326: int unit; ! 327: ! 328: unit = UNIT(dev); ! 329: tp = &dcm_tty[unit]; ! 330: (*linesw[tp->t_line].l_close)(tp); ! 331: if (tp->t_cflag&HUPCL || tp->t_state&TS_WOPEN || ! 332: (tp->t_state&TS_ISOPEN) == 0) ! 333: (void) dcmmctl(dev, MO_OFF, DMSET); ! 334: #ifdef DEBUG ! 335: if (dcmdebug & DDB_OPENCLOSE) ! 336: printf("dcmclose: u %x st %x fl %x\n", ! 337: unit, tp->t_state, tp->t_flags); ! 338: #endif ! 339: ttyclose(tp); ! 340: return(0); ! 341: } ! 342: ! 343: dcmread(dev, uio, flag) ! 344: dev_t dev; ! 345: struct uio *uio; ! 346: { ! 347: register struct tty *tp; ! 348: ! 349: tp = &dcm_tty[UNIT(dev)]; ! 350: return ((*linesw[tp->t_line].l_read)(tp, uio, flag)); ! 351: } ! 352: ! 353: dcmwrite(dev, uio, flag) ! 354: dev_t dev; ! 355: struct uio *uio; ! 356: { ! 357: int unit = UNIT(dev); ! 358: register struct tty *tp; ! 359: ! 360: tp = &dcm_tty[unit]; ! 361: /* ! 362: * XXX we disallow virtual consoles if the physical console is ! 363: * a serial port. This is in case there is a display attached that ! 364: * is not the console. In that situation we don't need/want the X ! 365: * server taking over the console. ! 366: */ ! 367: if (constty && unit == dcmconsole) ! 368: constty = NULL; ! 369: return ((*linesw[tp->t_line].l_write)(tp, uio, flag)); ! 370: } ! 371: ! 372: dcmintr(brd) ! 373: register int brd; ! 374: { ! 375: register struct dcmdevice *dcm = dcm_addr[brd]; ! 376: register struct dcmischeme *dis; ! 377: int i, code, pcnd[4], mcnd, delta; ! 378: ! 379: /* ! 380: * Do all guarded register accesses right off to minimize ! 381: * block out of hardware. ! 382: */ ! 383: SEM_LOCK(dcm); ! 384: if ((dcm->dcm_ic & IC_IR) == 0) { ! 385: SEM_UNLOCK(dcm); ! 386: return(0); ! 387: } ! 388: for (i = 0; i < 4; i++) { ! 389: pcnd[i] = dcm->dcm_icrtab[i].dcm_data; ! 390: dcm->dcm_icrtab[i].dcm_data = 0; ! 391: } ! 392: mcnd = dcm->dcm_mdmin; ! 393: code = dcm->dcm_iir & IIR_MASK; ! 394: dcm->dcm_iir = 0; /* XXX doc claims read clears interrupt?! */ ! 395: SEM_UNLOCK(dcm); ! 396: ! 397: #ifdef DEBUG ! 398: if (dcmdebug & DDB_INTR) ! 399: printf("dcmintr(%d): iir %x p0 %x p1 %x p2 %x p3 %x m %x\n", ! 400: brd, code, pcnd[0], pcnd[1], pcnd[2], pcnd[3], mcnd); ! 401: #endif ! 402: if (code & IIR_TIMEO) ! 403: dcmrint(brd, dcm); ! 404: if (code & IIR_PORT0) ! 405: dcmpint(MKUNIT(brd, 0), pcnd[0], dcm); ! 406: if (code & IIR_PORT1) ! 407: dcmpint(MKUNIT(brd, 1), pcnd[1], dcm); ! 408: if (code & IIR_PORT2) ! 409: dcmpint(MKUNIT(brd, 2), pcnd[2], dcm); ! 410: if (code & IIR_PORT3) ! 411: dcmpint(MKUNIT(brd, 3), pcnd[3], dcm); ! 412: if (code & IIR_MODM) ! 413: dcmmint(MKUNIT(brd, 0), mcnd, dcm); /* XXX always port 0 */ ! 414: ! 415: dis = &dcmischeme[brd]; ! 416: /* ! 417: * Chalk up a receiver interrupt if the timer running or one of ! 418: * the ports reports a special character interrupt. ! 419: */ ! 420: if ((code & IIR_TIMEO) || ! 421: ((pcnd[0]|pcnd[1]|pcnd[2]|pcnd[3]) & IT_SPEC)) ! 422: dis->dis_intr++; ! 423: /* ! 424: * See if it is time to check/change the interrupt rate. ! 425: */ ! 426: if (dcmistype < 0 && ! 427: (delta = time.tv_sec - dis->dis_time) >= dcminterval) { ! 428: /* ! 429: * If currently per-character and averaged over 70 interrupts ! 430: * per-second (66 is threshold of 600 baud) in last interval, ! 431: * switch to timer mode. ! 432: * ! 433: * XXX decay counts ala load average to avoid spikes? ! 434: */ ! 435: if (dis->dis_perchar && dis->dis_intr > 70 * delta) ! 436: dcmsetischeme(brd, DIS_TIMER); ! 437: /* ! 438: * If currently using timer and had more interrupts than ! 439: * received characters in the last interval, switch back ! 440: * to per-character. Note that after changing to per-char ! 441: * we must process any characters already in the queue ! 442: * since they may have arrived before the bitmap was setup. ! 443: * ! 444: * XXX decay counts? ! 445: */ ! 446: else if (!dis->dis_perchar && dis->dis_intr > dis->dis_char) { ! 447: dcmsetischeme(brd, DIS_PERCHAR); ! 448: dcmrint(brd, dcm); ! 449: } ! 450: dis->dis_intr = dis->dis_char = 0; ! 451: dis->dis_time = time.tv_sec; ! 452: } ! 453: return(1); ! 454: } ! 455: ! 456: /* ! 457: * Port interrupt. Can be two things: ! 458: * First, it might be a special character (exception interrupt); ! 459: * Second, it may be a buffer empty (transmit interrupt); ! 460: */ ! 461: dcmpint(unit, code, dcm) ! 462: int unit, code; ! 463: struct dcmdevice *dcm; ! 464: { ! 465: struct tty *tp = &dcm_tty[unit]; ! 466: ! 467: if (code & IT_SPEC) ! 468: dcmreadbuf(unit, dcm, tp); ! 469: if (code & IT_TX) ! 470: dcmxint(unit, dcm, tp); ! 471: } ! 472: ! 473: dcmrint(brd, dcm) ! 474: int brd; ! 475: register struct dcmdevice *dcm; ! 476: { ! 477: register int i, unit; ! 478: register struct tty *tp; ! 479: ! 480: unit = MKUNIT(brd, 0); ! 481: tp = &dcm_tty[unit]; ! 482: for (i = 0; i < 4; i++, tp++, unit++) ! 483: dcmreadbuf(unit, dcm, tp); ! 484: } ! 485: ! 486: dcmreadbuf(unit, dcm, tp) ! 487: int unit; ! 488: register struct dcmdevice *dcm; ! 489: register struct tty *tp; ! 490: { ! 491: int port = PORT(unit); ! 492: register struct dcmpreg *pp = dcm_preg(dcm, port); ! 493: register struct dcmrfifo *fifo; ! 494: register int c, stat; ! 495: register unsigned head; ! 496: int nch = 0; ! 497: #ifdef IOSTATS ! 498: struct dcmstats *dsp = &dcmstats[BOARD(unit)]; ! 499: ! 500: dsp->rints++; ! 501: #endif ! 502: if ((tp->t_state & TS_ISOPEN) == 0) { ! 503: #ifdef KGDB ! 504: if (unit == UNIT(kgdb_dev) && ! 505: (head = pp->r_head & RX_MASK) != (pp->r_tail & RX_MASK) && ! 506: dcm->dcm_rfifos[3-port][head>>1].data_char == '!') { ! 507: pp->r_head = (head + 2) & RX_MASK; ! 508: printf("kgdb trap from dcm%d\n", unit); ! 509: /* trap into kgdb */ ! 510: asm("trap #15;"); ! 511: return; ! 512: } ! 513: #endif ! 514: pp->r_head = pp->r_tail & RX_MASK; ! 515: return; ! 516: } ! 517: ! 518: head = pp->r_head & RX_MASK; ! 519: fifo = &dcm->dcm_rfifos[3-port][head>>1]; ! 520: /* ! 521: * XXX upper bound on how many chars we will take in one swallow? ! 522: */ ! 523: while (head != (pp->r_tail & RX_MASK)) { ! 524: /* ! 525: * Get character/status and update head pointer as fast ! 526: * as possible to make room for more characters. ! 527: */ ! 528: c = fifo->data_char; ! 529: stat = fifo->data_stat; ! 530: head = (head + 2) & RX_MASK; ! 531: pp->r_head = head; ! 532: fifo = head ? fifo+1 : &dcm->dcm_rfifos[3-port][0]; ! 533: nch++; ! 534: ! 535: #ifdef DEBUG ! 536: if (dcmdebug & DDB_INPUT) ! 537: printf("dcmreadbuf(%d): c%x('%c') s%x f%x h%x t%x\n", ! 538: unit, c&0xFF, c, stat&0xFF, ! 539: tp->t_flags, head, pp->r_tail); ! 540: #endif ! 541: /* ! 542: * Check for and handle errors ! 543: */ ! 544: if (stat & RD_MASK) { ! 545: #ifdef DEBUG ! 546: if (dcmdebug & (DDB_INPUT|DDB_SIOERR)) ! 547: printf("dcmreadbuf(%d): err: c%x('%c') s%x\n", ! 548: unit, stat, c&0xFF, c); ! 549: #endif ! 550: if (stat & (RD_BD | RD_FE)) ! 551: c |= TTY_FE; ! 552: else if (stat & RD_PE) ! 553: c |= TTY_PE; ! 554: else if (stat & RD_OVF) ! 555: log(LOG_WARNING, ! 556: "dcm%d: silo overflow\n", unit); ! 557: else if (stat & RD_OE) ! 558: log(LOG_WARNING, ! 559: "dcm%d: uart overflow\n", unit); ! 560: } ! 561: (*linesw[tp->t_line].l_rint)(c, tp); ! 562: } ! 563: dcmischeme[BOARD(unit)].dis_char += nch; ! 564: #ifdef IOSTATS ! 565: dsp->rchars += nch; ! 566: if (nch <= DCMRBSIZE) ! 567: dsp->rsilo[nch]++; ! 568: else ! 569: dsp->rsilo[DCMRBSIZE+1]++; ! 570: #endif ! 571: } ! 572: ! 573: dcmxint(unit, dcm, tp) ! 574: int unit; ! 575: struct dcmdevice *dcm; ! 576: register struct tty *tp; ! 577: { ! 578: tp->t_state &= ~TS_BUSY; ! 579: if (tp->t_state & TS_FLUSH) ! 580: tp->t_state &= ~TS_FLUSH; ! 581: if (tp->t_line) ! 582: (*linesw[tp->t_line].l_start)(tp); ! 583: else ! 584: dcmstart(tp); ! 585: } ! 586: ! 587: dcmmint(unit, mcnd, dcm) ! 588: register int unit; ! 589: register struct dcmdevice *dcm; ! 590: int mcnd; ! 591: { ! 592: register struct tty *tp; ! 593: int delta; ! 594: ! 595: #ifdef DEBUG ! 596: if (dcmdebug & DDB_MODEM) ! 597: printf("dcmmint: unit %x mcnd %x mcndlast\n", ! 598: unit, mcnd, mcndlast[unit]); ! 599: #endif ! 600: tp = &dcm_tty[unit]; ! 601: delta = mcnd ^ mcndlast[unit]; ! 602: mcndlast[unit] = mcnd; ! 603: if ((delta & MI_CD) && ! 604: (dcmsoftCAR[BOARD(unit)] & (1 << PORT(unit))) == 0) { ! 605: if (mcnd & MI_CD) ! 606: (void)(*linesw[tp->t_line].l_modem)(tp, 1); ! 607: else if ((*linesw[tp->t_line].l_modem)(tp, 0) == 0) { ! 608: dcm->dcm_mdmout &= ~(MO_DTR|MO_RTS); ! 609: SEM_LOCK(dcm); ! 610: dcm->dcm_cr |= CR_MODM; ! 611: SEM_UNLOCK(dcm); ! 612: DELAY(10); /* time to change lines */ ! 613: } ! 614: } else if ((delta & MI_CTS) && ! 615: (tp->t_state & TS_ISOPEN) && (tp->t_flags & CRTSCTS)) { ! 616: if (mcnd & MI_CTS) { ! 617: tp->t_state &= ~TS_TTSTOP; ! 618: ttstart(tp); ! 619: } else ! 620: tp->t_state |= TS_TTSTOP; /* inline dcmstop */ ! 621: } ! 622: } ! 623: ! 624: dcmioctl(dev, cmd, data, flag) ! 625: dev_t dev; ! 626: caddr_t data; ! 627: { ! 628: register struct tty *tp; ! 629: register int unit = UNIT(dev); ! 630: register struct dcmdevice *dcm; ! 631: register int port; ! 632: int error, s; ! 633: ! 634: #ifdef DEBUG ! 635: if (dcmdebug & DDB_IOCTL) ! 636: printf("dcmioctl: unit %d cmd %x data %x flag %x\n", ! 637: unit, cmd, *data, flag); ! 638: #endif ! 639: tp = &dcm_tty[unit]; ! 640: error = (*linesw[tp->t_line].l_ioctl)(tp, cmd, data, flag); ! 641: if (error >= 0) ! 642: return (error); ! 643: error = ttioctl(tp, cmd, data, flag); ! 644: if (error >= 0) ! 645: return (error); ! 646: ! 647: port = PORT(unit); ! 648: dcm = dcm_addr[BOARD(unit)]; ! 649: switch (cmd) { ! 650: case TIOCSBRK: ! 651: /* ! 652: * Wait for transmitter buffer to empty ! 653: */ ! 654: s = spltty(); ! 655: while (dcm->dcm_thead[port].ptr != dcm->dcm_ttail[port].ptr) ! 656: DELAY(DCM_USPERCH(tp->t_ospeed)); ! 657: SEM_LOCK(dcm); ! 658: dcm->dcm_cmdtab[port].dcm_data |= CT_BRK; ! 659: dcm->dcm_cr |= (1 << port); /* start break */ ! 660: SEM_UNLOCK(dcm); ! 661: splx(s); ! 662: break; ! 663: ! 664: case TIOCCBRK: ! 665: SEM_LOCK(dcm); ! 666: dcm->dcm_cmdtab[port].dcm_data |= CT_BRK; ! 667: dcm->dcm_cr |= (1 << port); /* end break */ ! 668: SEM_UNLOCK(dcm); ! 669: break; ! 670: ! 671: case TIOCSDTR: ! 672: (void) dcmmctl(dev, MO_ON, DMBIS); ! 673: break; ! 674: ! 675: case TIOCCDTR: ! 676: (void) dcmmctl(dev, MO_ON, DMBIC); ! 677: break; ! 678: ! 679: case TIOCMSET: ! 680: (void) dcmmctl(dev, *(int *)data, DMSET); ! 681: break; ! 682: ! 683: case TIOCMBIS: ! 684: (void) dcmmctl(dev, *(int *)data, DMBIS); ! 685: break; ! 686: ! 687: case TIOCMBIC: ! 688: (void) dcmmctl(dev, *(int *)data, DMBIC); ! 689: break; ! 690: ! 691: case TIOCMGET: ! 692: *(int *)data = dcmmctl(dev, 0, DMGET); ! 693: break; ! 694: ! 695: default: ! 696: return (ENOTTY); ! 697: } ! 698: return (0); ! 699: } ! 700: ! 701: dcmparam(tp, t) ! 702: register struct tty *tp; ! 703: register struct termios *t; ! 704: { ! 705: register struct dcmdevice *dcm; ! 706: register int port, mode, cflag = t->c_cflag; ! 707: int ospeed = ttspeedtab(t->c_ospeed, dcmspeedtab); ! 708: ! 709: /* check requested parameters */ ! 710: if (ospeed < 0 || (t->c_ispeed && t->c_ispeed != t->c_ospeed)) ! 711: return(EINVAL); ! 712: /* and copy to tty */ ! 713: tp->t_ispeed = t->c_ispeed; ! 714: tp->t_ospeed = t->c_ospeed; ! 715: tp->t_cflag = cflag; ! 716: if (ospeed == 0) { ! 717: (void) dcmmctl(UNIT(tp->t_dev), MO_OFF, DMSET); ! 718: return(0); ! 719: } ! 720: ! 721: mode = 0; ! 722: switch (cflag&CSIZE) { ! 723: case CS5: ! 724: mode = LC_5BITS; break; ! 725: case CS6: ! 726: mode = LC_6BITS; break; ! 727: case CS7: ! 728: mode = LC_7BITS; break; ! 729: case CS8: ! 730: mode = LC_8BITS; break; ! 731: } ! 732: if (cflag&PARENB) { ! 733: if (cflag&PARODD) ! 734: mode |= LC_PODD; ! 735: else ! 736: mode |= LC_PEVEN; ! 737: } ! 738: if (cflag&CSTOPB) ! 739: mode |= LC_2STOP; ! 740: else ! 741: mode |= LC_1STOP; ! 742: #ifdef DEBUG ! 743: if (dcmdebug & DDB_PARAM) ! 744: printf("dcmparam(%d): cflag %x mode %x speed %d uperch %d\n", ! 745: UNIT(tp->t_dev), cflag, mode, tp->t_ospeed, ! 746: DCM_USPERCH(tp->t_ospeed)); ! 747: #endif ! 748: ! 749: port = PORT(tp->t_dev); ! 750: dcm = dcm_addr[BOARD(tp->t_dev)]; ! 751: /* ! 752: * Wait for transmitter buffer to empty. ! 753: */ ! 754: while (dcm->dcm_thead[port].ptr != dcm->dcm_ttail[port].ptr) ! 755: DELAY(DCM_USPERCH(tp->t_ospeed)); ! 756: /* ! 757: * Make changes known to hardware. ! 758: */ ! 759: dcm->dcm_data[port].dcm_baud = ospeed; ! 760: dcm->dcm_data[port].dcm_conf = mode; ! 761: SEM_LOCK(dcm); ! 762: dcm->dcm_cmdtab[port].dcm_data |= CT_CON; ! 763: dcm->dcm_cr |= (1 << port); ! 764: SEM_UNLOCK(dcm); ! 765: /* ! 766: * Delay for config change to take place. Weighted by buad. ! 767: * XXX why do we do this? ! 768: */ ! 769: DELAY(16 * DCM_USPERCH(tp->t_ospeed)); ! 770: return(0); ! 771: } ! 772: ! 773: dcmstart(tp) ! 774: register struct tty *tp; ! 775: { ! 776: register struct dcmdevice *dcm; ! 777: register struct dcmpreg *pp; ! 778: register struct dcmtfifo *fifo; ! 779: register char *bp; ! 780: register unsigned tail, next; ! 781: register int port, nch; ! 782: unsigned head; ! 783: char buf[16]; ! 784: int s; ! 785: #ifdef IOSTATS ! 786: struct dcmstats *dsp = &dcmstats[BOARD(tp->t_dev)]; ! 787: int tch = 0; ! 788: #endif ! 789: ! 790: s = spltty(); ! 791: #ifdef IOSTATS ! 792: dsp->xints++; ! 793: #endif ! 794: #ifdef DEBUG ! 795: if (dcmdebug & DDB_OUTPUT) ! 796: printf("dcmstart(%d): state %x flags %x outcc %d\n", ! 797: UNIT(tp->t_dev), tp->t_state, tp->t_flags, ! 798: tp->t_outq.c_cc); ! 799: #endif ! 800: if (tp->t_state & (TS_TIMEOUT|TS_BUSY|TS_TTSTOP)) ! 801: goto out; ! 802: if (tp->t_outq.c_cc <= tp->t_lowat) { ! 803: if (tp->t_state&TS_ASLEEP) { ! 804: tp->t_state &= ~TS_ASLEEP; ! 805: wakeup((caddr_t)&tp->t_outq); ! 806: } ! 807: if (tp->t_wsel) { ! 808: selwakeup(tp->t_wsel, tp->t_state & TS_WCOLL); ! 809: tp->t_wsel = 0; ! 810: tp->t_state &= ~TS_WCOLL; ! 811: } ! 812: } ! 813: if (tp->t_outq.c_cc == 0) { ! 814: #ifdef IOSTATS ! 815: dsp->xempty++; ! 816: #endif ! 817: goto out; ! 818: } ! 819: ! 820: dcm = dcm_addr[BOARD(tp->t_dev)]; ! 821: port = PORT(tp->t_dev); ! 822: pp = dcm_preg(dcm, port); ! 823: tail = pp->t_tail & TX_MASK; ! 824: next = (tail + 1) & TX_MASK; ! 825: head = pp->t_head & TX_MASK; ! 826: if (head == next) ! 827: goto out; ! 828: fifo = &dcm->dcm_tfifos[3-port][tail]; ! 829: again: ! 830: nch = q_to_b(&tp->t_outq, buf, (head - next) & TX_MASK); ! 831: #ifdef IOSTATS ! 832: tch += nch; ! 833: #endif ! 834: #ifdef DEBUG ! 835: if (dcmdebug & DDB_OUTPUT) ! 836: printf("\thead %x tail %x nch %d\n", head, tail, nch); ! 837: #endif ! 838: /* ! 839: * Loop transmitting all the characters we can. ! 840: */ ! 841: for (bp = buf; --nch >= 0; bp++) { ! 842: fifo->data_char = *bp; ! 843: pp->t_tail = next; ! 844: /* ! 845: * If this is the first character, ! 846: * get the hardware moving right now. ! 847: */ ! 848: if (bp == buf) { ! 849: tp->t_state |= TS_BUSY; ! 850: SEM_LOCK(dcm); ! 851: dcm->dcm_cmdtab[port].dcm_data |= CT_TX; ! 852: dcm->dcm_cr |= (1 << port); ! 853: SEM_UNLOCK(dcm); ! 854: } ! 855: tail = next; ! 856: fifo = tail ? fifo+1 : &dcm->dcm_tfifos[3-port][0]; ! 857: next = (next + 1) & TX_MASK; ! 858: } ! 859: /* ! 860: * Head changed while we were loading the buffer, ! 861: * go back and load some more if we can. ! 862: */ ! 863: if (tp->t_outq.c_cc && head != (pp->t_head & TX_MASK)) { ! 864: #ifdef IOSTATS ! 865: dsp->xrestarts++; ! 866: #endif ! 867: head = pp->t_head & TX_MASK; ! 868: goto again; ! 869: } ! 870: /* ! 871: * Kick it one last time in case it finished while we were ! 872: * loading the last bunch. ! 873: */ ! 874: if (bp > &buf[1]) { ! 875: tp->t_state |= TS_BUSY; ! 876: SEM_LOCK(dcm); ! 877: dcm->dcm_cmdtab[port].dcm_data |= CT_TX; ! 878: dcm->dcm_cr |= (1 << port); ! 879: SEM_UNLOCK(dcm); ! 880: } ! 881: #ifdef DEBUG ! 882: if (dcmdebug & DDB_INTR) ! 883: printf("dcmstart(%d): head %x tail %x outqcc %d\n", ! 884: UNIT(tp->t_dev), head, tail, tp->t_outq.c_cc); ! 885: #endif ! 886: out: ! 887: #ifdef IOSTATS ! 888: dsp->xchars += tch; ! 889: if (tch <= DCMXBSIZE) ! 890: dsp->xsilo[tch]++; ! 891: else ! 892: dsp->xsilo[DCMXBSIZE+1]++; ! 893: #endif ! 894: splx(s); ! 895: } ! 896: ! 897: /* ! 898: * Stop output on a line. ! 899: */ ! 900: dcmstop(tp, flag) ! 901: register struct tty *tp; ! 902: { ! 903: int s; ! 904: ! 905: s = spltty(); ! 906: if (tp->t_state & TS_BUSY) { ! 907: /* XXX is there some way to safely stop transmission? */ ! 908: if ((tp->t_state&TS_TTSTOP) == 0) ! 909: tp->t_state |= TS_FLUSH; ! 910: } ! 911: splx(s); ! 912: } ! 913: ! 914: /* Modem control */ ! 915: ! 916: dcmmctl(dev, bits, how) ! 917: dev_t dev; ! 918: int bits, how; ! 919: { ! 920: register struct dcmdevice *dcm; ! 921: int s, hit = 0; ! 922: ! 923: /* ! 924: * Only port 0 has modem control lines. ! 925: * XXX ok for now but needs to changed for the 8 port board. ! 926: */ ! 927: if (PORT(UNIT(dev)) != 0) ! 928: return(bits); ! 929: ! 930: dcm = dcm_addr[BOARD(UNIT(dev))]; ! 931: s = spltty(); ! 932: switch (how) { ! 933: ! 934: case DMSET: ! 935: dcm->dcm_mdmout = bits; ! 936: hit++; ! 937: break; ! 938: ! 939: case DMBIS: ! 940: dcm->dcm_mdmout |= bits; ! 941: hit++; ! 942: break; ! 943: ! 944: case DMBIC: ! 945: dcm->dcm_mdmout &= ~bits; ! 946: hit++; ! 947: break; ! 948: ! 949: case DMGET: ! 950: bits = dcm->dcm_mdmin; ! 951: break; ! 952: } ! 953: if (hit) { ! 954: SEM_LOCK(dcm); ! 955: dcm->dcm_cr |= CR_MODM; ! 956: SEM_UNLOCK(dcm); ! 957: DELAY(10); /* delay until done */ ! 958: (void) splx(s); ! 959: } ! 960: return(bits); ! 961: } ! 962: ! 963: /* ! 964: * Set board to either interrupt per-character or at a fixed interval. ! 965: */ ! 966: dcmsetischeme(brd, flags) ! 967: int brd, flags; ! 968: { ! 969: register struct dcmdevice *dcm = dcm_addr[brd]; ! 970: register struct dcmischeme *dis = &dcmischeme[brd]; ! 971: register int i; ! 972: u_char mask; ! 973: int perchar = flags & DIS_PERCHAR; ! 974: ! 975: #ifdef DEBUG ! 976: if (dcmdebug & DDB_INTSCHM) ! 977: printf("dcmsetischeme(%d, %d): cur %d, ints %d, chars %d\n", ! 978: brd, perchar, dis->dis_perchar, ! 979: dis->dis_intr, dis->dis_char); ! 980: if ((flags & DIS_RESET) == 0 && perchar == dis->dis_perchar) { ! 981: printf("dcmsetischeme(%d): redundent request %d\n", ! 982: brd, perchar); ! 983: return; ! 984: } ! 985: #endif ! 986: /* ! 987: * If perchar is non-zero, we enable interrupts on all characters ! 988: * otherwise we disable perchar interrupts and use periodic ! 989: * polling interrupts. ! 990: */ ! 991: dis->dis_perchar = perchar; ! 992: mask = perchar ? 0xf : 0x0; ! 993: for (i = 0; i < 256; i++) ! 994: dcm->dcm_bmap[i].data_data = mask; ! 995: /* ! 996: * Don't slow down tandem mode, interrupt on flow control ! 997: * chars for any port on the board. ! 998: */ ! 999: if (!perchar) { ! 1000: register struct tty *tp = &dcm_tty[MKUNIT(brd, 0)]; ! 1001: int c; ! 1002: ! 1003: for (i = 0; i < 4; i++, tp++) { ! 1004: if ((c = tp->t_cc[VSTART]) != _POSIX_VDISABLE) ! 1005: dcm->dcm_bmap[c].data_data |= (1 << i); ! 1006: if ((c = tp->t_cc[VSTOP]) != _POSIX_VDISABLE) ! 1007: dcm->dcm_bmap[c].data_data |= (1 << i); ! 1008: } ! 1009: } ! 1010: /* ! 1011: * Board starts with timer disabled so if first call is to ! 1012: * set perchar mode then we don't want to toggle the timer. ! 1013: */ ! 1014: if (flags == (DIS_RESET|DIS_PERCHAR)) ! 1015: return; ! 1016: /* ! 1017: * Toggle card 16.7ms interrupts (we first make sure that card ! 1018: * has cleared the bit so it will see the toggle). ! 1019: */ ! 1020: while (dcm->dcm_cr & CR_TIMER) ! 1021: ; ! 1022: SEM_LOCK(dcm); ! 1023: dcm->dcm_cr |= CR_TIMER; ! 1024: SEM_UNLOCK(dcm); ! 1025: } ! 1026: ! 1027: /* ! 1028: * Following are all routines needed for DCM to act as console ! 1029: */ ! 1030: #include "machine/cons.h" ! 1031: ! 1032: dcmcnprobe(cp) ! 1033: struct consdev *cp; ! 1034: { ! 1035: register struct hp_hw *hw; ! 1036: int unit, i; ! 1037: extern int dcmopen(); ! 1038: ! 1039: /* ! 1040: * Implicitly assigns the lowest select code DCM card found to be ! 1041: * logical unit 0 (actually CONUNIT). If your config file does ! 1042: * anything different, you're screwed. ! 1043: */ ! 1044: for (hw = sc_table; hw->hw_type; hw++) ! 1045: if (hw->hw_type == COMMDCM && !badaddr((short *)hw->hw_addr)) ! 1046: break; ! 1047: if (hw->hw_type != COMMDCM) { ! 1048: cp->cn_pri = CN_DEAD; ! 1049: return; ! 1050: } ! 1051: unit = CONUNIT; ! 1052: dcm_addr[BOARD(CONUNIT)] = (struct dcmdevice *)hw->hw_addr; ! 1053: ! 1054: /* locate the major number */ ! 1055: for (i = 0; i < nchrdev; i++) ! 1056: if (cdevsw[i].d_open == dcmopen) ! 1057: break; ! 1058: ! 1059: /* initialize required fields */ ! 1060: cp->cn_dev = makedev(i, unit); ! 1061: cp->cn_tp = &dcm_tty[unit]; ! 1062: switch (dcm_addr[BOARD(unit)]->dcm_rsid) { ! 1063: case DCMID: ! 1064: cp->cn_pri = CN_NORMAL; ! 1065: break; ! 1066: case DCMID|DCMCON: ! 1067: cp->cn_pri = CN_REMOTE; ! 1068: break; ! 1069: default: ! 1070: cp->cn_pri = CN_DEAD; ! 1071: break; ! 1072: } ! 1073: } ! 1074: ! 1075: dcmcninit(cp) ! 1076: struct consdev *cp; ! 1077: { ! 1078: dcminit(cp->cn_dev, dcmdefaultrate); ! 1079: dcmconsole = UNIT(cp->cn_dev); ! 1080: } ! 1081: ! 1082: dcminit(dev, rate) ! 1083: dev_t dev; ! 1084: int rate; ! 1085: { ! 1086: register struct dcmdevice *dcm = dcm_addr[BOARD(dev)]; ! 1087: int s, mode, port; ! 1088: ! 1089: port = PORT(dev); ! 1090: mode = LC_8BITS | LC_1STOP; ! 1091: s = splhigh(); ! 1092: /* ! 1093: * Wait for transmitter buffer to empty. ! 1094: */ ! 1095: while (dcm->dcm_thead[port].ptr != dcm->dcm_ttail[port].ptr) ! 1096: DELAY(DCM_USPERCH(rate)); ! 1097: /* ! 1098: * Make changes known to hardware. ! 1099: */ ! 1100: dcm->dcm_data[port].dcm_baud = ttspeedtab(rate, dcmspeedtab); ! 1101: dcm->dcm_data[port].dcm_conf = mode; ! 1102: SEM_LOCK(dcm); ! 1103: dcm->dcm_cmdtab[port].dcm_data |= CT_CON; ! 1104: dcm->dcm_cr |= (1 << port); ! 1105: SEM_UNLOCK(dcm); ! 1106: /* ! 1107: * Delay for config change to take place. Weighted by buad. ! 1108: * XXX why do we do this? ! 1109: */ ! 1110: DELAY(16 * DCM_USPERCH(rate)); ! 1111: splx(s); ! 1112: } ! 1113: ! 1114: dcmcngetc(dev) ! 1115: dev_t dev; ! 1116: { ! 1117: register struct dcmdevice *dcm = dcm_addr[BOARD(dev)]; ! 1118: register struct dcmrfifo *fifo; ! 1119: register struct dcmpreg *pp; ! 1120: register unsigned head; ! 1121: int s, c, stat, port; ! 1122: ! 1123: port = PORT(dev); ! 1124: pp = dcm_preg(dcm, port); ! 1125: s = splhigh(); ! 1126: head = pp->r_head & RX_MASK; ! 1127: fifo = &dcm->dcm_rfifos[3-port][head>>1]; ! 1128: while (head == (pp->r_tail & RX_MASK)) ! 1129: ; ! 1130: /* ! 1131: * If board interrupts are enabled, just let our received char ! 1132: * interrupt through in case some other port on the board was ! 1133: * busy. Otherwise we must clear the interrupt. ! 1134: */ ! 1135: SEM_LOCK(dcm); ! 1136: if ((dcm->dcm_ic & IC_IE) == 0) ! 1137: stat = dcm->dcm_iir; ! 1138: SEM_UNLOCK(dcm); ! 1139: c = fifo->data_char; ! 1140: stat = fifo->data_stat; ! 1141: pp->r_head = (head + 2) & RX_MASK; ! 1142: splx(s); ! 1143: return(c); ! 1144: } ! 1145: ! 1146: /* ! 1147: * Console kernel output character routine. ! 1148: */ ! 1149: dcmcnputc(dev, c) ! 1150: dev_t dev; ! 1151: int c; ! 1152: { ! 1153: register struct dcmdevice *dcm = dcm_addr[BOARD(dev)]; ! 1154: register struct dcmpreg *pp; ! 1155: unsigned tail; ! 1156: int s, port, stat; ! 1157: ! 1158: port = PORT(dev); ! 1159: pp = dcm_preg(dcm, port); ! 1160: s = splhigh(); ! 1161: #ifdef KGDB ! 1162: if (dev != kgdb_dev) ! 1163: #endif ! 1164: if (dcmconsole == -1) { ! 1165: (void) dcminit(dev, dcmdefaultrate); ! 1166: dcmconsole = UNIT(dev); ! 1167: } ! 1168: tail = pp->t_tail & TX_MASK; ! 1169: while (tail != (pp->t_head & TX_MASK)) ! 1170: ; ! 1171: dcm->dcm_tfifos[3-port][tail].data_char = c; ! 1172: pp->t_tail = tail = (tail + 1) & TX_MASK; ! 1173: SEM_LOCK(dcm); ! 1174: dcm->dcm_cmdtab[port].dcm_data |= CT_TX; ! 1175: dcm->dcm_cr |= (1 << port); ! 1176: SEM_UNLOCK(dcm); ! 1177: while (tail != (pp->t_head & TX_MASK)) ! 1178: ; ! 1179: /* ! 1180: * If board interrupts are enabled, just let our completion ! 1181: * interrupt through in case some other port on the board ! 1182: * was busy. Otherwise we must clear the interrupt. ! 1183: */ ! 1184: if ((dcm->dcm_ic & IC_IE) == 0) { ! 1185: SEM_LOCK(dcm); ! 1186: stat = dcm->dcm_iir; ! 1187: SEM_UNLOCK(dcm); ! 1188: } ! 1189: splx(s); ! 1190: } ! 1191: #endif
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.