|
|
1.1 ! root 1: /* ! 2: * Mach Operating System ! 3: * Copyright (c) 1991,1990,1989 Carnegie Mellon University ! 4: * All Rights Reserved. ! 5: * ! 6: * Permission to use, copy, modify and distribute this software and its ! 7: * documentation is hereby granted, provided that both the copyright ! 8: * notice and this permission notice appear in all copies of the ! 9: * software, derivative works or modified versions, and any portions ! 10: * thereof, and that both notices appear in supporting documentation. ! 11: * ! 12: * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS" ! 13: * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR ! 14: * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE. ! 15: * ! 16: * Carnegie Mellon requests users of this software to return to ! 17: * ! 18: * Software Distribution Coordinator or [email protected] ! 19: * School of Computer Science ! 20: * Carnegie Mellon University ! 21: * Pittsburgh PA 15213-3890 ! 22: * ! 23: * any improvements or extensions that they make and grant Carnegie Mellon ! 24: * the rights to redistribute these changes. ! 25: */ ! 26: /* ! 27: * File: serial_console.c ! 28: * Author: Alessandro Forin, Carnegie Mellon University ! 29: * Date: 7/91 ! 30: * ! 31: * Console driver for serial-line based consoles. ! 32: */ ! 33: ! 34: #include <constty.h> ! 35: #if NCONSTTY > 0 ! 36: #include <bm.h> ! 37: #include <platforms.h> ! 38: ! 39: #include <mach_kdb.h> ! 40: ! 41: #include <machine/machspl.h> /* spl definitions */ ! 42: #include <device/io_req.h> ! 43: #include <device/tty.h> ! 44: #include <sys/syslog.h> ! 45: ! 46: #include <chips/busses.h> ! 47: #include <chips/screen_defs.h> ! 48: #include <chips/serial_defs.h> ! 49: ! 50: #ifdef DECSTATION ! 51: #include <mips/prom_interface.h> ! 52: #define find_rconsole(p) dec_check_rcline(p) ! 53: #define CONSOLE_SERIAL_LINE_NO 3 ! 54: #endif /*DECSTATION*/ ! 55: ! 56: #ifdef VAXSTATION ! 57: #define find_rconsole(p) ! 58: #define cnputc ser_putc ! 59: #define cngetc ser_getc ! 60: #define cnpollc ser_pollc ! 61: #define cnmaygetc ser_maygetc ! 62: #define CONSOLE_SERIAL_LINE_NO 3 ! 63: #endif /*VAXSTATION*/ ! 64: ! 65: #ifdef FLAMINGO ! 66: #define CONSOLE_SERIAL_LINE_NO 3 ! 67: #endif ! 68: ! 69: #ifndef CONSOLE_SERIAL_LINE_NO ! 70: #define CONSOLE_SERIAL_LINE_NO 0 ! 71: #endif ! 72: ! 73: /* Size this as max possible number of lines in any serial chip we might use */ ! 74: static struct tty console_tty_data[NCONSTTY]; ! 75: struct tty *console_tty[NCONSTTY]; /* exported */ ! 76: ! 77: #define DEFAULT_SPEED B9600 ! 78: #define DEFAULT_FLAGS (TF_EVENP|TF_ODDP|TF_ECHO) ! 79: ! 80: ! 81: /* ! 82: * A machine MUST have a console. In our case ! 83: * things are a little complicated by the graphic ! 84: * display: people expect it to be their "console", ! 85: * but we'd like to be able to live without it. ! 86: * This is not to be confused with the "rconsole" thing: ! 87: * that just duplicates the console I/O to ! 88: * another place (for debugging/logging purposes). ! 89: * ! 90: * There is then another historical kludge: if ! 91: * there is a graphic display it is assumed that ! 92: * the minor "1" is the mouse, with some more ! 93: * magic attached to it. And again, one might like to ! 94: * use the serial line 1 as a regular one. ! 95: * ! 96: */ ! 97: #define user_console 0 ! 98: ! 99: int console = 0; ! 100: ! 101: int (*console_probe)() = 0, ! 102: (*console_param)() = 0, ! 103: (*console_start)() = 0, ! 104: (*console_putc)() = 0, ! 105: (*console_getc)() = 0, ! 106: (*console_pollc)() = 0, ! 107: (*console_mctl)() = 0, ! 108: (*console_softCAR)() = 0; ! 109: ! 110: /* ! 111: * Lower-level (internal) interfaces, for printf and gets ! 112: */ ! 113: int cnunit = 0; /* which unit owns the 'console' */ ! 114: int cnline = 0; /* which line of that unit */ ! 115: int rcline = 3; /* alternate, "remote console" line */ ! 116: ! 117: rcoff() ! 118: { ! 119: spl_t s = splhigh(); ! 120: cnpollc(FALSE); ! 121: rcline = 0; ! 122: cnpollc(TRUE); ! 123: splx(s); ! 124: } ! 125: ! 126: rcputc(c) ! 127: { ! 128: if (rcline) ! 129: (*console_putc)( cnunit, rcline, c); ! 130: } ! 131: ! 132: cnputc(c) ! 133: { ! 134: #if NBM > 0 ! 135: if (SCREEN_ISA_CONSOLE()) { ! 136: /* this does its own rcputc */ ! 137: screen_blitc(SCREEN_CONS_UNIT(), c); ! 138: } else ! 139: #endif NBM > 0 ! 140: { ! 141: rcputc(c); ! 142: (*console_putc)( cnunit, cnline, c);/* insist on a console still */ ! 143: } ! 144: if (c == '\n') ! 145: cnputc('\r'); ! 146: } ! 147: ! 148: cngetc() ! 149: { ! 150: return (*console_getc)( cnunit, cnline, TRUE, FALSE); ! 151: } ! 152: ! 153: cnpollc(bool) ! 154: { ! 155: (*console_pollc)(cnunit, bool); ! 156: } ! 157: ! 158: ! 159: /* Debugger support */ ! 160: cnmaygetc() ! 161: { ! 162: return (*console_getc)( cnunit, cnline, FALSE, FALSE); ! 163: } ! 164: ! 165: ! 166: #if NBM > 0 ! 167: boolean_t ! 168: screen_captures(line) ! 169: register int line; ! 170: { ! 171: return (SCREEN_ISA_CONSOLE() && ! 172: ((line == SCREEN_LINE_KEYBOARD) || ! 173: (line == SCREEN_LINE_POINTER))); ! 174: } ! 175: #endif ! 176: ! 177: /* ! 178: * Higher level (external) interface, for GP use ! 179: */ ! 180: ! 181: ! 182: /* ! 183: * This is basically a special form of autoconf, ! 184: * to get printf() going before true autoconf. ! 185: */ ! 186: cons_find(tube) ! 187: boolean_t tube; ! 188: { ! 189: static struct bus_device d; ! 190: register int i; ! 191: struct tty *tp; ! 192: ! 193: for (i = 0; i < NCONSTTY; i++) ! 194: console_tty[i] = &console_tty_data[i]; ! 195: /* the hardware device will set tp->t_addr for valid ttys */ ! 196: ! 197: d.unit = 0; ! 198: ! 199: if ((console_probe == 0) || ! 200: ((*console_probe)(0, &d) == 0)) { ! 201: /* we have no console, but maybe that's ok */ ! 202: #if defined(DECSTATION) || defined(FLAMINGO) ! 203: /* no, it is not */ ! 204: dprintf("%s", "no console!\n"); ! 205: halt(); ! 206: #endif ! 207: return 0; ! 208: } ! 209: ! 210: /* ! 211: * Remote console line ! 212: */ ! 213: find_rconsole(&rcline); ! 214: ! 215: /* ! 216: * Console always on unit 0. Fix if you need to ! 217: */ ! 218: cnunit = 0; ! 219: ! 220: #if NBM > 0 ! 221: if (tube && screen_probe(0)) { ! 222: ! 223: /* associate screen to console iff */ ! 224: if (console == user_console) ! 225: screen_console = cnunit | SCREEN_CONS_ENBL; ! 226: cnline = SCREEN_LINE_KEYBOARD; ! 227: ! 228: /* mouse and keybd */ ! 229: tp = console_tty[SCREEN_LINE_KEYBOARD]; ! 230: tp->t_ispeed = B4800; ! 231: tp->t_ospeed = B4800; ! 232: tp->t_flags = TF_LITOUT|TF_EVENP|TF_ECHO|TF_XTABS|TF_CRMOD; ! 233: tp->t_dev = SCREEN_LINE_KEYBOARD; ! 234: (*console_param)(tp, SCREEN_LINE_KEYBOARD); ! 235: ! 236: tp = console_tty[SCREEN_LINE_POINTER]; ! 237: tp->t_ispeed = B4800; ! 238: tp->t_ospeed = B4800; ! 239: tp->t_flags = TF_LITOUT|TF_ODDP; ! 240: tp->t_dev = SCREEN_LINE_POINTER; ! 241: (*console_param)(tp, SCREEN_LINE_POINTER); ! 242: /* console_scan will turn on carrier */ ! 243: ! 244: } else { ! 245: #endif NBM > 0 ! 246: /* use non-graphic console as console */ ! 247: cnline = CONSOLE_SERIAL_LINE_NO; ! 248: ! 249: tp = console_tty[cnline]; ! 250: tp->t_ispeed = B9600; ! 251: tp->t_ospeed = B9600; ! 252: tp->t_flags = TF_LITOUT|TF_EVENP|TF_ECHO|TF_XTABS|TF_CRMOD; ! 253: (*console_softCAR)(cnunit, cnline, TRUE); ! 254: console = cnline; ! 255: tp->t_dev = console; ! 256: (*console_param)(tp, SCREEN_LINE_OTHER); ! 257: #if NBM > 0 ! 258: } ! 259: ! 260: /* ! 261: * Enable rconsole interrupts for KDB ! 262: */ ! 263: if (tube && rcline != cnline) { ! 264: tp = console_tty[rcline]; ! 265: tp->t_ispeed = B9600; ! 266: tp->t_ospeed = B9600; ! 267: tp->t_flags = TF_LITOUT|TF_EVENP|TF_ECHO|TF_XTABS|TF_CRMOD; ! 268: tp->t_dev = rcline; ! 269: (*console_softCAR)(cnunit, rcline, TRUE); ! 270: (*console_param)(tp, SCREEN_LINE_OTHER); ! 271: } else ! 272: rcline = 0; ! 273: #endif NBM > 0 ! 274: } ! 275: ! 276: /* ! 277: * Open routine ! 278: */ ! 279: extern int ! 280: cons_start(struct tty *), ! 281: cons_stop(struct tty *, int), ! 282: cons_mctl(struct tty *, int, int); ! 283: ! 284: cons_open(dev, flag, ior) ! 285: int dev; ! 286: int flag; ! 287: io_req_t ior; ! 288: { ! 289: register struct tty *tp; ! 290: register int ttyno; ! 291: ! 292: if (dev == user_console) ! 293: dev = console; ! 294: ! 295: ttyno = dev; ! 296: if (ttyno >= NCONSTTY) ! 297: return D_NO_SUCH_DEVICE; ! 298: tp = console_tty[ttyno]; ! 299: ! 300: /* But was it there at probe time */ ! 301: if (tp->t_addr == 0) ! 302: return D_NO_SUCH_DEVICE; ! 303: ! 304: tp->t_start = cons_start; ! 305: tp->t_stop = cons_stop; ! 306: tp->t_mctl = cons_mctl; ! 307: ! 308: #if NBM > 0 ! 309: if (screen_captures(ttyno)) ! 310: screen_open(SCREEN_CONS_UNIT(), ttyno==SCREEN_LINE_KEYBOARD); ! 311: #endif NBM > 0 ! 312: ! 313: if ((tp->t_state & TS_ISOPEN) == 0) { ! 314: if (tp->t_ispeed == 0) { ! 315: tp->t_ispeed = DEFAULT_SPEED; ! 316: tp->t_ospeed = DEFAULT_SPEED; ! 317: tp->t_flags = DEFAULT_FLAGS; ! 318: } ! 319: tp->t_dev = dev; ! 320: (*console_param)(tp, ttyno); ! 321: } ! 322: ! 323: return (char_open(dev, tp, flag, ior)); ! 324: } ! 325: ! 326: ! 327: /* ! 328: * Close routine ! 329: */ ! 330: cons_close(dev, flag) ! 331: int dev; ! 332: { ! 333: register struct tty *tp; ! 334: register int ttyno; ! 335: spl_t s; ! 336: ! 337: if (dev == user_console) ! 338: dev = console; ! 339: ! 340: ttyno = dev; ! 341: ! 342: #if NBM > 0 ! 343: if (screen_captures(ttyno)) ! 344: screen_close(SCREEN_CONS_UNIT(), ttyno==SCREEN_LINE_KEYBOARD); ! 345: #endif NBM > 0 ! 346: ! 347: tp = console_tty[ttyno]; ! 348: ! 349: s = spltty(); ! 350: simple_lock(&tp->t_lock); ! 351: ! 352: ttyclose(tp); ! 353: ! 354: simple_unlock(&tp->t_lock); ! 355: splx(s); ! 356: } ! 357: ! 358: cons_read(dev, ior) ! 359: int dev; ! 360: register io_req_t ior; ! 361: { ! 362: register struct tty *tp; ! 363: register ttyno; ! 364: ! 365: if (dev == user_console) ! 366: dev = console; ! 367: ! 368: ttyno = dev; ! 369: #if NBM > 0 ! 370: if (SCREEN_ISA_CONSOLE() && (ttyno == SCREEN_LINE_POINTER)) ! 371: return screen_read(SCREEN_CONS_UNIT(), ior); ! 372: #endif NBM > 0 ! 373: ! 374: tp = console_tty[ttyno]; ! 375: return char_read(tp, ior); ! 376: } ! 377: ! 378: ! 379: cons_write(dev, ior) ! 380: int dev; ! 381: register io_req_t ior; ! 382: { ! 383: register struct tty *tp; ! 384: register ttyno; ! 385: ! 386: if (dev == user_console) ! 387: dev = console; ! 388: ! 389: ttyno = dev; ! 390: #if NBM > 0 ! 391: if (screen_captures(ttyno)) ! 392: return screen_write(SCREEN_CONS_UNIT(), ior); ! 393: #endif NBM > 0 ! 394: ! 395: tp = console_tty[ttyno]; ! 396: return char_write(tp, ior); ! 397: } ! 398: ! 399: /* ! 400: * Start output on a line ! 401: */ ! 402: cons_start(tp) ! 403: register struct tty *tp; ! 404: { ! 405: spl_t s; ! 406: ! 407: s = spltty(); ! 408: if (tp->t_state & (TS_TIMEOUT|TS_BUSY|TS_TTSTOP)) ! 409: goto out; ! 410: ! 411: if (tp->t_outq.c_cc == 0) ! 412: goto out; ! 413: ! 414: tp->t_state |= TS_BUSY; ! 415: ! 416: (*console_start)(tp); ! 417: ! 418: out: ! 419: splx(s); ! 420: } ! 421: ! 422: /* ! 423: * Stop output on a line. ! 424: */ ! 425: cons_stop(tp, flag) ! 426: register struct tty *tp; ! 427: { ! 428: register spl_t s; ! 429: ! 430: s = spltty(); ! 431: if (tp->t_state & TS_BUSY) { ! 432: if ((tp->t_state&TS_TTSTOP)==0) ! 433: tp->t_state |= TS_FLUSH; ! 434: } ! 435: splx(s); ! 436: } ! 437: ! 438: ! 439: /* ! 440: * Modem control ! 441: */ ! 442: cons_mctl( ! 443: struct tty *tp, ! 444: int bits, ! 445: int how) ! 446: { ! 447: return (*console_mctl)(tp->t_dev, bits, how); ! 448: } ! 449: ! 450: /* ! 451: * Abnormal close ! 452: */ ! 453: cons_portdeath(dev, port) ! 454: int dev; ! 455: mach_port_t port; ! 456: { ! 457: if (dev == user_console) ! 458: dev = console; ! 459: return (tty_portdeath(console_tty[dev], port)); ! 460: } ! 461: ! 462: /* ! 463: * Get/Set status rotuines ! 464: */ ! 465: io_return_t ! 466: cons_get_status(dev, flavor, data, status_count) ! 467: int dev; ! 468: dev_flavor_t flavor; ! 469: int * data; /* pointer to OUT array */ ! 470: unsigned int *status_count; /* out */ ! 471: { ! 472: register struct tty *tp; ! 473: register int ttyno; ! 474: ! 475: if (dev == user_console) ! 476: dev = console; ! 477: ! 478: ttyno = dev; ! 479: ! 480: #if NBM > 0 ! 481: if (screen_captures(ttyno) && ! 482: (screen_get_status(SCREEN_CONS_UNIT(), ! 483: flavor, data, status_count) == D_SUCCESS)) ! 484: return D_SUCCESS; ! 485: #endif NBM > 0 ! 486: ! 487: tp = console_tty[ttyno]; ! 488: ! 489: switch (flavor) { ! 490: case TTY_MODEM: ! 491: /* Take all bits */ ! 492: *data = (*console_mctl)(dev, -1, DMGET); ! 493: *status_count = 1; ! 494: break; ! 495: default: ! 496: return (tty_get_status(tp, flavor, data, status_count)); ! 497: } ! 498: return (D_SUCCESS); ! 499: } ! 500: ! 501: io_return_t ! 502: cons_set_status(dev, flavor, data, status_count) ! 503: int dev; ! 504: dev_flavor_t flavor; ! 505: int * data; ! 506: unsigned int status_count; ! 507: { ! 508: register struct tty *tp; ! 509: register int ttyno; ! 510: ! 511: if (dev == user_console) ! 512: dev = console; ! 513: ! 514: ttyno = dev; ! 515: ! 516: #if NBM > 0 ! 517: if (screen_captures(ttyno) && ! 518: (screen_set_status(SCREEN_CONS_UNIT(), ! 519: flavor, data, status_count) == D_SUCCESS)) ! 520: return D_SUCCESS; ! 521: #endif NBM > 0 ! 522: ! 523: tp = console_tty[ttyno]; ! 524: ! 525: switch (flavor) { ! 526: case TTY_MODEM: ! 527: if (status_count < TTY_MODEM_COUNT) ! 528: return (D_INVALID_OPERATION); ! 529: (void) (*console_mctl)(dev, *data, DMSET); ! 530: break; ! 531: ! 532: case TTY_SET_BREAK: ! 533: (void) (*console_mctl)(dev, TM_BRK, DMBIS); ! 534: break; ! 535: ! 536: case TTY_CLEAR_BREAK: ! 537: (void) (*console_mctl)(dev, TM_BRK, DMBIC); ! 538: break; ! 539: ! 540: case TTY_STATUS: ! 541: { ! 542: register int error = D_SUCCESS; ! 543: struct tty_status *tsp; ! 544: ! 545: /* ! 546: * Defend from noise. The cshell... ! 547: */ ! 548: tsp = (struct tty_status *)data; ! 549: if ((tsp->tt_ispeed != tp->t_ispeed) || ! 550: (tsp->tt_ospeed != tp->t_ospeed) || ! 551: (tsp->tt_breakc != tp->t_breakc) || ! 552: ((tsp->tt_flags & ~TF_HUPCLS) != tp->t_flags)) { ! 553: ! 554: error = tty_set_status(tp, flavor, data, status_count); ! 555: if (error == 0) { ! 556: spl_t s = spltty(); ! 557: tp->t_state &= ~(TS_BUSY|TS_FLUSH); ! 558: (*console_param)(tp, ttyno); ! 559: splx(s); ! 560: } ! 561: } else ! 562: if (tsp->tt_flags & TF_HUPCLS) ! 563: tp->t_state |= TS_HUPCLS; ! 564: return (error); ! 565: } ! 566: default: ! 567: return (tty_set_status(tp, flavor, data, status_count)); ! 568: } ! 569: return (D_SUCCESS); ! 570: } ! 571: ! 572: ! 573: /* ! 574: * A simple scheme to dispatch interrupts. ! 575: * ! 576: * This deals with the fairly common case where we get an ! 577: * interrupt on each rx/tx character. A more elaborate ! 578: * scheme [someday here too..] would handle instead many ! 579: * characters per interrupt, perhaps using a DMA controller ! 580: * or a large SILO. Note that it is also possible to simulate ! 581: * a DMA chip with 'pseudo-dma' code that runs directly down ! 582: * in the interrupt routine. ! 583: */ ! 584: ! 585: /* ! 586: * We just received a character, ship it up for further processing. ! 587: * Arguments are the tty number for which it is meant, a flag that ! 588: * indicates a keyboard or mouse is potentially attached to that ! 589: * tty (-1 if not), the character proper stripped down to 8 bits, ! 590: * and an indication of any error conditions associated with the ! 591: * receipt of the character. ! 592: * We deal here with rconsole input handling and dispatching to ! 593: * mouse or keyboard translation routines. cons_input() does ! 594: * the rest. ! 595: */ ! 596: #if MACH_KDB ! 597: int l3break = 0x10; /* dear old ^P, we miss you so bad. */ ! 598: #endif MACH_KDB ! 599: ! 600: cons_simple_rint(ttyno, line, c, err) ! 601: int line; ! 602: int c; ! 603: { ! 604: /* ! 605: * Rconsole. Drop in the debugger on break or ^P. ! 606: * Otherwise pretend input came from keyboard. ! 607: */ ! 608: if (rcline && ttyno == rcline) { ! 609: #if MACH_KDB ! 610: if ((err & CONS_ERR_BREAK) || ! 611: ((c & 0x7f) == l3break)) ! 612: return gimmeabreak(); ! 613: #endif /* MACH_KDB */ ! 614: ttyno = console; ! 615: goto process_it; ! 616: } ! 617: ! 618: #if NBM > 0 ! 619: if (screen_captures(line)) { ! 620: if (line == SCREEN_LINE_POINTER) ! 621: return mouse_input(SCREEN_CONS_UNIT(), c); ! 622: if (line == SCREEN_LINE_KEYBOARD) { ! 623: c = lk201_rint(SCREEN_CONS_UNIT(), c, FALSE, FALSE); ! 624: if (c == -1) ! 625: return; /* shift or bad char */ ! 626: } ! 627: } ! 628: #endif NBM > 0 ! 629: process_it: ! 630: cons_input(ttyno, c, err); ! 631: } ! 632: ! 633: /* ! 634: * Send along a character on a tty. If we were waiting for ! 635: * this char to complete the open procedure do so; check ! 636: * for errors; if all is well proceed to ttyinput(). ! 637: */ ! 638: cons_input(ttyno, c, err) ! 639: { ! 640: register struct tty *tp; ! 641: ! 642: tp = console_tty[ttyno]; ! 643: ! 644: if ((tp->t_state & TS_ISOPEN) == 0) { ! 645: tt_open_wakeup(tp); ! 646: return; ! 647: } ! 648: if (err) { ! 649: if (err & CONS_ERR_OVERRUN) ! 650: log(LOG_WARNING, "sl%d: silo overflow\n", ttyno); ! 651: ! 652: if (err & CONS_ERR_PARITY) ! 653: if (((tp->t_flags & (TF_EVENP|TF_ODDP)) == TF_EVENP) ! 654: || ((tp->t_flags & (TF_EVENP|TF_ODDP)) == TF_ODDP)) ! 655: return; ! 656: if (err & CONS_ERR_BREAK) /* XXX autobaud XXX */ ! 657: c = tp->t_breakc; ! 658: } ! 659: ttyinput(c, tp); ! 660: } ! 661: ! 662: /* ! 663: * Transmission of a character is complete. ! 664: * Return the next character or -1 if none. ! 665: */ ! 666: cons_simple_tint(ttyno, all_sent) ! 667: boolean_t all_sent; ! 668: { ! 669: register struct tty *tp; ! 670: ! 671: tp = console_tty[ttyno]; ! 672: if ((tp->t_addr == 0) || /* not probed --> stray */ ! 673: (tp->t_state & TS_TTSTOP)) ! 674: return -1; ! 675: ! 676: if (all_sent) { ! 677: tp->t_state &= ~TS_BUSY; ! 678: if (tp->t_state & TS_FLUSH) ! 679: tp->t_state &= ~TS_FLUSH; ! 680: ! 681: cons_start(tp); ! 682: } ! 683: ! 684: if (tp->t_outq.c_cc == 0 || (tp->t_state&TS_BUSY)==0) ! 685: return -1; ! 686: ! 687: return getc(&tp->t_outq); ! 688: } ! 689: ! 690: ! 691: ! 692: ! 693: ! 694: #endif /*NCONSTTY > 0*/
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.