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; |
Gerd Hoffmann | 9826fd5 | 2012-02-23 13:45:23 +0100 | [diff] [blame] | 142 | uint32_t wakeup; |
aliguori | 81174da | 2008-08-11 14:17:04 +0000 | [diff] [blame] | 143 | |
| 144 | uint64_t last_xmit_ts; /* Time when the last byte was successfully sent out of the tsr */ |
| 145 | SerialFIFO recv_fifo; |
| 146 | SerialFIFO xmit_fifo; |
| 147 | |
| 148 | struct QEMUTimer *fifo_timeout_timer; |
| 149 | int timeout_ipending; /* timeout interrupt pending state */ |
| 150 | struct QEMUTimer *transmit_timer; |
| 151 | |
| 152 | |
| 153 | uint64_t char_transmit_time; /* time to transmit a char in ticks*/ |
| 154 | int poll_msl; |
| 155 | |
| 156 | struct QEMUTimer *modem_status_poll; |
Richard Henderson | 8e8ffc4 | 2011-08-11 16:18:59 -0700 | [diff] [blame] | 157 | MemoryRegion io; |
bellard | b41a2cd | 2004-03-14 21:46:48 +0000 | [diff] [blame] | 158 | }; |
bellard | 80cabfa | 2004-03-14 12:20:30 +0000 | [diff] [blame] | 159 | |
Gerd Hoffmann | ac0be99 | 2009-09-22 13:53:21 +0200 | [diff] [blame] | 160 | typedef struct ISASerialState { |
| 161 | ISADevice dev; |
Gerd Hoffmann | e8ee28f | 2009-10-13 13:38:39 +0200 | [diff] [blame] | 162 | uint32_t index; |
Gerd Hoffmann | ac0be99 | 2009-09-22 13:53:21 +0200 | [diff] [blame] | 163 | uint32_t iobase; |
| 164 | uint32_t isairq; |
| 165 | SerialState state; |
| 166 | } ISASerialState; |
| 167 | |
aliguori | 81174da | 2008-08-11 14:17:04 +0000 | [diff] [blame] | 168 | static void serial_receive1(void *opaque, const uint8_t *buf, int size); |
| 169 | |
| 170 | static void fifo_clear(SerialState *s, int fifo) |
| 171 | { |
| 172 | SerialFIFO *f = (fifo) ? &s->recv_fifo : &s->xmit_fifo; |
| 173 | memset(f->data, 0, UART_FIFO_LENGTH); |
| 174 | f->count = 0; |
| 175 | f->head = 0; |
| 176 | f->tail = 0; |
| 177 | } |
| 178 | |
| 179 | static int fifo_put(SerialState *s, int fifo, uint8_t chr) |
| 180 | { |
| 181 | SerialFIFO *f = (fifo) ? &s->recv_fifo : &s->xmit_fifo; |
| 182 | |
Justin T. Gibbs | 71e605f | 2010-02-10 14:35:54 -0700 | [diff] [blame] | 183 | /* Receive overruns do not overwrite FIFO contents. */ |
| 184 | if (fifo == XMIT_FIFO || f->count < UART_FIFO_LENGTH) { |
aliguori | 81174da | 2008-08-11 14:17:04 +0000 | [diff] [blame] | 185 | |
Justin T. Gibbs | 71e605f | 2010-02-10 14:35:54 -0700 | [diff] [blame] | 186 | f->data[f->head++] = chr; |
| 187 | |
| 188 | if (f->head == UART_FIFO_LENGTH) |
| 189 | f->head = 0; |
| 190 | } |
| 191 | |
| 192 | if (f->count < UART_FIFO_LENGTH) |
| 193 | f->count++; |
| 194 | else if (fifo == RECV_FIFO) |
| 195 | s->lsr |= UART_LSR_OE; |
aliguori | 81174da | 2008-08-11 14:17:04 +0000 | [diff] [blame] | 196 | |
| 197 | return 1; |
| 198 | } |
| 199 | |
| 200 | static uint8_t fifo_get(SerialState *s, int fifo) |
| 201 | { |
| 202 | SerialFIFO *f = (fifo) ? &s->recv_fifo : &s->xmit_fifo; |
| 203 | uint8_t c; |
| 204 | |
| 205 | if(f->count == 0) |
| 206 | return 0; |
| 207 | |
| 208 | c = f->data[f->tail++]; |
| 209 | if (f->tail == UART_FIFO_LENGTH) |
| 210 | f->tail = 0; |
| 211 | f->count--; |
| 212 | |
| 213 | return c; |
| 214 | } |
balrog | b2a5160 | 2008-02-10 13:40:52 +0000 | [diff] [blame] | 215 | |
bellard | b41a2cd | 2004-03-14 21:46:48 +0000 | [diff] [blame] | 216 | static void serial_update_irq(SerialState *s) |
bellard | 80cabfa | 2004-03-14 12:20:30 +0000 | [diff] [blame] | 217 | { |
aliguori | 81174da | 2008-08-11 14:17:04 +0000 | [diff] [blame] | 218 | uint8_t tmp_iir = UART_IIR_NO_INT; |
| 219 | |
aliguori | 81174da | 2008-08-11 14:17:04 +0000 | [diff] [blame] | 220 | if ((s->ier & UART_IER_RLSI) && (s->lsr & UART_LSR_INT_ANY)) { |
| 221 | tmp_iir = UART_IIR_RLSI; |
balrog | 5628a62 | 2008-09-17 00:21:05 +0000 | [diff] [blame] | 222 | } else if ((s->ier & UART_IER_RDI) && s->timeout_ipending) { |
balrog | c9a3305 | 2008-09-20 01:15:04 +0000 | [diff] [blame] | 223 | /* Note that(s->ier & UART_IER_RDI) can mask this interrupt, |
| 224 | * this is not in the specification but is observed on existing |
| 225 | * hardware. */ |
aliguori | 81174da | 2008-08-11 14:17:04 +0000 | [diff] [blame] | 226 | tmp_iir = UART_IIR_CTI; |
Juergen Lock | 2d6ee8e | 2009-09-12 18:52:22 +0200 | [diff] [blame] | 227 | } else if ((s->ier & UART_IER_RDI) && (s->lsr & UART_LSR_DR) && |
| 228 | (!(s->fcr & UART_FCR_FE) || |
| 229 | s->recv_fifo.count >= s->recv_fifo.itl)) { |
| 230 | tmp_iir = UART_IIR_RDI; |
aliguori | 81174da | 2008-08-11 14:17:04 +0000 | [diff] [blame] | 231 | } else if ((s->ier & UART_IER_THRI) && s->thr_ipending) { |
| 232 | tmp_iir = UART_IIR_THRI; |
| 233 | } else if ((s->ier & UART_IER_MSI) && (s->msr & UART_MSR_ANY_DELTA)) { |
| 234 | tmp_iir = UART_IIR_MSI; |
| 235 | } |
| 236 | |
| 237 | s->iir = tmp_iir | (s->iir & 0xF0); |
| 238 | |
| 239 | if (tmp_iir != UART_IIR_NO_INT) { |
pbrook | d537cf6 | 2007-04-07 18:14:41 +0000 | [diff] [blame] | 240 | qemu_irq_raise(s->irq); |
bellard | 80cabfa | 2004-03-14 12:20:30 +0000 | [diff] [blame] | 241 | } else { |
pbrook | d537cf6 | 2007-04-07 18:14:41 +0000 | [diff] [blame] | 242 | qemu_irq_lower(s->irq); |
bellard | 80cabfa | 2004-03-14 12:20:30 +0000 | [diff] [blame] | 243 | } |
| 244 | } |
| 245 | |
bellard | f8d179e | 2005-11-08 22:30:36 +0000 | [diff] [blame] | 246 | static void serial_update_parameters(SerialState *s) |
| 247 | { |
aliguori | 81174da | 2008-08-11 14:17:04 +0000 | [diff] [blame] | 248 | int speed, parity, data_bits, stop_bits, frame_size; |
bellard | 2122c51 | 2005-11-10 23:58:33 +0000 | [diff] [blame] | 249 | QEMUSerialSetParams ssp; |
bellard | f8d179e | 2005-11-08 22:30:36 +0000 | [diff] [blame] | 250 | |
aliguori | 81174da | 2008-08-11 14:17:04 +0000 | [diff] [blame] | 251 | if (s->divider == 0) |
| 252 | return; |
| 253 | |
Stefan Weil | 718b8ae | 2009-10-26 21:51:41 +0100 | [diff] [blame] | 254 | /* Start bit. */ |
aliguori | 81174da | 2008-08-11 14:17:04 +0000 | [diff] [blame] | 255 | frame_size = 1; |
bellard | f8d179e | 2005-11-08 22:30:36 +0000 | [diff] [blame] | 256 | if (s->lcr & 0x08) { |
Stefan Weil | 718b8ae | 2009-10-26 21:51:41 +0100 | [diff] [blame] | 257 | /* Parity bit. */ |
| 258 | frame_size++; |
bellard | f8d179e | 2005-11-08 22:30:36 +0000 | [diff] [blame] | 259 | if (s->lcr & 0x10) |
| 260 | parity = 'E'; |
| 261 | else |
| 262 | parity = 'O'; |
| 263 | } else { |
| 264 | parity = 'N'; |
| 265 | } |
ths | 5fafdf2 | 2007-09-16 21:08:06 +0000 | [diff] [blame] | 266 | if (s->lcr & 0x04) |
bellard | f8d179e | 2005-11-08 22:30:36 +0000 | [diff] [blame] | 267 | stop_bits = 2; |
| 268 | else |
| 269 | stop_bits = 1; |
aliguori | 81174da | 2008-08-11 14:17:04 +0000 | [diff] [blame] | 270 | |
bellard | f8d179e | 2005-11-08 22:30:36 +0000 | [diff] [blame] | 271 | data_bits = (s->lcr & 0x03) + 5; |
aliguori | 81174da | 2008-08-11 14:17:04 +0000 | [diff] [blame] | 272 | frame_size += data_bits + stop_bits; |
aurel32 | b6cd0ea | 2008-05-04 21:42:11 +0000 | [diff] [blame] | 273 | speed = s->baudbase / s->divider; |
bellard | 2122c51 | 2005-11-10 23:58:33 +0000 | [diff] [blame] | 274 | ssp.speed = speed; |
| 275 | ssp.parity = parity; |
| 276 | ssp.data_bits = data_bits; |
| 277 | ssp.stop_bits = stop_bits; |
Juan Quintela | 6ee093c | 2009-09-10 03:04:26 +0200 | [diff] [blame] | 278 | s->char_transmit_time = (get_ticks_per_sec() / speed) * frame_size; |
Anthony Liguori | 41084f1 | 2011-08-15 11:17:34 -0500 | [diff] [blame] | 279 | qemu_chr_fe_ioctl(s->chr, CHR_IOCTL_SERIAL_SET_PARAMS, &ssp); |
Michal Novotny | b660114 | 2010-09-13 14:32:32 +0200 | [diff] [blame] | 280 | |
| 281 | DPRINTF("speed=%d parity=%c data=%d stop=%d\n", |
bellard | f8d179e | 2005-11-08 22:30:36 +0000 | [diff] [blame] | 282 | speed, parity, data_bits, stop_bits); |
bellard | f8d179e | 2005-11-08 22:30:36 +0000 | [diff] [blame] | 283 | } |
| 284 | |
aliguori | 81174da | 2008-08-11 14:17:04 +0000 | [diff] [blame] | 285 | static void serial_update_msl(SerialState *s) |
| 286 | { |
| 287 | uint8_t omsr; |
| 288 | int flags; |
| 289 | |
| 290 | qemu_del_timer(s->modem_status_poll); |
| 291 | |
Anthony Liguori | 41084f1 | 2011-08-15 11:17:34 -0500 | [diff] [blame] | 292 | 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] | 293 | s->poll_msl = -1; |
| 294 | return; |
| 295 | } |
| 296 | |
| 297 | omsr = s->msr; |
| 298 | |
| 299 | s->msr = (flags & CHR_TIOCM_CTS) ? s->msr | UART_MSR_CTS : s->msr & ~UART_MSR_CTS; |
| 300 | s->msr = (flags & CHR_TIOCM_DSR) ? s->msr | UART_MSR_DSR : s->msr & ~UART_MSR_DSR; |
| 301 | s->msr = (flags & CHR_TIOCM_CAR) ? s->msr | UART_MSR_DCD : s->msr & ~UART_MSR_DCD; |
| 302 | s->msr = (flags & CHR_TIOCM_RI) ? s->msr | UART_MSR_RI : s->msr & ~UART_MSR_RI; |
| 303 | |
| 304 | if (s->msr != omsr) { |
| 305 | /* Set delta bits */ |
| 306 | s->msr = s->msr | ((s->msr >> 4) ^ (omsr >> 4)); |
| 307 | /* UART_MSR_TERI only if change was from 1 -> 0 */ |
| 308 | if ((s->msr & UART_MSR_TERI) && !(omsr & UART_MSR_RI)) |
| 309 | s->msr &= ~UART_MSR_TERI; |
| 310 | serial_update_irq(s); |
| 311 | } |
| 312 | |
| 313 | /* The real 16550A apparently has a 250ns response latency to line status changes. |
| 314 | We'll be lazy and poll only every 10ms, and only poll it at all if MSI interrupts are turned on */ |
| 315 | |
| 316 | if (s->poll_msl) |
Paolo Bonzini | 7447545 | 2011-03-11 16:47:48 +0100 | [diff] [blame] | 317 | 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] | 318 | } |
| 319 | |
| 320 | static void serial_xmit(void *opaque) |
| 321 | { |
| 322 | SerialState *s = opaque; |
Paolo Bonzini | 7447545 | 2011-03-11 16:47:48 +0100 | [diff] [blame] | 323 | uint64_t new_xmit_ts = qemu_get_clock_ns(vm_clock); |
aliguori | 81174da | 2008-08-11 14:17:04 +0000 | [diff] [blame] | 324 | |
| 325 | if (s->tsr_retry <= 0) { |
| 326 | if (s->fcr & UART_FCR_FE) { |
| 327 | s->tsr = fifo_get(s,XMIT_FIFO); |
| 328 | if (!s->xmit_fifo.count) |
| 329 | s->lsr |= UART_LSR_THRE; |
Anthony Liguori | 67c5322 | 2012-04-01 14:03:21 -0500 | [diff] [blame] | 330 | } else if ((s->lsr & UART_LSR_THRE)) { |
| 331 | return; |
aliguori | 81174da | 2008-08-11 14:17:04 +0000 | [diff] [blame] | 332 | } else { |
| 333 | s->tsr = s->thr; |
| 334 | s->lsr |= UART_LSR_THRE; |
Anthony Liguori | dfe844c | 2012-04-01 14:18:30 -0500 | [diff] [blame] | 335 | s->lsr &= ~UART_LSR_TEMT; |
aliguori | 81174da | 2008-08-11 14:17:04 +0000 | [diff] [blame] | 336 | } |
| 337 | } |
| 338 | |
| 339 | if (s->mcr & UART_MCR_LOOP) { |
| 340 | /* in loopback mode, say that we just received a char */ |
| 341 | serial_receive1(s, &s->tsr, 1); |
Anthony Liguori | 2cc6e0a | 2011-08-15 11:17:28 -0500 | [diff] [blame] | 342 | } else if (qemu_chr_fe_write(s->chr, &s->tsr, 1) != 1) { |
Anthony Liguori | 67c5322 | 2012-04-01 14:03:21 -0500 | [diff] [blame] | 343 | if ((s->tsr_retry >= 0) && (s->tsr_retry <= MAX_XMIT_RETRY)) { |
aliguori | 81174da | 2008-08-11 14:17:04 +0000 | [diff] [blame] | 344 | s->tsr_retry++; |
| 345 | qemu_mod_timer(s->transmit_timer, new_xmit_ts + s->char_transmit_time); |
| 346 | return; |
| 347 | } else if (s->poll_msl < 0) { |
| 348 | /* If we exceed MAX_XMIT_RETRY and the backend is not a real serial port, then |
| 349 | drop any further failed writes instantly, until we get one that goes through. |
| 350 | This is to prevent guests that log to unconnected pipes or pty's from stalling. */ |
| 351 | s->tsr_retry = -1; |
| 352 | } |
| 353 | } |
| 354 | else { |
| 355 | s->tsr_retry = 0; |
| 356 | } |
| 357 | |
Paolo Bonzini | 7447545 | 2011-03-11 16:47:48 +0100 | [diff] [blame] | 358 | s->last_xmit_ts = qemu_get_clock_ns(vm_clock); |
aliguori | 81174da | 2008-08-11 14:17:04 +0000 | [diff] [blame] | 359 | if (!(s->lsr & UART_LSR_THRE)) |
| 360 | qemu_mod_timer(s->transmit_timer, s->last_xmit_ts + s->char_transmit_time); |
| 361 | |
| 362 | if (s->lsr & UART_LSR_THRE) { |
| 363 | s->lsr |= UART_LSR_TEMT; |
| 364 | s->thr_ipending = 1; |
| 365 | serial_update_irq(s); |
| 366 | } |
| 367 | } |
| 368 | |
| 369 | |
bellard | b41a2cd | 2004-03-14 21:46:48 +0000 | [diff] [blame] | 370 | static void serial_ioport_write(void *opaque, uint32_t addr, uint32_t val) |
bellard | 80cabfa | 2004-03-14 12:20:30 +0000 | [diff] [blame] | 371 | { |
bellard | b41a2cd | 2004-03-14 21:46:48 +0000 | [diff] [blame] | 372 | SerialState *s = opaque; |
ths | 3b46e62 | 2007-09-17 08:09:54 +0000 | [diff] [blame] | 373 | |
bellard | 80cabfa | 2004-03-14 12:20:30 +0000 | [diff] [blame] | 374 | addr &= 7; |
Michal Novotny | b660114 | 2010-09-13 14:32:32 +0200 | [diff] [blame] | 375 | DPRINTF("write addr=0x%02x val=0x%02x\n", addr, val); |
bellard | 80cabfa | 2004-03-14 12:20:30 +0000 | [diff] [blame] | 376 | switch(addr) { |
| 377 | default: |
| 378 | case 0: |
| 379 | if (s->lcr & UART_LCR_DLAB) { |
| 380 | s->divider = (s->divider & 0xff00) | val; |
bellard | f8d179e | 2005-11-08 22:30:36 +0000 | [diff] [blame] | 381 | serial_update_parameters(s); |
bellard | 80cabfa | 2004-03-14 12:20:30 +0000 | [diff] [blame] | 382 | } else { |
aliguori | 81174da | 2008-08-11 14:17:04 +0000 | [diff] [blame] | 383 | s->thr = (uint8_t) val; |
| 384 | if(s->fcr & UART_FCR_FE) { |
Aurelien Jarno | 2f4f22b | 2010-03-06 20:23:09 +0100 | [diff] [blame] | 385 | fifo_put(s, XMIT_FIFO, s->thr); |
| 386 | s->thr_ipending = 0; |
| 387 | s->lsr &= ~UART_LSR_TEMT; |
| 388 | s->lsr &= ~UART_LSR_THRE; |
| 389 | serial_update_irq(s); |
aurel32 | 6936bfe | 2008-05-04 21:42:00 +0000 | [diff] [blame] | 390 | } else { |
Aurelien Jarno | 2f4f22b | 2010-03-06 20:23:09 +0100 | [diff] [blame] | 391 | s->thr_ipending = 0; |
| 392 | s->lsr &= ~UART_LSR_THRE; |
| 393 | serial_update_irq(s); |
balrog | b2a5160 | 2008-02-10 13:40:52 +0000 | [diff] [blame] | 394 | } |
aliguori | 81174da | 2008-08-11 14:17:04 +0000 | [diff] [blame] | 395 | serial_xmit(s); |
bellard | 80cabfa | 2004-03-14 12:20:30 +0000 | [diff] [blame] | 396 | } |
| 397 | break; |
| 398 | case 1: |
| 399 | if (s->lcr & UART_LCR_DLAB) { |
| 400 | s->divider = (s->divider & 0x00ff) | (val << 8); |
bellard | f8d179e | 2005-11-08 22:30:36 +0000 | [diff] [blame] | 401 | serial_update_parameters(s); |
bellard | 80cabfa | 2004-03-14 12:20:30 +0000 | [diff] [blame] | 402 | } else { |
bellard | 60e336d | 2004-08-24 21:55:28 +0000 | [diff] [blame] | 403 | s->ier = val & 0x0f; |
aliguori | 81174da | 2008-08-11 14:17:04 +0000 | [diff] [blame] | 404 | /* If the backend device is a real serial port, turn polling of the modem |
| 405 | status lines on physical port on or off depending on UART_IER_MSI state */ |
| 406 | if (s->poll_msl >= 0) { |
| 407 | if (s->ier & UART_IER_MSI) { |
| 408 | s->poll_msl = 1; |
| 409 | serial_update_msl(s); |
| 410 | } else { |
| 411 | qemu_del_timer(s->modem_status_poll); |
| 412 | s->poll_msl = 0; |
| 413 | } |
| 414 | } |
bellard | 60e336d | 2004-08-24 21:55:28 +0000 | [diff] [blame] | 415 | if (s->lsr & UART_LSR_THRE) { |
| 416 | s->thr_ipending = 1; |
aliguori | 81174da | 2008-08-11 14:17:04 +0000 | [diff] [blame] | 417 | serial_update_irq(s); |
bellard | 60e336d | 2004-08-24 21:55:28 +0000 | [diff] [blame] | 418 | } |
bellard | 80cabfa | 2004-03-14 12:20:30 +0000 | [diff] [blame] | 419 | } |
| 420 | break; |
| 421 | case 2: |
aliguori | 81174da | 2008-08-11 14:17:04 +0000 | [diff] [blame] | 422 | val = val & 0xFF; |
| 423 | |
| 424 | if (s->fcr == val) |
| 425 | break; |
| 426 | |
| 427 | /* Did the enable/disable flag change? If so, make sure FIFOs get flushed */ |
| 428 | if ((val ^ s->fcr) & UART_FCR_FE) |
| 429 | val |= UART_FCR_XFR | UART_FCR_RFR; |
| 430 | |
| 431 | /* FIFO clear */ |
| 432 | |
| 433 | if (val & UART_FCR_RFR) { |
| 434 | qemu_del_timer(s->fifo_timeout_timer); |
| 435 | s->timeout_ipending=0; |
| 436 | fifo_clear(s,RECV_FIFO); |
| 437 | } |
| 438 | |
| 439 | if (val & UART_FCR_XFR) { |
| 440 | fifo_clear(s,XMIT_FIFO); |
| 441 | } |
| 442 | |
| 443 | if (val & UART_FCR_FE) { |
| 444 | s->iir |= UART_IIR_FE; |
| 445 | /* Set RECV_FIFO trigger Level */ |
| 446 | switch (val & 0xC0) { |
| 447 | case UART_FCR_ITL_1: |
| 448 | s->recv_fifo.itl = 1; |
| 449 | break; |
| 450 | case UART_FCR_ITL_2: |
| 451 | s->recv_fifo.itl = 4; |
| 452 | break; |
| 453 | case UART_FCR_ITL_3: |
| 454 | s->recv_fifo.itl = 8; |
| 455 | break; |
| 456 | case UART_FCR_ITL_4: |
| 457 | s->recv_fifo.itl = 14; |
| 458 | break; |
| 459 | } |
| 460 | } else |
| 461 | s->iir &= ~UART_IIR_FE; |
| 462 | |
| 463 | /* Set fcr - or at least the bits in it that are supposed to "stick" */ |
| 464 | s->fcr = val & 0xC9; |
| 465 | serial_update_irq(s); |
bellard | 80cabfa | 2004-03-14 12:20:30 +0000 | [diff] [blame] | 466 | break; |
| 467 | case 3: |
bellard | f8d179e | 2005-11-08 22:30:36 +0000 | [diff] [blame] | 468 | { |
| 469 | int break_enable; |
| 470 | s->lcr = val; |
| 471 | serial_update_parameters(s); |
| 472 | break_enable = (val >> 6) & 1; |
| 473 | if (break_enable != s->last_break_enable) { |
| 474 | s->last_break_enable = break_enable; |
Anthony Liguori | 41084f1 | 2011-08-15 11:17:34 -0500 | [diff] [blame] | 475 | qemu_chr_fe_ioctl(s->chr, CHR_IOCTL_SERIAL_SET_BREAK, |
bellard | 2122c51 | 2005-11-10 23:58:33 +0000 | [diff] [blame] | 476 | &break_enable); |
bellard | f8d179e | 2005-11-08 22:30:36 +0000 | [diff] [blame] | 477 | } |
| 478 | } |
bellard | 80cabfa | 2004-03-14 12:20:30 +0000 | [diff] [blame] | 479 | break; |
| 480 | case 4: |
aliguori | 81174da | 2008-08-11 14:17:04 +0000 | [diff] [blame] | 481 | { |
| 482 | int flags; |
| 483 | int old_mcr = s->mcr; |
| 484 | s->mcr = val & 0x1f; |
| 485 | if (val & UART_MCR_LOOP) |
| 486 | break; |
| 487 | |
| 488 | if (s->poll_msl >= 0 && old_mcr != s->mcr) { |
| 489 | |
Anthony Liguori | 41084f1 | 2011-08-15 11:17:34 -0500 | [diff] [blame] | 490 | qemu_chr_fe_ioctl(s->chr,CHR_IOCTL_SERIAL_GET_TIOCM, &flags); |
aliguori | 81174da | 2008-08-11 14:17:04 +0000 | [diff] [blame] | 491 | |
| 492 | flags &= ~(CHR_TIOCM_RTS | CHR_TIOCM_DTR); |
| 493 | |
| 494 | if (val & UART_MCR_RTS) |
| 495 | flags |= CHR_TIOCM_RTS; |
| 496 | if (val & UART_MCR_DTR) |
| 497 | flags |= CHR_TIOCM_DTR; |
| 498 | |
Anthony Liguori | 41084f1 | 2011-08-15 11:17:34 -0500 | [diff] [blame] | 499 | qemu_chr_fe_ioctl(s->chr,CHR_IOCTL_SERIAL_SET_TIOCM, &flags); |
aliguori | 81174da | 2008-08-11 14:17:04 +0000 | [diff] [blame] | 500 | /* Update the modem status after a one-character-send wait-time, since there may be a response |
| 501 | from the device/computer at the other end of the serial line */ |
Paolo Bonzini | 7447545 | 2011-03-11 16:47:48 +0100 | [diff] [blame] | 502 | 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] | 503 | } |
| 504 | } |
bellard | 80cabfa | 2004-03-14 12:20:30 +0000 | [diff] [blame] | 505 | break; |
| 506 | case 5: |
| 507 | break; |
| 508 | case 6: |
bellard | 80cabfa | 2004-03-14 12:20:30 +0000 | [diff] [blame] | 509 | break; |
| 510 | case 7: |
| 511 | s->scr = val; |
| 512 | break; |
| 513 | } |
| 514 | } |
| 515 | |
bellard | b41a2cd | 2004-03-14 21:46:48 +0000 | [diff] [blame] | 516 | static uint32_t serial_ioport_read(void *opaque, uint32_t addr) |
bellard | 80cabfa | 2004-03-14 12:20:30 +0000 | [diff] [blame] | 517 | { |
bellard | b41a2cd | 2004-03-14 21:46:48 +0000 | [diff] [blame] | 518 | SerialState *s = opaque; |
bellard | 80cabfa | 2004-03-14 12:20:30 +0000 | [diff] [blame] | 519 | uint32_t ret; |
| 520 | |
| 521 | addr &= 7; |
| 522 | switch(addr) { |
| 523 | default: |
| 524 | case 0: |
| 525 | if (s->lcr & UART_LCR_DLAB) { |
ths | 5fafdf2 | 2007-09-16 21:08:06 +0000 | [diff] [blame] | 526 | ret = s->divider & 0xff; |
bellard | 80cabfa | 2004-03-14 12:20:30 +0000 | [diff] [blame] | 527 | } else { |
aliguori | 81174da | 2008-08-11 14:17:04 +0000 | [diff] [blame] | 528 | if(s->fcr & UART_FCR_FE) { |
| 529 | ret = fifo_get(s,RECV_FIFO); |
| 530 | if (s->recv_fifo.count == 0) |
| 531 | s->lsr &= ~(UART_LSR_DR | UART_LSR_BI); |
| 532 | else |
Paolo Bonzini | 7447545 | 2011-03-11 16:47:48 +0100 | [diff] [blame] | 533 | 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] | 534 | s->timeout_ipending = 0; |
| 535 | } else { |
| 536 | ret = s->rbr; |
| 537 | s->lsr &= ~(UART_LSR_DR | UART_LSR_BI); |
| 538 | } |
bellard | b41a2cd | 2004-03-14 21:46:48 +0000 | [diff] [blame] | 539 | serial_update_irq(s); |
balrog | b2a5160 | 2008-02-10 13:40:52 +0000 | [diff] [blame] | 540 | if (!(s->mcr & UART_MCR_LOOP)) { |
| 541 | /* in loopback mode, don't receive any data */ |
| 542 | qemu_chr_accept_input(s->chr); |
| 543 | } |
bellard | 80cabfa | 2004-03-14 12:20:30 +0000 | [diff] [blame] | 544 | } |
| 545 | break; |
| 546 | case 1: |
| 547 | if (s->lcr & UART_LCR_DLAB) { |
| 548 | ret = (s->divider >> 8) & 0xff; |
| 549 | } else { |
| 550 | ret = s->ier; |
| 551 | } |
| 552 | break; |
| 553 | case 2: |
| 554 | ret = s->iir; |
Aurelien Jarno | cdee7bd | 2010-03-06 22:19:53 +0100 | [diff] [blame] | 555 | if ((ret & UART_IIR_ID) == UART_IIR_THRI) { |
bellard | 80cabfa | 2004-03-14 12:20:30 +0000 | [diff] [blame] | 556 | s->thr_ipending = 0; |
Justin T. Gibbs | 71e605f | 2010-02-10 14:35:54 -0700 | [diff] [blame] | 557 | serial_update_irq(s); |
| 558 | } |
bellard | 80cabfa | 2004-03-14 12:20:30 +0000 | [diff] [blame] | 559 | break; |
| 560 | case 3: |
| 561 | ret = s->lcr; |
| 562 | break; |
| 563 | case 4: |
| 564 | ret = s->mcr; |
| 565 | break; |
| 566 | case 5: |
| 567 | ret = s->lsr; |
Justin T. Gibbs | 71e605f | 2010-02-10 14:35:54 -0700 | [diff] [blame] | 568 | /* Clear break and overrun interrupts */ |
| 569 | if (s->lsr & (UART_LSR_BI|UART_LSR_OE)) { |
| 570 | s->lsr &= ~(UART_LSR_BI|UART_LSR_OE); |
aliguori | 81174da | 2008-08-11 14:17:04 +0000 | [diff] [blame] | 571 | serial_update_irq(s); |
| 572 | } |
bellard | 80cabfa | 2004-03-14 12:20:30 +0000 | [diff] [blame] | 573 | break; |
| 574 | case 6: |
| 575 | if (s->mcr & UART_MCR_LOOP) { |
| 576 | /* in loopback, the modem output pins are connected to the |
| 577 | inputs */ |
| 578 | ret = (s->mcr & 0x0c) << 4; |
| 579 | ret |= (s->mcr & 0x02) << 3; |
| 580 | ret |= (s->mcr & 0x01) << 5; |
| 581 | } else { |
aliguori | 81174da | 2008-08-11 14:17:04 +0000 | [diff] [blame] | 582 | if (s->poll_msl >= 0) |
| 583 | serial_update_msl(s); |
bellard | 80cabfa | 2004-03-14 12:20:30 +0000 | [diff] [blame] | 584 | ret = s->msr; |
aliguori | 81174da | 2008-08-11 14:17:04 +0000 | [diff] [blame] | 585 | /* Clear delta bits & msr int after read, if they were set */ |
| 586 | if (s->msr & UART_MSR_ANY_DELTA) { |
| 587 | s->msr &= 0xF0; |
| 588 | serial_update_irq(s); |
| 589 | } |
bellard | 80cabfa | 2004-03-14 12:20:30 +0000 | [diff] [blame] | 590 | } |
| 591 | break; |
| 592 | case 7: |
| 593 | ret = s->scr; |
| 594 | break; |
| 595 | } |
Michal Novotny | b660114 | 2010-09-13 14:32:32 +0200 | [diff] [blame] | 596 | DPRINTF("read addr=0x%02x val=0x%02x\n", addr, ret); |
bellard | 80cabfa | 2004-03-14 12:20:30 +0000 | [diff] [blame] | 597 | return ret; |
| 598 | } |
| 599 | |
bellard | 82c643f | 2004-07-14 17:28:13 +0000 | [diff] [blame] | 600 | static int serial_can_receive(SerialState *s) |
bellard | 80cabfa | 2004-03-14 12:20:30 +0000 | [diff] [blame] | 601 | { |
aliguori | 81174da | 2008-08-11 14:17:04 +0000 | [diff] [blame] | 602 | if(s->fcr & UART_FCR_FE) { |
| 603 | if(s->recv_fifo.count < UART_FIFO_LENGTH) |
| 604 | /* Advertise (fifo.itl - fifo.count) bytes when count < ITL, and 1 if above. If UART_FIFO_LENGTH - fifo.count is |
| 605 | advertised the effect will be to almost always fill the fifo completely before the guest has a chance to respond, |
| 606 | effectively overriding the ITL that the guest has set. */ |
| 607 | return (s->recv_fifo.count <= s->recv_fifo.itl) ? s->recv_fifo.itl - s->recv_fifo.count : 1; |
| 608 | else |
| 609 | return 0; |
| 610 | } else { |
bellard | 80cabfa | 2004-03-14 12:20:30 +0000 | [diff] [blame] | 611 | return !(s->lsr & UART_LSR_DR); |
aliguori | 81174da | 2008-08-11 14:17:04 +0000 | [diff] [blame] | 612 | } |
bellard | 80cabfa | 2004-03-14 12:20:30 +0000 | [diff] [blame] | 613 | } |
| 614 | |
bellard | 82c643f | 2004-07-14 17:28:13 +0000 | [diff] [blame] | 615 | static void serial_receive_break(SerialState *s) |
bellard | 80cabfa | 2004-03-14 12:20:30 +0000 | [diff] [blame] | 616 | { |
bellard | 80cabfa | 2004-03-14 12:20:30 +0000 | [diff] [blame] | 617 | s->rbr = 0; |
Jason Wessel | 40ff162 | 2009-05-18 10:00:27 -0500 | [diff] [blame] | 618 | /* When the LSR_DR is set a null byte is pushed into the fifo */ |
| 619 | fifo_put(s, RECV_FIFO, '\0'); |
bellard | 80cabfa | 2004-03-14 12:20:30 +0000 | [diff] [blame] | 620 | s->lsr |= UART_LSR_BI | UART_LSR_DR; |
bellard | b41a2cd | 2004-03-14 21:46:48 +0000 | [diff] [blame] | 621 | serial_update_irq(s); |
bellard | 80cabfa | 2004-03-14 12:20:30 +0000 | [diff] [blame] | 622 | } |
| 623 | |
aliguori | 81174da | 2008-08-11 14:17:04 +0000 | [diff] [blame] | 624 | /* There's data in recv_fifo and s->rbr has not been read for 4 char transmit times */ |
| 625 | static void fifo_timeout_int (void *opaque) { |
| 626 | SerialState *s = opaque; |
| 627 | if (s->recv_fifo.count) { |
| 628 | s->timeout_ipending = 1; |
| 629 | serial_update_irq(s); |
| 630 | } |
| 631 | } |
| 632 | |
bellard | b41a2cd | 2004-03-14 21:46:48 +0000 | [diff] [blame] | 633 | static int serial_can_receive1(void *opaque) |
bellard | 80cabfa | 2004-03-14 12:20:30 +0000 | [diff] [blame] | 634 | { |
bellard | b41a2cd | 2004-03-14 21:46:48 +0000 | [diff] [blame] | 635 | SerialState *s = opaque; |
| 636 | return serial_can_receive(s); |
| 637 | } |
bellard | 80cabfa | 2004-03-14 12:20:30 +0000 | [diff] [blame] | 638 | |
bellard | b41a2cd | 2004-03-14 21:46:48 +0000 | [diff] [blame] | 639 | static void serial_receive1(void *opaque, const uint8_t *buf, int size) |
| 640 | { |
| 641 | SerialState *s = opaque; |
Gerd Hoffmann | 9826fd5 | 2012-02-23 13:45:23 +0100 | [diff] [blame] | 642 | |
| 643 | if (s->wakeup) { |
| 644 | qemu_system_wakeup_request(QEMU_WAKEUP_REASON_OTHER); |
| 645 | } |
aliguori | 81174da | 2008-08-11 14:17:04 +0000 | [diff] [blame] | 646 | if(s->fcr & UART_FCR_FE) { |
| 647 | int i; |
| 648 | for (i = 0; i < size; i++) { |
| 649 | fifo_put(s, RECV_FIFO, buf[i]); |
| 650 | } |
| 651 | s->lsr |= UART_LSR_DR; |
| 652 | /* call the timeout receive callback in 4 char transmit time */ |
Paolo Bonzini | 7447545 | 2011-03-11 16:47:48 +0100 | [diff] [blame] | 653 | 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] | 654 | } else { |
Justin T. Gibbs | 71e605f | 2010-02-10 14:35:54 -0700 | [diff] [blame] | 655 | if (s->lsr & UART_LSR_DR) |
| 656 | s->lsr |= UART_LSR_OE; |
aliguori | 81174da | 2008-08-11 14:17:04 +0000 | [diff] [blame] | 657 | s->rbr = buf[0]; |
| 658 | s->lsr |= UART_LSR_DR; |
| 659 | } |
| 660 | serial_update_irq(s); |
bellard | b41a2cd | 2004-03-14 21:46:48 +0000 | [diff] [blame] | 661 | } |
| 662 | |
bellard | 82c643f | 2004-07-14 17:28:13 +0000 | [diff] [blame] | 663 | static void serial_event(void *opaque, int event) |
| 664 | { |
| 665 | SerialState *s = opaque; |
Michal Novotny | b660114 | 2010-09-13 14:32:32 +0200 | [diff] [blame] | 666 | DPRINTF("event %x\n", event); |
bellard | 82c643f | 2004-07-14 17:28:13 +0000 | [diff] [blame] | 667 | if (event == CHR_EVENT_BREAK) |
| 668 | serial_receive_break(s); |
| 669 | } |
| 670 | |
Juan Quintela | d4bfa4d | 2009-09-29 22:48:22 +0200 | [diff] [blame] | 671 | static void serial_pre_save(void *opaque) |
bellard | 8738a8d | 2005-11-06 15:48:04 +0000 | [diff] [blame] | 672 | { |
Juan Quintela | d4bfa4d | 2009-09-29 22:48:22 +0200 | [diff] [blame] | 673 | SerialState *s = opaque; |
Juan Quintela | 747791f | 2009-09-10 03:04:46 +0200 | [diff] [blame] | 674 | s->fcr_vmstate = s->fcr; |
bellard | 8738a8d | 2005-11-06 15:48:04 +0000 | [diff] [blame] | 675 | } |
| 676 | |
Juan Quintela | e59fb37 | 2009-09-29 22:48:21 +0200 | [diff] [blame] | 677 | static int serial_post_load(void *opaque, int version_id) |
Juan Quintela | 747791f | 2009-09-10 03:04:46 +0200 | [diff] [blame] | 678 | { |
| 679 | SerialState *s = opaque; |
| 680 | |
Juan Quintela | 4c18ce9 | 2009-10-16 15:39:58 +0200 | [diff] [blame] | 681 | if (version_id < 3) { |
| 682 | s->fcr_vmstate = 0; |
| 683 | } |
Juan Quintela | 747791f | 2009-09-10 03:04:46 +0200 | [diff] [blame] | 684 | /* Initialize fcr via setter to perform essential side-effects */ |
| 685 | serial_ioport_write(s, 0x02, s->fcr_vmstate); |
Michal Novotny | 9a7c487 | 2010-09-15 15:35:53 +0200 | [diff] [blame] | 686 | serial_update_parameters(s); |
Juan Quintela | 747791f | 2009-09-10 03:04:46 +0200 | [diff] [blame] | 687 | return 0; |
| 688 | } |
| 689 | |
| 690 | static const VMStateDescription vmstate_serial = { |
| 691 | .name = "serial", |
| 692 | .version_id = 3, |
| 693 | .minimum_version_id = 2, |
| 694 | .pre_save = serial_pre_save, |
Juan Quintela | 747791f | 2009-09-10 03:04:46 +0200 | [diff] [blame] | 695 | .post_load = serial_post_load, |
| 696 | .fields = (VMStateField []) { |
| 697 | VMSTATE_UINT16_V(divider, SerialState, 2), |
| 698 | VMSTATE_UINT8(rbr, SerialState), |
| 699 | VMSTATE_UINT8(ier, SerialState), |
| 700 | VMSTATE_UINT8(iir, SerialState), |
| 701 | VMSTATE_UINT8(lcr, SerialState), |
| 702 | VMSTATE_UINT8(mcr, SerialState), |
| 703 | VMSTATE_UINT8(lsr, SerialState), |
| 704 | VMSTATE_UINT8(msr, SerialState), |
| 705 | VMSTATE_UINT8(scr, SerialState), |
| 706 | VMSTATE_UINT8_V(fcr_vmstate, SerialState, 3), |
| 707 | VMSTATE_END_OF_LIST() |
| 708 | } |
| 709 | }; |
| 710 | |
balrog | b2a5160 | 2008-02-10 13:40:52 +0000 | [diff] [blame] | 711 | static void serial_reset(void *opaque) |
| 712 | { |
| 713 | SerialState *s = opaque; |
| 714 | |
balrog | b2a5160 | 2008-02-10 13:40:52 +0000 | [diff] [blame] | 715 | s->rbr = 0; |
| 716 | s->ier = 0; |
| 717 | s->iir = UART_IIR_NO_INT; |
| 718 | s->lcr = 0; |
balrog | b2a5160 | 2008-02-10 13:40:52 +0000 | [diff] [blame] | 719 | s->lsr = UART_LSR_TEMT | UART_LSR_THRE; |
| 720 | s->msr = UART_MSR_DCD | UART_MSR_DSR | UART_MSR_CTS; |
Stefan Weil | 718b8ae | 2009-10-26 21:51:41 +0100 | [diff] [blame] | 721 | /* 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] | 722 | s->divider = 0x0C; |
| 723 | s->mcr = UART_MCR_OUT2; |
balrog | b2a5160 | 2008-02-10 13:40:52 +0000 | [diff] [blame] | 724 | s->scr = 0; |
aliguori | 81174da | 2008-08-11 14:17:04 +0000 | [diff] [blame] | 725 | s->tsr_retry = 0; |
Stefan Weil | 718b8ae | 2009-10-26 21:51:41 +0100 | [diff] [blame] | 726 | s->char_transmit_time = (get_ticks_per_sec() / 9600) * 10; |
aliguori | 81174da | 2008-08-11 14:17:04 +0000 | [diff] [blame] | 727 | s->poll_msl = 0; |
| 728 | |
| 729 | fifo_clear(s,RECV_FIFO); |
| 730 | fifo_clear(s,XMIT_FIFO); |
| 731 | |
Paolo Bonzini | 7447545 | 2011-03-11 16:47:48 +0100 | [diff] [blame] | 732 | s->last_xmit_ts = qemu_get_clock_ns(vm_clock); |
balrog | b2a5160 | 2008-02-10 13:40:52 +0000 | [diff] [blame] | 733 | |
| 734 | s->thr_ipending = 0; |
| 735 | s->last_break_enable = 0; |
| 736 | qemu_irq_lower(s->irq); |
| 737 | } |
| 738 | |
Gerd Hoffmann | ac0be99 | 2009-09-22 13:53:21 +0200 | [diff] [blame] | 739 | static void serial_init_core(SerialState *s) |
aliguori | 81174da | 2008-08-11 14:17:04 +0000 | [diff] [blame] | 740 | { |
Gerd Hoffmann | ac0be99 | 2009-09-22 13:53:21 +0200 | [diff] [blame] | 741 | if (!s->chr) { |
Aurelien Jarno | 387f4a5 | 2009-09-15 01:16:28 +0200 | [diff] [blame] | 742 | fprintf(stderr, "Can't create serial device, empty char device\n"); |
| 743 | exit(1); |
| 744 | } |
| 745 | |
Paolo Bonzini | 7447545 | 2011-03-11 16:47:48 +0100 | [diff] [blame] | 746 | 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] | 747 | |
Paolo Bonzini | 7447545 | 2011-03-11 16:47:48 +0100 | [diff] [blame] | 748 | s->fifo_timeout_timer = qemu_new_timer_ns(vm_clock, (QEMUTimerCB *) fifo_timeout_int, s); |
| 749 | s->transmit_timer = qemu_new_timer_ns(vm_clock, (QEMUTimerCB *) serial_xmit, s); |
aliguori | 81174da | 2008-08-11 14:17:04 +0000 | [diff] [blame] | 750 | |
Jan Kiszka | a08d436 | 2009-06-27 09:25:07 +0200 | [diff] [blame] | 751 | qemu_register_reset(serial_reset, s); |
aliguori | 81174da | 2008-08-11 14:17:04 +0000 | [diff] [blame] | 752 | |
aurel32 | b47543c | 2009-01-18 14:28:10 +0000 | [diff] [blame] | 753 | qemu_chr_add_handlers(s->chr, serial_can_receive1, serial_receive1, |
| 754 | serial_event, s); |
aliguori | 81174da | 2008-08-11 14:17:04 +0000 | [diff] [blame] | 755 | } |
| 756 | |
Stefan Weil | 038eaf8 | 2009-10-31 11:28:11 +0100 | [diff] [blame] | 757 | /* Change the main reference oscillator frequency. */ |
| 758 | void serial_set_frequency(SerialState *s, uint32_t frequency) |
| 759 | { |
| 760 | s->baudbase = frequency; |
| 761 | serial_update_parameters(s); |
| 762 | } |
| 763 | |
Gerd Hoffmann | e8ee28f | 2009-10-13 13:38:39 +0200 | [diff] [blame] | 764 | static const int isa_serial_io[MAX_SERIAL_PORTS] = { 0x3f8, 0x2f8, 0x3e8, 0x2e8 }; |
| 765 | static const int isa_serial_irq[MAX_SERIAL_PORTS] = { 4, 3, 4, 3 }; |
| 766 | |
Richard Henderson | a941ae4 | 2011-08-10 15:28:18 -0700 | [diff] [blame] | 767 | static const MemoryRegionPortio serial_portio[] = { |
| 768 | { 0, 8, 1, .read = serial_ioport_read, .write = serial_ioport_write }, |
| 769 | PORTIO_END_OF_LIST() |
| 770 | }; |
| 771 | |
| 772 | static const MemoryRegionOps serial_io_ops = { |
| 773 | .old_portio = serial_portio |
| 774 | }; |
| 775 | |
Gerd Hoffmann | ac0be99 | 2009-09-22 13:53:21 +0200 | [diff] [blame] | 776 | static int serial_isa_initfn(ISADevice *dev) |
| 777 | { |
Gerd Hoffmann | e8ee28f | 2009-10-13 13:38:39 +0200 | [diff] [blame] | 778 | static int index; |
Gerd Hoffmann | ac0be99 | 2009-09-22 13:53:21 +0200 | [diff] [blame] | 779 | ISASerialState *isa = DO_UPCAST(ISASerialState, dev, dev); |
| 780 | SerialState *s = &isa->state; |
| 781 | |
Gerd Hoffmann | e8ee28f | 2009-10-13 13:38:39 +0200 | [diff] [blame] | 782 | if (isa->index == -1) |
| 783 | isa->index = index; |
| 784 | if (isa->index >= MAX_SERIAL_PORTS) |
| 785 | return -1; |
| 786 | if (isa->iobase == -1) |
| 787 | isa->iobase = isa_serial_io[isa->index]; |
| 788 | if (isa->isairq == -1) |
| 789 | isa->isairq = isa_serial_irq[isa->index]; |
| 790 | index++; |
| 791 | |
Gerd Hoffmann | ac0be99 | 2009-09-22 13:53:21 +0200 | [diff] [blame] | 792 | s->baudbase = 115200; |
| 793 | isa_init_irq(dev, &s->irq, isa->isairq); |
| 794 | serial_init_core(s); |
Jan Kiszka | 1cc9f51 | 2010-05-15 13:32:41 +0200 | [diff] [blame] | 795 | qdev_set_legacy_instance_id(&dev->qdev, isa->iobase, 3); |
Gerd Hoffmann | ac0be99 | 2009-09-22 13:53:21 +0200 | [diff] [blame] | 796 | |
Richard Henderson | 8e8ffc4 | 2011-08-11 16:18:59 -0700 | [diff] [blame] | 797 | memory_region_init_io(&s->io, &serial_io_ops, s, "serial", 8); |
| 798 | isa_register_ioport(dev, &s->io, isa->iobase); |
Gerd Hoffmann | ac0be99 | 2009-09-22 13:53:21 +0200 | [diff] [blame] | 799 | return 0; |
| 800 | } |
| 801 | |
Jan Kiszka | 1cc9f51 | 2010-05-15 13:32:41 +0200 | [diff] [blame] | 802 | static const VMStateDescription vmstate_isa_serial = { |
| 803 | .name = "serial", |
| 804 | .version_id = 3, |
| 805 | .minimum_version_id = 2, |
| 806 | .fields = (VMStateField []) { |
| 807 | VMSTATE_STRUCT(state, ISASerialState, 0, vmstate_serial, SerialState), |
| 808 | VMSTATE_END_OF_LIST() |
| 809 | } |
| 810 | }; |
| 811 | |
aurel32 | b6cd0ea | 2008-05-04 21:42:11 +0000 | [diff] [blame] | 812 | SerialState *serial_init(int base, qemu_irq irq, int baudbase, |
| 813 | CharDriverState *chr) |
bellard | b41a2cd | 2004-03-14 21:46:48 +0000 | [diff] [blame] | 814 | { |
| 815 | SerialState *s; |
| 816 | |
Anthony Liguori | 7267c09 | 2011-08-20 22:09:37 -0500 | [diff] [blame] | 817 | s = g_malloc0(sizeof(SerialState)); |
balrog | b2a5160 | 2008-02-10 13:40:52 +0000 | [diff] [blame] | 818 | |
Gerd Hoffmann | ac0be99 | 2009-09-22 13:53:21 +0200 | [diff] [blame] | 819 | s->irq = irq; |
| 820 | s->baudbase = baudbase; |
| 821 | s->chr = chr; |
| 822 | serial_init_core(s); |
aurel32 | 6936bfe | 2008-05-04 21:42:00 +0000 | [diff] [blame] | 823 | |
Alex Williamson | 0be71e3 | 2010-06-25 11:09:07 -0600 | [diff] [blame] | 824 | vmstate_register(NULL, base, &vmstate_serial, s); |
bellard | 8738a8d | 2005-11-06 15:48:04 +0000 | [diff] [blame] | 825 | |
bellard | b41a2cd | 2004-03-14 21:46:48 +0000 | [diff] [blame] | 826 | register_ioport_write(base, 8, 1, serial_ioport_write, s); |
| 827 | register_ioport_read(base, 8, 1, serial_ioport_read, s); |
bellard | b41a2cd | 2004-03-14 21:46:48 +0000 | [diff] [blame] | 828 | return s; |
bellard | 80cabfa | 2004-03-14 12:20:30 +0000 | [diff] [blame] | 829 | } |
bellard | e5d13e2 | 2005-11-23 21:11:49 +0000 | [diff] [blame] | 830 | |
| 831 | /* Memory mapped interface */ |
Richard Henderson | 8e8ffc4 | 2011-08-11 16:18:59 -0700 | [diff] [blame] | 832 | static uint64_t serial_mm_read(void *opaque, target_phys_addr_t addr, |
| 833 | unsigned size) |
bellard | e5d13e2 | 2005-11-23 21:11:49 +0000 | [diff] [blame] | 834 | { |
| 835 | SerialState *s = opaque; |
Richard Henderson | 8e8ffc4 | 2011-08-11 16:18:59 -0700 | [diff] [blame] | 836 | return serial_ioport_read(s, addr >> s->it_shift); |
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 void serial_mm_write(void *opaque, target_phys_addr_t addr, |
| 840 | uint64_t value, unsigned size) |
bellard | e5d13e2 | 2005-11-23 21:11:49 +0000 | [diff] [blame] | 841 | { |
| 842 | SerialState *s = opaque; |
Richard Henderson | 8e8ffc4 | 2011-08-11 16:18:59 -0700 | [diff] [blame] | 843 | value &= ~0u >> (32 - (size * 8)); |
pbrook | 8da3ff1 | 2008-12-01 18:59:50 +0000 | [diff] [blame] | 844 | serial_ioport_write(s, addr >> s->it_shift, value); |
bellard | e5d13e2 | 2005-11-23 21:11:49 +0000 | [diff] [blame] | 845 | } |
| 846 | |
Richard Henderson | 8e8ffc4 | 2011-08-11 16:18:59 -0700 | [diff] [blame] | 847 | static const MemoryRegionOps serial_mm_ops[3] = { |
| 848 | [DEVICE_NATIVE_ENDIAN] = { |
| 849 | .read = serial_mm_read, |
| 850 | .write = serial_mm_write, |
| 851 | .endianness = DEVICE_NATIVE_ENDIAN, |
| 852 | }, |
| 853 | [DEVICE_LITTLE_ENDIAN] = { |
| 854 | .read = serial_mm_read, |
| 855 | .write = serial_mm_write, |
| 856 | .endianness = DEVICE_LITTLE_ENDIAN, |
| 857 | }, |
| 858 | [DEVICE_BIG_ENDIAN] = { |
| 859 | .read = serial_mm_read, |
| 860 | .write = serial_mm_write, |
| 861 | .endianness = DEVICE_BIG_ENDIAN, |
| 862 | }, |
bellard | e5d13e2 | 2005-11-23 21:11:49 +0000 | [diff] [blame] | 863 | }; |
| 864 | |
Richard Henderson | 39186d8 | 2011-08-11 16:07:16 -0700 | [diff] [blame] | 865 | SerialState *serial_mm_init(MemoryRegion *address_space, |
| 866 | target_phys_addr_t base, int it_shift, |
| 867 | qemu_irq irq, int baudbase, |
| 868 | CharDriverState *chr, enum device_endian end) |
bellard | e5d13e2 | 2005-11-23 21:11:49 +0000 | [diff] [blame] | 869 | { |
| 870 | SerialState *s; |
bellard | e5d13e2 | 2005-11-23 21:11:49 +0000 | [diff] [blame] | 871 | |
Anthony Liguori | 7267c09 | 2011-08-20 22:09:37 -0500 | [diff] [blame] | 872 | s = g_malloc0(sizeof(SerialState)); |
aliguori | 81174da | 2008-08-11 14:17:04 +0000 | [diff] [blame] | 873 | |
bellard | e5d13e2 | 2005-11-23 21:11:49 +0000 | [diff] [blame] | 874 | s->it_shift = it_shift; |
Gerd Hoffmann | ac0be99 | 2009-09-22 13:53:21 +0200 | [diff] [blame] | 875 | s->irq = irq; |
| 876 | s->baudbase = baudbase; |
| 877 | s->chr = chr; |
bellard | e5d13e2 | 2005-11-23 21:11:49 +0000 | [diff] [blame] | 878 | |
Gerd Hoffmann | ac0be99 | 2009-09-22 13:53:21 +0200 | [diff] [blame] | 879 | serial_init_core(s); |
Alex Williamson | 0be71e3 | 2010-06-25 11:09:07 -0600 | [diff] [blame] | 880 | vmstate_register(NULL, base, &vmstate_serial, s); |
bellard | e5d13e2 | 2005-11-23 21:11:49 +0000 | [diff] [blame] | 881 | |
Richard Henderson | 8e8ffc4 | 2011-08-11 16:18:59 -0700 | [diff] [blame] | 882 | memory_region_init_io(&s->io, &serial_mm_ops[end], s, |
| 883 | "serial", 8 << it_shift); |
Richard Henderson | 39186d8 | 2011-08-11 16:07:16 -0700 | [diff] [blame] | 884 | memory_region_add_subregion(address_space, base, &s->io); |
Richard Henderson | 2ff0c7c | 2011-08-11 16:07:15 -0700 | [diff] [blame] | 885 | |
aliguori | 81174da | 2008-08-11 14:17:04 +0000 | [diff] [blame] | 886 | serial_update_msl(s); |
bellard | e5d13e2 | 2005-11-23 21:11:49 +0000 | [diff] [blame] | 887 | return s; |
| 888 | } |
Gerd Hoffmann | ac0be99 | 2009-09-22 13:53:21 +0200 | [diff] [blame] | 889 | |
Anthony Liguori | 39bffca | 2011-12-07 21:34:16 -0600 | [diff] [blame] | 890 | static Property serial_isa_properties[] = { |
| 891 | DEFINE_PROP_UINT32("index", ISASerialState, index, -1), |
| 892 | DEFINE_PROP_HEX32("iobase", ISASerialState, iobase, -1), |
| 893 | DEFINE_PROP_UINT32("irq", ISASerialState, isairq, -1), |
| 894 | DEFINE_PROP_CHR("chardev", ISASerialState, state.chr), |
Gerd Hoffmann | 9826fd5 | 2012-02-23 13:45:23 +0100 | [diff] [blame] | 895 | DEFINE_PROP_UINT32("wakeup", ISASerialState, state.wakeup, 0), |
Anthony Liguori | 39bffca | 2011-12-07 21:34:16 -0600 | [diff] [blame] | 896 | DEFINE_PROP_END_OF_LIST(), |
| 897 | }; |
| 898 | |
Anthony Liguori | 8f04ee0 | 2011-12-04 11:52:49 -0600 | [diff] [blame] | 899 | static void serial_isa_class_initfn(ObjectClass *klass, void *data) |
| 900 | { |
Anthony Liguori | 39bffca | 2011-12-07 21:34:16 -0600 | [diff] [blame] | 901 | DeviceClass *dc = DEVICE_CLASS(klass); |
Anthony Liguori | 8f04ee0 | 2011-12-04 11:52:49 -0600 | [diff] [blame] | 902 | ISADeviceClass *ic = ISA_DEVICE_CLASS(klass); |
| 903 | ic->init = serial_isa_initfn; |
Anthony Liguori | 39bffca | 2011-12-07 21:34:16 -0600 | [diff] [blame] | 904 | dc->vmsd = &vmstate_isa_serial; |
| 905 | dc->props = serial_isa_properties; |
Anthony Liguori | 8f04ee0 | 2011-12-04 11:52:49 -0600 | [diff] [blame] | 906 | } |
| 907 | |
Anthony Liguori | 39bffca | 2011-12-07 21:34:16 -0600 | [diff] [blame] | 908 | static TypeInfo serial_isa_info = { |
| 909 | .name = "isa-serial", |
| 910 | .parent = TYPE_ISA_DEVICE, |
| 911 | .instance_size = sizeof(ISASerialState), |
| 912 | .class_init = serial_isa_class_initfn, |
Gerd Hoffmann | ac0be99 | 2009-09-22 13:53:21 +0200 | [diff] [blame] | 913 | }; |
| 914 | |
Andreas Färber | 83f7d43 | 2012-02-09 15:20:55 +0100 | [diff] [blame] | 915 | static void serial_register_types(void) |
Gerd Hoffmann | ac0be99 | 2009-09-22 13:53:21 +0200 | [diff] [blame] | 916 | { |
Anthony Liguori | 39bffca | 2011-12-07 21:34:16 -0600 | [diff] [blame] | 917 | type_register_static(&serial_isa_info); |
Gerd Hoffmann | ac0be99 | 2009-09-22 13:53:21 +0200 | [diff] [blame] | 918 | } |
| 919 | |
Andreas Färber | 83f7d43 | 2012-02-09 15:20:55 +0100 | [diff] [blame] | 920 | type_init(serial_register_types) |