1 /* 2 * Copyright (c) 1998 Michael Smith (msmith@freebsd.org) 3 * 4 * Redistribution and use in source and binary forms, with or without 5 * modification, are permitted provided that the following conditions 6 * are met: 7 * 1. Redistributions of source code must retain the above copyright 8 * notice, this list of conditions and the following disclaimer. 9 * 2. Redistributions in binary form must reproduce the above copyright 10 * notice, this list of conditions and the following disclaimer in the 11 * documentation and/or other materials provided with the distribution. 12 * 13 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 14 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 15 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 16 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 17 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 18 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 19 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 20 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 21 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 22 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 23 * SUCH DAMAGE. 24 */ 25 26 #include <sys/cdefs.h> 27 28 #include <stand.h> 29 #include <sys/errno.h> 30 #include <bootstrap.h> 31 32 #include <efi.h> 33 #include <efilib.h> 34 35 #include "loader_efi.h" 36 37 static EFI_GUID serial = SERIAL_IO_PROTOCOL; 38 39 #define COMC_TXWAIT 0x40000 /* transmit timeout */ 40 41 #ifndef COMSPEED 42 #define COMSPEED 9600 43 #endif 44 45 struct serial { 46 uint64_t baudrate; 47 uint8_t databits; 48 EFI_PARITY_TYPE parity; 49 EFI_STOP_BITS_TYPE stopbits; 50 uint8_t ignore_cd; /* boolean */ 51 uint8_t rtsdtr_off; /* boolean */ 52 int ioaddr; /* index in handles array */ 53 SERIAL_IO_INTERFACE *sio; 54 }; 55 56 static void comc_probe(struct console *); 57 static int comc_init(struct console *, int); 58 static void comc_putchar(struct console *, int); 59 static int comc_getchar(struct console *); 60 static int comc_ischar(struct console *); 61 static void comc_setup(struct console *); 62 static char *comc_asprint_mode(struct serial *); 63 static int comc_parse_mode(struct serial *, const char *); 64 static int comc_mode_set(struct env_var *, int, const void *); 65 static int comc_cd_set(struct env_var *, int, const void *); 66 static int comc_rtsdtr_set(struct env_var *, int, const void *); 67 68 struct console ttya = { 69 .c_name = "ttya", 70 .c_desc = "serial port a", 71 .c_flags = 0, 72 .c_probe = comc_probe, 73 .c_init = comc_init, 74 .c_out = comc_putchar, 75 .c_in = comc_getchar, 76 .c_ready = comc_ischar, 77 .c_private = NULL 78 }; 79 80 struct console ttyb = { 81 .c_name = "ttyb", 82 .c_desc = "serial port b", 83 .c_flags = 0, 84 .c_probe = comc_probe, 85 .c_init = comc_init, 86 .c_out = comc_putchar, 87 .c_in = comc_getchar, 88 .c_ready = comc_ischar, 89 .c_private = NULL 90 }; 91 92 struct console ttyc = { 93 .c_name = "ttyc", 94 .c_desc = "serial port c", 95 .c_flags = 0, 96 .c_probe = comc_probe, 97 .c_init = comc_init, 98 .c_out = comc_putchar, 99 .c_in = comc_getchar, 100 .c_ready = comc_ischar, 101 .c_private = NULL 102 }; 103 104 struct console ttyd = { 105 .c_name = "ttyd", 106 .c_desc = "serial port d", 107 .c_flags = 0, 108 .c_probe = comc_probe, 109 .c_init = comc_init, 110 .c_out = comc_putchar, 111 .c_in = comc_getchar, 112 .c_ready = comc_ischar, 113 .c_private = NULL 114 }; 115 116 EFI_STATUS 117 efi_serial_init(EFI_HANDLE **handlep, int *nhandles) 118 { 119 UINTN bufsz = 0; 120 EFI_STATUS status; 121 EFI_HANDLE *handles; 122 123 /* 124 * get buffer size 125 */ 126 *nhandles = 0; 127 status = BS->LocateHandle(ByProtocol, &serial, NULL, &bufsz, handles); 128 if (status != EFI_BUFFER_TOO_SMALL) 129 return (status); 130 131 if ((handles = malloc(bufsz)) == NULL) 132 return (ENOMEM); 133 134 *nhandles = (int)(bufsz/sizeof (EFI_HANDLE)); 135 /* 136 * get handle array 137 */ 138 status = BS->LocateHandle(ByProtocol, &serial, NULL, &bufsz, handles); 139 if (EFI_ERROR(status)) { 140 free(handles); 141 *nhandles = 0; 142 } else 143 *handlep = handles; 144 return (status); 145 } 146 147 static void 148 comc_probe(struct console *cp) 149 { 150 EFI_STATUS status; 151 struct serial *port; 152 char name[20]; 153 char value[20]; 154 char *cons, *env; 155 EFI_HANDLE *handles = NULL; /* array of handles */ 156 int nhandles = 0; /* number of handles in array */ 157 158 /* are we already set up? */ 159 if (cp->c_private != NULL) 160 return; 161 162 /* make sure the handles are available */ 163 status = efi_serial_init(&handles, &nhandles); 164 165 cp->c_private = malloc(sizeof (struct serial)); 166 port = cp->c_private; 167 port->baudrate = COMSPEED; 168 169 if (strcmp(cp->c_name, "ttya") == 0) 170 port->ioaddr = 0; 171 else if (strcmp(cp->c_name, "ttyb") == 0) 172 port->ioaddr = 1; 173 else if (strcmp(cp->c_name, "ttyc") == 0) 174 port->ioaddr = 2; 175 else if (strcmp(cp->c_name, "ttyd") == 0) 176 port->ioaddr = 3; 177 178 if (port->ioaddr >= nhandles) 179 port->ioaddr = -1; /* invalid port */ 180 181 port->databits = 8; /* 8,n,1 */ 182 port->parity = NoParity; /* 8,n,1 */ 183 port->stopbits = OneStopBit; /* 8,n,1 */ 184 port->ignore_cd = 1; /* ignore cd */ 185 port->rtsdtr_off = 0; /* rts-dtr is on */ 186 port->sio = NULL; 187 188 if (port->ioaddr != -1) { 189 status = BS->OpenProtocol(handles[port->ioaddr], 190 &serial, (void**)&port->sio, IH, NULL, 191 EFI_OPEN_PROTOCOL_GET_PROTOCOL); 192 193 if (EFI_ERROR(status)) 194 port->ioaddr = -1; /* invalid port */ 195 } 196 if (handles != NULL) 197 free(handles); 198 199 snprintf(name, sizeof (name), "%s-mode", cp->c_name); 200 env = getenv(name); 201 202 if (env != NULL) 203 (void) comc_parse_mode(port, env); 204 205 env = comc_asprint_mode(port); 206 207 if (env != NULL) { 208 unsetenv(name); 209 env_setenv(name, EV_VOLATILE, env, comc_mode_set, env_nounset); 210 free(env); 211 } 212 213 snprintf(name, sizeof (name), "%s-ignore-cd", cp->c_name); 214 env = getenv(name); 215 if (env != NULL) { 216 if (strcmp(env, "true") == 0) 217 port->ignore_cd = 1; 218 else if (strcmp(env, "false") == 0) 219 port->ignore_cd = 0; 220 } 221 222 snprintf(value, sizeof (value), "%s", 223 port->ignore_cd? "true" : "false"); 224 unsetenv(name); 225 env_setenv(name, EV_VOLATILE, value, comc_cd_set, env_nounset); 226 227 snprintf(name, sizeof (name), "%s-rts-dtr-off", cp->c_name); 228 env = getenv(name); 229 if (env != NULL) { 230 if (strcmp(env, "true") == 0) 231 port->rtsdtr_off = 1; 232 else if (strcmp(env, "false") == 0) 233 port->rtsdtr_off = 0; 234 } 235 236 snprintf(value, sizeof (value), "%s", 237 port->rtsdtr_off? "true" : "false"); 238 unsetenv(name); 239 env_setenv(name, EV_VOLATILE, value, comc_rtsdtr_set, env_nounset); 240 comc_setup(cp); 241 } 242 243 static int 244 comc_init(struct console *cp, int arg __attribute((unused))) 245 { 246 247 comc_setup(cp); 248 249 if ((cp->c_flags & (C_PRESENTIN | C_PRESENTOUT)) == 250 (C_PRESENTIN | C_PRESENTOUT)) 251 return (CMD_OK); 252 return (CMD_ERROR); 253 } 254 255 static void 256 comc_putchar(struct console *cp, int c) 257 { 258 int wait; 259 EFI_STATUS status; 260 UINTN bufsz = 1; 261 char cb = c; 262 struct serial *sp = cp->c_private; 263 264 if (sp->sio == NULL) 265 return; 266 267 for (wait = COMC_TXWAIT; wait > 0; wait--) { 268 status = sp->sio->Write(sp->sio, &bufsz, &cb); 269 if (status != EFI_TIMEOUT) 270 break; 271 } 272 } 273 274 static int 275 comc_getchar(struct console *cp) 276 { 277 EFI_STATUS status; 278 UINTN bufsz = 1; 279 char c; 280 struct serial *sp = cp->c_private; 281 282 if (sp->sio == NULL || !comc_ischar(cp)) 283 return (-1); 284 285 status = sp->sio->Read(sp->sio, &bufsz, &c); 286 if (EFI_ERROR(status) || bufsz == 0) 287 return (-1); 288 289 return (c); 290 } 291 292 static int 293 comc_ischar(struct console *cp) 294 { 295 EFI_STATUS status; 296 uint32_t control; 297 struct serial *sp = cp->c_private; 298 299 if (sp->sio == NULL) 300 return (0); 301 302 status = sp->sio->GetControl(sp->sio, &control); 303 if (EFI_ERROR(status)) 304 return (0); 305 306 return (!(status & EFI_SERIAL_INPUT_BUFFER_EMPTY)); 307 } 308 309 static char * 310 comc_asprint_mode(struct serial *sp) 311 { 312 char par = 'n', *buf; 313 int stop = 1; 314 315 if (sp == NULL) 316 return (NULL); 317 318 switch (sp->parity) { 319 case NoParity: par = 'n'; 320 break; 321 case EvenParity: par = 'e'; 322 break; 323 case OddParity: par = 'o'; 324 break; 325 } 326 switch (sp->stopbits) { 327 case OneStopBit: stop = 1; 328 break; 329 case TwoStopBits: stop = 2; 330 break; 331 } 332 333 asprintf(&buf, "%ju,%d,%c,%d,-", sp->baudrate, sp->databits, par, stop); 334 return (buf); 335 } 336 337 static int 338 comc_parse_mode(struct serial *sp, const char *value) 339 { 340 unsigned long n; 341 uint64_t baudrate; 342 uint8_t databits = 8; 343 int parity = NoParity; 344 int stopbits = OneStopBit; 345 char *ep; 346 347 if (value == NULL || *value == '\0') 348 return (CMD_ERROR); 349 350 errno = 0; 351 n = strtoul(value, &ep, 10); 352 if (errno != 0 || *ep != ',') 353 return (CMD_ERROR); 354 baudrate = n; 355 356 ep++; 357 n = strtoul(ep, &ep, 10); 358 if (errno != 0 || *ep != ',') 359 return (CMD_ERROR); 360 361 switch (n) { 362 case 7: databits = 7; 363 break; 364 case 8: databits = 8; 365 break; 366 default: 367 return (CMD_ERROR); 368 } 369 370 ep++; 371 switch (*ep++) { 372 case 'n': parity = NoParity; 373 break; 374 case 'e': parity = EvenParity; 375 break; 376 case 'o': parity = OddParity; 377 break; 378 default: 379 return (CMD_ERROR); 380 } 381 382 if (*ep == ',') 383 ep++; 384 else 385 return (CMD_ERROR); 386 387 switch (*ep++) { 388 case '1': stopbits = OneStopBit; 389 break; 390 case '2': stopbits = TwoStopBits; 391 break; 392 default: 393 return (CMD_ERROR); 394 } 395 396 /* handshake is ignored, but we check syntax anyhow */ 397 if (*ep == ',') 398 ep++; 399 else 400 return (CMD_ERROR); 401 402 switch (*ep++) { 403 case '-': 404 case 'h': 405 case 's': 406 break; 407 default: 408 return (CMD_ERROR); 409 } 410 411 if (*ep != '\0') 412 return (CMD_ERROR); 413 414 sp->baudrate = baudrate; 415 sp->databits = databits; 416 sp->parity = parity; 417 sp->stopbits = stopbits; 418 return (CMD_OK); 419 } 420 421 static struct console * 422 get_console(char *name) 423 { 424 struct console *cp = NULL; 425 426 switch (name[3]) { 427 case 'a': cp = &ttya; 428 break; 429 case 'b': cp = &ttyb; 430 break; 431 case 'c': cp = &ttyc; 432 break; 433 case 'd': cp = &ttyd; 434 break; 435 } 436 return (cp); 437 } 438 439 static int 440 comc_mode_set(struct env_var *ev, int flags, const void *value) 441 { 442 struct console *cp; 443 444 if (value == NULL) 445 return (CMD_ERROR); 446 447 if ((cp = get_console(ev->ev_name)) == NULL) 448 return (CMD_ERROR); 449 450 if (comc_parse_mode(cp->c_private, value) == CMD_ERROR) 451 return (CMD_ERROR); 452 453 comc_setup(cp); 454 env_setenv(ev->ev_name, flags | EV_NOHOOK, value, NULL, NULL); 455 456 return (CMD_OK); 457 } 458 459 static int 460 comc_cd_set(struct env_var *ev, int flags, const void *value) 461 { 462 struct console *cp; 463 struct serial *sp; 464 465 if (value == NULL) 466 return (CMD_ERROR); 467 468 if ((cp = get_console(ev->ev_name)) == NULL) 469 return (CMD_ERROR); 470 471 sp = cp->c_private; 472 if (strcmp(value, "true") == 0) 473 sp->ignore_cd = 1; 474 else if (strcmp(value, "false") == 0) 475 sp->ignore_cd = 0; 476 else 477 return (CMD_ERROR); 478 479 comc_setup(cp); 480 env_setenv(ev->ev_name, flags | EV_NOHOOK, value, NULL, NULL); 481 482 return (CMD_OK); 483 } 484 485 static int 486 comc_rtsdtr_set(struct env_var *ev, int flags, const void *value) 487 { 488 struct console *cp; 489 struct serial *sp; 490 491 if (value == NULL) 492 return (CMD_ERROR); 493 494 if ((cp = get_console(ev->ev_name)) == NULL) 495 return (CMD_ERROR); 496 497 sp = cp->c_private; 498 if (strcmp(value, "true") == 0) 499 sp->rtsdtr_off = 1; 500 else if (strcmp(value, "false") == 0) 501 sp->rtsdtr_off = 0; 502 else 503 return (CMD_ERROR); 504 505 comc_setup(cp); 506 env_setenv(ev->ev_name, flags | EV_NOHOOK, value, NULL, NULL); 507 508 return (CMD_OK); 509 } 510 511 static void 512 comc_setup(struct console *cp) 513 { 514 EFI_STATUS status; 515 UINT32 control; 516 struct serial *sp = cp->c_private; 517 518 if ((cp->c_flags & (C_ACTIVEIN | C_ACTIVEOUT)) == 0) 519 return; 520 521 /* port is not usable */ 522 if (sp->sio == NULL) { 523 cp->c_flags &= ~(C_PRESENTIN | C_PRESENTOUT); 524 return; 525 } 526 527 cp->c_flags |= (C_PRESENTIN | C_PRESENTOUT); 528 status = sp->sio->Reset(sp->sio); 529 if (EFI_ERROR(status)) { 530 cp->c_flags &= ~(C_PRESENTIN | C_PRESENTOUT); 531 } 532 533 status = sp->sio->SetAttributes(sp->sio, sp->baudrate, 0, 0, sp->parity, 534 sp->databits, sp->stopbits); 535 if (EFI_ERROR(status)) { 536 cp->c_flags &= ~(C_PRESENTIN | C_PRESENTOUT); 537 } 538 539 if (sp->rtsdtr_off) { 540 status = sp->sio->GetControl(sp->sio, &control); 541 if (EFI_ERROR(status)) { 542 cp->c_flags &= ~(C_PRESENTIN | C_PRESENTOUT); 543 } 544 control &= ~(EFI_SERIAL_REQUEST_TO_SEND | 545 EFI_SERIAL_DATA_TERMINAL_READY); 546 status = sp->sio->SetControl(sp->sio, control); 547 if (EFI_ERROR(status)) { 548 cp->c_flags &= ~(C_PRESENTIN | C_PRESENTOUT); 549 } 550 } 551 } 552