serial: only resample THR interrupt on rising edge of IER.THRI
There is disagreement on whether LSR.THRE should be resampled when IER.THRI goes from 1 to 1. Bochs only does it if IER.THRI goes from 0 to 1; PCE does it even if IER.THRI is unchanged. But the Windows driver seems to always go from 1 to 0 and back to 1, so do things in agreement with Bochs, because the handling of thr_ipending was reported in 2010 (https://lists.gnu.org/archive/html/qemu-devel/2010-03/msg01914.html) as breaking DR-DOS Plus. Reported-by: Roy Tam <roytam@gmail.com> Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
This commit is contained in:
		@@ -336,10 +336,12 @@ static void serial_ioport_write(void *opaque, hwaddr addr, uint64_t val,
 | 
			
		||||
            s->divider = (s->divider & 0x00ff) | (val << 8);
 | 
			
		||||
            serial_update_parameters(s);
 | 
			
		||||
        } else {
 | 
			
		||||
            uint8_t changed = (s->ier ^ val) & 0x0f;
 | 
			
		||||
            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) {
 | 
			
		||||
             * status lines on physical port on or off depending on UART_IER_MSI state.
 | 
			
		||||
             */
 | 
			
		||||
            if ((changed & UART_IER_MSI) && s->poll_msl >= 0) {
 | 
			
		||||
                if (s->ier & UART_IER_MSI) {
 | 
			
		||||
                     s->poll_msl = 1;
 | 
			
		||||
                     serial_update_msl(s);
 | 
			
		||||
@@ -354,18 +356,23 @@ static void serial_ioport_write(void *opaque, hwaddr addr, uint64_t val,
 | 
			
		||||
             * This is not in the datasheet, but Windows relies on it.  It is
 | 
			
		||||
             * unclear if THRE has to be resampled every time THRI becomes
 | 
			
		||||
             * 1, or only on the rising edge.  Bochs does the latter, and Windows
 | 
			
		||||
             * always toggles IER to all zeroes and back to all ones.  But for
 | 
			
		||||
             * now leave it as it has always been in QEMU.
 | 
			
		||||
             * always toggles IER to all zeroes and back to all ones, so do the
 | 
			
		||||
             * same.
 | 
			
		||||
             *
 | 
			
		||||
             * If IER.THRI is zero, thr_ipending is not used.  Set it to zero
 | 
			
		||||
             * so that the thr_ipending subsection is not migrated.
 | 
			
		||||
             */
 | 
			
		||||
            if ((s->ier & UART_IER_THRI) && (s->lsr & UART_LSR_THRE)) {
 | 
			
		||||
                s->thr_ipending = 1;
 | 
			
		||||
            } else {
 | 
			
		||||
                s->thr_ipending = 0;
 | 
			
		||||
            if (changed & UART_IER_THRI) {
 | 
			
		||||
                if ((s->ier & UART_IER_THRI) && (s->lsr & UART_LSR_THRE)) {
 | 
			
		||||
                    s->thr_ipending = 1;
 | 
			
		||||
                } else {
 | 
			
		||||
                    s->thr_ipending = 0;
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            if (changed) {
 | 
			
		||||
                serial_update_irq(s);
 | 
			
		||||
            }
 | 
			
		||||
            serial_update_irq(s);
 | 
			
		||||
        }
 | 
			
		||||
        break;
 | 
			
		||||
    case 2:
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user