1 /* $OpenBSD: ns8250.c,v 1.30 2021/03/29 13:09:41 dv Exp $ */ 2 /* 3 * Copyright (c) 2016 Mike Larkin <mlarkin@openbsd.org> 4 * 5 * Permission to use, copy, modify, and distribute this software for any 6 * purpose with or without fee is hereby granted, provided that the above 7 * copyright notice and this permission notice appear in all copies. 8 * 9 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 10 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 11 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 12 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 13 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 14 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 15 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 16 */ 17 18 #include <sys/types.h> 19 #include <sys/ttycom.h> 20 21 #include <dev/ic/comreg.h> 22 23 #include <machine/vmmvar.h> 24 25 #include <errno.h> 26 #include <event.h> 27 #include <pthread.h> 28 #include <string.h> 29 #include <unistd.h> 30 31 #include "ns8250.h" 32 #include "proc.h" 33 #include "vmd.h" 34 #include "vmm.h" 35 #include "atomicio.h" 36 37 extern char *__progname; 38 struct ns8250_dev com1_dev; 39 40 /* Flags to distinguish calling threads to com_rcv */ 41 #define NS8250_DEV_THREAD 0 42 #define NS8250_CPU_THREAD 1 43 44 static struct vm_dev_pipe dev_pipe; 45 46 static void com_rcv_event(int, short, void *); 47 static void com_rcv(struct ns8250_dev *, uint32_t, uint32_t, int); 48 49 /* 50 * ns8250_pipe_dispatch 51 * 52 * Reads a message off the pipe, expecting a reguest to reset events after a 53 * zero-byte read from the com device. 54 */ 55 static void 56 ns8250_pipe_dispatch(int fd, short event, void *arg) 57 { 58 enum pipe_msg_type msg; 59 60 msg = vm_pipe_recv(&dev_pipe); 61 switch(msg) { 62 case NS8250_ZERO_READ: 63 log_debug("%s: resetting events after zero byte read", __func__); 64 event_del(&com1_dev.event); 65 event_add(&com1_dev.wake, NULL); 66 break; 67 case NS8250_RATELIMIT: 68 evtimer_add(&com1_dev.rate, &com1_dev.rate_tv); 69 break; 70 default: 71 fatalx("%s: unexpected pipe message %d", __func__, msg); 72 } 73 } 74 75 /* 76 * ratelimit 77 * 78 * Timeout callback function used when we have to slow down the output rate 79 * from the emulated serial port. 80 * 81 * Parameters: 82 * fd: unused 83 * type: unused 84 * arg: unused 85 */ 86 static void 87 ratelimit(int fd, short type, void *arg) 88 { 89 /* Set TXRDY and clear "no pending interrupt" */ 90 mutex_lock(&com1_dev.mutex); 91 com1_dev.regs.iir |= IIR_TXRDY; 92 com1_dev.regs.iir &= ~IIR_NOPEND; 93 94 vcpu_assert_pic_irq(com1_dev.vmid, 0, com1_dev.irq); 95 vcpu_deassert_pic_irq(com1_dev.vmid, 0, com1_dev.irq); 96 mutex_unlock(&com1_dev.mutex); 97 } 98 99 void 100 ns8250_init(int fd, uint32_t vmid) 101 { 102 int ret; 103 104 memset(&com1_dev, 0, sizeof(com1_dev)); 105 ret = pthread_mutex_init(&com1_dev.mutex, NULL); 106 if (ret) { 107 errno = ret; 108 fatal("could not initialize com1 mutex"); 109 } 110 111 com1_dev.fd = fd; 112 com1_dev.irq = 4; 113 com1_dev.portid = NS8250_COM1; 114 com1_dev.rcv_pending = 0; 115 com1_dev.vmid = vmid; 116 com1_dev.byte_out = 0; 117 com1_dev.regs.divlo = 1; 118 com1_dev.baudrate = 115200; 119 120 /* 121 * Our serial port is essentially instantaneous, with infinite 122 * baudrate capability. To adjust for the selected baudrate, 123 * we calculate how many characters could be transmitted in a 10ms 124 * period (pause_ct) and then delay 10ms after each pause_ct sized 125 * group of characters have been transmitted. Since it takes nearly 126 * zero time to send the actual characters, the total amount of time 127 * spent is roughly equal to what it would be on real hardware. 128 * 129 * To make things simple, we don't adjust for different sized bytes 130 * (and parity, stop bits, etc) and simply assume each character 131 * output is 8 bits. 132 */ 133 com1_dev.pause_ct = (com1_dev.baudrate / 8) / 1000 * 10; 134 135 event_set(&com1_dev.event, com1_dev.fd, EV_READ | EV_PERSIST, 136 com_rcv_event, (void *)(intptr_t)vmid); 137 138 /* 139 * Whenever fd is writable implies that the pty slave is connected. 140 * Until then, avoid waiting for read events since EOF would constantly 141 * be reached. 142 */ 143 event_set(&com1_dev.wake, com1_dev.fd, EV_WRITE, 144 com_rcv_event, (void *)(intptr_t)vmid); 145 event_add(&com1_dev.wake, NULL); 146 147 /* Rate limiter for simulating baud rate */ 148 timerclear(&com1_dev.rate_tv); 149 com1_dev.rate_tv.tv_usec = 10000; 150 evtimer_set(&com1_dev.rate, ratelimit, NULL); 151 152 vm_pipe_init(&dev_pipe, ns8250_pipe_dispatch); 153 event_add(&dev_pipe.read_ev, NULL); 154 } 155 156 static void 157 com_rcv_event(int fd, short kind, void *arg) 158 { 159 mutex_lock(&com1_dev.mutex); 160 161 if (kind == EV_WRITE) { 162 event_add(&com1_dev.event, NULL); 163 mutex_unlock(&com1_dev.mutex); 164 return; 165 } 166 167 /* 168 * We already have other data pending to be received. The data that 169 * has become available now will be moved to the com port later by 170 * the vcpu. 171 */ 172 if (!com1_dev.rcv_pending) { 173 if (com1_dev.regs.lsr & LSR_RXRDY) 174 com1_dev.rcv_pending = 1; 175 else 176 com_rcv(&com1_dev, (uintptr_t)arg, 0, NS8250_DEV_THREAD); 177 } 178 179 /* If pending interrupt, inject */ 180 if ((com1_dev.regs.iir & IIR_NOPEND) == 0) { 181 /* XXX: vcpu_id */ 182 vcpu_assert_pic_irq((uintptr_t)arg, 0, com1_dev.irq); 183 vcpu_deassert_pic_irq((uintptr_t)arg, 0, com1_dev.irq); 184 } 185 186 mutex_unlock(&com1_dev.mutex); 187 } 188 189 /* 190 * com_rcv_handle_break 191 * 192 * Set/clear break detected condition based on received TIOCUCNTL_{S,C}BRK. 193 */ 194 static int 195 com_rcv_handle_break(struct ns8250_dev *com, uint8_t cmd) 196 { 197 switch (cmd) { 198 case 0: /* DATA */ 199 return 0; 200 case TIOCUCNTL_SBRK: 201 com->regs.lsr |= LSR_BI; 202 break; 203 case TIOCUCNTL_CBRK: 204 com->regs.lsr &= ~LSR_BI; 205 break; 206 default: 207 log_warnx("unexpected UCNTL ioctl: %d", cmd); 208 } 209 210 return 1; 211 } 212 213 /* 214 * com_rcv 215 * 216 * Move received byte into com data register. 217 * Must be called with the mutex of the com device acquired 218 */ 219 static void 220 com_rcv(struct ns8250_dev *com, uint32_t vm_id, uint32_t vcpu_id, int thread) 221 { 222 char buf[2]; 223 ssize_t sz; 224 225 /* 226 * Is there a new character available on com1? 227 * If so, consume the character, buffer it into the com1 data register 228 * assert IRQ4, and set the line status register RXRDY bit. 229 */ 230 sz = read(com->fd, buf, sizeof(buf)); 231 if (sz == -1) { 232 /* 233 * If we get EAGAIN, we'll retry and get the character later. 234 * This error can happen when typing two characters at once 235 * at the keyboard, for example. 236 */ 237 if (errno != EAGAIN) 238 log_warn("unexpected read error on com device"); 239 } else if (sz == 0) { 240 if (thread == NS8250_DEV_THREAD) { 241 event_del(&com->event); 242 event_add(&com->wake, NULL); 243 } else { 244 /* Called by vcpu thread, use pipe for event changes */ 245 vm_pipe_send(&dev_pipe, NS8250_ZERO_READ); 246 } 247 return; 248 } else if (sz != 1 && sz != 2) 249 log_warnx("unexpected read return value %zd on com device", sz); 250 else { 251 if (com_rcv_handle_break(com, buf[0])) 252 buf[1] = 0; 253 254 com->regs.lsr |= LSR_RXRDY; 255 com->regs.data = buf[1]; 256 257 if (com->regs.ier & IER_ERXRDY) { 258 com->regs.iir |= IIR_RXRDY; 259 com->regs.iir &= ~IIR_NOPEND; 260 } 261 } 262 263 com->rcv_pending = fd_hasdata(com->fd); 264 } 265 266 /* 267 * vcpu_process_com_data 268 * 269 * Emulate in/out instructions to the com1 (ns8250) UART data register 270 * 271 * Parameters: 272 * vei: vm exit information from vmm(4) containing information on the in/out 273 * instruction being performed 274 * 275 * Return value: 276 * interrupt to inject, or 0xFF if nothing to inject 277 */ 278 uint8_t 279 vcpu_process_com_data(struct vm_exit *vei, uint32_t vm_id, uint32_t vcpu_id) 280 { 281 /* 282 * vei_dir == VEI_DIR_OUT : out instruction 283 * 284 * The guest wrote to the data register. Since we are emulating a 285 * no-fifo chip, write the character immediately to the pty and 286 * assert TXRDY in IIR (if the guest has requested TXRDY interrupt 287 * reporting) 288 */ 289 if (vei->vei.vei_dir == VEI_DIR_OUT) { 290 if (com1_dev.regs.lcr & LCR_DLAB) { 291 com1_dev.regs.divlo = vei->vei.vei_data; 292 return 0xFF; 293 } 294 295 write(com1_dev.fd, &vei->vei.vei_data, 1); 296 com1_dev.byte_out++; 297 298 if (com1_dev.regs.ier & IER_ETXRDY) { 299 /* Limit output rate if needed */ 300 if (com1_dev.pause_ct > 0 && 301 com1_dev.byte_out % com1_dev.pause_ct == 0) { 302 vm_pipe_send(&dev_pipe, NS8250_RATELIMIT); 303 } else { 304 /* Set TXRDY and clear "no pending interrupt" */ 305 com1_dev.regs.iir |= IIR_TXRDY; 306 com1_dev.regs.iir &= ~IIR_NOPEND; 307 } 308 } 309 } else { 310 if (com1_dev.regs.lcr & LCR_DLAB) { 311 set_return_data(vei, com1_dev.regs.divlo); 312 return 0xFF; 313 } 314 /* 315 * vei_dir == VEI_DIR_IN : in instruction 316 * 317 * The guest read from the data register. Check to see if 318 * there is data available (RXRDY) and if so, consume the 319 * input data and return to the guest. Also clear the 320 * interrupt info register regardless. 321 */ 322 if (com1_dev.regs.lsr & LSR_RXRDY) { 323 set_return_data(vei, com1_dev.regs.data); 324 com1_dev.regs.data = 0x0; 325 com1_dev.regs.lsr &= ~LSR_RXRDY; 326 } else { 327 set_return_data(vei, com1_dev.regs.data); 328 log_warnx("%s: guest reading com1 when not ready", 329 __func__); 330 } 331 332 /* Reading the data register always clears RXRDY from IIR */ 333 com1_dev.regs.iir &= ~IIR_RXRDY; 334 335 /* 336 * Clear "interrupt pending" by setting IIR low bit to 1 337 * if no interrupt are pending 338 */ 339 if (com1_dev.regs.iir == 0x0) 340 com1_dev.regs.iir = 0x1; 341 342 if (com1_dev.rcv_pending) 343 com_rcv(&com1_dev, vm_id, vcpu_id, NS8250_CPU_THREAD); 344 } 345 346 /* If pending interrupt, make sure it gets injected */ 347 if ((com1_dev.regs.iir & IIR_NOPEND) == 0) 348 return (com1_dev.irq); 349 350 return (0xFF); 351 } 352 353 /* 354 * vcpu_process_com_lcr 355 * 356 * Emulate in/out instructions to the com1 (ns8250) UART line control register 357 * 358 * Paramters: 359 * vei: vm exit information from vmm(4) containing information on the in/out 360 * instruction being performed 361 */ 362 void 363 vcpu_process_com_lcr(struct vm_exit *vei) 364 { 365 uint8_t data = (uint8_t)vei->vei.vei_data; 366 uint16_t divisor; 367 368 /* 369 * vei_dir == VEI_DIR_OUT : out instruction 370 * 371 * Write content to line control register 372 */ 373 if (vei->vei.vei_dir == VEI_DIR_OUT) { 374 if (com1_dev.regs.lcr & LCR_DLAB) { 375 if (!(data & LCR_DLAB)) { 376 if (com1_dev.regs.divlo == 0 && 377 com1_dev.regs.divhi == 0) { 378 log_warnx("%s: ignoring invalid " 379 "baudrate", __func__); 380 } else { 381 divisor = com1_dev.regs.divlo | 382 com1_dev.regs.divhi << 8; 383 com1_dev.baudrate = 115200 / divisor; 384 com1_dev.pause_ct = 385 (com1_dev.baudrate / 8) / 1000 * 10; 386 } 387 388 log_debug("%s: set baudrate = %d", __func__, 389 com1_dev.baudrate); 390 } 391 } 392 com1_dev.regs.lcr = (uint8_t)vei->vei.vei_data; 393 } else { 394 /* 395 * vei_dir == VEI_DIR_IN : in instruction 396 * 397 * Read line control register 398 */ 399 set_return_data(vei, com1_dev.regs.lcr); 400 } 401 } 402 403 /* 404 * vcpu_process_com_iir 405 * 406 * Emulate in/out instructions to the com1 (ns8250) UART interrupt information 407 * register. Note that writes to this register actually are to a different 408 * register, the FCR (FIFO control register) that we don't emulate but still 409 * consume the data provided. 410 * 411 * Parameters: 412 * vei: vm exit information from vmm(4) containing information on the in/out 413 * instruction being performed 414 */ 415 void 416 vcpu_process_com_iir(struct vm_exit *vei) 417 { 418 /* 419 * vei_dir == VEI_DIR_OUT : out instruction 420 * 421 * Write to FCR 422 */ 423 if (vei->vei.vei_dir == VEI_DIR_OUT) { 424 com1_dev.regs.fcr = vei->vei.vei_data; 425 } else { 426 /* 427 * vei_dir == VEI_DIR_IN : in instruction 428 * 429 * Read IIR. Reading the IIR resets the TXRDY bit in the IIR 430 * after the data is read. 431 */ 432 set_return_data(vei, com1_dev.regs.iir); 433 com1_dev.regs.iir &= ~IIR_TXRDY; 434 435 /* 436 * Clear "interrupt pending" by setting IIR low bit to 1 437 * if no interrupts are pending 438 */ 439 if (com1_dev.regs.iir == 0x0) 440 com1_dev.regs.iir = 0x1; 441 } 442 } 443 444 /* 445 * vcpu_process_com_mcr 446 * 447 * Emulate in/out instructions to the com1 (ns8250) UART modem control 448 * register. 449 * 450 * Parameters: 451 * vei: vm exit information from vmm(4) containing information on the in/out 452 * instruction being performed 453 */ 454 void 455 vcpu_process_com_mcr(struct vm_exit *vei) 456 { 457 /* 458 * vei_dir == VEI_DIR_OUT : out instruction 459 * 460 * Write to MCR 461 */ 462 if (vei->vei.vei_dir == VEI_DIR_OUT) { 463 com1_dev.regs.mcr = vei->vei.vei_data; 464 } else { 465 /* 466 * vei_dir == VEI_DIR_IN : in instruction 467 * 468 * Read from MCR 469 */ 470 set_return_data(vei, com1_dev.regs.mcr); 471 } 472 } 473 474 /* 475 * vcpu_process_com_lsr 476 * 477 * Emulate in/out instructions to the com1 (ns8250) UART line status register. 478 * 479 * Parameters: 480 * vei: vm exit information from vmm(4) containing information on the in/out 481 * instruction being performed 482 */ 483 void 484 vcpu_process_com_lsr(struct vm_exit *vei) 485 { 486 /* 487 * vei_dir == VEI_DIR_OUT : out instruction 488 * 489 * Write to LSR. This is an illegal operation, so we just log it and 490 * continue. 491 */ 492 if (vei->vei.vei_dir == VEI_DIR_OUT) { 493 log_warnx("%s: LSR UART write 0x%x unsupported", 494 __progname, vei->vei.vei_data); 495 } else { 496 /* 497 * vei_dir == VEI_DIR_IN : in instruction 498 * 499 * Read from LSR. We always report TXRDY and TSRE since we 500 * can process output characters immediately (at any time). 501 */ 502 set_return_data(vei, com1_dev.regs.lsr | LSR_TSRE | LSR_TXRDY); 503 } 504 } 505 506 /* 507 * vcpu_process_com_msr 508 * 509 * Emulate in/out instructions to the com1 (ns8250) UART modem status register. 510 * 511 * Parameters: 512 * vei: vm exit information from vmm(4) containing information on the in/out 513 * instruction being performed 514 */ 515 void 516 vcpu_process_com_msr(struct vm_exit *vei) 517 { 518 /* 519 * vei_dir == VEI_DIR_OUT : out instruction 520 * 521 * Write to MSR. This is an illegal operation, so we just log it and 522 * continue. 523 */ 524 if (vei->vei.vei_dir == VEI_DIR_OUT) { 525 log_warnx("%s: MSR UART write 0x%x unsupported", 526 __progname, vei->vei.vei_data); 527 } else { 528 /* 529 * vei_dir == VEI_DIR_IN : in instruction 530 * 531 * Read from MSR. We always report DCD, DSR, and CTS. 532 */ 533 set_return_data(vei, com1_dev.regs.lsr | MSR_DCD | MSR_DSR | 534 MSR_CTS); 535 } 536 } 537 538 /* 539 * vcpu_process_com_scr 540 * 541 * Emulate in/out instructions to the com1 (ns8250) UART scratch register. 542 * 543 * Parameters: 544 * vei: vm exit information from vmm(4) containing information on the in/out 545 * instruction being performed 546 */ 547 void 548 vcpu_process_com_scr(struct vm_exit *vei) 549 { 550 /* 551 * vei_dir == VEI_DIR_OUT : out instruction 552 * 553 * The 8250 does not have a scratch register. 554 */ 555 if (vei->vei.vei_dir == VEI_DIR_OUT) { 556 com1_dev.regs.scr = 0xFF; 557 } else { 558 /* 559 * vei_dir == VEI_DIR_IN : in instruction 560 * 561 * Read from SCR 562 */ 563 set_return_data(vei, com1_dev.regs.scr); 564 } 565 } 566 567 /* 568 * vcpu_process_com_ier 569 * 570 * Emulate in/out instructions to the com1 (ns8250) UART interrupt enable 571 * register. 572 * 573 * Parameters: 574 * vei: vm exit information from vmm(4) containing information on the in/out 575 * instruction being performed 576 */ 577 void 578 vcpu_process_com_ier(struct vm_exit *vei) 579 { 580 /* 581 * vei_dir == VEI_DIR_OUT : out instruction 582 * 583 * Write to IER 584 */ 585 if (vei->vei.vei_dir == VEI_DIR_OUT) { 586 if (com1_dev.regs.lcr & LCR_DLAB) { 587 com1_dev.regs.divhi = vei->vei.vei_data; 588 return; 589 } 590 com1_dev.regs.ier = vei->vei.vei_data; 591 if (com1_dev.regs.ier & IER_ETXRDY) 592 com1_dev.regs.iir |= IIR_TXRDY; 593 } else { 594 if (com1_dev.regs.lcr & LCR_DLAB) { 595 set_return_data(vei, com1_dev.regs.divhi); 596 return; 597 } 598 /* 599 * vei_dir == VEI_DIR_IN : in instruction 600 * 601 * Read from IER 602 */ 603 set_return_data(vei, com1_dev.regs.ier); 604 } 605 } 606 607 /* 608 * vcpu_exit_com 609 * 610 * Process com1 (ns8250) UART exits. vmd handles most basic 8250 611 * features 612 * 613 * Parameters: 614 * vrp: vcpu run parameters containing guest state for this exit 615 * 616 * Return value: 617 * Interrupt to inject to the guest VM, or 0xFF if no interrupt should 618 * be injected. 619 */ 620 uint8_t 621 vcpu_exit_com(struct vm_run_params *vrp) 622 { 623 uint8_t intr = 0xFF; 624 struct vm_exit *vei = vrp->vrp_exit; 625 626 mutex_lock(&com1_dev.mutex); 627 628 switch (vei->vei.vei_port) { 629 case COM1_LCR: 630 vcpu_process_com_lcr(vei); 631 break; 632 case COM1_IER: 633 vcpu_process_com_ier(vei); 634 break; 635 case COM1_IIR: 636 vcpu_process_com_iir(vei); 637 break; 638 case COM1_MCR: 639 vcpu_process_com_mcr(vei); 640 break; 641 case COM1_LSR: 642 vcpu_process_com_lsr(vei); 643 break; 644 case COM1_MSR: 645 vcpu_process_com_msr(vei); 646 break; 647 case COM1_SCR: 648 vcpu_process_com_scr(vei); 649 break; 650 case COM1_DATA: 651 intr = vcpu_process_com_data(vei, vrp->vrp_vm_id, 652 vrp->vrp_vcpu_id); 653 break; 654 } 655 656 mutex_unlock(&com1_dev.mutex); 657 658 return (intr); 659 } 660 661 int 662 ns8250_dump(int fd) 663 { 664 log_debug("%s: sending UART", __func__); 665 if (atomicio(vwrite, fd, &com1_dev.regs, 666 sizeof(com1_dev.regs)) != sizeof(com1_dev.regs)) { 667 log_warnx("%s: error writing UART to fd", __func__); 668 return (-1); 669 } 670 return (0); 671 } 672 673 int 674 ns8250_restore(int fd, int con_fd, uint32_t vmid) 675 { 676 int ret; 677 log_debug("%s: receiving UART", __func__); 678 if (atomicio(read, fd, &com1_dev.regs, 679 sizeof(com1_dev.regs)) != sizeof(com1_dev.regs)) { 680 log_warnx("%s: error reading UART from fd", __func__); 681 return (-1); 682 } 683 684 ret = pthread_mutex_init(&com1_dev.mutex, NULL); 685 if (ret) { 686 errno = ret; 687 fatal("could not initialize com1 mutex"); 688 } 689 com1_dev.fd = con_fd; 690 com1_dev.irq = 4; 691 com1_dev.portid = NS8250_COM1; 692 com1_dev.rcv_pending = 0; 693 com1_dev.vmid = vmid; 694 com1_dev.byte_out = 0; 695 com1_dev.regs.divlo = 1; 696 com1_dev.baudrate = 115200; 697 com1_dev.pause_ct = (com1_dev.baudrate / 8) / 1000 * 10; 698 699 event_set(&com1_dev.event, com1_dev.fd, EV_READ | EV_PERSIST, 700 com_rcv_event, (void *)(intptr_t)vmid); 701 702 event_set(&com1_dev.wake, com1_dev.fd, EV_WRITE, 703 com_rcv_event, (void *)(intptr_t)vmid); 704 705 timerclear(&com1_dev.rate_tv); 706 com1_dev.rate_tv.tv_usec = 10000; 707 evtimer_set(&com1_dev.rate, ratelimit, NULL); 708 709 vm_pipe_init(&dev_pipe, ns8250_pipe_dispatch); 710 711 return (0); 712 } 713 714 void 715 ns8250_stop() 716 { 717 if(event_del(&com1_dev.event)) 718 log_warn("could not delete ns8250 event handler"); 719 event_del(&dev_pipe.read_ev); 720 evtimer_del(&com1_dev.rate); 721 } 722 723 void 724 ns8250_start() 725 { 726 event_add(&com1_dev.event, NULL); 727 event_add(&com1_dev.wake, NULL); 728 event_add(&dev_pipe.read_ev, NULL); 729 evtimer_add(&com1_dev.rate, &com1_dev.rate_tv); 730 } 731