bellard | 80cabfa | 2004-03-14 12:20:30 +0000 | [diff] [blame] | 1 | /* |
aliguori | 81174da | 2008-08-11 14:17:04 +0000 | [diff] [blame] | 2 | * QEMU 16550A UART emulation |
ths | 5fafdf2 | 2007-09-16 21:08:06 +0000 | [diff] [blame] | 3 | * |
bellard | 80cabfa | 2004-03-14 12:20:30 +0000 | [diff] [blame] | 4 | * Copyright (c) 2003-2004 Fabrice Bellard |
aliguori | 81174da | 2008-08-11 14:17:04 +0000 | [diff] [blame] | 5 | * Copyright (c) 2008 Citrix Systems, Inc. |
ths | 5fafdf2 | 2007-09-16 21:08:06 +0000 | [diff] [blame] | 6 | * |
bellard | 80cabfa | 2004-03-14 12:20:30 +0000 | [diff] [blame] | 7 | * Permission is hereby granted, free of charge, to any person obtaining a copy |
| 8 | * of this software and associated documentation files (the "Software"), to deal |
| 9 | * in the Software without restriction, including without limitation the rights |
| 10 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell |
| 11 | * copies of the Software, and to permit persons to whom the Software is |
| 12 | * furnished to do so, subject to the following conditions: |
| 13 | * |
| 14 | * The above copyright notice and this permission notice shall be included in |
| 15 | * all copies or substantial portions of the Software. |
| 16 | * |
| 17 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
| 18 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
| 19 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL |
| 20 | * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
| 21 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, |
| 22 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN |
| 23 | * THE SOFTWARE. |
| 24 | */ |
pbrook | 87ecb68 | 2007-11-17 17:14:51 +0000 | [diff] [blame] | 25 | #include "hw.h" |
| 26 | #include "qemu-char.h" |
| 27 | #include "isa.h" |
| 28 | #include "pc.h" |
aurel32 | 6936bfe | 2008-05-04 21:42:00 +0000 | [diff] [blame] | 29 | #include "qemu-timer.h" |
Markus Armbruster | 666daa6 | 2010-06-02 18:48:27 +0200 | [diff] [blame] | 30 | #include "sysemu.h" |
bellard | 80cabfa | 2004-03-14 12:20:30 +0000 | [diff] [blame] | 31 | |
| 32 | //#define DEBUG_SERIAL |
| 33 | |
| 34 | #define UART_LCR_DLAB 0x80 /* Divisor latch access bit */ |
| 35 | |
| 36 | #define UART_IER_MSI 0x08 /* Enable Modem status interrupt */ |
| 37 | #define UART_IER_RLSI 0x04 /* Enable receiver line status interrupt */ |
| 38 | #define UART_IER_THRI 0x02 /* Enable Transmitter holding register int. */ |
| 39 | #define UART_IER_RDI 0x01 /* Enable receiver data interrupt */ |
| 40 | |
| 41 | #define UART_IIR_NO_INT 0x01 /* No interrupts pending */ |
| 42 | #define UART_IIR_ID 0x06 /* Mask for the interrupt ID */ |
| 43 | |
| 44 | #define UART_IIR_MSI 0x00 /* Modem status interrupt */ |
| 45 | #define UART_IIR_THRI 0x02 /* Transmitter holding register empty */ |
| 46 | #define UART_IIR_RDI 0x04 /* Receiver data interrupt */ |
| 47 | #define UART_IIR_RLSI 0x06 /* Receiver line status interrupt */ |
aliguori | 81174da | 2008-08-11 14:17:04 +0000 | [diff] [blame] | 48 | #define UART_IIR_CTI 0x0C /* Character Timeout Indication */ |
| 49 | |
| 50 | #define UART_IIR_FENF 0x80 /* Fifo enabled, but not functionning */ |
| 51 | #define UART_IIR_FE 0xC0 /* Fifo enabled */ |
bellard | 80cabfa | 2004-03-14 12:20:30 +0000 | [diff] [blame] | 52 | |
| 53 | /* |
| 54 | * These are the definitions for the Modem Control Register |
| 55 | */ |
| 56 | #define UART_MCR_LOOP 0x10 /* Enable loopback test mode */ |
| 57 | #define UART_MCR_OUT2 0x08 /* Out2 complement */ |
| 58 | #define UART_MCR_OUT1 0x04 /* Out1 complement */ |
| 59 | #define UART_MCR_RTS 0x02 /* RTS complement */ |
| 60 | #define UART_MCR_DTR 0x01 /* DTR complement */ |
| 61 | |
| 62 | /* |
| 63 | * These are the definitions for the Modem Status Register |
| 64 | */ |
| 65 | #define UART_MSR_DCD 0x80 /* Data Carrier Detect */ |
| 66 | #define UART_MSR_RI 0x40 /* Ring Indicator */ |
| 67 | #define UART_MSR_DSR 0x20 /* Data Set Ready */ |
| 68 | #define UART_MSR_CTS 0x10 /* Clear to Send */ |
| 69 | #define UART_MSR_DDCD 0x08 /* Delta DCD */ |
| 70 | #define UART_MSR_TERI 0x04 /* Trailing edge ring indicator */ |
| 71 | #define UART_MSR_DDSR 0x02 /* Delta DSR */ |
| 72 | #define UART_MSR_DCTS 0x01 /* Delta CTS */ |
| 73 | #define UART_MSR_ANY_DELTA 0x0F /* Any of the delta bits! */ |
| 74 | |
| 75 | #define UART_LSR_TEMT 0x40 /* Transmitter empty */ |
| 76 | #define UART_LSR_THRE 0x20 /* Transmit-hold-register empty */ |
| 77 | #define UART_LSR_BI 0x10 /* Break interrupt indicator */ |
| 78 | #define UART_LSR_FE 0x08 /* Frame error indicator */ |
| 79 | #define UART_LSR_PE 0x04 /* Parity error indicator */ |
| 80 | #define UART_LSR_OE 0x02 /* Overrun error indicator */ |
| 81 | #define UART_LSR_DR 0x01 /* Receiver data ready */ |
aliguori | 81174da | 2008-08-11 14:17:04 +0000 | [diff] [blame] | 82 | #define UART_LSR_INT_ANY 0x1E /* Any of the lsr-interrupt-triggering status bits */ |
bellard | 80cabfa | 2004-03-14 12:20:30 +0000 | [diff] [blame] | 83 | |
aliguori | 81174da | 2008-08-11 14:17:04 +0000 | [diff] [blame] | 84 | /* Interrupt trigger levels. The byte-counts are for 16550A - in newer UARTs the byte-count for each ITL is higher. */ |
| 85 | |
| 86 | #define UART_FCR_ITL_1 0x00 /* 1 byte ITL */ |
| 87 | #define UART_FCR_ITL_2 0x40 /* 4 bytes ITL */ |
| 88 | #define UART_FCR_ITL_3 0x80 /* 8 bytes ITL */ |
| 89 | #define UART_FCR_ITL_4 0xC0 /* 14 bytes ITL */ |
| 90 | |
| 91 | #define UART_FCR_DMS 0x08 /* DMA Mode Select */ |
| 92 | #define UART_FCR_XFR 0x04 /* XMIT Fifo Reset */ |
| 93 | #define UART_FCR_RFR 0x02 /* RCVR Fifo Reset */ |
| 94 | #define UART_FCR_FE 0x01 /* FIFO Enable */ |
| 95 | |
| 96 | #define UART_FIFO_LENGTH 16 /* 16550A Fifo Length */ |
| 97 | |
| 98 | #define XMIT_FIFO 0 |
| 99 | #define RECV_FIFO 1 |
| 100 | #define MAX_XMIT_RETRY 4 |
| 101 | |
Michal Novotny | b660114 | 2010-09-13 14:32:32 +0200 | [diff] [blame] | 102 | #ifdef DEBUG_SERIAL |
| 103 | #define DPRINTF(fmt, ...) \ |
Stefan Weil | 46411f8 | 2010-09-13 21:21:57 +0200 | [diff] [blame] | 104 | do { fprintf(stderr, "serial: " fmt , ## __VA_ARGS__); } while (0) |
Michal Novotny | b660114 | 2010-09-13 14:32:32 +0200 | [diff] [blame] | 105 | #else |
| 106 | #define DPRINTF(fmt, ...) \ |
Stefan Weil | 46411f8 | 2010-09-13 21:21:57 +0200 | [diff] [blame] | 107 | do {} while (0) |
Michal Novotny | b660114 | 2010-09-13 14:32:32 +0200 | [diff] [blame] | 108 | #endif |
| 109 | |
Juan Quintela | 2b321d6 | 2009-09-23 01:18:59 +0200 | [diff] [blame] | 110 | typedef struct SerialFIFO { |
aliguori | 81174da | 2008-08-11 14:17:04 +0000 | [diff] [blame] | 111 | uint8_t data[UART_FIFO_LENGTH]; |
| 112 | uint8_t count; |
| 113 | uint8_t itl; /* Interrupt Trigger Level */ |
| 114 | uint8_t tail; |
| 115 | uint8_t head; |
Juan Quintela | 2b321d6 | 2009-09-23 01:18:59 +0200 | [diff] [blame] | 116 | } SerialFIFO; |
aurel32 | 6936bfe | 2008-05-04 21:42:00 +0000 | [diff] [blame] | 117 | |
bellard | b41a2cd | 2004-03-14 21:46:48 +0000 | [diff] [blame] | 118 | struct SerialState { |
bellard | 508d92d | 2006-08-26 18:00:36 +0000 | [diff] [blame] | 119 | uint16_t divider; |
bellard | 80cabfa | 2004-03-14 12:20:30 +0000 | [diff] [blame] | 120 | uint8_t rbr; /* receive register */ |
aliguori | 81174da | 2008-08-11 14:17:04 +0000 | [diff] [blame] | 121 | uint8_t thr; /* transmit holding register */ |
| 122 | uint8_t tsr; /* transmit shift register */ |
bellard | 80cabfa | 2004-03-14 12:20:30 +0000 | [diff] [blame] | 123 | uint8_t ier; |
| 124 | uint8_t iir; /* read only */ |
| 125 | uint8_t lcr; |
| 126 | uint8_t mcr; |
| 127 | uint8_t lsr; /* read only */ |
bellard | 3e749fe | 2006-04-12 20:42:42 +0000 | [diff] [blame] | 128 | uint8_t msr; /* read only */ |
bellard | 80cabfa | 2004-03-14 12:20:30 +0000 | [diff] [blame] | 129 | uint8_t scr; |
aliguori | 81174da | 2008-08-11 14:17:04 +0000 | [diff] [blame] | 130 | uint8_t fcr; |
Juan Quintela | 747791f | 2009-09-10 03:04:46 +0200 | [diff] [blame] | 131 | uint8_t fcr_vmstate; /* we can't write directly this value |
| 132 | it has side effects */ |
bellard | 80cabfa | 2004-03-14 12:20:30 +0000 | [diff] [blame] | 133 | /* NOTE: this hidden state is necessary for tx irq generation as |
| 134 | it can be reset while reading iir */ |
| 135 | int thr_ipending; |
pbrook | d537cf6 | 2007-04-07 18:14:41 +0000 | [diff] [blame] | 136 | qemu_irq irq; |
bellard | 82c643f | 2004-07-14 17:28:13 +0000 | [diff] [blame] | 137 | CharDriverState *chr; |
bellard | f8d179e | 2005-11-08 22:30:36 +0000 | [diff] [blame] | 138 | int last_break_enable; |
bellard | e5d13e2 | 2005-11-23 21:11:49 +0000 | [diff] [blame] | 139 | int it_shift; |
aurel32 | b6cd0ea | 2008-05-04 21:42:11 +0000 | [diff] [blame] | 140 | int baudbase; |
aliguori | 81174da | 2008-08-11 14:17:04 +0000 | [diff] [blame] | 141 | int tsr_retry; |
| 142 | |
| 143 | uint64_t last_xmit_ts; /* Time when the last byte was successfully sent out of the tsr */ |
| 144 | SerialFIFO recv_fifo; |
| 145 | SerialFIFO xmit_fifo; |
| 146 | |
| 147 | struct QEMUTimer *fifo_timeout_timer; |
| 148 | int timeout_ipending; /* timeout interrupt pending state */ |
| 149 | struct QEMUTimer *transmit_timer; |
| 150 | |
| 151 | |
| 152 | uint64_t char_transmit_time; /* time to transmit a char in ticks*/ |
| 153 | int poll_msl; |
| 154 | |
| 155 | struct QEMUTimer *modem_status_poll; |
Richard Henderson | 8e8ffc4 | 2011-08-11 16:18:59 -0700 | [diff] [blame] | 156 | MemoryRegion io; |
bellard | b41a2cd | 2004-03-14 21:46:48 +0000 | [diff] [blame] | 157 | }; |
bellard | 80cabfa | 2004-03-14 12:20:30 +0000 | [diff] [blame] | 158 | |
Gerd Hoffmann | ac0be99 | 2009-09-22 13:53:21 +0200 | [diff] [blame] | 159 | typedef struct ISASerialState { |
| 160 | ISADevice dev; |
Gerd Hoffmann | e8ee28f | 2009-10-13 13:38:39 +0200 | [diff] [blame] | 161 | uint32_t index; |
Gerd Hoffmann | ac0be99 | 2009-09-22 13:53:21 +0200 | [diff] [blame] | 162 | uint32_t iobase; |
| 163 | uint32_t isairq; |
| 164 | SerialState state; |
| 165 | } ISASerialState; |
| 166 | |
aliguori | 81174da | 2008-08-11 14:17:04 +0000 | [diff] [blame] | 167 | static void serial_receive1(void *opaque, const uint8_t *buf, int size); |
| 168 | |
| 169 | static void fifo_clear(SerialState *s, int fifo) |
| 170 | { |
| 171 | SerialFIFO *f = (fifo) ? &s->recv_fifo : &s->xmit_fifo; |
| 172 | memset(f->data, 0, UART_FIFO_LENGTH); |
| 173 | f->count = 0; |
| 174 | f->head = 0; |
| 175 | f->tail = 0; |
| 176 | } |
| 177 | |
| 178 | static int fifo_put(SerialState *s, int fifo, uint8_t chr) |
| 179 | { |
| 180 | SerialFIFO *f = (fifo) ? &s->recv_fifo : &s->xmit_fifo; |
| 181 | |
Justin T. Gibbs | 71e605f | 2010-02-10 14:35:54 -0700 | [diff] [blame] | 182 | /* Receive overruns do not overwrite FIFO contents. */ |
| 183 | if (fifo == XMIT_FIFO || f->count < UART_FIFO_LENGTH) { |
aliguori | 81174da | 2008-08-11 14:17:04 +0000 | [diff] [blame] | 184 | |
Justin T. Gibbs | 71e605f | 2010-02-10 14:35:54 -0700 | [diff] [blame] | 185 | f->data[f->head++] = chr; |
| 186 | |
| 187 | if (f->head == UART_FIFO_LENGTH) |
| 188 | f->head = 0; |
| 189 | } |
| 190 | |
| 191 | if (f->count < UART_FIFO_LENGTH) |
| 192 | f->count++; |
| 193 | else if (fifo == RECV_FIFO) |
| 194 | s->lsr |= UART_LSR_OE; |
aliguori | 81174da | 2008-08-11 14:17:04 +0000 | [diff] [blame] | 195 | |
| 196 | return 1; |
| 197 | } |
| 198 | |
| 199 | static uint8_t fifo_get(SerialState *s, int fifo) |
| 200 | { |
| 201 | SerialFIFO *f = (fifo) ? &s->recv_fifo : &s->xmit_fifo; |
| 202 | uint8_t c; |
| 203 | |
| 204 | if(f->count == 0) |
| 205 | return 0; |
| 206 | |
| 207 | c = f->data[f->tail++]; |
| 208 | if (f->tail == UART_FIFO_LENGTH) |
| 209 | f->tail = 0; |
| 210 | f->count--; |
| 211 | |
| 212 | return c; |
| 213 | } |
balrog | b2a5160 | 2008-02-10 13:40:52 +0000 | [diff] [blame] | 214 | |
bellard | b41a2cd | 2004-03-14 21:46:48 +0000 | [diff] [blame] | 215 | static void serial_update_irq(SerialState *s) |
bellard | 80cabfa | 2004-03-14 12:20:30 +0000 | [diff] [blame] | 216 | { |
aliguori | 81174da | 2008-08-11 14:17:04 +0000 | [diff] [blame] | 217 | uint8_t tmp_iir = UART_IIR_NO_INT; |
| 218 | |
aliguori | 81174da | 2008-08-11 14:17:04 +0000 | [diff] [blame] | 219 | if ((s->ier & UART_IER_RLSI) && (s->lsr & UART_LSR_INT_ANY)) { |
| 220 | tmp_iir = UART_IIR_RLSI; |
balrog | 5628a62 | 2008-09-17 00:21:05 +0000 | [diff] [blame] | 221 | } else if ((s->ier & UART_IER_RDI) && s->timeout_ipending) { |
balrog | c9a3305 | 2008-09-20 01:15:04 +0000 | [diff] [blame] | 222 | /* Note that(s->ier & UART_IER_RDI) can mask this interrupt, |
| 223 | * this is not in the specification but is observed on existing |
| 224 | * hardware. */ |
aliguori | 81174da | 2008-08-11 14:17:04 +0000 | [diff] [blame] | 225 | tmp_iir = UART_IIR_CTI; |
Juergen Lock | 2d6ee8e | 2009-09-12 18:52:22 +0200 | [diff] [blame] | 226 | } else if ((s->ier & UART_IER_RDI) && (s->lsr & UART_LSR_DR) && |
| 227 | (!(s->fcr & UART_FCR_FE) || |
| 228 | s->recv_fifo.count >= s->recv_fifo.itl)) { |
| 229 | tmp_iir = UART_IIR_RDI; |
aliguori | 81174da | 2008-08-11 14:17:04 +0000 | [diff] [blame] | 230 | } else if ((s->ier & UART_IER_THRI) && s->thr_ipending) { |
| 231 | tmp_iir = UART_IIR_THRI; |
| 232 | } else if ((s->ier & UART_IER_MSI) && (s->msr & UART_MSR_ANY_DELTA)) { |
| 233 | tmp_iir = UART_IIR_MSI; |
| 234 | } |
| 235 | |
| 236 | s->iir = tmp_iir | (s->iir & 0xF0); |
| 237 | |
| 238 | if (tmp_iir != UART_IIR_NO_INT) { |
pbrook | d537cf6 | 2007-04-07 18:14:41 +0000 | [diff] [blame] | 239 | qemu_irq_raise(s->irq); |
bellard | 80cabfa | 2004-03-14 12:20:30 +0000 | [diff] [blame] | 240 | } else { |
pbrook | d537cf6 | 2007-04-07 18:14:41 +0000 | [diff] [blame] | 241 | qemu_irq_lower(s->irq); |
bellard | 80cabfa | 2004-03-14 12:20:30 +0000 | [diff] [blame] | 242 | } |
| 243 | } |
| 244 | |
bellard | f8d179e | 2005-11-08 22:30:36 +0000 | [diff] [blame] | 245 | static void serial_update_parameters(SerialState *s) |
| 246 | { |
aliguori | 81174da | 2008-08-11 14:17:04 +0000 | [diff] [blame] | 247 | int speed, parity, data_bits, stop_bits, frame_size; |
bellard | 2122c51 | 2005-11-10 23:58:33 +0000 | [diff] [blame] | 248 | QEMUSerialSetParams ssp; |
bellard | f8d179e | 2005-11-08 22:30:36 +0000 | [diff] [blame] | 249 | |
aliguori | 81174da | 2008-08-11 14:17:04 +0000 | [diff] [blame] | 250 | if (s->divider == 0) |
| 251 | return; |
| 252 | |
Stefan Weil | 718b8ae | 2009-10-26 21:51:41 +0100 | [diff] [blame] | 253 | /* Start bit. */ |
aliguori | 81174da | 2008-08-11 14:17:04 +0000 | [diff] [blame] | 254 | frame_size = 1; |
bellard | f8d179e | 2005-11-08 22:30:36 +0000 | [diff] [blame] | 255 | if (s->lcr & 0x08) { |
Stefan Weil | 718b8ae | 2009-10-26 21:51:41 +0100 | [diff] [blame] | 256 | /* Parity bit. */ |
| 257 | frame_size++; |
bellard | f8d179e | 2005-11-08 22:30:36 +0000 | [diff] [blame] | 258 | if (s->lcr & 0x10) |
| 259 | parity = 'E'; |
| 260 | else |
| 261 | parity = 'O'; |
| 262 | } else { |
| 263 | parity = 'N'; |
| 264 | } |
ths | 5fafdf2 | 2007-09-16 21:08:06 +0000 | [diff] [blame] | 265 | if (s->lcr & 0x04) |
bellard | f8d179e | 2005-11-08 22:30:36 +0000 | [diff] [blame] | 266 | stop_bits = 2; |
| 267 | else |
| 268 | stop_bits = 1; |
aliguori | 81174da | 2008-08-11 14:17:04 +0000 | [diff] [blame] | 269 | |
bellard | f8d179e | 2005-11-08 22:30:36 +0000 | [diff] [blame] | 270 | data_bits = (s->lcr & 0x03) + 5; |
aliguori | 81174da | 2008-08-11 14:17:04 +0000 | [diff] [blame] | 271 | frame_size += data_bits + stop_bits; |
aurel32 | b6cd0ea | 2008-05-04 21:42:11 +0000 | [diff] [blame] | 272 | speed = s->baudbase / s->divider; |
bellard | 2122c51 | 2005-11-10 23:58:33 +0000 | [diff] [blame] | 273 | ssp.speed = speed; |
| 274 | ssp.parity = parity; |
| 275 | ssp.data_bits = data_bits; |
| 276 | ssp.stop_bits = stop_bits; |
Juan Quintela | 6ee093c | 2009-09-10 03:04:26 +0200 | [diff] [blame] | 277 | s->char_transmit_time = (get_ticks_per_sec() / speed) * frame_size; |
Anthony Liguori | 41084f1 | 2011-08-15 11:17:34 -0500 | [diff] [blame] | 278 | qemu_chr_fe_ioctl(s->chr, CHR_IOCTL_SERIAL_SET_PARAMS, &ssp); |
Michal Novotny | b660114 | 2010-09-13 14:32:32 +0200 | [diff] [blame] | 279 | |
| 280 | DPRINTF("speed=%d parity=%c data=%d stop=%d\n", |
bellard | f8d179e | 2005-11-08 22:30:36 +0000 | [diff] [blame] | 281 | speed, parity, data_bits, stop_bits); |
bellard | f8d179e | 2005-11-08 22:30:36 +0000 | [diff] [blame] | 282 | } |
| 283 | |
aliguori | 81174da | 2008-08-11 14:17:04 +0000 | [diff] [blame] | 284 | static void serial_update_msl(SerialState *s) |
| 285 | { |
| 286 | uint8_t omsr; |
| 287 | int flags; |
| 288 | |
| 289 | qemu_del_timer(s->modem_status_poll); |
| 290 | |
Anthony Liguori | 41084f1 | 2011-08-15 11:17:34 -0500 | [diff] [blame] | 291 | if (qemu_chr_fe_ioctl(s->chr,CHR_IOCTL_SERIAL_GET_TIOCM, &flags) == -ENOTSUP) { |
aliguori | 81174da | 2008-08-11 14:17:04 +0000 | [diff] [blame] | 292 | s->poll_msl = -1; |
| 293 | return; |
| 294 | } |
| 295 | |
| 296 | omsr = s->msr; |
| 297 | |
| 298 | s->msr = (flags & CHR_TIOCM_CTS) ? s->msr | UART_MSR_CTS : s->msr & ~UART_MSR_CTS; |
| 299 | s->msr = (flags & CHR_TIOCM_DSR) ? s->msr | UART_MSR_DSR : s->msr & ~UART_MSR_DSR; |
| 300 | s->msr = (flags & CHR_TIOCM_CAR) ? s->msr | UART_MSR_DCD : s->msr & ~UART_MSR_DCD; |
| 301 | s->msr = (flags & CHR_TIOCM_RI) ? s->msr | UART_MSR_RI : s->msr & ~UART_MSR_RI; |
| 302 | |
| 303 | if (s->msr != omsr) { |
| 304 | /* Set delta bits */ |
| 305 | s->msr = s->msr | ((s->msr >> 4) ^ (omsr >> 4)); |
| 306 | /* UART_MSR_TERI only if change was from 1 -> 0 */ |
| 307 | if ((s->msr & UART_MSR_TERI) && !(omsr & UART_MSR_RI)) |
| 308 | s->msr &= ~UART_MSR_TERI; |
| 309 | serial_update_irq(s); |
| 310 | } |
| 311 | |
| 312 | /* The real 16550A apparently has a 250ns response latency to line status changes. |
| 313 | We'll be lazy and poll only every 10ms, and only poll it at all if MSI interrupts are turned on */ |
| 314 | |
| 315 | if (s->poll_msl) |
Paolo Bonzini | 7447545 | 2011-03-11 16:47:48 +0100 | [diff] [blame] | 316 | qemu_mod_timer(s->modem_status_poll, qemu_get_clock_ns(vm_clock) + get_ticks_per_sec() / 100); |
aliguori | 81174da | 2008-08-11 14:17:04 +0000 | [diff] [blame] | 317 | } |
| 318 | |
| 319 | static void serial_xmit(void *opaque) |
| 320 | { |
| 321 | SerialState *s = opaque; |
Paolo Bonzini | 7447545 | 2011-03-11 16:47:48 +0100 | [diff] [blame] | 322 | uint64_t new_xmit_ts = qemu_get_clock_ns(vm_clock); |
aliguori | 81174da | 2008-08-11 14:17:04 +0000 | [diff] [blame] | 323 | |
| 324 | if (s->tsr_retry <= 0) { |
| 325 | if (s->fcr & UART_FCR_FE) { |
| 326 | s->tsr = fifo_get(s,XMIT_FIFO); |
| 327 | if (!s->xmit_fifo.count) |
| 328 | s->lsr |= UART_LSR_THRE; |
| 329 | } else { |
| 330 | s->tsr = s->thr; |
| 331 | s->lsr |= UART_LSR_THRE; |
| 332 | } |
| 333 | } |
| 334 | |
| 335 | if (s->mcr & UART_MCR_LOOP) { |
| 336 | /* in loopback mode, say that we just received a char */ |
| 337 | serial_receive1(s, &s->tsr, 1); |
Anthony Liguori | 2cc6e0a | 2011-08-15 11:17:28 -0500 | [diff] [blame] | 338 | } else if (qemu_chr_fe_write(s->chr, &s->tsr, 1) != 1) { |
aliguori | 81174da | 2008-08-11 14:17:04 +0000 | [diff] [blame] | 339 | if ((s->tsr_retry > 0) && (s->tsr_retry <= MAX_XMIT_RETRY)) { |
| 340 | s->tsr_retry++; |
| 341 | qemu_mod_timer(s->transmit_timer, new_xmit_ts + s->char_transmit_time); |
| 342 | return; |
| 343 | } else if (s->poll_msl < 0) { |
| 344 | /* If we exceed MAX_XMIT_RETRY and the backend is not a real serial port, then |
| 345 | drop any further failed writes instantly, until we get one that goes through. |
| 346 | This is to prevent guests that log to unconnected pipes or pty's from stalling. */ |
| 347 | s->tsr_retry = -1; |
| 348 | } |
| 349 | } |
| 350 | else { |
| 351 | s->tsr_retry = 0; |
| 352 | } |
| 353 | |
Paolo Bonzini | 7447545 | 2011-03-11 16:47:48 +0100 | [diff] [blame] | 354 | s->last_xmit_ts = qemu_get_clock_ns(vm_clock); |
aliguori | 81174da | 2008-08-11 14:17:04 +0000 | [diff] [blame] | 355 | if (!(s->lsr & UART_LSR_THRE)) |
| 356 | qemu_mod_timer(s->transmit_timer, s->last_xmit_ts + s->char_transmit_time); |
| 357 | |
| 358 | if (s->lsr & UART_LSR_THRE) { |
| 359 | s->lsr |= UART_LSR_TEMT; |
| 360 | s->thr_ipending = 1; |
| 361 | serial_update_irq(s); |
| 362 | } |
| 363 | } |
| 364 | |
| 365 | |
bellard | b41a2cd | 2004-03-14 21:46:48 +0000 | [diff] [blame] | 366 | static void serial_ioport_write(void *opaque, uint32_t addr, uint32_t val) |
bellard | 80cabfa | 2004-03-14 12:20:30 +0000 | [diff] [blame] | 367 | { |
bellard | b41a2cd | 2004-03-14 21:46:48 +0000 | [diff] [blame] | 368 | SerialState *s = opaque; |
ths | 3b46e62 | 2007-09-17 08:09:54 +0000 | [diff] [blame] | 369 | |
bellard | 80cabfa | 2004-03-14 12:20:30 +0000 | [diff] [blame] | 370 | addr &= 7; |
Michal Novotny | b660114 | 2010-09-13 14:32:32 +0200 | [diff] [blame] | 371 | DPRINTF("write addr=0x%02x val=0x%02x\n", addr, val); |
bellard | 80cabfa | 2004-03-14 12:20:30 +0000 | [diff] [blame] | 372 | switch(addr) { |
| 373 | default: |
| 374 | case 0: |
| 375 | if (s->lcr & UART_LCR_DLAB) { |
| 376 | s->divider = (s->divider & 0xff00) | val; |
bellard | f8d179e | 2005-11-08 22:30:36 +0000 | [diff] [blame] | 377 | serial_update_parameters(s); |
bellard | 80cabfa | 2004-03-14 12:20:30 +0000 | [diff] [blame] | 378 | } else { |
aliguori | 81174da | 2008-08-11 14:17:04 +0000 | [diff] [blame] | 379 | s->thr = (uint8_t) val; |
| 380 | if(s->fcr & UART_FCR_FE) { |
Aurelien Jarno | 2f4f22b | 2010-03-06 20:23:09 +0100 | [diff] [blame] | 381 | fifo_put(s, XMIT_FIFO, s->thr); |
| 382 | s->thr_ipending = 0; |
| 383 | s->lsr &= ~UART_LSR_TEMT; |
| 384 | s->lsr &= ~UART_LSR_THRE; |
| 385 | serial_update_irq(s); |
aurel32 | 6936bfe | 2008-05-04 21:42:00 +0000 | [diff] [blame] | 386 | } else { |
Aurelien Jarno | 2f4f22b | 2010-03-06 20:23:09 +0100 | [diff] [blame] | 387 | s->thr_ipending = 0; |
| 388 | s->lsr &= ~UART_LSR_THRE; |
| 389 | serial_update_irq(s); |
balrog | b2a5160 | 2008-02-10 13:40:52 +0000 | [diff] [blame] | 390 | } |
aliguori | 81174da | 2008-08-11 14:17:04 +0000 | [diff] [blame] | 391 | serial_xmit(s); |
bellard | 80cabfa | 2004-03-14 12:20:30 +0000 | [diff] [blame] | 392 | } |
| 393 | break; |
| 394 | case 1: |
| 395 | if (s->lcr & UART_LCR_DLAB) { |
| 396 | s->divider = (s->divider & 0x00ff) | (val << 8); |
bellard | f8d179e | 2005-11-08 22:30:36 +0000 | [diff] [blame] | 397 | serial_update_parameters(s); |
bellard | 80cabfa | 2004-03-14 12:20:30 +0000 | [diff] [blame] | 398 | } else { |
bellard | 60e336d | 2004-08-24 21:55:28 +0000 | [diff] [blame] | 399 | s->ier = val & 0x0f; |
aliguori | 81174da | 2008-08-11 14:17:04 +0000 | [diff] [blame] | 400 | /* If the backend device is a real serial port, turn polling of the modem |
| 401 | status lines on physical port on or off depending on UART_IER_MSI state */ |
| 402 | if (s->poll_msl >= 0) { |
| 403 | if (s->ier & UART_IER_MSI) { |
| 404 | s->poll_msl = 1; |
| 405 | serial_update_msl(s); |
| 406 | } else { |
| 407 | qemu_del_timer(s->modem_status_poll); |
| 408 | s->poll_msl = 0; |
| 409 | } |
| 410 | } |
bellard | 60e336d | 2004-08-24 21:55:28 +0000 | [diff] [blame] | 411 | if (s->lsr & UART_LSR_THRE) { |
| 412 | s->thr_ipending = 1; |
aliguori | 81174da | 2008-08-11 14:17:04 +0000 | [diff] [blame] | 413 | serial_update_irq(s); |
bellard | 60e336d | 2004-08-24 21:55:28 +0000 | [diff] [blame] | 414 | } |
bellard | 80cabfa | 2004-03-14 12:20:30 +0000 | [diff] [blame] | 415 | } |
| 416 | break; |
| 417 | case 2: |
aliguori | 81174da | 2008-08-11 14:17:04 +0000 | [diff] [blame] | 418 | val = val & 0xFF; |
| 419 | |
| 420 | if (s->fcr == val) |
| 421 | break; |
| 422 | |
| 423 | /* Did the enable/disable flag change? If so, make sure FIFOs get flushed */ |
| 424 | if ((val ^ s->fcr) & UART_FCR_FE) |
| 425 | val |= UART_FCR_XFR | UART_FCR_RFR; |
| 426 | |
| 427 | /* FIFO clear */ |
| 428 | |
| 429 | if (val & UART_FCR_RFR) { |
| 430 | qemu_del_timer(s->fifo_timeout_timer); |
| 431 | s->timeout_ipending=0; |
| 432 | fifo_clear(s,RECV_FIFO); |
| 433 | } |
| 434 | |
| 435 | if (val & UART_FCR_XFR) { |
| 436 | fifo_clear(s,XMIT_FIFO); |
| 437 | } |
| 438 | |
| 439 | if (val & UART_FCR_FE) { |
| 440 | s->iir |= UART_IIR_FE; |
| 441 | /* Set RECV_FIFO trigger Level */ |
| 442 | switch (val & 0xC0) { |
| 443 | case UART_FCR_ITL_1: |
| 444 | s->recv_fifo.itl = 1; |
| 445 | break; |
| 446 | case UART_FCR_ITL_2: |
| 447 | s->recv_fifo.itl = 4; |
| 448 | break; |
| 449 | case UART_FCR_ITL_3: |
| 450 | s->recv_fifo.itl = 8; |
| 451 | break; |
| 452 | case UART_FCR_ITL_4: |
| 453 | s->recv_fifo.itl = 14; |
| 454 | break; |
| 455 | } |
| 456 | } else |
| 457 | s->iir &= ~UART_IIR_FE; |
| 458 | |
| 459 | /* Set fcr - or at least the bits in it that are supposed to "stick" */ |
| 460 | s->fcr = val & 0xC9; |
| 461 | serial_update_irq(s); |
bellard | 80cabfa | 2004-03-14 12:20:30 +0000 | [diff] [blame] | 462 | break; |
| 463 | case 3: |
bellard | f8d179e | 2005-11-08 22:30:36 +0000 | [diff] [blame] | 464 | { |
| 465 | int break_enable; |
| 466 | s->lcr = val; |
| 467 | serial_update_parameters(s); |
| 468 | break_enable = (val >> 6) & 1; |
| 469 | if (break_enable != s->last_break_enable) { |
| 470 | s->last_break_enable = break_enable; |
Anthony Liguori | 41084f1 | 2011-08-15 11:17:34 -0500 | [diff] [blame] | 471 | qemu_chr_fe_ioctl(s->chr, CHR_IOCTL_SERIAL_SET_BREAK, |
bellard | 2122c51 | 2005-11-10 23:58:33 +0000 | [diff] [blame] | 472 | &break_enable); |
bellard | f8d179e | 2005-11-08 22:30:36 +0000 | [diff] [blame] | 473 | } |
| 474 | } |
bellard | 80cabfa | 2004-03-14 12:20:30 +0000 | [diff] [blame] | 475 | break; |
| 476 | case 4: |
aliguori | 81174da | 2008-08-11 14:17:04 +0000 | [diff] [blame] | 477 | { |
| 478 | int flags; |
| 479 | int old_mcr = s->mcr; |
| 480 | s->mcr = val & 0x1f; |
| 481 | if (val & UART_MCR_LOOP) |
| 482 | break; |
| 483 | |
| 484 | if (s->poll_msl >= 0 && old_mcr != s->mcr) { |
| 485 | |
Anthony Liguori | 41084f1 | 2011-08-15 11:17:34 -0500 | [diff] [blame] | 486 | qemu_chr_fe_ioctl(s->chr,CHR_IOCTL_SERIAL_GET_TIOCM, &flags); |
aliguori | 81174da | 2008-08-11 14:17:04 +0000 | [diff] [blame] | 487 | |
| 488 | flags &= ~(CHR_TIOCM_RTS | CHR_TIOCM_DTR); |
| 489 | |
| 490 | if (val & UART_MCR_RTS) |
| 491 | flags |= CHR_TIOCM_RTS; |
| 492 | if (val & UART_MCR_DTR) |
| 493 | flags |= CHR_TIOCM_DTR; |
| 494 | |
Anthony Liguori | 41084f1 | 2011-08-15 11:17:34 -0500 | [diff] [blame] | 495 | qemu_chr_fe_ioctl(s->chr,CHR_IOCTL_SERIAL_SET_TIOCM, &flags); |
aliguori | 81174da | 2008-08-11 14:17:04 +0000 | [diff] [blame] | 496 | /* Update the modem status after a one-character-send wait-time, since there may be a response |
| 497 | from the device/computer at the other end of the serial line */ |
Paolo Bonzini | 7447545 | 2011-03-11 16:47:48 +0100 | [diff] [blame] | 498 | qemu_mod_timer(s->modem_status_poll, qemu_get_clock_ns(vm_clock) + s->char_transmit_time); |
aliguori | 81174da | 2008-08-11 14:17:04 +0000 | [diff] [blame] | 499 | } |
| 500 | } |
bellard | 80cabfa | 2004-03-14 12:20:30 +0000 | [diff] [blame] | 501 | break; |
| 502 | case 5: |
| 503 | break; |
| 504 | case 6: |
bellard | 80cabfa | 2004-03-14 12:20:30 +0000 | [diff] [blame] | 505 | break; |
| 506 | case 7: |
| 507 | s->scr = val; |
| 508 | break; |
| 509 | } |
| 510 | } |
| 511 | |
bellard | b41a2cd | 2004-03-14 21:46:48 +0000 | [diff] [blame] | 512 | static uint32_t serial_ioport_read(void *opaque, uint32_t addr) |
bellard | 80cabfa | 2004-03-14 12:20:30 +0000 | [diff] [blame] | 513 | { |
bellard | b41a2cd | 2004-03-14 21:46:48 +0000 | [diff] [blame] | 514 | SerialState *s = opaque; |
bellard | 80cabfa | 2004-03-14 12:20:30 +0000 | [diff] [blame] | 515 | uint32_t ret; |
| 516 | |
| 517 | addr &= 7; |
| 518 | switch(addr) { |
| 519 | default: |
| 520 | case 0: |
| 521 | if (s->lcr & UART_LCR_DLAB) { |
ths | 5fafdf2 | 2007-09-16 21:08:06 +0000 | [diff] [blame] | 522 | ret = s->divider & 0xff; |
bellard | 80cabfa | 2004-03-14 12:20:30 +0000 | [diff] [blame] | 523 | } else { |
aliguori | 81174da | 2008-08-11 14:17:04 +0000 | [diff] [blame] | 524 | if(s->fcr & UART_FCR_FE) { |
| 525 | ret = fifo_get(s,RECV_FIFO); |
| 526 | if (s->recv_fifo.count == 0) |
| 527 | s->lsr &= ~(UART_LSR_DR | UART_LSR_BI); |
| 528 | else |
Paolo Bonzini | 7447545 | 2011-03-11 16:47:48 +0100 | [diff] [blame] | 529 | qemu_mod_timer(s->fifo_timeout_timer, qemu_get_clock_ns (vm_clock) + s->char_transmit_time * 4); |
aliguori | 81174da | 2008-08-11 14:17:04 +0000 | [diff] [blame] | 530 | s->timeout_ipending = 0; |
| 531 | } else { |
| 532 | ret = s->rbr; |
| 533 | s->lsr &= ~(UART_LSR_DR | UART_LSR_BI); |
| 534 | } |
bellard | b41a2cd | 2004-03-14 21:46:48 +0000 | [diff] [blame] | 535 | serial_update_irq(s); |
balrog | b2a5160 | 2008-02-10 13:40:52 +0000 | [diff] [blame] | 536 | if (!(s->mcr & UART_MCR_LOOP)) { |
| 537 | /* in loopback mode, don't receive any data */ |
| 538 | qemu_chr_accept_input(s->chr); |
| 539 | } |
bellard | 80cabfa | 2004-03-14 12:20:30 +0000 | [diff] [blame] | 540 | } |
| 541 | break; |
| 542 | case 1: |
| 543 | if (s->lcr & UART_LCR_DLAB) { |
| 544 | ret = (s->divider >> 8) & 0xff; |
| 545 | } else { |
| 546 | ret = s->ier; |
| 547 | } |
| 548 | break; |
| 549 | case 2: |
| 550 | ret = s->iir; |
Aurelien Jarno | cdee7bd | 2010-03-06 22:19:53 +0100 | [diff] [blame] | 551 | if ((ret & UART_IIR_ID) == UART_IIR_THRI) { |
bellard | 80cabfa | 2004-03-14 12:20:30 +0000 | [diff] [blame] | 552 | s->thr_ipending = 0; |
Justin T. Gibbs | 71e605f | 2010-02-10 14:35:54 -0700 | [diff] [blame] | 553 | serial_update_irq(s); |
| 554 | } |
bellard | 80cabfa | 2004-03-14 12:20:30 +0000 | [diff] [blame] | 555 | break; |
| 556 | case 3: |
| 557 | ret = s->lcr; |
| 558 | break; |
| 559 | case 4: |
| 560 | ret = s->mcr; |
| 561 | break; |
| 562 | case 5: |
| 563 | ret = s->lsr; |
Justin T. Gibbs | 71e605f | 2010-02-10 14:35:54 -0700 | [diff] [blame] | 564 | /* Clear break and overrun interrupts */ |
| 565 | if (s->lsr & (UART_LSR_BI|UART_LSR_OE)) { |
| 566 | s->lsr &= ~(UART_LSR_BI|UART_LSR_OE); |
aliguori | 81174da | 2008-08-11 14:17:04 +0000 | [diff] [blame] | 567 | serial_update_irq(s); |
| 568 | } |
bellard | 80cabfa | 2004-03-14 12:20:30 +0000 | [diff] [blame] | 569 | break; |
| 570 | case 6: |
| 571 | if (s->mcr & UART_MCR_LOOP) { |
| 572 | /* in loopback, the modem output pins are connected to the |
| 573 | inputs */ |
| 574 | ret = (s->mcr & 0x0c) << 4; |
| 575 | ret |= (s->mcr & 0x02) << 3; |
| 576 | ret |= (s->mcr & 0x01) << 5; |
| 577 | } else { |
aliguori | 81174da | 2008-08-11 14:17:04 +0000 | [diff] [blame] | 578 | if (s->poll_msl >= 0) |
| 579 | serial_update_msl(s); |
bellard | 80cabfa | 2004-03-14 12:20:30 +0000 | [diff] [blame] | 580 | ret = s->msr; |
aliguori | 81174da | 2008-08-11 14:17:04 +0000 | [diff] [blame] | 581 | /* Clear delta bits & msr int after read, if they were set */ |
| 582 | if (s->msr & UART_MSR_ANY_DELTA) { |
| 583 | s->msr &= 0xF0; |
| 584 | serial_update_irq(s); |
| 585 | } |
bellard | 80cabfa | 2004-03-14 12:20:30 +0000 | [diff] [blame] | 586 | } |
| 587 | break; |
| 588 | case 7: |
| 589 | ret = s->scr; |
| 590 | break; |
| 591 | } |
Michal Novotny | b660114 | 2010-09-13 14:32:32 +0200 | [diff] [blame] | 592 | DPRINTF("read addr=0x%02x val=0x%02x\n", addr, ret); |
bellard | 80cabfa | 2004-03-14 12:20:30 +0000 | [diff] [blame] | 593 | return ret; |
| 594 | } |
| 595 | |
bellard | 82c643f | 2004-07-14 17:28:13 +0000 | [diff] [blame] | 596 | static int serial_can_receive(SerialState *s) |
bellard | 80cabfa | 2004-03-14 12:20:30 +0000 | [diff] [blame] | 597 | { |
aliguori | 81174da | 2008-08-11 14:17:04 +0000 | [diff] [blame] | 598 | if(s->fcr & UART_FCR_FE) { |
| 599 | if(s->recv_fifo.count < UART_FIFO_LENGTH) |
| 600 | /* Advertise (fifo.itl - fifo.count) bytes when count < ITL, and 1 if above. If UART_FIFO_LENGTH - fifo.count is |
| 601 | advertised the effect will be to almost always fill the fifo completely before the guest has a chance to respond, |
| 602 | effectively overriding the ITL that the guest has set. */ |
| 603 | return (s->recv_fifo.count <= s->recv_fifo.itl) ? s->recv_fifo.itl - s->recv_fifo.count : 1; |
| 604 | else |
| 605 | return 0; |
| 606 | } else { |
bellard | 80cabfa | 2004-03-14 12:20:30 +0000 | [diff] [blame] | 607 | return !(s->lsr & UART_LSR_DR); |
aliguori | 81174da | 2008-08-11 14:17:04 +0000 | [diff] [blame] | 608 | } |
bellard | 80cabfa | 2004-03-14 12:20:30 +0000 | [diff] [blame] | 609 | } |
| 610 | |
bellard | 82c643f | 2004-07-14 17:28:13 +0000 | [diff] [blame] | 611 | static void serial_receive_break(SerialState *s) |
bellard | 80cabfa | 2004-03-14 12:20:30 +0000 | [diff] [blame] | 612 | { |
bellard | 80cabfa | 2004-03-14 12:20:30 +0000 | [diff] [blame] | 613 | s->rbr = 0; |
Jason Wessel | 40ff162 | 2009-05-18 10:00:27 -0500 | [diff] [blame] | 614 | /* When the LSR_DR is set a null byte is pushed into the fifo */ |
| 615 | fifo_put(s, RECV_FIFO, '\0'); |
bellard | 80cabfa | 2004-03-14 12:20:30 +0000 | [diff] [blame] | 616 | s->lsr |= UART_LSR_BI | UART_LSR_DR; |
bellard | b41a2cd | 2004-03-14 21:46:48 +0000 | [diff] [blame] | 617 | serial_update_irq(s); |
bellard | 80cabfa | 2004-03-14 12:20:30 +0000 | [diff] [blame] | 618 | } |
| 619 | |
aliguori | 81174da | 2008-08-11 14:17:04 +0000 | [diff] [blame] | 620 | /* There's data in recv_fifo and s->rbr has not been read for 4 char transmit times */ |
| 621 | static void fifo_timeout_int (void *opaque) { |
| 622 | SerialState *s = opaque; |
| 623 | if (s->recv_fifo.count) { |
| 624 | s->timeout_ipending = 1; |
| 625 | serial_update_irq(s); |
| 626 | } |
| 627 | } |
| 628 | |
bellard | b41a2cd | 2004-03-14 21:46:48 +0000 | [diff] [blame] | 629 | static int serial_can_receive1(void *opaque) |
bellard | 80cabfa | 2004-03-14 12:20:30 +0000 | [diff] [blame] | 630 | { |
bellard | b41a2cd | 2004-03-14 21:46:48 +0000 | [diff] [blame] | 631 | SerialState *s = opaque; |
| 632 | return serial_can_receive(s); |
| 633 | } |
bellard | 80cabfa | 2004-03-14 12:20:30 +0000 | [diff] [blame] | 634 | |
bellard | b41a2cd | 2004-03-14 21:46:48 +0000 | [diff] [blame] | 635 | static void serial_receive1(void *opaque, const uint8_t *buf, int size) |
| 636 | { |
| 637 | SerialState *s = opaque; |
aliguori | 81174da | 2008-08-11 14:17:04 +0000 | [diff] [blame] | 638 | if(s->fcr & UART_FCR_FE) { |
| 639 | int i; |
| 640 | for (i = 0; i < size; i++) { |
| 641 | fifo_put(s, RECV_FIFO, buf[i]); |
| 642 | } |
| 643 | s->lsr |= UART_LSR_DR; |
| 644 | /* call the timeout receive callback in 4 char transmit time */ |
Paolo Bonzini | 7447545 | 2011-03-11 16:47:48 +0100 | [diff] [blame] | 645 | qemu_mod_timer(s->fifo_timeout_timer, qemu_get_clock_ns (vm_clock) + s->char_transmit_time * 4); |
aliguori | 81174da | 2008-08-11 14:17:04 +0000 | [diff] [blame] | 646 | } else { |
Justin T. Gibbs | 71e605f | 2010-02-10 14:35:54 -0700 | [diff] [blame] | 647 | if (s->lsr & UART_LSR_DR) |
| 648 | s->lsr |= UART_LSR_OE; |
aliguori | 81174da | 2008-08-11 14:17:04 +0000 | [diff] [blame] | 649 | s->rbr = buf[0]; |
| 650 | s->lsr |= UART_LSR_DR; |
| 651 | } |
| 652 | serial_update_irq(s); |
bellard | b41a2cd | 2004-03-14 21:46:48 +0000 | [diff] [blame] | 653 | } |
| 654 | |
bellard | 82c643f | 2004-07-14 17:28:13 +0000 | [diff] [blame] | 655 | static void serial_event(void *opaque, int event) |
| 656 | { |
| 657 | SerialState *s = opaque; |
Michal Novotny | b660114 | 2010-09-13 14:32:32 +0200 | [diff] [blame] | 658 | DPRINTF("event %x\n", event); |
bellard | 82c643f | 2004-07-14 17:28:13 +0000 | [diff] [blame] | 659 | if (event == CHR_EVENT_BREAK) |
| 660 | serial_receive_break(s); |
| 661 | } |
| 662 | |
Juan Quintela | d4bfa4d | 2009-09-29 22:48:22 +0200 | [diff] [blame] | 663 | static void serial_pre_save(void *opaque) |
bellard | 8738a8d | 2005-11-06 15:48:04 +0000 | [diff] [blame] | 664 | { |
Juan Quintela | d4bfa4d | 2009-09-29 22:48:22 +0200 | [diff] [blame] | 665 | SerialState *s = opaque; |
Juan Quintela | 747791f | 2009-09-10 03:04:46 +0200 | [diff] [blame] | 666 | s->fcr_vmstate = s->fcr; |
bellard | 8738a8d | 2005-11-06 15:48:04 +0000 | [diff] [blame] | 667 | } |
| 668 | |
Juan Quintela | e59fb37 | 2009-09-29 22:48:21 +0200 | [diff] [blame] | 669 | static int serial_post_load(void *opaque, int version_id) |
Juan Quintela | 747791f | 2009-09-10 03:04:46 +0200 | [diff] [blame] | 670 | { |
| 671 | SerialState *s = opaque; |
| 672 | |
Juan Quintela | 4c18ce9 | 2009-10-16 15:39:58 +0200 | [diff] [blame] | 673 | if (version_id < 3) { |
| 674 | s->fcr_vmstate = 0; |
| 675 | } |
Juan Quintela | 747791f | 2009-09-10 03:04:46 +0200 | [diff] [blame] | 676 | /* Initialize fcr via setter to perform essential side-effects */ |
| 677 | serial_ioport_write(s, 0x02, s->fcr_vmstate); |
Michal Novotny | 9a7c487 | 2010-09-15 15:35:53 +0200 | [diff] [blame] | 678 | serial_update_parameters(s); |
Juan Quintela | 747791f | 2009-09-10 03:04:46 +0200 | [diff] [blame] | 679 | return 0; |
| 680 | } |
| 681 | |
| 682 | static const VMStateDescription vmstate_serial = { |
| 683 | .name = "serial", |
| 684 | .version_id = 3, |
| 685 | .minimum_version_id = 2, |
| 686 | .pre_save = serial_pre_save, |
Juan Quintela | 747791f | 2009-09-10 03:04:46 +0200 | [diff] [blame] | 687 | .post_load = serial_post_load, |
| 688 | .fields = (VMStateField []) { |
| 689 | VMSTATE_UINT16_V(divider, SerialState, 2), |
| 690 | VMSTATE_UINT8(rbr, SerialState), |
| 691 | VMSTATE_UINT8(ier, SerialState), |
| 692 | VMSTATE_UINT8(iir, SerialState), |
| 693 | VMSTATE_UINT8(lcr, SerialState), |
| 694 | VMSTATE_UINT8(mcr, SerialState), |
| 695 | VMSTATE_UINT8(lsr, SerialState), |
| 696 | VMSTATE_UINT8(msr, SerialState), |
| 697 | VMSTATE_UINT8(scr, SerialState), |
| 698 | VMSTATE_UINT8_V(fcr_vmstate, SerialState, 3), |
| 699 | VMSTATE_END_OF_LIST() |
| 700 | } |
| 701 | }; |
| 702 | |
balrog | b2a5160 | 2008-02-10 13:40:52 +0000 | [diff] [blame] | 703 | static void serial_reset(void *opaque) |
| 704 | { |
| 705 | SerialState *s = opaque; |
| 706 | |
balrog | b2a5160 | 2008-02-10 13:40:52 +0000 | [diff] [blame] | 707 | s->rbr = 0; |
| 708 | s->ier = 0; |
| 709 | s->iir = UART_IIR_NO_INT; |
| 710 | s->lcr = 0; |
balrog | b2a5160 | 2008-02-10 13:40:52 +0000 | [diff] [blame] | 711 | s->lsr = UART_LSR_TEMT | UART_LSR_THRE; |
| 712 | s->msr = UART_MSR_DCD | UART_MSR_DSR | UART_MSR_CTS; |
Stefan Weil | 718b8ae | 2009-10-26 21:51:41 +0100 | [diff] [blame] | 713 | /* Default to 9600 baud, 1 start bit, 8 data bits, 1 stop bit, no parity. */ |
aliguori | 81174da | 2008-08-11 14:17:04 +0000 | [diff] [blame] | 714 | s->divider = 0x0C; |
| 715 | s->mcr = UART_MCR_OUT2; |
balrog | b2a5160 | 2008-02-10 13:40:52 +0000 | [diff] [blame] | 716 | s->scr = 0; |
aliguori | 81174da | 2008-08-11 14:17:04 +0000 | [diff] [blame] | 717 | s->tsr_retry = 0; |
Stefan Weil | 718b8ae | 2009-10-26 21:51:41 +0100 | [diff] [blame] | 718 | s->char_transmit_time = (get_ticks_per_sec() / 9600) * 10; |
aliguori | 81174da | 2008-08-11 14:17:04 +0000 | [diff] [blame] | 719 | s->poll_msl = 0; |
| 720 | |
| 721 | fifo_clear(s,RECV_FIFO); |
| 722 | fifo_clear(s,XMIT_FIFO); |
| 723 | |
Paolo Bonzini | 7447545 | 2011-03-11 16:47:48 +0100 | [diff] [blame] | 724 | s->last_xmit_ts = qemu_get_clock_ns(vm_clock); |
balrog | b2a5160 | 2008-02-10 13:40:52 +0000 | [diff] [blame] | 725 | |
| 726 | s->thr_ipending = 0; |
| 727 | s->last_break_enable = 0; |
| 728 | qemu_irq_lower(s->irq); |
| 729 | } |
| 730 | |
Gerd Hoffmann | ac0be99 | 2009-09-22 13:53:21 +0200 | [diff] [blame] | 731 | static void serial_init_core(SerialState *s) |
aliguori | 81174da | 2008-08-11 14:17:04 +0000 | [diff] [blame] | 732 | { |
Gerd Hoffmann | ac0be99 | 2009-09-22 13:53:21 +0200 | [diff] [blame] | 733 | if (!s->chr) { |
Aurelien Jarno | 387f4a5 | 2009-09-15 01:16:28 +0200 | [diff] [blame] | 734 | fprintf(stderr, "Can't create serial device, empty char device\n"); |
| 735 | exit(1); |
| 736 | } |
| 737 | |
Paolo Bonzini | 7447545 | 2011-03-11 16:47:48 +0100 | [diff] [blame] | 738 | s->modem_status_poll = qemu_new_timer_ns(vm_clock, (QEMUTimerCB *) serial_update_msl, s); |
aliguori | 81174da | 2008-08-11 14:17:04 +0000 | [diff] [blame] | 739 | |
Paolo Bonzini | 7447545 | 2011-03-11 16:47:48 +0100 | [diff] [blame] | 740 | s->fifo_timeout_timer = qemu_new_timer_ns(vm_clock, (QEMUTimerCB *) fifo_timeout_int, s); |
| 741 | s->transmit_timer = qemu_new_timer_ns(vm_clock, (QEMUTimerCB *) serial_xmit, s); |
aliguori | 81174da | 2008-08-11 14:17:04 +0000 | [diff] [blame] | 742 | |
Jan Kiszka | a08d436 | 2009-06-27 09:25:07 +0200 | [diff] [blame] | 743 | qemu_register_reset(serial_reset, s); |
aliguori | 81174da | 2008-08-11 14:17:04 +0000 | [diff] [blame] | 744 | |
aurel32 | b47543c | 2009-01-18 14:28:10 +0000 | [diff] [blame] | 745 | qemu_chr_add_handlers(s->chr, serial_can_receive1, serial_receive1, |
| 746 | serial_event, s); |
aliguori | 81174da | 2008-08-11 14:17:04 +0000 | [diff] [blame] | 747 | } |
| 748 | |
Stefan Weil | 038eaf8 | 2009-10-31 11:28:11 +0100 | [diff] [blame] | 749 | /* Change the main reference oscillator frequency. */ |
| 750 | void serial_set_frequency(SerialState *s, uint32_t frequency) |
| 751 | { |
| 752 | s->baudbase = frequency; |
| 753 | serial_update_parameters(s); |
| 754 | } |
| 755 | |
Gerd Hoffmann | e8ee28f | 2009-10-13 13:38:39 +0200 | [diff] [blame] | 756 | static const int isa_serial_io[MAX_SERIAL_PORTS] = { 0x3f8, 0x2f8, 0x3e8, 0x2e8 }; |
| 757 | static const int isa_serial_irq[MAX_SERIAL_PORTS] = { 4, 3, 4, 3 }; |
| 758 | |
Richard Henderson | a941ae4 | 2011-08-10 15:28:18 -0700 | [diff] [blame] | 759 | static const MemoryRegionPortio serial_portio[] = { |
| 760 | { 0, 8, 1, .read = serial_ioport_read, .write = serial_ioport_write }, |
| 761 | PORTIO_END_OF_LIST() |
| 762 | }; |
| 763 | |
| 764 | static const MemoryRegionOps serial_io_ops = { |
| 765 | .old_portio = serial_portio |
| 766 | }; |
| 767 | |
Gerd Hoffmann | ac0be99 | 2009-09-22 13:53:21 +0200 | [diff] [blame] | 768 | static int serial_isa_initfn(ISADevice *dev) |
| 769 | { |
Gerd Hoffmann | e8ee28f | 2009-10-13 13:38:39 +0200 | [diff] [blame] | 770 | static int index; |
Gerd Hoffmann | ac0be99 | 2009-09-22 13:53:21 +0200 | [diff] [blame] | 771 | ISASerialState *isa = DO_UPCAST(ISASerialState, dev, dev); |
| 772 | SerialState *s = &isa->state; |
| 773 | |
Gerd Hoffmann | e8ee28f | 2009-10-13 13:38:39 +0200 | [diff] [blame] | 774 | if (isa->index == -1) |
| 775 | isa->index = index; |
| 776 | if (isa->index >= MAX_SERIAL_PORTS) |
| 777 | return -1; |
| 778 | if (isa->iobase == -1) |
| 779 | isa->iobase = isa_serial_io[isa->index]; |
| 780 | if (isa->isairq == -1) |
| 781 | isa->isairq = isa_serial_irq[isa->index]; |
| 782 | index++; |
| 783 | |
Gerd Hoffmann | ac0be99 | 2009-09-22 13:53:21 +0200 | [diff] [blame] | 784 | s->baudbase = 115200; |
| 785 | isa_init_irq(dev, &s->irq, isa->isairq); |
| 786 | serial_init_core(s); |
Jan Kiszka | 1cc9f51 | 2010-05-15 13:32:41 +0200 | [diff] [blame] | 787 | qdev_set_legacy_instance_id(&dev->qdev, isa->iobase, 3); |
Gerd Hoffmann | ac0be99 | 2009-09-22 13:53:21 +0200 | [diff] [blame] | 788 | |
Richard Henderson | 8e8ffc4 | 2011-08-11 16:18:59 -0700 | [diff] [blame] | 789 | memory_region_init_io(&s->io, &serial_io_ops, s, "serial", 8); |
| 790 | isa_register_ioport(dev, &s->io, isa->iobase); |
Gerd Hoffmann | ac0be99 | 2009-09-22 13:53:21 +0200 | [diff] [blame] | 791 | return 0; |
| 792 | } |
| 793 | |
Jan Kiszka | 1cc9f51 | 2010-05-15 13:32:41 +0200 | [diff] [blame] | 794 | static const VMStateDescription vmstate_isa_serial = { |
| 795 | .name = "serial", |
| 796 | .version_id = 3, |
| 797 | .minimum_version_id = 2, |
| 798 | .fields = (VMStateField []) { |
| 799 | VMSTATE_STRUCT(state, ISASerialState, 0, vmstate_serial, SerialState), |
| 800 | VMSTATE_END_OF_LIST() |
| 801 | } |
| 802 | }; |
| 803 | |
aurel32 | b6cd0ea | 2008-05-04 21:42:11 +0000 | [diff] [blame] | 804 | SerialState *serial_init(int base, qemu_irq irq, int baudbase, |
| 805 | CharDriverState *chr) |
bellard | b41a2cd | 2004-03-14 21:46:48 +0000 | [diff] [blame] | 806 | { |
| 807 | SerialState *s; |
| 808 | |
Anthony Liguori | 7267c09 | 2011-08-20 22:09:37 -0500 | [diff] [blame] | 809 | s = g_malloc0(sizeof(SerialState)); |
balrog | b2a5160 | 2008-02-10 13:40:52 +0000 | [diff] [blame] | 810 | |
Gerd Hoffmann | ac0be99 | 2009-09-22 13:53:21 +0200 | [diff] [blame] | 811 | s->irq = irq; |
| 812 | s->baudbase = baudbase; |
| 813 | s->chr = chr; |
| 814 | serial_init_core(s); |
aurel32 | 6936bfe | 2008-05-04 21:42:00 +0000 | [diff] [blame] | 815 | |
Alex Williamson | 0be71e3 | 2010-06-25 11:09:07 -0600 | [diff] [blame] | 816 | vmstate_register(NULL, base, &vmstate_serial, s); |
bellard | 8738a8d | 2005-11-06 15:48:04 +0000 | [diff] [blame] | 817 | |
bellard | b41a2cd | 2004-03-14 21:46:48 +0000 | [diff] [blame] | 818 | register_ioport_write(base, 8, 1, serial_ioport_write, s); |
| 819 | register_ioport_read(base, 8, 1, serial_ioport_read, s); |
bellard | b41a2cd | 2004-03-14 21:46:48 +0000 | [diff] [blame] | 820 | return s; |
bellard | 80cabfa | 2004-03-14 12:20:30 +0000 | [diff] [blame] | 821 | } |
bellard | e5d13e2 | 2005-11-23 21:11:49 +0000 | [diff] [blame] | 822 | |
| 823 | /* Memory mapped interface */ |
Richard Henderson | 8e8ffc4 | 2011-08-11 16:18:59 -0700 | [diff] [blame] | 824 | static uint64_t serial_mm_read(void *opaque, target_phys_addr_t addr, |
| 825 | unsigned size) |
bellard | e5d13e2 | 2005-11-23 21:11:49 +0000 | [diff] [blame] | 826 | { |
| 827 | SerialState *s = opaque; |
Richard Henderson | 8e8ffc4 | 2011-08-11 16:18:59 -0700 | [diff] [blame] | 828 | return serial_ioport_read(s, addr >> s->it_shift); |
bellard | e5d13e2 | 2005-11-23 21:11:49 +0000 | [diff] [blame] | 829 | } |
| 830 | |
Richard Henderson | 8e8ffc4 | 2011-08-11 16:18:59 -0700 | [diff] [blame] | 831 | static void serial_mm_write(void *opaque, target_phys_addr_t addr, |
| 832 | uint64_t value, unsigned size) |
bellard | e5d13e2 | 2005-11-23 21:11:49 +0000 | [diff] [blame] | 833 | { |
| 834 | SerialState *s = opaque; |
Richard Henderson | 8e8ffc4 | 2011-08-11 16:18:59 -0700 | [diff] [blame] | 835 | value &= ~0u >> (32 - (size * 8)); |
pbrook | 8da3ff1 | 2008-12-01 18:59:50 +0000 | [diff] [blame] | 836 | serial_ioport_write(s, addr >> s->it_shift, value); |
bellard | e5d13e2 | 2005-11-23 21:11:49 +0000 | [diff] [blame] | 837 | } |
| 838 | |
Richard Henderson | 8e8ffc4 | 2011-08-11 16:18:59 -0700 | [diff] [blame] | 839 | static const MemoryRegionOps serial_mm_ops[3] = { |
| 840 | [DEVICE_NATIVE_ENDIAN] = { |
| 841 | .read = serial_mm_read, |
| 842 | .write = serial_mm_write, |
| 843 | .endianness = DEVICE_NATIVE_ENDIAN, |
| 844 | }, |
| 845 | [DEVICE_LITTLE_ENDIAN] = { |
| 846 | .read = serial_mm_read, |
| 847 | .write = serial_mm_write, |
| 848 | .endianness = DEVICE_LITTLE_ENDIAN, |
| 849 | }, |
| 850 | [DEVICE_BIG_ENDIAN] = { |
| 851 | .read = serial_mm_read, |
| 852 | .write = serial_mm_write, |
| 853 | .endianness = DEVICE_BIG_ENDIAN, |
| 854 | }, |
bellard | e5d13e2 | 2005-11-23 21:11:49 +0000 | [diff] [blame] | 855 | }; |
| 856 | |
Richard Henderson | 39186d8 | 2011-08-11 16:07:16 -0700 | [diff] [blame] | 857 | SerialState *serial_mm_init(MemoryRegion *address_space, |
| 858 | target_phys_addr_t base, int it_shift, |
| 859 | qemu_irq irq, int baudbase, |
| 860 | CharDriverState *chr, enum device_endian end) |
bellard | e5d13e2 | 2005-11-23 21:11:49 +0000 | [diff] [blame] | 861 | { |
| 862 | SerialState *s; |
bellard | e5d13e2 | 2005-11-23 21:11:49 +0000 | [diff] [blame] | 863 | |
Anthony Liguori | 7267c09 | 2011-08-20 22:09:37 -0500 | [diff] [blame] | 864 | s = g_malloc0(sizeof(SerialState)); |
aliguori | 81174da | 2008-08-11 14:17:04 +0000 | [diff] [blame] | 865 | |
bellard | e5d13e2 | 2005-11-23 21:11:49 +0000 | [diff] [blame] | 866 | s->it_shift = it_shift; |
Gerd Hoffmann | ac0be99 | 2009-09-22 13:53:21 +0200 | [diff] [blame] | 867 | s->irq = irq; |
| 868 | s->baudbase = baudbase; |
| 869 | s->chr = chr; |
bellard | e5d13e2 | 2005-11-23 21:11:49 +0000 | [diff] [blame] | 870 | |
Gerd Hoffmann | ac0be99 | 2009-09-22 13:53:21 +0200 | [diff] [blame] | 871 | serial_init_core(s); |
Alex Williamson | 0be71e3 | 2010-06-25 11:09:07 -0600 | [diff] [blame] | 872 | vmstate_register(NULL, base, &vmstate_serial, s); |
bellard | e5d13e2 | 2005-11-23 21:11:49 +0000 | [diff] [blame] | 873 | |
Richard Henderson | 8e8ffc4 | 2011-08-11 16:18:59 -0700 | [diff] [blame] | 874 | memory_region_init_io(&s->io, &serial_mm_ops[end], s, |
| 875 | "serial", 8 << it_shift); |
Richard Henderson | 39186d8 | 2011-08-11 16:07:16 -0700 | [diff] [blame] | 876 | memory_region_add_subregion(address_space, base, &s->io); |
Richard Henderson | 2ff0c7c | 2011-08-11 16:07:15 -0700 | [diff] [blame] | 877 | |
aliguori | 81174da | 2008-08-11 14:17:04 +0000 | [diff] [blame] | 878 | serial_update_msl(s); |
bellard | e5d13e2 | 2005-11-23 21:11:49 +0000 | [diff] [blame] | 879 | return s; |
| 880 | } |
Gerd Hoffmann | ac0be99 | 2009-09-22 13:53:21 +0200 | [diff] [blame] | 881 | |
| 882 | static ISADeviceInfo serial_isa_info = { |
| 883 | .qdev.name = "isa-serial", |
| 884 | .qdev.size = sizeof(ISASerialState), |
Jan Kiszka | 1cc9f51 | 2010-05-15 13:32:41 +0200 | [diff] [blame] | 885 | .qdev.vmsd = &vmstate_isa_serial, |
Gerd Hoffmann | ac0be99 | 2009-09-22 13:53:21 +0200 | [diff] [blame] | 886 | .init = serial_isa_initfn, |
| 887 | .qdev.props = (Property[]) { |
Gerd Hoffmann | 51954d5 | 2009-11-17 11:28:41 +0100 | [diff] [blame] | 888 | DEFINE_PROP_UINT32("index", ISASerialState, index, -1), |
Gerd Hoffmann | e8ee28f | 2009-10-13 13:38:39 +0200 | [diff] [blame] | 889 | DEFINE_PROP_HEX32("iobase", ISASerialState, iobase, -1), |
| 890 | DEFINE_PROP_UINT32("irq", ISASerialState, isairq, -1), |
Gerd Hoffmann | ac0be99 | 2009-09-22 13:53:21 +0200 | [diff] [blame] | 891 | DEFINE_PROP_CHR("chardev", ISASerialState, state.chr), |
| 892 | DEFINE_PROP_END_OF_LIST(), |
| 893 | }, |
| 894 | }; |
| 895 | |
| 896 | static void serial_register_devices(void) |
| 897 | { |
| 898 | isa_qdev_register(&serial_isa_info); |
| 899 | } |
| 900 | |
| 901 | device_init(serial_register_devices) |