📄 serial.c
字号:
if ( !(s->lsr & UART_LSR_THRE) ) qemu_mod_timer(s->transmit_timer, s->last_xmit_ts + s->char_transmit_time ); if ( s->lsr & UART_LSR_THRE ) { s->lsr |= UART_LSR_TEMT; s->thr_ipending = 1; serial_update_irq(s); }}static void serial_ioport_write(void *opaque, uint32_t addr, uint32_t val){ SerialState *s = opaque; addr &= 7;#ifdef DEBUG_SERIAL printf("serial: write addr=0x%02x val=0x%02x\n", addr, val);#endif switch(addr) { default: case 0: if (s->lcr & UART_LCR_DLAB) { s->divider = (s->divider & 0xff00) | val; serial_update_parameters(s); } else { s->thr = (uint8_t) val; if(s->fcr & UART_FCR_FE) { fifo_put(s, XMIT_FIFO, s->thr); s->thr_ipending = 0; s->lsr &= ~UART_LSR_TEMT; s->lsr &= ~UART_LSR_THRE; serial_update_irq(s); } else { s->thr_ipending = 0; s->lsr &= ~UART_LSR_THRE; serial_update_irq(s); } serial_xmit(s); } break; case 1: if (s->lcr & UART_LCR_DLAB) { s->divider = (s->divider & 0x00ff) | (val << 8); serial_update_parameters(s); } else { s->ier = val & 0x0f; /* If the backend device is a real serial port, turn polling of the modem status lines on physical port on or off depending on UART_IER_MSI state */ if ( s->poll_msl >= 0 ) { if ( s->ier & UART_IER_MSI ) { s->poll_msl = 1; serial_update_msl(s); } else { qemu_del_timer(s->modem_status_poll); s->poll_msl = 0; } } if (s->lsr & UART_LSR_THRE) { s->thr_ipending = 1; serial_update_irq(s); } } break; case 2: val = val & 0xFF; if ( s->fcr == val) break; /* Did the enable/disable flag change? If so, make sure FIFOs get flushed */ if ( (val ^ s->fcr) & UART_FCR_FE ) val |= UART_FCR_XFR | UART_FCR_RFR; /* FIFO clear */ if ( val & UART_FCR_RFR ) { qemu_del_timer(s->fifo_timeout_timer); s->timeout_ipending=0; fifo_clear(s,RECV_FIFO); } if ( val & UART_FCR_XFR ) { fifo_clear(s,XMIT_FIFO); } if ( val & UART_FCR_FE ) { s->iir |= UART_IIR_FE; /* Set RECV_FIFO trigger Level */ switch ( val & 0xC0 ) { case UART_FCR_ITL_1: s->recv_fifo.itl = 1; break; case UART_FCR_ITL_2: s->recv_fifo.itl = 4; break; case UART_FCR_ITL_3: s->recv_fifo.itl = 8; break; case UART_FCR_ITL_4: s->recv_fifo.itl = 14; break; } } else s->iir &= ~UART_IIR_FE; /* Set fcr - or at least the bits in it that are supposed to "stick" */ s->fcr = val & 0xC9; serial_update_irq(s); break; case 3: { int break_enable; s->lcr = val; serial_update_parameters(s); break_enable = (val >> 6) & 1; if (break_enable != s->last_break_enable) { s->last_break_enable = break_enable; qemu_chr_ioctl(s->chr, CHR_IOCTL_SERIAL_SET_BREAK, &break_enable); } } break; case 4: { int flags; int old_mcr = s->mcr; s->mcr = val & 0x1f; if ( val & UART_MCR_LOOP ) break; if ( s->poll_msl >= 0 && old_mcr != s->mcr ) { qemu_chr_ioctl(s->chr,CHR_IOCTL_SERIAL_GET_TIOCM, &flags); flags &= ~( TIOCM_RTS | TIOCM_DTR ); if ( val & UART_MCR_RTS ) flags |= TIOCM_RTS; if ( val & UART_MCR_DTR ) flags |= TIOCM_DTR; qemu_chr_ioctl(s->chr,CHR_IOCTL_SERIAL_SET_TIOCM, &flags); /* Update the modem status after a one-character-send wait-time, since there may be a response from the device/computer at the other end of the serial line */ qemu_mod_timer(s->modem_status_poll, qemu_get_clock(vm_clock) + s->char_transmit_time ); } } break; case 5: break; case 6: break; case 7: s->scr = val; break; }}static uint32_t serial_ioport_read(void *opaque, uint32_t addr){ SerialState *s = opaque; uint32_t ret; addr &= 7; switch(addr) { default: case 0: if (s->lcr & UART_LCR_DLAB) { ret = s->divider & 0xff; } else { if(s->fcr & UART_FCR_FE) { ret = fifo_get(s,RECV_FIFO); if ( s->recv_fifo.count == 0 ) s->lsr &= ~(UART_LSR_DR | UART_LSR_BI); else qemu_mod_timer(s->fifo_timeout_timer, qemu_get_clock (vm_clock) + s->char_transmit_time * 4 ); s->timeout_ipending = 0; } else { ret = s->rbr; s->lsr &= ~(UART_LSR_DR | UART_LSR_BI); } serial_update_irq(s); } break; case 1: if (s->lcr & UART_LCR_DLAB) { ret = (s->divider >> 8) & 0xff; } else { ret = s->ier; } break; case 2: ret = s->iir; s->thr_ipending = 0; serial_update_irq(s); break; case 3: ret = s->lcr; break; case 4: ret = s->mcr; break; case 5: serial_get_token(); ret = s->lsr; /* Clear break interrupt */ if ( s->lsr & UART_LSR_BI ) { s->lsr &= ~UART_LSR_BI; serial_update_irq(s); } break; case 6: serial_get_token(); if (s->mcr & UART_MCR_LOOP) { /* in loopback, the modem output pins are connected to the inputs */ ret = (s->mcr & 0x0c) << 4; ret |= (s->mcr & 0x02) << 3; ret |= (s->mcr & 0x01) << 5; } else { if ( s->poll_msl >= 0 ) serial_update_msl(s); ret = s->msr; /* Clear delta bits & msr int after read, if they were set */ if ( s->msr & UART_MSR_ANY_DELTA ) { s->msr &= 0xF0; serial_update_irq(s); } } break; case 7: ret = s->scr; break; }#ifdef DEBUG_SERIAL printf("serial: read addr=0x%02x val=0x%02x\n", addr, ret);#endif return ret;}static int serial_can_receive(SerialState *s){ if(s->fcr & UART_FCR_FE) { if(s->recv_fifo.count < UART_FIFO_LENGTH) /* Advertise (fifo.itl - fifo.count) bytes when count < ITL, and 1 if above. If UART_FIFO_LENGTH - fifo.count is advertised the effect will be to almost always fill the fifo completely before the guest has a chance to respond, effectively overriding the ITL that the guest has set. */ return ( s->recv_fifo.count <= s->recv_fifo.itl ) ? s->recv_fifo.itl - s->recv_fifo.count : 1; else return 0; } else { return !(s->lsr & UART_LSR_DR); }}static void serial_receive_break(SerialState *s){ s->rbr = 0; s->lsr |= UART_LSR_BI | UART_LSR_DR; serial_update_irq(s);} /* There's data in recv_fifo and s->rbr has not been read for 4 char transmit times */static void fifo_timeout_int (void *opaque) { SerialState *s = opaque; if ( s->recv_fifo.count ) { s->timeout_ipending = 1; serial_update_irq(s); }}static int serial_can_receive1(void *opaque){ SerialState *s = opaque; return serial_can_receive(s);}static void serial_receive1(void *opaque, const uint8_t *buf, int size){ SerialState *s = opaque; tokens_avail = TOKENS_MAX; if(s->fcr & UART_FCR_FE) { int i; for (i = 0; i < size; i++) { fifo_put(s, RECV_FIFO, buf[i]); } s->lsr |= UART_LSR_DR; /* call the timeout receive callback in 4 char transmit time */ qemu_mod_timer(s->fifo_timeout_timer, qemu_get_clock (vm_clock) + s->char_transmit_time * 4); } else { s->rbr = buf[0]; s->lsr |= UART_LSR_DR; } serial_update_irq(s);}static void serial_event(void *opaque, int event){ SerialState *s = opaque; tokens_avail = TOKENS_MAX; if (event == CHR_EVENT_BREAK) serial_receive_break(s);}static void serial_save(QEMUFile *f, void *opaque){ SerialState *s = opaque; qemu_put_be16s(f,&s->divider); qemu_put_8s(f,&s->rbr); qemu_put_8s(f,&s->ier); qemu_put_8s(f,&s->iir); qemu_put_8s(f,&s->lcr); qemu_put_8s(f,&s->mcr); qemu_put_8s(f,&s->lsr); qemu_put_8s(f,&s->msr); qemu_put_8s(f,&s->scr); qemu_get_8s(f,&s->fcr);}static int serial_load(QEMUFile *f, void *opaque, int version_id){ SerialState *s = opaque; if(version_id > 2) return -EINVAL; if (version_id >= 2) qemu_get_be16s(f, &s->divider); else s->divider = qemu_get_byte(f); qemu_get_8s(f,&s->rbr); qemu_get_8s(f,&s->ier); qemu_get_8s(f,&s->iir); qemu_get_8s(f,&s->lcr); qemu_get_8s(f,&s->mcr); qemu_get_8s(f,&s->lsr); qemu_get_8s(f,&s->msr); qemu_get_8s(f,&s->scr); qemu_get_8s(f,&s->fcr); return 0;}/* If fd is zero, it means that the serial device uses the console */SerialState *serial_init(SetIRQFunc *set_irq, void *opaque, int base, int irq, CharDriverState *chr){ SerialState *s; s = qemu_mallocz(sizeof(SerialState)); if (!s) return NULL; s->set_irq = set_irq; s->irq_opaque = opaque; s->irq = irq; s->ier = 0; s->lsr = UART_LSR_TEMT | UART_LSR_THRE; s->iir = UART_IIR_NO_INT; s->mcr = UART_MCR_OUT2; s->msr = UART_MSR_DCD | UART_MSR_DSR | UART_MSR_CTS; /* Default to 9600 baud, no parity, one stop bit */ s->divider = 0x0C; s->tsr_retry = 0; s->char_transmit_time = ( ticks_per_sec / 9600 ) * 9; s->modem_status_poll = qemu_new_timer(vm_clock, ( QEMUTimerCB *) serial_update_msl, s); s->poll_msl = 0; fifo_clear(s,RECV_FIFO); fifo_clear(s,XMIT_FIFO); s->last_xmit_ts = qemu_get_clock(vm_clock); s->fifo_timeout_timer = qemu_new_timer(vm_clock, ( QEMUTimerCB *) fifo_timeout_int, s); s->transmit_timer = qemu_new_timer(vm_clock, ( QEMUTimerCB *) serial_xmit, s); register_savevm("serial", base, 2, serial_save, serial_load, s); register_ioport_write(base, 8, 1, serial_ioport_write, s); register_ioport_read(base, 8, 1, serial_ioport_read, s); s->chr = chr; qemu_chr_add_handlers(chr, serial_can_receive1, serial_receive1, serial_event, s); serial_update_msl(s); return s;}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -