upgrading emulated UART to 16550A
PATCH 1/1 upgrading emulated UART to 16550A
This is an improved version of the UART 16550A emulation patch. The changes compared to previous version are:
- no token bucket anymore;
- fixed a small bug handling IRQs; this was the problem that prevented kgdb to work over the serial (thanks to Jason Wessel for the help spotting and reproducing this bug).
---
hw/serial.c +392 -91
@@ -1,7 +1,7 @@/* -* QEMU 16450 UART emulation +* QEMU 16550A UART emulation * -* Copyright (c) 2003-2004 Fabrice Bellard +* Copyright (c) 2003-2008 Fabrice Bellard * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal
@@ -21,6 +21,10 @@* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ + +#include <termios.h> +#include <sys/ioctl.h> +#include "hw.h" #include "qemu-char.h" #include "isa.h"
@@ -43,6 +47,10 @@#define UART_IIR_THRI 0x02/* Transmitter holding register empty */ #define UART_IIR_RDI 0x04/* Receiver data interrupt */ #define UART_IIR_RLSI 0x06/* Receiver line status interrupt */ +#define UART_IIR_CTI 0x0C/* Character Timeout Indication */ + +#define UART_IIR_FENF 0x80/* Fifo enabled, but not functionning */ +#define UART_IIR_FE 0xC0/* Fifo enabled */ /* * These are the definitions for the Modem Control Register
@@ -73,17 +81,39 @@#define UART_LSR_PE 0x04/* Parity error indicator */ #define UART_LSR_OE 0x02/* Overrun error indicator */ #define UART_LSR_DR 0x01/* Receiver data ready */ - -/* -* Delay TX IRQ after sending as much characters as the given interval would -* contain on real hardware. This avoids overloading the guest if it processes -* its output buffer in a loop inside the TX IRQ handler. -*/ -#define THROTTLE_TX_INTERVAL 10/* ms */ +#define UART_LSR_INT_ANY 0x1E/* Any of the lsr-interrupt-triggering status bits */ + +/* Interrupt trigger levels. The byte-counts are for 16550A - in newer UARTs the byte-count for each ITL is higher. */ + +#define UART_FCR_ITL_1 0x00/* 1 byte ITL */ +#define UART_FCR_ITL_2 0x40/* 4 bytes ITL */ +#define UART_FCR_ITL_3 0x80/* 8 bytes ITL */ +#define UART_FCR_ITL_4 0xC0/* 14 bytes ITL */ + +#define UART_FCR_DMS 0x08/* DMA Mode Select */ +#define UART_FCR_XFR 0x04/* XMIT Fifo Reset */ +#define UART_FCR_RFR 0x02/* RCVR Fifo Reset */ +#define UART_FCR_FE 0x01/* FIFO Enable */ + +#define UART_FIFO_LENGTH 16/* 16550A Fifo Length */ + +#define XMIT_FIFO 0 +#define RECV_FIFO 1 +#define MAX_XMIT_RETRY 4 + +struct SerialFIFO { +uint8_t data[UART_FIFO_LENGTH ]; +uint8_t count; +uint8_t itl;/* Interrupt Trigger Level */ +uint8_t tail; +uint8_t head; +} typedef SerialFIFO;struct SerialState {uint16_t divider;uint8_t rbr;/* receive register */ +uint8_t thr;/* transmit holding register */ +uint8_t tsr;/* transmit shift register */ uint8_t ier;uint8_t iir;/* read only */ uint8_t lcr;
@@ -91,6 +121,7 @@ struct SerialState {uint8_t lsr;/* read only */ uint8_t msr;/* read only */ uint8_t scr; +uint8_t fcr;/* NOTE: this hidden state is necessary for tx irq generation as it can be reset while reading iir */ int thr_ipending;
@@ -100,55 +131,105 @@ struct SerialState {target_phys_addr_t base;int it_shift;int baudbase; -QEMUTimer *tx_timer; -int tx_burst; +int tsr_retry; + +uint64_t last_xmit_ts;/* Time when the last byte was successfully sent out of the tsr */ +SerialFIFO recv_fifo; +SerialFIFO xmit_fifo; + +struct QEMUTimer *fifo_timeout_timer; +int timeout_ipending;/* timeout interrupt pending state */ +struct QEMUTimer *transmit_timer; + + +uint64_t char_transmit_time;/* time to transmit a char in ticks*/ +int poll_msl; + +struct QEMUTimer *modem_status_poll; }; +static void serial_receive1 (void *opaque,const uint8_t *buf,int size); + +static void fifo_clear (SerialState *s,int fifo) { +SerialFIFO *f = ( fifo ) ? &s->recv_fifo : &s->xmit_fifo; +memset (f->data,0 ,UART_FIFO_LENGTH ); + f->count =0 ; + f->head =0 ; + f->tail =0 ; +} + +static int fifo_put (SerialState *s,int fifo,uint8_t chr) { +SerialFIFO *f = ( fifo ) ? &s->recv_fifo : &s->xmit_fifo; + + f->data[f->head++] = chr; + +if (f->head ==UART_FIFO_LENGTH ) + f->head =0 ; + f->count++; + +return 1 ; +} + +uint8_t fifo_get (SerialState *s,int fifo) { +SerialFIFO *f = ( fifo ) ? &s->recv_fifo : &s->xmit_fifo; +uint8_t c; + +if ( f->count ==0 ) +return 0 ; + + c = f->data[f->tail++]; +if (f->tail ==UART_FIFO_LENGTH ) + f->tail =0 ; + f->count--; + +return c; +} +static void serial_receive_byte (SerialState *s,int ch);static void serial_update_irq (SerialState *s) { -if ((s->lsr &UART_LSR_DR ) && (s->ier &UART_IER_RDI )) { - s->iir =UART_IIR_RDI ; - }else if (s->thr_ipending && (s->ier &UART_IER_THRI )) { - s->iir =UART_IIR_THRI ; - }else { - s->iir =UART_IIR_NO_INT ; - } -if (s->iir !=UART_IIR_NO_INT ) { -qemu_irq_raise (s->irq); - }else { +uint8_t tmp_iir =UART_IIR_NO_INT ; + +if (!s->ier) {qemu_irq_lower (s->irq); +return ; } -} - -static void serial_tx_done (void *opaque) -{ -SerialState *s = opaque; -if (s->tx_burst <0 ) { -uint16_t divider; +if ( ( s->ier &UART_IER_RLSI ) && (s->lsr &UART_LSR_INT_ANY ) ) { + tmp_iir =UART_IIR_RLSI ; + }else if ( s->timeout_ipending ) { + tmp_iir =UART_IIR_CTI ; + }else if ( ( s->ier &UART_IER_RDI ) && (s->lsr &UART_LSR_DR ) ) { +if ( !(s->fcr &UART_FCR_FE ) ) { + tmp_iir =UART_IIR_RDI ; + }else if ( s->recv_fifo.count >= s->recv_fifo.itl ) { + tmp_iir =UART_IIR_RDI ; + } + }else if ( (s->ier &UART_IER_THRI ) && s->thr_ipending ) { + tmp_iir =UART_IIR_THRI ; + }else if ( (s->ier &UART_IER_MSI ) && (s->msr &UART_MSR_ANY_DELTA ) ) { + tmp_iir =UART_IIR_MSI ; + } -if (s->divider) - divider = s->divider; -else - divider =1 ; + s->iir = tmp_iir | ( s->iir &0xF0 ); -/* We assume 10 bits/char, OK for this purpose. */ - s->tx_burst =THROTTLE_TX_INTERVAL *1000 / - (1000000 *10 / (s->baudbase / divider)); +if ( tmp_iir !=UART_IIR_NO_INT ) { +qemu_irq_raise (s->irq); + }else { +qemu_irq_lower (s->irq); } - s->thr_ipending =1 ; - s->lsr |=UART_LSR_THRE ; - s->lsr |=UART_LSR_TEMT ; -serial_update_irq (s); }static void serial_update_parameters (SerialState *s) { -int speed, parity, data_bits, stop_bits; +int speed, parity, data_bits, stop_bits, frame_size;QEMUSerialSetParams ssp; +if (s->divider ==0 ) +return ; + + frame_size =1 ;if (s->lcr &0x08 ) {if (s->lcr &0x10 ) parity ='E' ;
@@ -156,19 +237,21 @@ static void serial_update_parameters(SerialState *s) parity ='O' ; }else { parity ='N' ; + frame_size =0 ; }if (s->lcr &0x04 ) stop_bits =2 ;else stop_bits =1 ; + data_bits = (s->lcr &0x03 ) +5 ; -if (s->divider ==0 ) -return ; + frame_size += data_bits + stop_bits; speed = s->baudbase / s->divider; ssp.speed = speed; ssp.parity = parity; ssp.data_bits = data_bits; ssp.stop_bits = stop_bits; + s->char_transmit_time = ( ticks_per_sec / speed ) * frame_size;qemu_chr_ioctl (s->chr,CHR_IOCTL_SERIAL_SET_PARAMS , &ssp);#if 0 printf("speed=%d parity=%c data=%d stop=%d\n",
@@ -176,6 +259,88 @@ static void serial_update_parameters(SerialState *s)#endif } +static void serial_update_msl (SerialState *s ) +{ +uint8_t omsr; +int flags; + +qemu_del_timer (s->modem_status_poll); + +if (qemu_chr_ioctl (s->chr,CHR_IOCTL_SERIAL_GET_TIOCM , &flags) == -ENOTSUP ) { + s->poll_msl =-1 ; +return ; + } + + omsr = s->msr; + + s->msr = ( flags &TIOCM_CTS ) ? s->msr |UART_MSR_CTS : s->msr & ~UART_MSR_CTS ; + s->msr = ( flags &TIOCM_DSR ) ? s->msr |UART_MSR_DSR : s->msr & ~UART_MSR_DSR ; + s->msr = ( flags &TIOCM_CAR ) ? s->msr |UART_MSR_DCD : s->msr & ~UART_MSR_DCD ; + s->msr = ( flags &TIOCM_RI ) ? s->msr |UART_MSR_RI : s->msr & ~UART_MSR_RI ; + +if ( s->msr != omsr ) { +/* Set delta bits */ + s->msr = s->msr | ( ( s->msr >>4 ) ^ ( omsr >>4 ) ); +/* UART_MSR_TERI only if change was from 1 -> 0 */ +if ( ( s->msr &UART_MSR_TERI ) && !( omsr &UART_MSR_RI ) ) + s->msr &= ~UART_MSR_TERI ; +serial_update_irq (s); + } + +/* The real 16550A apparently has a 250ns response latency to line status changes. +We'll be lazy and poll only every 10ms, and only poll it at all if MSI interrupts are turned on */ + +if ( s->poll_msl ) +qemu_mod_timer (s->modem_status_poll,qemu_get_clock (vm_clock) + ticks_per_sec /100 ); +} + +static void serial_xmit (void *opaque) { +SerialState *s = opaque; +uint64_t new_xmit_ts =qemu_get_clock (vm_clock); + +if ( s->tsr_retry <=0 ) { +if (s->fcr &UART_FCR_FE ) { + s->tsr =fifo_get (s,XMIT_FIFO ); +if ( !s->xmit_fifo.count ) + s->lsr |=UART_LSR_THRE ; + }else { + s->tsr = s->thr; + s->lsr |=UART_LSR_THRE ; + } + } + +if ( (s->mcr &UART_MCR_LOOP +/* in loopback mode, say that we just received a char */ + ? (serial_receive1 (s, &s->tsr,1 ),1 ) + :qemu_chr_write (s->chr, &s->tsr,1 )) + !=1 ) { +if ( ( s->tsr_retry >0 ) && ( s->tsr_retry <=MAX_XMIT_RETRY ) ) { + s->tsr_retry++; +qemu_mod_timer (s->transmit_timer, new_xmit_ts + s->char_transmit_time ); +return ; + }else if ( s->poll_msl <0 ) { +/* If we exceed MAX_XMIT_RETRY and the backend is not a real serial port, then +drop any further failed writes instantly, until we get one that goes through. +This is to prevent guests that log to unconnected pipes or pty's from stalling. */ + s->tsr_retry =-1 ; + } + } +else { + s->tsr_retry =0 ; + } + + s->last_xmit_ts =qemu_get_clock (vm_clock); +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;
@@ -192,25 +357,19 @@ static void serial_ioport_write(void *opaque, uint32_t addr, uint32_t val) 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); - ch = val; -if (!(s->mcr &UART_MCR_LOOP )) { -/* when not in loopback mode, send the char */ -qemu_chr_write (s->chr, &ch,1 ); }else { -/* in loopback mode, say that we just received a char */ -serial_receive_byte (s, ch); - } -if (s->tx_burst >0 ) { - s->tx_burst--; -serial_tx_done (s); - }else if (s->tx_burst ==0 ) { - s->tx_burst--; -qemu_mod_timer (s->tx_timer,qemu_get_clock (vm_clock) + - ticks_per_sec *THROTTLE_TX_INTERVAL /1000 ); + s->thr_ipending =0 ; + s->lsr &= ~UART_LSR_THRE ; +serial_update_irq (s); } +serial_xmit (s); }break ;case 1 :
@@ -219,13 +378,68 @@ static void serial_ioport_write(void *opaque, uint32_t addr, uint32_t val)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); } -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 : {
@@ -241,7 +455,30 @@ static void serial_ioport_write(void *opaque, uint32_t addr, uint32_t val) }break ;case 4 : - s->mcr = val &0x1f ; + { +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 ;
@@ -265,8 +502,17 @@ static uint32_t serial_ioport_read(void *opaque, uint32_t addr)if (s->lcr &UART_LCR_DLAB ) { ret = s->divider &0xff ; }else { - ret = s->rbr; - s->lsr &= ~(UART_LSR_DR |UART_LSR_BI ); +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);if (!(s->mcr &UART_MCR_LOOP )) {/* in loopback mode, don't receive any data */
@@ -283,8 +529,6 @@ static uint32_t serial_ioport_read(void *opaque, uint32_t addr)break ;case 2 : ret = s->iir; -/* reset THR pending bit */ -if ((ret &0x7 ) ==UART_IIR_THRI ) s->thr_ipending =0 ;serial_update_irq (s);break ;
@@ -296,6 +540,11 @@ static uint32_t serial_ioport_read(void *opaque, uint32_t addr)break ;case 5 : ret = s->lsr; +/* Clear break interrupt */ +if ( s->lsr &UART_LSR_BI ) { + s->lsr &= ~UART_LSR_BI ; +serial_update_irq (s); + }break ;case 6 :if (s->mcr &UART_MCR_LOOP ) {
@@ -305,7 +554,14 @@ static uint32_t serial_ioport_read(void *opaque, uint32_t addr) 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 :
@@ -320,14 +576,17 @@ static uint32_t serial_ioport_read(void *opaque, uint32_t addr)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_byte (SerialState *s,int ch) -{ - s->rbr = ch; - s->lsr |=UART_LSR_DR ; -serial_update_irq (s); + } }static void serial_receive_break (SerialState *s)
@@ -337,6 +596,15 @@ static void serial_receive_break(SerialState *s)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;
@@ -346,12 +614,27 @@ static int serial_can_receive1(void *opaque)static void serial_receive1 (void *opaque,const uint8_t *buf,int size) {SerialState *s = opaque; -serial_receive_byte (s, buf[0 ]); +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; +#ifdef DEBUG_SERIAL +printf ("serial: event %x\n" , event); +#endif if (event ==CHR_EVENT_BREAK )serial_receive_break (s); }
@@ -369,11 +652,13 @@ static void serial_save(QEMUFile *f, void *opaque)qemu_put_8s (f,&s->lsr);qemu_put_8s (f,&s->msr);qemu_put_8s (f,&s->scr); +qemu_put_8s (f,&s->fcr); }static int serial_load (QEMUFile *f,void *opaque,int version_id) {SerialState *s = opaque; +uint8_t fcr =0 ;if (version_id >2 )return -EINVAL ;
@@ -391,6 +676,11 @@ static int serial_load(QEMUFile *f, void *opaque, int version_id)qemu_get_8s (f,&s->msr);qemu_get_8s (f,&s->scr); +if (version_id >=2 ) +qemu_get_8s (f,&fcr); + +/* Initialize fcr via setter to perform essential side-effects */ +serial_ioport_write (s,0x02 , fcr);return 0 ; }
@@ -398,21 +688,47 @@ static void serial_reset(void *opaque) {SerialState *s = opaque; - s->divider =0 ; s->rbr =0 ; s->ier =0 ; s->iir =UART_IIR_NO_INT ; s->lcr =0 ; - s->mcr =0 ; s->lsr =UART_LSR_TEMT |UART_LSR_THRE ; s->msr =UART_MSR_DCD |UART_MSR_DSR |UART_MSR_CTS ; +/* Default to 9600 baud, no parity, one stop bit */ + s->divider =0x0C ; + s->mcr =UART_MCR_OUT2 ; s->scr =0 ; + s->tsr_retry =0 ; + s->char_transmit_time = ( ticks_per_sec /9600 ) *9 ; + s->poll_msl =0 ; + +fifo_clear (s,RECV_FIFO ); +fifo_clear (s,XMIT_FIFO ); + + s->last_xmit_ts =qemu_get_clock (vm_clock); s->thr_ipending =0 ; s->last_break_enable =0 ;qemu_irq_lower (s->irq); } +static void serial_init_core (SerialState *s,qemu_irq irq,int baudbase, +CharDriverState *chr) +{ + s->irq = irq; + s->baudbase = baudbase; + s->chr = chr; + + s->modem_status_poll =qemu_new_timer (vm_clock, (QEMUTimerCB *) serial_update_msl, s); + + 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); + +qemu_register_reset (serial_reset, s); +serial_reset (s); + +} +/* If fd is zero, it means that the serial device uses the console */ SerialState *serial_init (int base,qemu_irq irq,int baudbase,CharDriverState *chr)
@@ -422,21 +738,13 @@ SerialState *serial_init(int base, qemu_irq irq, int baudbase, s =qemu_mallocz (sizeof (SerialState));if (!s)return NULL ; - s->irq = irq; - s->baudbase = baudbase; - - s->tx_timer =qemu_new_timer (vm_clock, serial_tx_done, s); -if (!s->tx_timer) -return NULL ; -qemu_register_reset (serial_reset, s); -serial_reset (s); +serial_init_core (s, irq, baudbase, chr);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);return s;
@@ -524,18 +832,11 @@ SerialState *serial_mm_init (target_phys_addr_t base, int it_shift, s =qemu_mallocz (sizeof (SerialState));if (!s)return NULL ; - s->irq = irq; + s->base = base; s->it_shift = it_shift; - s->baudbase= baudbase; - - s->tx_timer =qemu_new_timer (vm_clock, serial_tx_done, s); -if (!s->tx_timer) -return NULL ; - -qemu_register_reset (serial_reset, s); -serial_reset (s); +serial_init_core (s, irq, baudbase, chr);register_savevm ("serial" , base,2 , serial_save, serial_load, s);if (ioregister) {
@@ -543,8 +844,8 @@ SerialState *serial_mm_init (target_phys_addr_t base, int it_shift, serial_mm_write, s);cpu_register_physical_memory (base,8 << it_shift, s_io_memory); } - s->chr = chr;qemu_chr_add_handlers (chr, serial_can_receive1, serial_receive1, serial_event, s); +serial_update_msl (s);return s; }
qemu-char.h +3 -0
@@ -28,6 +28,9 @@ typedef struct {#define CHR_IOCTL_PP_EPP_WRITE_ADDR 10#define CHR_IOCTL_PP_EPP_WRITE 11 +#define CHR_IOCTL_SERIAL_SET_TIOCM 12 +#define CHR_IOCTL_SERIAL_GET_TIOCM 13 +typedef void IOEventHandler (void *opaque,int event);struct CharDriverState {
vl.c +11 -0
@@ -2721,6 +2721,17 @@ static int tty_serial_ioctl(CharDriverState *chr, int cmd, void *arg)tcsendbreak (s->fd_in,1 ); }break ; +case CHR_IOCTL_SERIAL_GET_TIOCM : + { +ioctl (s->fd_in,TIOCMGET , arg); + } +break ; +case CHR_IOCTL_SERIAL_SET_TIOCM : + { +ioctl (s->fd_in,TIOCMSET , arg); + } +break ; +default :return -ENOTSUP ; }
Quoted 18 lines · Stefano Stabellini
This is an improved version of the UART 16550A emulation patch. The changes compared to previous version are:
- no token bucket anymore;
- fixed a small bug handling IRQs; this was the problem that prevented kgdb to work over the serial (thanks to Jason Wessel for the help spotting and reproducing this bug).
Signed-off-by Stefano Stabellini <[email protected]>---
hw/serial.c +0
-0
hw/serial.c line 5
Quoted 7 lines
hw/serial.c +2
-2@@ -1,7 +1,7 @@/* -* QEMU 16450 UART emulation +* QEMU 16550A UART emulation * -* Copyright (c) 2003-2004 Fabrice Bellard +* Copyright (c) 2003-2008 Fabrice Bellard
I think the idea was to add a new copyright entry, not to modify Fabrice's copyright.
hw/serial.c line 27
Quoted 11 lines
hw/serial.c +0
-0* * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal
hw/serial.c +3
-0@@ -21,6 +21,10 @@* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ + +#include <termios.h> +#include <sys/ioctl.h>
This doesn't look Windows friendly.
Quoted 17 lines
hw/serial.c +11
-2@@ -100,55 +131,105 @@ struct SerialState {target_phys_addr_t base;int it_shift;int baudbase; -QEMUTimer *tx_timer; -int tx_burst; +int tsr_retry; + +uint64_t last_xmit_ts;/* Time when the last byte was successfully sent out of the tsr */ +SerialFIFO recv_fifo; +SerialFIFO xmit_fifo; + +struct QEMUTimer *fifo_timeout_timer; +int timeout_ipending;/* timeout interrupt pending state */ +struct QEMUTimer *transmit_timer; + +
hw/serial.c struct SerialState { · line 154
Quoted 10 lines
hw/serial.c +7
-0+uint64_t char_transmit_time;/* time to transmit a char in ticks*/ +int poll_msl; + +struct QEMUTimer *modem_status_poll; }; +static void serial_receive1 (void *opaque,const uint8_t *buf,int size); + +static void fifo_clear (SerialState *s,int fifo) {
minor nit, but the '{' should be on a new line.
Quoted 11 lines
hw/serial.c +11
-0+SerialFIFO *f = ( fifo ) ? &s->recv_fifo : &s->xmit_fifo; +memset (f->data,0 ,UART_FIFO_LENGTH ); + f->count =0 ; + f->head =0 ; + f->tail =0 ; +} + +static int fifo_put (SerialState *s,int fifo,uint8_t chr) { +SerialFIFO *f = ( fifo ) ? &s->recv_fifo : &s->xmit_fifo; + + f->data[f->head++] = chr;
hw/serial.c struct SerialState { · line 189
Quoted 10 lines
hw/serial.c +9
-0+ +if (f->head ==UART_FIFO_LENGTH ) + f->head =0 ; + f->count++; + +return 1 ; +} + +uint8_t fifo_get (SerialState *s,int fifo) {
Any reason for this not to be static?
Quoted 26 lines
hw/serial.c +15
-9-static void serial_tx_done (void *opaque) -{ -SerialState *s = opaque; -if (s->tx_burst <0 ) { -uint16_t divider; +if ( ( s->ier &UART_IER_RLSI ) && (s->lsr &UART_LSR_INT_ANY ) ) { + tmp_iir =UART_IIR_RLSI ; + }else if ( s->timeout_ipending ) { + tmp_iir =UART_IIR_CTI ; + }else if ( ( s->ier &UART_IER_RDI ) && (s->lsr &UART_LSR_DR ) ) { +if ( !(s->fcr &UART_FCR_FE ) ) { + tmp_iir =UART_IIR_RDI ; + }else if ( s->recv_fifo.count >= s->recv_fifo.itl ) { + tmp_iir =UART_IIR_RDI ; + } + }else if ( (s->ier &UART_IER_THRI ) && s->thr_ipending ) { + tmp_iir =UART_IIR_THRI ; + }else if ( (s->ier &UART_IER_MSI ) && (s->msr &UART_MSR_ANY_DELTA ) ) { + tmp_iir =UART_IIR_MSI ; + } -if (s->divider) - divider = s->divider; -else - divider =1 ;
hw/serial.c struct SerialState { · line 223
Quoted 10 lines
hw/serial.c +5
-3+ s->iir = tmp_iir | ( s->iir &0xF0 ); -/* We assume 10 bits/char, OK for this purpose. */ - s->tx_burst =THROTTLE_TX_INTERVAL *1000 / - (1000000 *10 / (s->baudbase / divider)); +if ( tmp_iir !=UART_IIR_NO_INT ) { +qemu_irq_raise (s->irq); + }else { +qemu_irq_lower (s->irq);
More nits, lose the whitespace in the conditions. For instance:
} else if ( ( s->ier & UART_IER_RDI ) => } else if ((s->ier & UART_IER_RDI).
The rest of the file uses the later style so it's a little weird to have portions of the code be different.
Quoted 45 lines
hw/serial.c +9
-7} - s->thr_ipending =1 ; - s->lsr |=UART_LSR_THRE ; - s->lsr |=UART_LSR_TEMT ; -serial_update_irq (s); }static void serial_update_parameters (SerialState *s) { -int speed, parity, data_bits, stop_bits; +int speed, parity, data_bits, stop_bits, frame_size;QEMUSerialSetParams ssp; +if (s->divider ==0 ) +return ; + + frame_size =1 ;if (s->lcr &0x08 ) {if (s->lcr &0x10 ) parity ='E' ;@@ -156,19 +237,21 @@ static void serial_update_parameters(SerialState *s) parity ='O' ; }else { parity ='N' ; + frame_size =0 ; }if (s->lcr &0x04 ) stop_bits =2 ;else stop_bits =1 ; + data_bits = (s->lcr &0x03 ) +5 ; -if (s->divider ==0 ) -return ; + frame_size += data_bits + stop_bits; speed = s->baudbase / s->divider; ssp.speed = speed; ssp.parity = parity; ssp.data_bits = data_bits; ssp.stop_bits = stop_bits; + s->char_transmit_time = ( ticks_per_sec / speed ) * frame_size;qemu_chr_ioctl (s->chr,CHR_IOCTL_SERIAL_SET_PARAMS , &ssp);#if 0 printf("speed=%d parity=%c data=%d stop=%d\n",
hw/serial.c static void serial_update_parameters(SerialState *s) · line 263
Quoted 5 lines
hw/serial.c +1
-0@@ -176,6 +259,88 @@ static void serial_update_parameters(SerialState *s)#endif } +static void serial_update_msl (SerialState *s )
More bad whitespace.
Quoted 10 lines
hw/serial.c +10
-0+static void serial_xmit (void *opaque) { +SerialState *s = opaque; +uint64_t new_xmit_ts =qemu_get_clock (vm_clock); + +if ( s->tsr_retry <=0 ) { +if (s->fcr &UART_FCR_FE ) { + s->tsr =fifo_get (s,XMIT_FIFO ); +if ( !s->xmit_fifo.count ) + s->lsr |=UART_LSR_THRE ; + }else {
hw/serial.c static void serial_update_parameters(SerialState *s)
Quoted 10 lines
hw/serial.c +9
-0+ s->tsr = s->thr; + s->lsr |=UART_LSR_THRE ; + } + } + +if ( (s->mcr &UART_MCR_LOOP +/* in loopback mode, say that we just received a char */ + ? (serial_receive1 (s, &s->tsr,1 ),1 ) + : qemu_chr_write(s->chr, &s->tsr, 1))
This is just nutty :-) Please rewrite this if() statement to be a little less obscure.
Quoted 10 lines
hw/serial.c +1
-3@@ -524,18 +832,11 @@ SerialState *serial_mm_init (target_phys_addr_t base, int it_shift, s =qemu_mallocz (sizeof (SerialState));if (!s)return NULL ; - s->irq = irq; + s->base = base; s->it_shift = it_shift; - s->baudbase= baudbase; -
hw/serial.c SerialState *serial_mm_init (target_phys_addr_t base, int it_shift, · line 841
Quoted 10 lines
hw/serial.c +1
-6- s->tx_timer =qemu_new_timer (vm_clock, serial_tx_done, s); -if (!s->tx_timer) -return NULL ; - -qemu_register_reset (serial_reset, s); -serial_reset (s); +serial_init_core (s, irq, baudbase, chr);register_savevm ("serial" , base,2 , serial_save, serial_load, s);
Isn't it necessary to bump the savevm() version number since you've changed the format.
Regards,
Anthony Liguori
Quoted 12 lines · Stefano Stabellini
This is an improved version of the UART 16550A emulation patch. The changes compared to previous version are:
- no token bucket anymore;
- fixed a small bug handling IRQs; this was the problem that prevented kgdb to work over the serial (thanks to Jason Wessel for the help spotting and reproducing this bug).
Signed-off-by Stefano Stabellini <[email protected]>
Looks good now from my perspective for the regression tests I have. Thanks for fixing the problem. I can confirm that polled mode and interrupt driven mode work correctly on 2.6 Linux kernels for the x86_64, i386, and mips BE/LE emulations.
Jason.
Quoted 11 lines · Anthony Liguori, le Thu 07 Aug 2008 13:58:43 -0500, a écrit :
hw/serial.c line 28
Quoted 9 lines
hw/serial.c +3
-0@@ -21,6 +21,10 @@* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ + +#include <termios.h> +#include <sys/ioctl.h> This doesn't look Windows friendly.
How is qemu compiled on windows? Native with the windows headers? That's quite different then indeed.
Samuel
Quoted 15 lines · Samuel Thibault
Quoted 12 lines · Anthony Liguori, le Thu 07 Aug 2008 13:58:43 -0500, a écrit :
hw/serial.c line 29
Quoted 10 lines
hw/serial.c +3
-0@@ -21,6 +21,10 @@* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ + +#include <termios.h> +#include <sys/ioctl.h> This doesn't look Windows friendly.
Quoted 3 lines
How is qemu compiled on windows? Native with the windows headers? That's quite different then indeed.
Yup, via mingw. But it doesn't seem termios or sys/ioctl are really being used in this file anyway?
Regards,
Anthony Liguori
Quoted 4 lines
Samuel
Quoted 16 lines · Anthony Liguori, le Thu 07 Aug 2008 16:13:10 -0500, a écrit :
Quoted 15 lines · Samuel Thibault
Quoted 12 lines · Anthony Liguori, le Thu 07 Aug 2008 13:58:43 -0500, a écrit :
hw/serial.c line 26
Quoted 10 lines
(quoted diff) +0
-0@@ -21,6 +21,10 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHERDEALINGS IN
* THE SOFTWARE.
*/hw/serial.c +3
-0+ +#include <termios.h> +#include <sys/ioctl.h> This doesn't look Windows friendly.
Quoted 6 lines
Quoted 3 lines
How is qemu compiled on windows? Native with the windows headers? That's quite different then indeed.
Yup, via mingw. But it doesn't seem termios or sys/ioctl are really being used in this file anyway?
Actually I checked the patch. I thought these includes were in vl.c. These are at least used for TIOCM_*. I guess those should rather receive a qemu abstracted name, and be converted to TIOCM_* in vl.c
Samuel
Quoted 3 lines · Anthony Liguori
I think the idea was to add a new copyright entry, not to modify Fabrice's copyright.
Oops, I am going to fix that.
Quoted 6 lines
hw/serial.c line 26
Quoted 4 lines
hw/serial.c +3
-0+ +#include <termios.h> +#include <sys/ioctl.h> This doesn't look Windows friendly.
I am going to follow Samuel advice and declare more constants in qemu-char.h so that we don't need those two headers anymore.
Quoted 3 lines
minor nit, but the '{' should be on a new line.
...
Quoted 9 lines
More nits, lose the whitespace in the conditions. For instance:
} else if ( (
s->ier&UART_IER_RDI) => } else if ((s->ier&UART_IER_RDI).The rest of the file uses the later style so it's a little weird to have portions of the code be different.
...
Quoted 3 lines
More bad whitespace.
...
Quoted 4 lines
This is just nutty :-) Please rewrite this
if()statement to be a little less obscure.
All these style problems will be fixed in the next version.
Quoted 7 lines
Quoted 3 lines
+ serial_init_core(s, irq, baudbase, chr); register_savevm("serial", base, 2, serial_save, serial_load, s);Isn't it necessary to bump the
savevm()version number since you've changed the format.
Yes, you are right.
Thanks for this very helpful review.