ia64/xen-unstable

changeset 16537:bf21e00155b7

[QEMU-DM] Modem control line & msl/mcr register support.

This patch enables handling of the modem/flow control lines of a
serial port when the backend for the virtual port is a physical serial
port. During initialization, it tries to load the msr with the
detected status from the real port (this is consistent with physical
uart, which starts with its msr values set according to the status of
the modem status lines). If the ioctl returns -ENOTSUP, then the code
assumes the backend is not a real serial port and will disable any
further attempts to manipulate or read the physical port's line
status.

It's tries to be as "correct" as possible in its msr/msl handling,
with the exception of modem line status change interrupts. A real
16550 uart apparently have a delay time of 250ns between when a modem
status line changes and the IRQ line goes high. In this patch, an
"idle" port is polled for line status changes only if the guest has
enabled UART_IER_MSI is enabled, and only polled every 10 ms.

Signed-off-by: Trolle Selander <trolle.selander@gmail.com>
author Keir Fraser <keir.fraser@citrix.com>
date Wed Dec 05 14:18:34 2007 +0000 (2007-12-05)
parents 89e7031e153c
children f0ac46de680c
files tools/ioemu/hw/serial.c tools/ioemu/vl.c tools/ioemu/vl.h
line diff
     1.1 --- a/tools/ioemu/hw/serial.c	Wed Dec 05 14:01:39 2007 +0000
     1.2 +++ b/tools/ioemu/hw/serial.c	Wed Dec 05 14:18:34 2007 +0000
     1.3 @@ -25,6 +25,7 @@
     1.4  #include <sys/time.h>
     1.5  #include <time.h>
     1.6  #include <assert.h>
     1.7 +#include <asm/termios.h>
     1.8  
     1.9  //#define DEBUG_SERIAL
    1.10  
    1.11 @@ -63,7 +64,7 @@
    1.12  #define UART_MSR_TERI	0x04	/* Trailing edge ring indicator */
    1.13  #define UART_MSR_DDSR	0x02	/* Delta DSR */
    1.14  #define UART_MSR_DCTS	0x01	/* Delta CTS */
    1.15 -#define UART_MSR_ANY_DELTA 0x0F	/* Any of the delta bits! */
    1.16 +#define UART_MSR_ANY_DELTA 0x0F	/* Any of the msr delta bits */
    1.17  
    1.18  #define UART_LSR_TEMT	0x40	/* Transmitter empty */
    1.19  #define UART_LSR_THRE	0x20	/* Transmit-hold-register empty */
    1.20 @@ -72,6 +73,7 @@
    1.21  #define UART_LSR_PE	0x04	/* Parity error indicator */
    1.22  #define UART_LSR_OE	0x02	/* Overrun error indicator */
    1.23  #define UART_LSR_DR	0x01	/* Receiver data ready */
    1.24 +#define UART_LSR_INT_ANY 0x1E	/* Any of the lsr-interrupt-triggering status bits */
    1.25  
    1.26  /* Maximum retries for a single byte transmit. */
    1.27  #define WRITE_MAX_SINGLE_RETRIES 3
    1.28 @@ -98,6 +100,8 @@ struct SerialState {
    1.29      int last_break_enable;
    1.30      target_ulong base;
    1.31      int it_shift;
    1.32 +    uint64_t char_transmit_time;               /* time to transmit a char in ticks*/
    1.33 +    int poll_msl;
    1.34  
    1.35      /*
    1.36       * If a character transmitted via UART cannot be written to its
    1.37 @@ -111,18 +115,31 @@ struct SerialState {
    1.38      int write_total_retries;
    1.39      char write_chr;
    1.40      QEMUTimer *write_retry_timer;
    1.41 +    QEMUTimer *modem_status_poll;
    1.42  };
    1.43  
    1.44  static void serial_update_irq(SerialState *s)
    1.45  {
    1.46 -    if ((s->lsr & UART_LSR_DR) && (s->ier & UART_IER_RDI)) {
    1.47 -        s->iir = UART_IIR_RDI;
    1.48 -    } else if (s->thr_ipending && (s->ier & UART_IER_THRI)) {
    1.49 -        s->iir = UART_IIR_THRI;
    1.50 -    } else {
    1.51 -        s->iir = UART_IIR_NO_INT;
    1.52 +    uint8_t tmp_iir = UART_IIR_NO_INT;
    1.53 +
    1.54 +    if (!s->ier) {
    1.55 +        s->set_irq(s->irq_opaque, s->irq, 0);
    1.56 +	return;
    1.57      }
    1.58 -    if (s->iir != UART_IIR_NO_INT) {
    1.59 +
    1.60 +    if ( ( s->ier & UART_IER_RLSI ) && ( s->lsr & UART_LSR_INT_ANY ) ) {
    1.61 +        tmp_iir = UART_IIR_RLSI;
    1.62 +    } else if ( ( s->ier & UART_IER_RDI ) && ( s->lsr & UART_LSR_DR ) ) {
    1.63 +        tmp_iir = UART_IIR_RDI;
    1.64 +    } else if ( ( s->ier & UART_IER_THRI ) && s->thr_ipending ) {
    1.65 +        tmp_iir = UART_IIR_THRI; 
    1.66 +    } else if ( ( s->ier & UART_IER_MSI ) && ( s->msr & UART_MSR_ANY_DELTA ) ) {
    1.67 +        tmp_iir = UART_IIR_MSI;
    1.68 +    }
    1.69 +
    1.70 +    s->iir = tmp_iir | ( s->iir & 0xF0 );
    1.71 +
    1.72 +    if ( tmp_iir != UART_IIR_NO_INT ) {
    1.73          s->set_irq(s->irq_opaque, s->irq, 1);
    1.74      } else {
    1.75          s->set_irq(s->irq_opaque, s->irq, 0);
    1.76 @@ -131,9 +148,13 @@ static void serial_update_irq(SerialStat
    1.77  
    1.78  static void serial_update_parameters(SerialState *s)
    1.79  {
    1.80 -    int speed, parity, data_bits, stop_bits;
    1.81 +    int speed, parity, data_bits, stop_bits, frame_size;
    1.82      QEMUSerialSetParams ssp;
    1.83  
    1.84 +    if (s->divider == 0)
    1.85 +        return;
    1.86 +
    1.87 +    frame_size = 1;
    1.88      if (s->lcr & 0x08) {
    1.89          if (s->lcr & 0x10)
    1.90              parity = 'E';
    1.91 @@ -141,19 +162,22 @@ static void serial_update_parameters(Ser
    1.92              parity = 'O';
    1.93      } else {
    1.94              parity = 'N';
    1.95 +            frame_size = 0;
    1.96      }
    1.97      if (s->lcr & 0x04) 
    1.98          stop_bits = 2;
    1.99      else
   1.100          stop_bits = 1;
   1.101 +
   1.102      data_bits = (s->lcr & 0x03) + 5;
   1.103 -    if (s->divider == 0)
   1.104 -        return;
   1.105 +    frame_size += data_bits + stop_bits;
   1.106 +
   1.107      speed = 115200 / s->divider;
   1.108      ssp.speed = speed;
   1.109      ssp.parity = parity;
   1.110      ssp.data_bits = data_bits;
   1.111      ssp.stop_bits = stop_bits;
   1.112 +    s->char_transmit_time =  ( ticks_per_sec / speed ) * frame_size;
   1.113      qemu_chr_ioctl(s->chr, CHR_IOCTL_SERIAL_SET_PARAMS, &ssp);
   1.114  #if 0
   1.115      printf("speed=%d parity=%c data=%d stop=%d\n", 
   1.116 @@ -250,6 +274,41 @@ static void serial_chr_write(void *opaqu
   1.117      serial_update_irq(s);
   1.118  }
   1.119  
   1.120 +static void serial_update_msl( SerialState *s )
   1.121 +{
   1.122 +    uint8_t omsr;
   1.123 +    int flags;
   1.124 +
   1.125 +    qemu_del_timer(s->modem_status_poll);
   1.126 +
   1.127 +    if ( qemu_chr_ioctl(s->chr,CHR_IOCTL_SERIAL_GET_TIOCM, &flags) == -ENOTSUP ) {
   1.128 +        s->poll_msl = -1;
   1.129 +        return;
   1.130 +    }
   1.131 +
   1.132 +    omsr = s->msr;
   1.133 +
   1.134 +    s->msr = ( flags & TIOCM_CTS ) ? s->msr | UART_MSR_CTS : s->msr & ~UART_MSR_CTS;
   1.135 +    s->msr = ( flags & TIOCM_DSR ) ? s->msr | UART_MSR_DSR : s->msr & ~UART_MSR_DSR;
   1.136 +    s->msr = ( flags & TIOCM_CAR ) ? s->msr | UART_MSR_DCD : s->msr & ~UART_MSR_DCD;
   1.137 +    s->msr = ( flags & TIOCM_RI ) ? s->msr | UART_MSR_RI : s->msr & ~UART_MSR_RI;
   1.138 +
   1.139 +    if ( s->msr != omsr ) {
   1.140 +         /* Set delta bits */
   1.141 +         s->msr = s->msr | ( ( s->msr >> 4 ) ^ ( omsr >> 4 ) );
   1.142 +         /* UART_MSR_TERI only if change was from 1 -> 0 */
   1.143 +         if ( (s->msr & UART_MSR_TERI) && !(omsr & UART_MSR_RI))
   1.144 +             s->msr &= ~UART_MSR_TERI;
   1.145 +         serial_update_irq(s);
   1.146 +    }
   1.147 +
   1.148 +    /* The real 16550A apparently has a 250ns response latency to line status changes.
   1.149 +       We'll be lazy and poll only every 10ms, and only poll it at all if MSI interrupts are turned on */
   1.150 +
   1.151 +    if (s->poll_msl)
   1.152 +        qemu_mod_timer(s->modem_status_poll, qemu_get_clock(vm_clock) + ticks_per_sec / 100);
   1.153 +}
   1.154 +
   1.155  static void serial_ioport_write(void *opaque, uint32_t addr, uint32_t val)
   1.156  {
   1.157      SerialState *s = opaque;
   1.158 @@ -279,6 +338,17 @@ static void serial_ioport_write(void *op
   1.159              serial_update_parameters(s);
   1.160          } else {
   1.161              s->ier = val & 0x0f;
   1.162 +            /* Turn on polling of modem status lines if guest has IER_MSI turned on */
   1.163 +            if ( s->poll_msl >= 0 ) { 
   1.164 +                if ( s->ier & UART_IER_MSI ) {
   1.165 +                     s->poll_msl = 1;
   1.166 +                     serial_update_msl(s);
   1.167 +                } else {
   1.168 +                     qemu_del_timer(s->modem_status_poll);
   1.169 +                     s->poll_msl = 0;
   1.170 +                }
   1.171 +            }
   1.172 +
   1.173              if (s->lsr & UART_LSR_THRE) {
   1.174                  s->thr_ipending = 1;
   1.175              }
   1.176 @@ -301,7 +371,30 @@ static void serial_ioport_write(void *op
   1.177          }
   1.178          break;
   1.179      case 4:
   1.180 -        s->mcr = val & 0x1f;
   1.181 +	{
   1.182 +            int flags;
   1.183 +	    int old_mcr = s->mcr;
   1.184 +            s->mcr = val & 0x1f;
   1.185 +            if ( val & UART_MCR_LOOP )
   1.186 +                break;
   1.187 +
   1.188 +            if ( ( s->poll_msl >= 0 ) && ( old_mcr != s->mcr ) ) {
   1.189 +
   1.190 +                qemu_chr_ioctl(s->chr,CHR_IOCTL_SERIAL_GET_TIOCM, &flags);
   1.191 +
   1.192 +                flags &= ~( TIOCM_RTS | TIOCM_DTR );
   1.193 +
   1.194 +                if ( val & UART_MCR_RTS )
   1.195 +                    flags |= TIOCM_RTS;
   1.196 +                if ( val & UART_MCR_DTR )
   1.197 +                    flags |= TIOCM_DTR;
   1.198 +
   1.199 +                qemu_chr_ioctl(s->chr,CHR_IOCTL_SERIAL_SET_TIOCM, &flags);
   1.200 +
   1.201 +                /* Update the modem status after a one-character-send wait-time. The dev */
   1.202 +                qemu_mod_timer(s->modem_status_poll, qemu_get_clock(vm_clock) + s->char_transmit_time );
   1.203 +            }
   1.204 +	}
   1.205          break;
   1.206      case 5:
   1.207          break;
   1.208 @@ -363,7 +456,12 @@ static uint32_t serial_ioport_read(void 
   1.209              ret |= (s->mcr & 0x02) << 3;
   1.210              ret |= (s->mcr & 0x01) << 5;
   1.211          } else {
   1.212 +            ret = 0;
   1.213 +            if ( s->poll_msl >= 0 )
   1.214 +                serial_update_msl(s);
   1.215              ret = s->msr;
   1.216 +            s->msr &= 0xF0; /* Clear delta bits after read */
   1.217 +            serial_update_irq(s);
   1.218          }
   1.219          break;
   1.220      case 7:
   1.221 @@ -466,10 +564,19 @@ SerialState *serial_init(SetIRQFunc *set
   1.222      s->set_irq = set_irq;
   1.223      s->irq_opaque = opaque;
   1.224      s->irq = irq;
   1.225 +    s->ier = 0;
   1.226      s->lsr = UART_LSR_TEMT | UART_LSR_THRE;
   1.227      s->iir = UART_IIR_NO_INT;
   1.228 +    s->mcr = UART_MCR_OUT2;
   1.229      s->msr = UART_MSR_DCD | UART_MSR_DSR | UART_MSR_CTS;
   1.230 +    /* Default to 9600 baud, no parity, one stop bit */
   1.231 +    s->divider = 0x0C;
   1.232 +    s->char_transmit_time = ticks_per_sec * ( 9 / 9600 );
   1.233 +
   1.234      s->write_retry_timer = qemu_new_timer(vm_clock, serial_chr_write, s);
   1.235 +    s->modem_status_poll = qemu_new_timer(vm_clock, serial_update_msl, s);
   1.236 + 
   1.237 +    s->poll_msl = 0;
   1.238  
   1.239      register_savevm("serial", base, 2, serial_save, serial_load, s);
   1.240  
   1.241 @@ -478,6 +585,8 @@ SerialState *serial_init(SetIRQFunc *set
   1.242      s->chr = chr;
   1.243      qemu_chr_add_handlers(chr, serial_can_receive1, serial_receive1,
   1.244                            serial_event, s);
   1.245 +    serial_update_msl(s);
   1.246 +
   1.247      return s;
   1.248  }
   1.249  
   1.250 @@ -552,12 +661,21 @@ SerialState *serial_mm_init (SetIRQFunc 
   1.251      s->set_irq = set_irq;
   1.252      s->irq_opaque = opaque;
   1.253      s->irq = irq;
   1.254 +    s->ier = 0;
   1.255      s->lsr = UART_LSR_TEMT | UART_LSR_THRE;
   1.256      s->iir = UART_IIR_NO_INT;
   1.257 +    s->mcr = UART_MCR_OUT2;
   1.258      s->msr = UART_MSR_DCD | UART_MSR_DSR | UART_MSR_CTS;
   1.259 +    /* Default to 9600 baud, no parity, one stop bit */
   1.260 +    s->divider = 0x0C;
   1.261 +    s->char_transmit_time = ticks_per_sec * ( 9 / 9600 );
   1.262 +
   1.263      s->base = base;
   1.264      s->it_shift = it_shift;
   1.265 +
   1.266      s->write_retry_timer = qemu_new_timer(vm_clock, serial_chr_write, s);
   1.267 +    s->modem_status_poll = qemu_new_timer(vm_clock, serial_update_msl, s); 
   1.268 +    s->poll_msl = 0;
   1.269  
   1.270      register_savevm("serial", base, 2, serial_save, serial_load, s);
   1.271  
     2.1 --- a/tools/ioemu/vl.c	Wed Dec 05 14:01:39 2007 +0000
     2.2 +++ b/tools/ioemu/vl.c	Wed Dec 05 14:18:34 2007 +0000
     2.3 @@ -1932,6 +1932,16 @@ static int tty_serial_ioctl(CharDriverSt
     2.4                  tcsendbreak(s->fd_in, 1);
     2.5          }
     2.6          break;
     2.7 +    case CHR_IOCTL_SERIAL_GET_TIOCM:
     2.8 +        {
     2.9 +            ioctl(s->fd_in, TIOCMGET, arg);
    2.10 +        }
    2.11 +        break;
    2.12 +    case CHR_IOCTL_SERIAL_SET_TIOCM:
    2.13 +        {
    2.14 +            ioctl(s->fd_in, TIOCMSET, arg);
    2.15 +        }
    2.16 +        break;
    2.17      default:
    2.18          return -ENOTSUP;
    2.19      }
     3.1 --- a/tools/ioemu/vl.h	Wed Dec 05 14:01:39 2007 +0000
     3.2 +++ b/tools/ioemu/vl.h	Wed Dec 05 14:18:34 2007 +0000
     3.3 @@ -317,6 +317,10 @@ typedef struct {
     3.4  #define CHR_IOCTL_PP_WRITE_CONTROL    6
     3.5  #define CHR_IOCTL_PP_READ_STATUS      7
     3.6  
     3.7 +
     3.8 +#define CHR_IOCTL_SERIAL_SET_TIOCM    8
     3.9 +#define CHR_IOCTL_SERIAL_GET_TIOCM    9
    3.10 +
    3.11  typedef void IOEventHandler(void *opaque, int event);
    3.12  
    3.13  typedef struct CharDriverState {