debuggers.hg

view xen/drivers/char/serial.c @ 3515:d331c6994d28

bitkeeper revision 1.1159.223.12 (41f14d3cE4GADmEAEr6XE9nXX4dyGw)

Common-code cleanups. Moved arch-specific code out into arch/x86
and asm-x86.
author kaf24@scramble.cl.cam.ac.uk
date Fri Jan 21 18:43:08 2005 +0000 (2005-01-21)
parents 60e5912b6b28
children 46c14b1a4351 0dc3b8b8c298
line source
1 /******************************************************************************
2 * serial.c
3 *
4 * Driver for 16550-series UARTs. This driver is to be kept within Xen as
5 * it permits debugging of seriously-toasted machines (e.g., in situations
6 * where a device driver within a guest OS would be inaccessible).
7 *
8 * Copyright (c) 2003-2004, K A Fraser
9 */
11 #include <xen/config.h>
12 #include <xen/init.h>
13 #include <xen/irq.h>
14 #include <xen/keyhandler.h>
15 #include <asm/pdb.h>
16 #include <xen/reboot.h>
17 #include <xen/sched.h>
18 #include <xen/serial.h>
19 #include <asm/io.h>
21 /* opt_com[12]: Config serial port with a string <baud>,DPS,<io-base>,<irq>. */
22 static unsigned char opt_com1[30] = "", opt_com2[30] = "";
23 string_param("com1", opt_com1);
24 string_param("com2", opt_com2);
26 /* Register offsets */
27 #define RBR 0x00 /* receive buffer */
28 #define THR 0x00 /* transmit holding */
29 #define IER 0x01 /* interrupt enable */
30 #define IIR 0x02 /* interrupt identity */
31 #define FCR 0x02 /* FIFO control */
32 #define LCR 0x03 /* line control */
33 #define MCR 0x04 /* Modem control */
34 #define LSR 0x05 /* line status */
35 #define MSR 0x06 /* Modem status */
36 #define DLL 0x00 /* divisor latch (ls) ( DLAB=1) */
37 #define DLM 0x01 /* divisor latch (ms) ( DLAB=1) */
39 /* Interrupt Enable Register */
40 #define IER_ERDAI 0x01 /* rx data recv'd */
41 #define IER_ETHREI 0x02 /* tx reg. empty */
42 #define IER_ELSI 0x04 /* rx line status */
43 #define IER_EMSI 0x08 /* MODEM status */
45 /* FIFO control register */
46 #define FCR_ENABLE 0x01 /* enable FIFO */
47 #define FCR_CLRX 0x02 /* clear Rx FIFO */
48 #define FCR_CLTX 0x04 /* clear Tx FIFO */
49 #define FCR_DMA 0x10 /* enter DMA mode */
50 #define FCR_TRG1 0x00 /* Rx FIFO trig lev 1 */
51 #define FCR_TRG4 0x40 /* Rx FIFO trig lev 4 */
52 #define FCR_TRG8 0x80 /* Rx FIFO trig lev 8 */
53 #define FCR_TRG14 0xc0 /* Rx FIFO trig lev 14 */
55 /* Line control register */
56 #define LCR_DLAB 0x80 /* Divisor Latch Access */
58 /* Modem Control Register */
59 #define MCR_DTR 0x01 /* Data Terminal Ready */
60 #define MCR_RTS 0x02 /* Request to Send */
61 #define MCR_OUT2 0x08 /* OUT2: interrupt mask */
63 /* Line Status Register */
64 #define LSR_DR 0x01 /* Data ready */
65 #define LSR_OE 0x02 /* Overrun */
66 #define LSR_PE 0x04 /* Parity error */
67 #define LSR_FE 0x08 /* Framing error */
68 #define LSR_BI 0x10 /* Break */
69 #define LSR_THRE 0x20 /* Xmit hold reg empty */
70 #define LSR_TEMT 0x40 /* Xmitter empty */
71 #define LSR_ERR 0x80 /* Error */
73 /* These parity settings can be ORed directly into the LCR. */
74 #define PARITY_NONE (0<<3)
75 #define PARITY_ODD (1<<3)
76 #define PARITY_EVEN (3<<3)
77 #define PARITY_MARK (5<<3)
78 #define PARITY_SPACE (7<<3)
80 #define RXBUFSZ 32
81 #define MASK_RXBUF_IDX(_i) ((_i)&(RXBUFSZ-1))
82 typedef struct {
83 int baud, data_bits, parity, stop_bits, io_base, irq;
84 serial_rx_fn rx_lo, rx_hi, rx;
85 spinlock_t lock;
86 unsigned char rxbuf[RXBUFSZ];
87 unsigned int rxbufp, rxbufc;
88 struct irqaction irqaction;
89 } uart_t;
91 static uart_t com[2] = {
92 { 0, 0, 0, 0, 0x3f8, 4,
93 NULL, NULL, NULL,
94 SPIN_LOCK_UNLOCKED },
95 { 0, 0, 0, 0, 0x2f8, 3,
96 NULL, NULL, NULL,
97 SPIN_LOCK_UNLOCKED }
98 };
100 #define UART_ENABLED(_u) ((_u)->baud != 0)
101 #define DISABLE_UART(_u) ((_u)->baud = 0)
103 #ifdef CONFIG_X86
104 static inline int arch_serial_putc(uart_t *uart, unsigned char c)
105 {
106 int space;
107 if ( (space = (inb(uart->io_base + LSR) & LSR_THRE)) )
108 outb(c, uart->io_base + THR);
109 return space;
110 }
111 #endif
114 /***********************
115 * PRIVATE FUNCTIONS
116 */
118 static void uart_rx(uart_t *uart, struct xen_regs *regs)
119 {
120 unsigned char c;
122 if ( !UART_ENABLED(uart) )
123 return;
125 /*
126 * No need for the uart spinlock here. Only the uart's own interrupt
127 * handler will read from the RBR and the handler isn't reentrant.
128 * Calls to serial_getc() will disable this handler before proceeding.
129 */
130 while ( inb(uart->io_base + LSR) & LSR_DR )
131 {
132 c = inb(uart->io_base + RBR);
133 if ( uart->rx != NULL )
134 uart->rx(c, regs);
135 else if ( (c & 0x80) && (uart->rx_hi != NULL) )
136 uart->rx_hi(c&0x7f, regs);
137 else if ( !(c & 0x80) && (uart->rx_lo != NULL) )
138 uart->rx_lo(c&0x7f, regs);
139 else if ( (uart->rxbufp - uart->rxbufc) != RXBUFSZ )
140 uart->rxbuf[MASK_RXBUF_IDX(uart->rxbufp++)] = c;
141 }
142 }
144 static void serial_interrupt(int irq, void *dev_id, struct xen_regs *regs)
145 {
146 uart_rx((uart_t *)dev_id, regs);
147 }
149 static inline void __serial_putc(uart_t *uart, int handle, unsigned char c)
150 {
151 unsigned long flags;
152 int space;
154 if ( (c == '\n') && (handle & SERHND_COOKED) )
155 __serial_putc(uart, handle, '\r');
157 if ( handle & SERHND_HI )
158 c |= 0x80;
159 else if ( handle & SERHND_LO )
160 c &= 0x7f;
162 do {
163 spin_lock_irqsave(&uart->lock, flags);
164 space = arch_serial_putc(uart, c);
165 spin_unlock_irqrestore(&uart->lock, flags);
166 }
167 while ( !space );
168 }
170 #define PARSE_ERR(_f, _a...) \
171 do { \
172 printk( "ERROR: " _f "\n" , ## _a ); \
173 DISABLE_UART(uart); \
174 return; \
175 } while ( 0 )
177 static void parse_port_config(char *conf, uart_t *uart)
178 {
179 if ( *conf == '\0' )
180 return;
182 uart->baud = simple_strtol(conf, &conf, 10);
183 if ( (uart->baud < 1200) || (uart->baud > 115200) )
184 PARSE_ERR("Baud rate %d outside supported range.", uart->baud);
186 if ( *conf != ',' )
187 PARSE_ERR("Missing data/parity/stop specifiers.");
189 conf++;
191 uart->data_bits = simple_strtol(conf, &conf, 10);
192 if ( (uart->data_bits < 5) || (uart->data_bits > 8) )
193 PARSE_ERR("%d data bits are unsupported.", uart->data_bits);
195 switch ( *conf )
196 {
197 case 'n':
198 uart->parity = PARITY_NONE;
199 break;
200 case 'o':
201 uart->parity = PARITY_ODD;
202 break;
203 case 'e':
204 uart->parity = PARITY_EVEN;
205 break;
206 case 'm':
207 uart->parity = PARITY_MARK;
208 break;
209 case 's':
210 uart->parity = PARITY_SPACE;
211 break;
213 default:
214 PARSE_ERR("Invalid parity specifier '%c'.", *conf);
215 }
217 conf++;
219 uart->stop_bits = simple_strtol(conf, &conf, 10);
220 if ( (uart->stop_bits < 1) || (uart->stop_bits > 2) )
221 PARSE_ERR("%d stop bits are unsupported.", uart->stop_bits);
223 if ( *conf == ',' )
224 {
225 conf++;
227 uart->io_base = simple_strtol(conf, &conf, 0);
228 if ( (uart->io_base <= 0x0000) || (uart->io_base > 0xfff0) )
229 PARSE_ERR("I/O port base 0x%x is outside the supported range.",
230 uart->io_base);
232 if ( *conf != ',' )
233 PARSE_ERR("Missing IRQ specifier.");
235 conf++;
237 uart->irq = simple_strtol(conf, &conf, 10);
238 if ( (uart->irq <= 0) || (uart->irq >= 32) )
239 PARSE_ERR("IRQ %d is outside the supported range.", uart->irq);
240 }
241 }
243 static void uart_config_stage1(uart_t *uart)
244 {
245 unsigned char lcr;
247 if ( !UART_ENABLED(uart) )
248 return;
250 lcr = (uart->data_bits - 5) | ((uart->stop_bits - 1) << 2) | uart->parity;
252 /* No interrupts. */
253 outb(0, uart->io_base + IER);
255 /* Line control and baud-rate generator. */
256 outb(lcr | LCR_DLAB, uart->io_base + LCR);
257 outb(115200/uart->baud, uart->io_base + DLL); /* baud lo */
258 outb(0, uart->io_base + DLM); /* baud hi */
259 outb(lcr, uart->io_base + LCR); /* parity, data, stop */
261 /* No flow ctrl: DTR and RTS are both wedged high to keep remote happy. */
262 outb(MCR_DTR | MCR_RTS, uart->io_base + MCR);
264 /* Enable and clear the FIFOs. Set a large trigger threshold. */
265 outb(FCR_ENABLE | FCR_CLRX | FCR_CLTX | FCR_TRG14, uart->io_base + FCR);
266 }
268 static void uart_config_stage2(uart_t *uart)
269 {
270 int rc;
272 if ( !UART_ENABLED(uart) )
273 return;
275 uart->irqaction.handler = serial_interrupt;
276 uart->irqaction.name = "serial";
277 uart->irqaction.dev_id = uart;
278 if ( (rc = setup_irq(uart->irq, &uart->irqaction)) != 0 )
279 printk("ERROR: Failed to allocate serial IRQ %d\n", uart->irq);
281 /* For sanity, clear the receive FIFO. */
282 outb(FCR_ENABLE | FCR_CLRX | FCR_TRG14, uart->io_base + FCR);
284 /* Master interrupt enable; also keep DTR/RTS asserted. */
285 outb(MCR_OUT2 | MCR_DTR | MCR_RTS, uart->io_base + MCR);
287 /* Enable receive interrupts. */
288 outb(IER_ERDAI, uart->io_base + IER);
289 }
292 /***********************
293 * PUBLIC FUNCTIONS
294 */
296 void serial_init_stage1(void)
297 {
298 parse_port_config(opt_com1, &com[0]);
299 parse_port_config(opt_com2, &com[1]);
301 uart_config_stage1(&com[0]);
302 uart_config_stage1(&com[1]);
303 }
305 void serial_init_stage2(void)
306 {
307 uart_config_stage2(&com[0]);
308 uart_config_stage2(&com[1]);
309 }
311 int parse_serial_handle(char *conf)
312 {
313 int handle;
315 /* Silently fail if user has explicitly requested no serial I/O. */
316 if ( strcmp(conf, "none") == 0 )
317 return -1;
319 if ( strncmp(conf, "com", 3) != 0 )
320 goto fail;
322 switch ( conf[3] )
323 {
324 case '1':
325 handle = 0;
326 break;
327 case '2':
328 handle = 1;
329 break;
330 default:
331 goto fail;
332 }
334 if ( !UART_ENABLED(&com[handle]) )
335 {
336 printk("ERROR: cannot use unconfigured serial port COM%d\n", handle+1);
337 return -1;
338 }
340 if ( conf[4] == 'H' )
341 handle |= SERHND_HI;
342 else if ( conf[4] == 'L' )
343 handle |= SERHND_LO;
345 handle |= SERHND_COOKED;
347 return handle;
349 fail:
350 printk("ERROR: bad serial-interface specification '%s'\n", conf);
351 return -1;
352 }
354 void serial_set_rx_handler(int handle, serial_rx_fn fn)
355 {
356 uart_t *uart = &com[handle & SERHND_IDX];
357 unsigned long flags;
359 if ( handle == -1 )
360 return;
362 spin_lock_irqsave(&uart->lock, flags);
364 if ( uart->rx != NULL )
365 goto fail;
367 if ( handle & SERHND_LO )
368 {
369 if ( uart->rx_lo != NULL )
370 goto fail;
371 uart->rx_lo = fn;
372 }
373 else if ( handle & SERHND_HI )
374 {
375 if ( uart->rx_hi != NULL )
376 goto fail;
377 uart->rx_hi = fn;
378 }
379 else
380 {
381 if ( (uart->rx_hi != NULL) || (uart->rx_lo != NULL) )
382 goto fail;
383 uart->rx = fn;
384 }
386 spin_unlock_irqrestore(&uart->lock, flags);
387 return;
389 fail:
390 spin_unlock_irqrestore(&uart->lock, flags);
391 printk("ERROR: Conflicting receive handlers for COM%d\n",
392 handle & SERHND_IDX);
393 }
395 void serial_putc(int handle, unsigned char c)
396 {
397 uart_t *uart = &com[handle & SERHND_IDX];
399 if ( handle == -1 )
400 return;
402 __serial_putc(uart, handle, c);
403 }
405 void serial_puts(int handle, const unsigned char *s)
406 {
407 uart_t *uart = &com[handle & SERHND_IDX];
409 if ( handle == -1 )
410 return;
412 while ( *s != '\0' )
413 __serial_putc(uart, handle, *s++);
414 }
416 /* Returns TRUE if given character (*pc) matches the serial handle. */
417 static int byte_matches(int handle, unsigned char *pc)
418 {
419 if ( !(handle & SERHND_HI) )
420 {
421 if ( !(handle & SERHND_LO) || !(*pc & 0x80) )
422 return 1;
423 }
424 else if ( *pc & 0x80 )
425 {
426 *pc &= 0x7f;
427 return 1;
428 }
429 return 0;
430 }
432 unsigned char irq_serial_getc(int handle)
433 {
434 uart_t *uart = &com[handle & SERHND_IDX];
435 unsigned char c;
437 while ( uart->rxbufp != uart->rxbufc )
438 {
439 c = uart->rxbuf[MASK_RXBUF_IDX(uart->rxbufc++)];
440 if ( byte_matches(handle, &c) )
441 goto out;
442 }
444 /* We now wait for the UART to receive a suitable character. */
445 do {
446 while ( (inb(uart->io_base + LSR) & LSR_DR) == 0 )
447 barrier();
448 c = inb(uart->io_base + RBR);
449 }
450 while ( !byte_matches(handle, &c) );
452 out:
453 return c;
454 }
456 unsigned char serial_getc(int handle)
457 {
458 uart_t *uart = &com[handle & SERHND_IDX];
459 unsigned char c;
460 unsigned long flags;
462 spin_lock_irqsave(&uart->lock, flags);
464 while ( uart->rxbufp != uart->rxbufc )
465 {
466 c = uart->rxbuf[MASK_RXBUF_IDX(uart->rxbufc++)];
467 if ( byte_matches(handle, &c) )
468 goto out;
469 }
471 disable_irq(uart->irq);
473 c = irq_serial_getc(handle);
475 enable_irq(uart->irq);
476 out:
477 spin_unlock_irqrestore(&uart->lock, flags);
478 return c;
479 }
481 void serial_force_unlock(int handle)
482 {
483 uart_t *uart = &com[handle & SERHND_IDX];
484 if ( handle != -1 )
485 uart->lock = SPIN_LOCK_UNLOCKED;
486 }