1/* $NetBSD: wsemul_sun.c,v 1.34 2022/01/01 11:57:44 hannken Exp $ */ 2 3/* 4 * Copyright (c) 1996, 1997 Christopher G. Demetriou. All rights reserved. 5 * 6 * Redistribution and use in source and binary forms, with or without 7 * modification, are permitted provided that the following conditions 8 * are met: 9 * 1. Redistributions of source code must retain the above copyright 10 * notice, this list of conditions and the following disclaimer. 11 * 2. Redistributions in binary form must reproduce the above copyright 12 * notice, this list of conditions and the following disclaimer in the 13 * documentation and/or other materials provided with the distribution. 14 * 3. All advertising materials mentioning features or use of this software 15 * must display the following acknowledgement: 16 * This product includes software developed by Christopher G. Demetriou 17 * for the NetBSD Project. 18 * 4. The name of the author may not be used to endorse or promote products 19 * derived from this software without specific prior written permission 20 * 21 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 22 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 23 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 24 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 25 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 26 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 27 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 28 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 29 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 30 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 31 */ 32 33/* XXX DESCRIPTION/SOURCE OF INFORMATION */ 34 35#include <sys/cdefs.h> 36__KERNEL_RCSID(0, "$NetBSD: wsemul_sun.c,v 1.34 2022/01/01 11:57:44 hannken Exp $"); 37 38#include <sys/param.h> 39#include <sys/systm.h> 40#include <sys/time.h> 41#include <sys/malloc.h> 42#include <sys/fcntl.h> 43 44#include <dev/wscons/wsconsio.h> 45#include <dev/wscons/wsdisplayvar.h> 46#include <dev/wscons/wsemulvar.h> 47#include <dev/wscons/wsksymdef.h> 48#include <dev/wscons/ascii.h> 49 50void *wsemul_sun_cnattach(const struct wsscreen_descr *, void *, 51 int, int, long); 52void *wsemul_sun_attach(int console, const struct wsscreen_descr *, 53 void *, int, int, void *, long); 54void wsemul_sun_output(void *cookie, const u_char *data, u_int count, 55 int); 56int wsemul_sun_translate(void *cookie, keysym_t, const char **); 57void wsemul_sun_detach(void *cookie, u_int *crowp, u_int *ccolp); 58void wsemul_sun_resetop(void *, enum wsemul_resetops); 59 60const struct wsemul_ops wsemul_sun_ops = { 61 .name = "sun", 62 .cnattach = wsemul_sun_cnattach, 63 .attach = wsemul_sun_attach, 64 .output = wsemul_sun_output, 65 .translate = wsemul_sun_translate, 66 .detach = wsemul_sun_detach, 67 .reset = wsemul_sun_resetop, 68 .getmsgattrs = NULL, 69 .setmsgattrs = NULL, 70 .resize = NULL, 71}; 72 73#define SUN_EMUL_STATE_NORMAL 0 /* normal processing */ 74#define SUN_EMUL_STATE_HAVEESC 1 /* seen start of ctl seq */ 75#define SUN_EMUL_STATE_CONTROL 2 /* processing ctl seq */ 76 77#define SUN_EMUL_NARGS 2 /* max # of args to a command */ 78 79struct wsemul_sun_emuldata { 80 const struct wsdisplay_emulops *emulops; 81 void *emulcookie; 82 void *cbcookie; 83 int scrcapabilities; 84 u_int nrows, ncols, crow, ccol; 85 86 u_int state; /* processing state */ 87 u_int args[SUN_EMUL_NARGS]; /* command args, if CONTROL */ 88 u_int scrolldist; /* distance to scroll */ 89 long defattr; /* default attribute (rendition) */ 90 long bowattr; /* attribute for reversed mode */ 91 int rendflags; 92#define REND_BOW 1 93#define REND_SO 2 94 long curattr; /* currently used attribute */ 95 long kernattr; /* attribute for kernel output */ 96#ifdef DIAGNOSTIC 97 int console; 98#endif 99}; 100 101static u_int wsemul_sun_output_normal(struct wsemul_sun_emuldata *, 102 u_char, int); 103static u_int wsemul_sun_output_haveesc(struct wsemul_sun_emuldata *, u_char); 104static u_int wsemul_sun_output_control(struct wsemul_sun_emuldata *, u_char); 105static void wsemul_sun_control(struct wsemul_sun_emuldata *, u_char); 106 107struct wsemul_sun_emuldata wsemul_sun_console_emuldata; 108 109/* some useful utility macros */ 110#define ARG(n) (edp->args[(n)]) 111#define NORMALIZE_ARG(n) (ARG(n) ? ARG(n) : 1) 112#define COLS_LEFT (edp->ncols - edp->ccol - 1) 113#define ROWS_LEFT (edp->nrows - edp->crow - 1) 114 115void * 116wsemul_sun_cnattach(const struct wsscreen_descr *type, void *cookie, 117 int ccol, int crow, long defattr) 118{ 119 struct wsemul_sun_emuldata *edp; 120 int res; 121 122 edp = &wsemul_sun_console_emuldata; 123 124 edp->emulops = type->textops; 125 edp->emulcookie = cookie; 126 edp->scrcapabilities = type->capabilities; 127 edp->nrows = type->nrows; 128 edp->ncols = type->ncols; 129 edp->crow = crow; 130 edp->ccol = ccol; 131 edp->curattr = edp->defattr = defattr; 132#if defined(WS_KERNEL_FG) || defined(WS_KERNEL_BG) || \ 133 defined(WS_KERNEL_COLATTR) || defined(WS_KERNEL_MONOATTR) 134#ifndef WS_KERNEL_FG 135#define WS_KERNEL_FG WSCOL_WHITE 136#endif 137#ifndef WS_KERNEL_BG 138#define WS_KERNEL_BG WSCOL_BLACK 139#endif 140#ifndef WS_KERNEL_COLATTR 141#define WS_KERNEL_COLATTR 0 142#endif 143#ifndef WS_KERNEL_MONOATTR 144#define WS_KERNEL_MONOATTR 0 145#endif 146 if (type->capabilities & WSSCREEN_WSCOLORS) 147 res = (*edp->emulops->allocattr)(cookie, 148 WS_KERNEL_FG, WS_KERNEL_BG, 149 WS_KERNEL_COLATTR | WSATTR_WSCOLORS, 150 &edp->kernattr); 151 else 152 res = (*edp->emulops->allocattr)(cookie, 0, 0, 153 WS_KERNEL_MONOATTR, 154 &edp->kernattr); 155#else 156 res = EINVAL; 157#endif 158 if (res) 159 edp->kernattr = defattr; 160 161 edp->cbcookie = NULL; 162 163 edp->state = SUN_EMUL_STATE_NORMAL; 164 edp->scrolldist = 1; 165#ifdef DIAGNOSTIC 166 edp->console = 1; 167#endif 168 return (edp); 169} 170 171void * 172wsemul_sun_attach(int console, const struct wsscreen_descr *type, 173 void *cookie, int ccol, int crow, void *cbcookie, long defattr) 174{ 175 struct wsemul_sun_emuldata *edp; 176 177 if (console) { 178 edp = &wsemul_sun_console_emuldata; 179#ifdef DIAGNOSTIC 180 KASSERT(edp->console == 1); 181#endif 182 } else { 183 edp = malloc(sizeof *edp, M_DEVBUF, M_WAITOK); 184 185 edp->emulops = type->textops; 186 edp->emulcookie = cookie; 187 edp->scrcapabilities = type->capabilities; 188 edp->nrows = type->nrows; 189 edp->ncols = type->ncols; 190 edp->crow = crow; 191 edp->ccol = ccol; 192 edp->defattr = defattr; 193 194 edp->state = SUN_EMUL_STATE_NORMAL; 195 edp->scrolldist = 1; 196#ifdef DIAGNOSTIC 197 edp->console = 0; 198#endif 199 } 200 201 edp->cbcookie = cbcookie; 202 203 if ((!(edp->scrcapabilities & WSSCREEN_REVERSE) || 204 (*edp->emulops->allocattr)(edp->emulcookie, 0, 0, 205 WSATTR_REVERSE, 206 &edp->bowattr)) && 207 (!(edp->scrcapabilities & WSSCREEN_WSCOLORS) || 208 (*edp->emulops->allocattr)(edp->emulcookie, 209 WSCOL_BLACK, WSCOL_WHITE, 210 WSATTR_WSCOLORS, 211 &edp->bowattr))) 212 edp->bowattr = edp->defattr; 213 214 edp->curattr = edp->defattr; 215 edp->rendflags = 0; 216 217 return (edp); 218} 219 220static inline u_int 221wsemul_sun_output_normal(struct wsemul_sun_emuldata *edp, u_char c, int kernel) 222{ 223 u_int newstate = SUN_EMUL_STATE_NORMAL; 224 u_int n; 225 226 switch (c) { 227 case ASCII_BEL: /* "Bell (BEL)" */ 228 wsdisplay_emulbell(edp->cbcookie); 229 break; 230 231 case ASCII_BS: /* "Backspace (BS)" */ 232 if (edp->ccol > 0) 233 edp->ccol--; 234 break; 235 236 case ASCII_CR: /* "Return (CR)" */ 237 edp->ccol = 0; 238 break; 239 240 case ASCII_HT: /* "Tab (TAB)" */ 241 n = uimin(8 - (edp->ccol & 7), COLS_LEFT); 242 (*edp->emulops->erasecols)(edp->emulcookie, edp->crow, 243 edp->ccol, n, 244 kernel ? edp->kernattr : edp->curattr); 245 edp->ccol += n; 246 break; 247 248 case ASCII_FF: /* "Form Feed (FF)" */ 249 (*edp->emulops->eraserows)(edp->emulcookie, 0, edp->nrows, 250 kernel ? edp->kernattr : edp->curattr); 251 /* XXX possible in kernel output? */ 252 edp->ccol = 0; 253 edp->crow = 0; 254 break; 255 256 case ASCII_VT: /* "Reverse Line Feed" */ 257 if (edp->crow > 0) 258 edp->crow--; 259 break; 260 261 case ASCII_ESC: /* "Escape (ESC)" */ 262 if (kernel) { 263 printf("wsemul_sun_output_normal: ESC in kernel output ignored\n"); 264 break; /* ignore the ESC */ 265 } 266 267 if (edp->state == SUN_EMUL_STATE_NORMAL) { 268 newstate = SUN_EMUL_STATE_HAVEESC; 269 break; 270 } 271 /* special case: fall through, we're printing one out */ 272 /* FALLTHRU */ 273 274 default: /* normal character */ 275 (*edp->emulops->putchar)(edp->emulcookie, edp->crow, edp->ccol, 276 c, kernel ? edp->kernattr : edp->curattr); 277 edp->ccol++; 278 279 /* if cur col is still on cur line, done. */ 280 if (edp->ccol < edp->ncols) 281 break; 282 283 /* wrap the column around. */ 284 edp->ccol = 0; 285 286 /* FALLTHRU */ 287 288 case ASCII_LF: /* "Line Feed (LF)" */ 289 /* if the cur line isn't the last, incr and leave. */ 290 if (edp->crow < edp->nrows - 1) { 291 edp->crow++; 292 break; 293 } 294 295 /* 296 * if we're in wrap-around mode, go to the first 297 * line and clear it. 298 */ 299 if (edp->scrolldist == 0) { 300 edp->crow = 0; 301 (*edp->emulops->eraserows)(edp->emulcookie, 0, 1, 302 edp->curattr); 303 break; 304 } 305 306 /* scroll by the scrolling distance. */ 307 (*edp->emulops->copyrows)(edp->emulcookie, edp->scrolldist, 0, 308 edp->nrows - edp->scrolldist); 309 (*edp->emulops->eraserows)(edp->emulcookie, 310 edp->nrows - edp->scrolldist, edp->scrolldist, 311 edp->curattr); 312 edp->crow -= edp->scrolldist - 1; 313 break; 314 } 315 316 return (newstate); 317} 318 319static inline u_int 320wsemul_sun_output_haveesc(struct wsemul_sun_emuldata *edp, u_char c) 321{ 322 u_int newstate; 323 324 switch (c) { 325 case '[': /* continuation of multi-char sequence */ 326 memset(edp->args, 0, sizeof (edp->args)); 327 newstate = SUN_EMUL_STATE_CONTROL; 328 break; 329 330 default: 331 /* spit out the escape char (???), then the new character */ 332 wsemul_sun_output_normal(edp, ASCII_ESC, 0); /* ??? */ 333 newstate = wsemul_sun_output_normal(edp, c, 0); 334 break; 335 } 336 337 return (newstate); 338} 339 340static inline void 341wsemul_sun_control(struct wsemul_sun_emuldata *edp, u_char c) 342{ 343 u_int n, src, dst; 344 345 switch (c) { 346 case '@': /* "Insert Character (ICH)" */ 347 n = uimin(NORMALIZE_ARG(0), COLS_LEFT + 1); 348 src = edp->ccol; 349 dst = edp->ccol + n; 350 if (dst < edp->ncols) { 351 (*edp->emulops->copycols)(edp->emulcookie, edp->crow, 352 src, dst, edp->ncols - dst); 353 } 354 (*edp->emulops->erasecols)(edp->emulcookie, edp->crow, 355 src, dst - src, edp->curattr); 356 break; 357 358 case 'A': /* "Cursor Up (CUU)" */ 359 edp->crow -= uimin(NORMALIZE_ARG(0), edp->crow); 360 break; 361 362 case 'E': /* "Cursor Next Line (CNL)" */ 363 edp->ccol = 0; 364 /* FALLTHRU */ 365 case 'B': /* "Cursor Down (CUD)" */ 366 edp->crow += uimin(NORMALIZE_ARG(0), ROWS_LEFT); 367 break; 368 369 case 'C': /* "Cursor Forward (CUF)" */ 370 edp->ccol += uimin(NORMALIZE_ARG(0), COLS_LEFT); 371 break; 372 373 case 'D': /* "Cursor Backward (CUB)" */ 374 edp->ccol -= uimin(NORMALIZE_ARG(0), edp->ccol); 375 break; 376 377 case 'f': /* "Horizontal And Vertical Position (HVP)" */ 378 case 'H': /* "Cursor Position (CUP)" */ 379 edp->crow = uimin(NORMALIZE_ARG(1), edp->nrows) - 1; 380 edp->ccol = uimin(NORMALIZE_ARG(0), edp->ncols) - 1; 381 break; 382 383 case 'J': /* "Erase in Display (ED)" */ 384 if (ROWS_LEFT > 0) { 385 (*edp->emulops->eraserows)(edp->emulcookie, 386 edp->crow + 1, ROWS_LEFT, edp->curattr); 387 } 388 /* FALLTHRU */ 389 case 'K': /* "Erase in Line (EL)" */ 390 (*edp->emulops->erasecols)(edp->emulcookie, edp->crow, 391 edp->ccol, COLS_LEFT + 1, edp->curattr); 392 break; 393 394 case 'L': /* "Insert Line (IL)" */ 395 n = uimin(NORMALIZE_ARG(0), ROWS_LEFT + 1); 396 src = edp->crow; 397 dst = edp->crow + n; 398 if (dst < edp->nrows) { 399 (*edp->emulops->copyrows)(edp->emulcookie, 400 src, dst, edp->nrows - dst); 401 } 402 (*edp->emulops->eraserows)(edp->emulcookie, 403 src, dst - src, edp->curattr); 404 break; 405 406 case 'M': /* "Delete Line (DL)" */ 407 n = uimin(NORMALIZE_ARG(0), ROWS_LEFT + 1); 408 src = edp->crow + n; 409 dst = edp->crow; 410 if (src < edp->nrows) { 411 (*edp->emulops->copyrows)(edp->emulcookie, 412 src, dst, edp->nrows - src); 413 } 414 (*edp->emulops->eraserows)(edp->emulcookie, 415 dst + edp->nrows - src, src - dst, edp->curattr); 416 break; 417 418 case 'P': /* "Delete Character (DCH)" */ 419 n = uimin(NORMALIZE_ARG(0), COLS_LEFT + 1); 420 src = edp->ccol + n; 421 dst = edp->ccol; 422 if (src < edp->ncols) { 423 (*edp->emulops->copycols)(edp->emulcookie, edp->crow, 424 src, dst, edp->ncols - src); 425 } 426 (*edp->emulops->erasecols)(edp->emulcookie, edp->crow, 427 dst + edp->ncols - src, src - dst, edp->curattr); 428 break; 429 430 case 'm': /* "Select Graphic Rendition (SGR)" */ 431 if (ARG(0)) 432 edp->rendflags |= REND_SO; 433 else 434 edp->rendflags &= ~REND_SO; 435 goto setattr; 436 437 case 'p': /* "Black On White (SUNBOW)" */ 438 edp->rendflags |= REND_BOW; 439 goto setattr; 440 441 case 'q': /* "White On Black (SUNWOB)" */ 442 edp->rendflags &= ~REND_BOW; 443 goto setattr; 444 445 case 'r': /* "Set Scrolling (SUNSCRL)" */ 446 edp->scrolldist = uimin(ARG(0), edp->nrows); 447 break; 448 449 case 's': /* "Reset Terminal Emulator (SUNRESET)" */ 450 edp->scrolldist = 1; 451 edp->rendflags = 0; 452setattr: 453 if (((edp->rendflags & REND_BOW) != 0) ^ 454 ((edp->rendflags & REND_SO) != 0)) 455 edp->curattr = edp->bowattr; 456 else 457 edp->curattr = edp->defattr; 458 break; 459 } 460} 461 462static inline u_int 463wsemul_sun_output_control(struct wsemul_sun_emuldata *edp, u_char c) 464{ 465 u_int newstate = SUN_EMUL_STATE_CONTROL; 466 u_int i; 467 468 switch (c) { 469 case '0': case '1': case '2': case '3': case '4': /* argument digit */ 470 case '5': case '6': case '7': case '8': case '9': 471 edp->args[0] = (edp->args[0] * 10) + (c - '0'); 472 break; 473 474 case ';': /* argument terminator */ 475 for (i = 1; i < SUN_EMUL_NARGS; i++) 476 edp->args[i] = edp->args[i - 1]; 477 edp->args[0] = 0; 478 break; 479 480 default: /* end of escape sequence */ 481 wsemul_sun_control(edp, c); 482 newstate = SUN_EMUL_STATE_NORMAL; 483 break; 484 } 485 return (newstate); 486} 487 488void 489wsemul_sun_output(void *cookie, const u_char *data, u_int count, int kernel) 490{ 491 struct wsemul_sun_emuldata *edp = cookie; 492 u_int newstate; 493 494#ifdef DIAGNOSTIC 495 if (kernel && !edp->console) 496 panic("wsemul_sun_output: kernel output, not console"); 497#endif 498 499 /* XXX */ 500 (*edp->emulops->cursor)(edp->emulcookie, 0, edp->crow, edp->ccol); 501 for (; count > 0; data++, count--) { 502 if (kernel) { 503 wsemul_sun_output_normal(edp, *data, 1); 504 continue; 505 } 506 switch (edp->state) { 507 case SUN_EMUL_STATE_NORMAL: 508 /* XXX SCAN INPUT FOR NEWLINES, DO PRESCROLLING */ 509 newstate = wsemul_sun_output_normal(edp, *data, 0); 510 break; 511 case SUN_EMUL_STATE_HAVEESC: 512 newstate = wsemul_sun_output_haveesc(edp, *data); 513 break; 514 case SUN_EMUL_STATE_CONTROL: 515 newstate = wsemul_sun_output_control(edp, *data); 516 break; 517 default: 518#ifdef DIAGNOSTIC 519 panic("wsemul_sun: invalid state %d", edp->state); 520#endif 521 /* try to recover, if things get screwed up... */ 522 newstate = wsemul_sun_output_normal(edp, *data, 0); 523 break; 524 } 525 edp->state = newstate; 526 } 527 /* XXX */ 528 (*edp->emulops->cursor)(edp->emulcookie, 1, edp->crow, edp->ccol); 529} 530 531static const char *sun_fkeys[] = { 532 "\033[224z", /* F1 */ 533 "\033[225z", 534 "\033[226z", 535 "\033[227z", 536 "\033[228z", 537 "\033[229z", 538 "\033[230z", 539 "\033[231z", 540 "\033[232z", 541 "\033[233z", /* F10 */ 542}; 543 544int 545wsemul_sun_translate(void *cookie, keysym_t in, const char **out) 546{ 547 static char c; 548 549 if (KS_GROUP(in) == KS_GROUP_Plain) { 550 /* allow ISO-1 */ 551 c = KS_VALUE(in); 552 *out = &c; 553 return (1); 554 } 555 556 if (KS_GROUP(in) == KS_GROUP_Keypad && (in & 0x80) == 0) { 557 c = in & 0xff; /* turn into ASCII */ 558 *out = &c; 559 return (1); 560 } 561 562 if (in >= KS_f1 && in <= KS_f10) { 563 *out = sun_fkeys[in - KS_f1]; 564 return (6); 565 } 566 if (in >= KS_F1 && in <= KS_F10) { 567 *out = sun_fkeys[in - KS_F1]; 568 return (6); 569 } 570 if (in >= KS_KP_F1 && in <= KS_KP_F4) { 571 *out = sun_fkeys[in - KS_KP_F1]; 572 return (6); 573 } 574 575 switch (in) { 576 case KS_Home: 577 case KS_KP_Home: 578 case KS_KP_Begin: 579 *out = "\033[214z"; 580 return (6); 581 case KS_End: 582 case KS_KP_End: 583 *out = "\033[220z"; 584 return (6); 585 case KS_Prior: 586 case KS_KP_Prior: 587 *out = "\033[216z"; 588 return (6); 589 case KS_Next: 590 case KS_KP_Next: 591 *out = "\033[222z"; 592 return (6); 593 case KS_Up: 594 case KS_KP_Up: 595 *out = "\033[A"; 596 return (3); 597 case KS_Down: 598 case KS_KP_Down: 599 *out = "\033[B"; 600 return (3); 601 case KS_Left: 602 case KS_KP_Left: 603 *out = "\033[D"; 604 return (3); 605 case KS_Right: 606 case KS_KP_Right: 607 *out = "\033[C"; 608 return (3); 609 case KS_KP_Delete: 610 *out = "\177"; 611 return (1); 612 } 613 return (0); 614} 615 616void 617wsemul_sun_detach(void *cookie, u_int *crowp, u_int *ccolp) 618{ 619 struct wsemul_sun_emuldata *edp = cookie; 620 621 *crowp = edp->crow; 622 *ccolp = edp->ccol; 623 if (edp != &wsemul_sun_console_emuldata) 624 free(edp, M_DEVBUF); 625} 626 627void 628wsemul_sun_resetop(void *cookie, enum wsemul_resetops op) 629{ 630 struct wsemul_sun_emuldata *edp = cookie; 631 632 switch (op) { 633 case WSEMUL_RESET: 634 edp->state = SUN_EMUL_STATE_NORMAL; 635 edp->scrolldist = 1; 636 edp->rendflags = 0; 637 edp->curattr = edp->defattr; 638 break; 639 case WSEMUL_CLEARSCREEN: 640 (*edp->emulops->eraserows)(edp->emulcookie, 0, edp->nrows, 641 edp->defattr); 642 edp->ccol = edp->crow = 0; 643 (*edp->emulops->cursor)(edp->emulcookie, 1, 0, 0); 644 break; 645 default: 646 break; 647 } 648} 649