From 0147883450fe84bb8de2d4a58381881f4262ce9b Mon Sep 17 00:00:00 2001 From: Calvin Lee Date: Fri, 11 May 2018 18:05:44 -0600 Subject: PC Chipset: Improve serial divisor calculation This fixes several problems I found in the UART serial implementation. Now all divisor values are allowed, while before divisor values of zero and below the base baud rate were rejected. All changes are in reference to http://www.sci.muni.cz/docs/pc/serport.txt Signed-off-by: Calvin Lee Message-Id: <20180512000545.966-2-cyrus296@gmail.com> Signed-off-by: Paolo Bonzini --- hw/char/serial.c | 23 +++++++++++++---------- 1 file changed, 13 insertions(+), 10 deletions(-) (limited to 'hw/char') diff --git a/hw/char/serial.c b/hw/char/serial.c index cd7d747c68..d3b75f09f6 100644 --- a/hw/char/serial.c +++ b/hw/char/serial.c @@ -150,13 +150,10 @@ static void serial_update_irq(SerialState *s) static void serial_update_parameters(SerialState *s) { - int speed, parity, data_bits, stop_bits, frame_size; + float speed; + int parity, data_bits, stop_bits, frame_size; QEMUSerialSetParams ssp; - if (s->divider == 0 || s->divider > s->baudbase) { - return; - } - /* Start bit. */ frame_size = 1; if (s->lcr & 0x08) { @@ -169,14 +166,16 @@ static void serial_update_parameters(SerialState *s) } else { parity = 'N'; } - if (s->lcr & 0x04) + if (s->lcr & 0x04) { stop_bits = 2; - else + } else { stop_bits = 1; + } data_bits = (s->lcr & 0x03) + 5; frame_size += data_bits + stop_bits; - speed = s->baudbase / s->divider; + /* Zero divisor should give about 3500 baud */ + speed = (s->divider == 0) ? 3500 : (float) s->baudbase / s->divider; ssp.speed = speed; ssp.parity = parity; ssp.data_bits = data_bits; @@ -184,7 +183,7 @@ static void serial_update_parameters(SerialState *s) s->char_transmit_time = (NANOSECONDS_PER_SECOND / speed) * frame_size; qemu_chr_fe_ioctl(&s->chr, CHR_IOCTL_SERIAL_SET_PARAMS, &ssp); - DPRINTF("speed=%d parity=%c data=%d stop=%d\n", + DPRINTF("speed=%.2f parity=%c data=%d stop=%d\n", speed, parity, data_bits, stop_bits); } @@ -341,7 +340,11 @@ static void serial_ioport_write(void *opaque, hwaddr addr, uint64_t val, default: case 0: if (s->lcr & UART_LCR_DLAB) { - s->divider = (s->divider & 0xff00) | val; + if (size == 2) { + s->divider = (s->divider & 0xff00) | val; + } else if (size == 4) { + s->divider = val; + } serial_update_parameters(s); } else { s->thr = (uint8_t) val; -- cgit v1.2.3 From f3575af130c700cea060b51a89008a76dae22259 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= Date: Mon, 16 Jul 2018 13:07:55 +0200 Subject: hw/char/serial: retry write if EAGAIN MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit If the chardev returns -1 with EAGAIN errno on write(), it should try to send it again (EINTR is handled by the chardev itself). This fixes commit 019288bf137183bf3407c9824655b753bfafc99f "hw/char/serial: Only retry if qemu_chr_fe_write returns 0" Tested-by: Igor Mammedov Signed-off-by: Marc-André Lureau Message-Id: <20180716110755.12499-1-marcandre.lureau@redhat.com> Signed-off-by: Paolo Bonzini --- hw/char/serial.c | 23 ++++++++++++++--------- 1 file changed, 14 insertions(+), 9 deletions(-) (limited to 'hw/char') diff --git a/hw/char/serial.c b/hw/char/serial.c index d3b75f09f6..251f40fdac 100644 --- a/hw/char/serial.c +++ b/hw/char/serial.c @@ -260,15 +260,20 @@ static void serial_xmit(SerialState *s) if (s->mcr & UART_MCR_LOOP) { /* in loopback mode, say that we just received a char */ serial_receive1(s, &s->tsr, 1); - } else if (qemu_chr_fe_write(&s->chr, &s->tsr, 1) == 0 && - s->tsr_retry < MAX_XMIT_RETRY) { - assert(s->watch_tag == 0); - s->watch_tag = - qemu_chr_fe_add_watch(&s->chr, G_IO_OUT | G_IO_HUP, - serial_watch_cb, s); - if (s->watch_tag > 0) { - s->tsr_retry++; - return; + } else { + int rc = qemu_chr_fe_write(&s->chr, &s->tsr, 1); + + if ((rc == 0 || + (rc == -1 && errno == EAGAIN)) && + s->tsr_retry < MAX_XMIT_RETRY) { + assert(s->watch_tag == 0); + s->watch_tag = + qemu_chr_fe_add_watch(&s->chr, G_IO_OUT | G_IO_HUP, + serial_watch_cb, s); + if (s->watch_tag > 0) { + s->tsr_retry++; + return; + } } } s->tsr_retry = 0; -- cgit v1.2.3