diff options
-rw-r--r-- | .rootkeys | 2 | ||||
-rw-r--r-- | xen/arch/ia64/xensetup.c | 3 | ||||
-rw-r--r-- | xen/arch/x86/cdb.c | 12 | ||||
-rw-r--r-- | xen/arch/x86/setup.c | 5 | ||||
-rw-r--r-- | xen/drivers/char/console.c | 21 | ||||
-rw-r--r-- | xen/drivers/char/ns16550.c | 263 | ||||
-rw-r--r-- | xen/drivers/char/serial.c | 408 | ||||
-rw-r--r-- | xen/include/asm-ia64/config.h | 3 | ||||
-rw-r--r-- | xen/include/asm-ia64/serial.h | 14 | ||||
-rw-r--r-- | xen/include/asm-x86/serial.h | 22 | ||||
-rw-r--r-- | xen/include/xen/console.h | 4 | ||||
-rw-r--r-- | xen/include/xen/serial.h | 129 |
12 files changed, 440 insertions, 446 deletions
@@ -1232,6 +1232,7 @@ 40715b2cNVOegtvyft_AHFKJYRprfA xen/drivers/acpi/tables.c 3e4a8cb7alzQCDKS7MlioPoHBKYkdQ xen/drivers/char/Makefile 4049e6bfNSIq7s7OV-Bd69QD0RpR2Q xen/drivers/char/console.c +4298e018XQtZkCdufpyFimOGZqqsFA xen/drivers/char/ns16550.c 3e4a8cb7nMChlro4wvOBo76n__iCFA xen/drivers/char/serial.c 40715b2cFpte_UNWnBZW0Du7z9AhTQ xen/include/acpi/acconfig.h 40715b2ctNvVZ058w8eM8DR9hOat_A xen/include/acpi/acexcep.h @@ -1356,7 +1357,6 @@ 3ddb79c2QF5-pZGzuX4QukPCDAl59A xen/include/asm-x86/processor.h 40cf1596bim9F9DNdV75klgRSZ6Y2A xen/include/asm-x86/regs.h 3ddb79c2plf7ciNgoNjU-RsbUzawsw xen/include/asm-x86/rwlock.h -42136b49h6MkeayfuOaTPcJQWKga2g xen/include/asm-x86/serial.h 405b8599BsDsDwKEJLS0XipaiQW3TA xen/include/asm-x86/shadow.h 3ddb79c3Hgbb2g8CyWLMCK-6_ZVQSQ xen/include/asm-x86/smp.h 3ddb79c3jn8ALV_S9W5aeTYUQRKBpg xen/include/asm-x86/smpboot.h diff --git a/xen/arch/ia64/xensetup.c b/xen/arch/ia64/xensetup.c index ba6cd64f94..f1f10a1f15 100644 --- a/xen/arch/ia64/xensetup.c +++ b/xen/arch/ia64/xensetup.c @@ -154,7 +154,8 @@ void start_kernel(void) early_setup_arch(&cmdline); /* We initialise the serial devices very early so we can get debugging. */ - serial_init_stage1(); + ns16550_init(); + serial_init_preirq(); init_console(); set_printk_prefix("(XEN) "); diff --git a/xen/arch/x86/cdb.c b/xen/arch/x86/cdb.c index f92e78f9c6..806b0c479e 100644 --- a/xen/arch/x86/cdb.c +++ b/xen/arch/x86/cdb.c @@ -91,11 +91,11 @@ attempt_receive_packet(char *recv_buf, struct xendbg_context *ctx) u8 ch; /* Skip over everything up to the first '$' */ - while ((ch = irq_serial_getc(ctx->serhnd)) != '$') + while ((ch = serial_getc(ctx->serhnd)) != '$') ; csum = 0; for (count = 0; count < 4096; count++) { - ch = irq_serial_getc(ctx->serhnd); + ch = serial_getc(ctx->serhnd); if (ch == '#') break; recv_buf[count] = ch; @@ -106,8 +106,8 @@ attempt_receive_packet(char *recv_buf, struct xendbg_context *ctx) return -1; } recv_buf[count] = 0; - received_csum = hex_char_val(irq_serial_getc(ctx->serhnd)) * 16 + - hex_char_val(irq_serial_getc(ctx->serhnd)); + received_csum = hex_char_val(serial_getc(ctx->serhnd)) * 16 + + hex_char_val(serial_getc(ctx->serhnd)); if (received_csum == csum) { return 0; } else { @@ -163,7 +163,7 @@ xendbg_finish_reply(struct xendbg_context *ctx) xendbg_put_char('#', ctx); xendbg_send(buf, 2, ctx); - ch = irq_serial_getc(ctx->serhnd); + ch = serial_getc(ctx->serhnd); if (ch == '+') return 0; else @@ -394,7 +394,7 @@ initialize_xendbg(void) { if (!strcmp(opt_cdb, "none")) return 0; - xdb_ctx.serhnd = parse_serial_handle(opt_cdb); + xdb_ctx.serhnd = serial_parse_handle(opt_cdb); if (xdb_ctx.serhnd == -1) panic("Can't parse %s as CDB serial info.\n", opt_cdb); diff --git a/xen/arch/x86/setup.c b/xen/arch/x86/setup.c index 1a8ff62933..2d16e56a52 100644 --- a/xen/arch/x86/setup.c +++ b/xen/arch/x86/setup.c @@ -216,7 +216,7 @@ static void __init start_of_day(void) initialize_keytable(); - serial_init_stage2(); + serial_init_postirq(); init_xen_time(); @@ -263,7 +263,8 @@ void __init __start_xen(multiboot_info_t *mbi) smp_prepare_boot_cpu(); /* We initialise the serial devices very early so we can get debugging. */ - serial_init_stage1(); + ns16550_init(); + serial_init_preirq(); init_console(); diff --git a/xen/drivers/char/console.c b/xen/drivers/char/console.c index b8e0f15fa4..f588b58945 100644 --- a/xen/drivers/char/console.c +++ b/xen/drivers/char/console.c @@ -260,7 +260,7 @@ static void switch_serial_input(void) } } -static void __serial_rx(unsigned char c, struct cpu_user_regs *regs) +static void __serial_rx(char c, struct cpu_user_regs *regs) { if ( xen_rx ) return handle_keypress(c, regs); @@ -272,7 +272,7 @@ static void __serial_rx(unsigned char c, struct cpu_user_regs *regs) send_guest_virq(dom0->exec_domain[0], VIRQ_CONSOLE); } -static void serial_rx(unsigned char c, struct cpu_user_regs *regs) +static void serial_rx(char c, struct cpu_user_regs *regs) { static int switch_code_count = 0; @@ -416,7 +416,7 @@ void init_console(void) if ( *p == ',' ) p++; if ( strncmp(p, "com", 3) == 0 ) - sercon_handle = parse_serial_handle(p); + sercon_handle = serial_parse_handle(p); else if ( strncmp(p, "vga", 3) == 0 ) vgacon_enabled = 1; } @@ -465,21 +465,6 @@ void console_force_lock(void) spin_lock(&console_lock); } -void console_putc(char c) -{ - serial_putc(sercon_handle, c); -} - -int console_getc(void) -{ - return serial_getc(sercon_handle); -} - -int irq_console_getc(void) -{ - return irq_serial_getc(sercon_handle); -} - /* * ************************************************************** diff --git a/xen/drivers/char/ns16550.c b/xen/drivers/char/ns16550.c new file mode 100644 index 0000000000..89e64497e2 --- /dev/null +++ b/xen/drivers/char/ns16550.c @@ -0,0 +1,263 @@ +/****************************************************************************** + * ns16550.c + * + * Driver for 16550-series UARTs. This driver is to be kept within Xen as + * it permits debugging of seriously-toasted machines (e.g., in situations + * where a device driver within a guest OS would be inaccessible). + * + * Copyright (c) 2003-2005, K A Fraser + */ + +#include <xen/config.h> +#include <xen/init.h> +#include <xen/irq.h> +#include <xen/sched.h> +#include <xen/serial.h> +#include <asm/io.h> + +/* Config serial port with a string <baud>,DPS,<io-base>,<irq>. */ +static char opt_com1[30] = "", opt_com2[30] = ""; +string_param("com1", opt_com1); +string_param("com2", opt_com2); + +static struct ns16550 { + int baud, data_bits, parity, stop_bits, io_base, irq; + struct irqaction irqaction; +} ns16550_com[2] = { + { 0, 0, 0, 0, 0x3f8, 4 }, + { 0, 0, 0, 0, 0x2f8, 3 } +}; + +/* Register offsets */ +#define RBR 0x00 /* receive buffer */ +#define THR 0x00 /* transmit holding */ +#define IER 0x01 /* interrupt enable */ +#define IIR 0x02 /* interrupt identity */ +#define FCR 0x02 /* FIFO control */ +#define LCR 0x03 /* line control */ +#define MCR 0x04 /* Modem control */ +#define LSR 0x05 /* line status */ +#define MSR 0x06 /* Modem status */ +#define DLL 0x00 /* divisor latch (ls) (DLAB=1) */ +#define DLM 0x01 /* divisor latch (ms) (DLAB=1) */ + +/* Interrupt Enable Register */ +#define IER_ERDAI 0x01 /* rx data recv'd */ +#define IER_ETHREI 0x02 /* tx reg. empty */ +#define IER_ELSI 0x04 /* rx line status */ +#define IER_EMSI 0x08 /* MODEM status */ + +/* FIFO control register */ +#define FCR_ENABLE 0x01 /* enable FIFO */ +#define FCR_CLRX 0x02 /* clear Rx FIFO */ +#define FCR_CLTX 0x04 /* clear Tx FIFO */ +#define FCR_DMA 0x10 /* enter DMA mode */ +#define FCR_TRG1 0x00 /* Rx FIFO trig lev 1 */ +#define FCR_TRG4 0x40 /* Rx FIFO trig lev 4 */ +#define FCR_TRG8 0x80 /* Rx FIFO trig lev 8 */ +#define FCR_TRG14 0xc0 /* Rx FIFO trig lev 14 */ + +/* Line control register */ +#define LCR_DLAB 0x80 /* Divisor Latch Access */ + +/* Modem Control Register */ +#define MCR_DTR 0x01 /* Data Terminal Ready */ +#define MCR_RTS 0x02 /* Request to Send */ +#define MCR_OUT2 0x08 /* OUT2: interrupt mask */ + +/* Line Status Register */ +#define LSR_DR 0x01 /* Data ready */ +#define LSR_OE 0x02 /* Overrun */ +#define LSR_PE 0x04 /* Parity error */ +#define LSR_FE 0x08 /* Framing error */ +#define LSR_BI 0x10 /* Break */ +#define LSR_THRE 0x20 /* Xmit hold reg empty */ +#define LSR_TEMT 0x40 /* Xmitter empty */ +#define LSR_ERR 0x80 /* Error */ + +/* These parity settings can be ORed directly into the LCR. */ +#define PARITY_NONE (0<<3) +#define PARITY_ODD (1<<3) +#define PARITY_EVEN (3<<3) +#define PARITY_MARK (5<<3) +#define PARITY_SPACE (7<<3) + +static void ns16550_interrupt( + int irq, void *dev_id, struct cpu_user_regs *regs) +{ + serial_rx_interrupt(dev_id, regs); +} + +static void ns16550_putc(struct serial_port *port, char c) +{ + struct ns16550 *uart = port->uart; + + while ( !(inb(uart->io_base + LSR) & LSR_THRE) ) + cpu_relax(); + + outb(c, uart->io_base + THR); +} + +static int ns16550_getc(struct serial_port *port, char *pc) +{ + struct ns16550 *uart = port->uart; + + if ( !(inb(uart->io_base + LSR) & LSR_DR) ) + return 0; + + *pc = inb(uart->io_base + RBR); + return 1; +} + +static void ns16550_init_preirq(struct serial_port *port) +{ + struct ns16550 *uart = port->uart; + unsigned char lcr; + + lcr = (uart->data_bits - 5) | ((uart->stop_bits - 1) << 2) | uart->parity; + + /* No interrupts. */ + outb(0, uart->io_base + IER); + + /* Line control and baud-rate generator. */ + outb(lcr | LCR_DLAB, uart->io_base + LCR); + outb(115200/uart->baud, uart->io_base + DLL); /* baud lo */ + outb(0, uart->io_base + DLM); /* baud hi */ + outb(lcr, uart->io_base + LCR); /* parity, data, stop */ + + /* No flow ctrl: DTR and RTS are both wedged high to keep remote happy. */ + outb(MCR_DTR | MCR_RTS, uart->io_base + MCR); + + /* Enable and clear the FIFOs. Set a large trigger threshold. */ + outb(FCR_ENABLE | FCR_CLRX | FCR_CLTX | FCR_TRG14, uart->io_base + FCR); +} + +static void ns16550_init_postirq(struct serial_port *port) +{ + struct ns16550 *uart = port->uart; + int rc; + + uart->irqaction.handler = ns16550_interrupt; + uart->irqaction.name = "ns16550"; + uart->irqaction.dev_id = port; + if ( (rc = setup_irq(uart->irq, &uart->irqaction)) != 0 ) + printk("ERROR: Failed to allocate na16550 IRQ %d\n", uart->irq); + + /* For sanity, clear the receive FIFO. */ + outb(FCR_ENABLE | FCR_CLRX | FCR_TRG14, uart->io_base + FCR); + + /* Master interrupt enable; also keep DTR/RTS asserted. */ + outb(MCR_OUT2 | MCR_DTR | MCR_RTS, uart->io_base + MCR); + + /* Enable receive interrupts. */ + outb(IER_ERDAI, uart->io_base + IER); +} + +#ifdef CONFIG_X86 +#include <asm/physdev.h> +static void ns16550_endboot(struct serial_port *port) +{ + struct ns16550 *uart = port->uart; + physdev_modify_ioport_access_range(dom0, 0, uart->io_base, 8); +} +#else +#define ns16550_endboot NULL +#endif + +static struct uart_driver ns16550_driver = { + .init_preirq = ns16550_init_preirq, + .init_postirq = ns16550_init_postirq, + .endboot = ns16550_endboot, + .putc = ns16550_putc, + .getc = ns16550_getc +}; + +#define PARSE_ERR(_f, _a...) \ + do { \ + printk( "ERROR: " _f "\n" , ## _a ); \ + return; \ + } while ( 0 ) + +static void ns16550_parse_port_config(struct ns16550 *uart, char *conf) +{ + if ( *conf == '\0' ) + return; + + uart->baud = simple_strtol(conf, &conf, 10); + if ( (uart->baud < 1200) || (uart->baud > 115200) ) + PARSE_ERR("Baud rate %d outside supported range.", uart->baud); + + if ( *conf != ',' ) + PARSE_ERR("Missing data/parity/stop specifiers."); + + conf++; + + uart->data_bits = simple_strtol(conf, &conf, 10); + if ( (uart->data_bits < 5) || (uart->data_bits > 8) ) + PARSE_ERR("%d data bits are unsupported.", uart->data_bits); + + switch ( *conf ) + { + case 'n': + uart->parity = PARITY_NONE; + break; + case 'o': + uart->parity = PARITY_ODD; + break; + case 'e': + uart->parity = PARITY_EVEN; + break; + case 'm': + uart->parity = PARITY_MARK; + break; + case 's': + uart->parity = PARITY_SPACE; + break; + + default: + PARSE_ERR("Invalid parity specifier '%c'.", *conf); + } + + conf++; + + uart->stop_bits = simple_strtol(conf, &conf, 10); + if ( (uart->stop_bits < 1) || (uart->stop_bits > 2) ) + PARSE_ERR("%d stop bits are unsupported.", uart->stop_bits); + + if ( *conf == ',' ) + { + conf++; + + uart->io_base = simple_strtol(conf, &conf, 0); + if ( (uart->io_base <= 0x0000) || (uart->io_base > 0xfff0) ) + PARSE_ERR("I/O port base 0x%x is outside the supported range.", + uart->io_base); + + if ( *conf != ',' ) + PARSE_ERR("Missing IRQ specifier."); + + conf++; + + uart->irq = simple_strtol(conf, &conf, 10); + if ( (uart->irq <= 0) || (uart->irq >= 32) ) + PARSE_ERR("IRQ %d is outside the supported range.", uart->irq); + } + + serial_register_uart(uart - ns16550_com, &ns16550_driver, uart); +} + +void ns16550_init(void) +{ + ns16550_parse_port_config(&ns16550_com[0], opt_com1); + ns16550_parse_port_config(&ns16550_com[1], opt_com2); +} + +/* + * Local variables: + * mode: C + * c-set-style: "BSD" + * c-basic-offset: 4 + * tab-width: 4 + * indent-tabs-mode: nil + * End: + */ diff --git a/xen/drivers/char/serial.c b/xen/drivers/char/serial.c index 7d9852ebab..c578de152c 100644 --- a/xen/drivers/char/serial.c +++ b/xen/drivers/char/serial.c @@ -1,9 +1,7 @@ /****************************************************************************** * serial.c * - * Driver for 16550-series UARTs. This driver is to be kept within Xen as - * it permits debugging of seriously-toasted machines (e.g., in situations - * where a device driver within a guest OS would be inaccessible). + * Framework for serial device drivers. * * Copyright (c) 2003-2005, K A Fraser */ @@ -15,230 +13,119 @@ #include <xen/reboot.h> #include <xen/sched.h> #include <xen/serial.h> -#include <asm/io.h> - -#ifdef CONFIG_X86 -#include <asm/physdev.h> -#endif - -/* Config serial port with a string <baud>,DPS,<io-base>,<irq>. */ -static char opt_com1[30] = OPT_COM1_STR, opt_com2[30] = OPT_COM2_STR; -string_param("com1", opt_com1); -string_param("com2", opt_com2); - -static struct uart com[2] = { - { 0, 0, 0, 0, 0x3f8, 4, - NULL, NULL, NULL, - SPIN_LOCK_UNLOCKED }, - { 0, 0, 0, 0, 0x2f8, 3, - NULL, NULL, NULL, - SPIN_LOCK_UNLOCKED } -}; - -#define UART_ENABLED(_u) ((_u)->baud != 0) -#define DISABLE_UART(_u) ((_u)->baud = 0) - -/*********************** - * PRIVATE FUNCTIONS - */ +static struct serial_port com[2] = { + { .lock = SPIN_LOCK_UNLOCKED }, + { .lock = SPIN_LOCK_UNLOCKED } +}; -static void uart_rx(struct uart *uart, struct cpu_user_regs *regs) +void serial_rx_interrupt(struct serial_port *port, struct cpu_user_regs *regs) { - unsigned char c; + char c; + serial_rx_fn fn; + unsigned long flags; - if ( !UART_ENABLED(uart) ) - return; + BUG_ON(!port->driver); + BUG_ON(!port->driver->getc); - /* - * No need for the uart spinlock here. Only the uart's own interrupt - * handler will read from the RBR and the handler isn't reentrant. - * Calls to serial_getc() will disable this handler before proceeding. - */ - while ( inb(uart->io_base + LSR) & LSR_DR ) + spin_lock_irqsave(&port->lock, flags); + + while ( port->driver->getc(port, &c) ) { - c = inb(uart->io_base + RBR); - if ( uart->rx != NULL ) - uart->rx(c, regs); - else if ( (c & 0x80) && (uart->rx_hi != NULL) ) - uart->rx_hi(c&0x7f, regs); - else if ( !(c & 0x80) && (uart->rx_lo != NULL) ) - uart->rx_lo(c&0x7f, regs); - else if ( (uart->rxbufp - uart->rxbufc) != RXBUFSZ ) - uart->rxbuf[MASK_RXBUF_IDX(uart->rxbufp++)] = c; + fn = NULL; + + if ( port->rx != NULL ) + fn = port->rx; + else if ( (c & 0x80) && (port->rx_hi != NULL) ) + fn = port->rx_hi; + else if ( !(c & 0x80) && (port->rx_lo != NULL) ) + fn = port->rx_lo; + else if ( (port->rxbufp - port->rxbufc) != RXBUFSZ ) + port->rxbuf[MASK_RXBUF_IDX(port->rxbufp++)] = c; + + if ( fn != NULL ) + { + spin_unlock_irqrestore(&port->lock, flags); + (*fn)(c & 0x7f, regs); + spin_lock_irqsave(&port->lock, flags); + } } -} -static void serial_interrupt( - int irq, void *dev_id, struct cpu_user_regs *regs) -{ - uart_rx((struct uart *)dev_id, regs); + spin_unlock_irqrestore(&port->lock, flags); } -static inline void __serial_putc( - struct uart *uart, int handle, unsigned char c) +void serial_putc(int handle, char c) { + struct serial_port *port = &com[handle & SERHND_IDX]; unsigned long flags; - int space; + + if ( (handle == -1) || !port->driver || !port->driver->putc ) + return; + + spin_lock_irqsave(&port->lock, flags); if ( (c == '\n') && (handle & SERHND_COOKED) ) - __serial_putc(uart, handle, '\r'); + port->driver->putc(port, '\r'); if ( handle & SERHND_HI ) c |= 0x80; else if ( handle & SERHND_LO ) c &= 0x7f; - do { - spin_lock_irqsave(&uart->lock, flags); - space = arch_serial_putc(uart, c); - spin_unlock_irqrestore(&uart->lock, flags); - } - while ( !space ); + port->driver->putc(port, c); + + spin_unlock_irqrestore(&port->lock, flags); } -#define PARSE_ERR(_f, _a...) \ - do { \ - printk( "ERROR: " _f "\n" , ## _a ); \ - DISABLE_UART(uart); \ - return; \ -} while ( 0 ) - -static void parse_port_config(char *conf, struct uart *uart) +void serial_puts(int handle, const char *s) { - if ( *conf == '\0' ) - return; - - uart->baud = simple_strtol(conf, &conf, 10); - if ( (uart->baud < 1200) || (uart->baud > 115200) ) - PARSE_ERR("Baud rate %d outside supported range.", uart->baud); - - if ( *conf != ',' ) - PARSE_ERR("Missing data/parity/stop specifiers."); - - conf++; - - uart->data_bits = simple_strtol(conf, &conf, 10); - if ( (uart->data_bits < 5) || (uart->data_bits > 8) ) - PARSE_ERR("%d data bits are unsupported.", uart->data_bits); + while ( *s != '\0' ) + serial_putc(handle, *s++); +} - switch ( *conf ) +/* Returns TRUE if given character (*pc) matches the serial handle. */ +static int byte_matches(int handle, unsigned char *pc) +{ + if ( !(handle & SERHND_HI) ) { - case 'n': - uart->parity = PARITY_NONE; - break; - case 'o': - uart->parity = PARITY_ODD; - break; - case 'e': - uart->parity = PARITY_EVEN; - break; - case 'm': - uart->parity = PARITY_MARK; - break; - case 's': - uart->parity = PARITY_SPACE; - break; - - default: - PARSE_ERR("Invalid parity specifier '%c'.", *conf); + if ( !(handle & SERHND_LO) || !(*pc & 0x80) ) + return 1; } - - conf++; - - uart->stop_bits = simple_strtol(conf, &conf, 10); - if ( (uart->stop_bits < 1) || (uart->stop_bits > 2) ) - PARSE_ERR("%d stop bits are unsupported.", uart->stop_bits); - - if ( *conf == ',' ) + else if ( *pc & 0x80 ) { - conf++; - - uart->io_base = simple_strtol(conf, &conf, 0); - if ( (uart->io_base <= 0x0000) || (uart->io_base > 0xfff0) ) - PARSE_ERR("I/O port base 0x%x is outside the supported range.", - uart->io_base); - - if ( *conf != ',' ) - PARSE_ERR("Missing IRQ specifier."); - - conf++; - - uart->irq = simple_strtol(conf, &conf, 10); - if ( (uart->irq <= 0) || (uart->irq >= 32) ) - PARSE_ERR("IRQ %d is outside the supported range.", uart->irq); + *pc &= 0x7f; + return 1; } + return 0; } -static void uart_config_stage1(struct uart *uart) -{ - unsigned char lcr; - - if ( !UART_ENABLED(uart) ) - return; - - lcr = (uart->data_bits - 5) | ((uart->stop_bits - 1) << 2) | uart->parity; - - /* No interrupts. */ - outb(0, uart->io_base + IER); - - /* Line control and baud-rate generator. */ - outb(lcr | LCR_DLAB, uart->io_base + LCR); - outb(115200/uart->baud, uart->io_base + DLL); /* baud lo */ - outb(0, uart->io_base + DLM); /* baud hi */ - outb(lcr, uart->io_base + LCR); /* parity, data, stop */ - - /* No flow ctrl: DTR and RTS are both wedged high to keep remote happy. */ - outb(MCR_DTR | MCR_RTS, uart->io_base + MCR); - - /* Enable and clear the FIFOs. Set a large trigger threshold. */ - outb(FCR_ENABLE | FCR_CLRX | FCR_CLTX | FCR_TRG14, uart->io_base + FCR); -} - -static void uart_config_stage2(struct uart *uart) +char serial_getc(int handle) { - int rc; - - if ( !UART_ENABLED(uart) ) - return; - - uart->irqaction.handler = serial_interrupt; - uart->irqaction.name = "serial"; - uart->irqaction.dev_id = uart; - if ( (rc = setup_irq(uart->irq, &uart->irqaction)) != 0 ) - printk("ERROR: Failed to allocate serial IRQ %d\n", uart->irq); - - /* For sanity, clear the receive FIFO. */ - outb(FCR_ENABLE | FCR_CLRX | FCR_TRG14, uart->io_base + FCR); - - /* Master interrupt enable; also keep DTR/RTS asserted. */ - outb(MCR_OUT2 | MCR_DTR | MCR_RTS, uart->io_base + MCR); - - /* Enable receive interrupts. */ - outb(IER_ERDAI, uart->io_base + IER); -} - + struct serial_port *port = &com[handle & SERHND_IDX]; + unsigned char c; + unsigned long flags; -/*********************** - * PUBLIC FUNCTIONS - */ + if ( (handle == -1) || !port->driver || !port->driver->getc ) + return '\0'; -void serial_init_stage1(void) -{ - parse_port_config(opt_com1, &com[0]); - parse_port_config(opt_com2, &com[1]); + spin_lock_irqsave(&port->lock, flags); - uart_config_stage1(&com[0]); - uart_config_stage1(&com[1]); -} + while ( port->rxbufp != port->rxbufc ) + { + c = port->rxbuf[MASK_RXBUF_IDX(port->rxbufc++)]; + if ( byte_matches(handle, &c) ) + goto out; + } + + while ( !port->driver->getc(port, &c) && !byte_matches(handle, &c) ) + continue; -void serial_init_stage2(void) -{ - uart_config_stage2(&com[0]); - uart_config_stage2(&com[1]); + out: + spin_unlock_irqrestore(&port->lock, flags); + return c; } -int parse_serial_handle(char *conf) +int serial_parse_handle(char *conf) { int handle; @@ -261,14 +148,6 @@ int parse_serial_handle(char *conf) goto fail; } -#ifndef NO_UART_CONFIG_OK - if ( !UART_ENABLED(&com[handle]) ) - { - printk("ERROR: cannot use unconfigured serial port COM%d\n", handle+1); - return -1; - } -#endif - if ( conf[4] == 'H' ) handle |= SERHND_HI; else if ( conf[4] == 'L' ) @@ -285,147 +164,80 @@ int parse_serial_handle(char *conf) void serial_set_rx_handler(int handle, serial_rx_fn fn) { - struct uart *uart = &com[handle & SERHND_IDX]; + struct serial_port *port = &com[handle & SERHND_IDX]; unsigned long flags; if ( handle == -1 ) return; - spin_lock_irqsave(&uart->lock, flags); + spin_lock_irqsave(&port->lock, flags); - if ( uart->rx != NULL ) + if ( port->rx != NULL ) goto fail; if ( handle & SERHND_LO ) { - if ( uart->rx_lo != NULL ) + if ( port->rx_lo != NULL ) goto fail; - uart->rx_lo = fn; + port->rx_lo = fn; } else if ( handle & SERHND_HI ) { - if ( uart->rx_hi != NULL ) + if ( port->rx_hi != NULL ) goto fail; - uart->rx_hi = fn; + port->rx_hi = fn; } else { - if ( (uart->rx_hi != NULL) || (uart->rx_lo != NULL) ) + if ( (port->rx_hi != NULL) || (port->rx_lo != NULL) ) goto fail; - uart->rx = fn; + port->rx = fn; } - spin_unlock_irqrestore(&uart->lock, flags); + spin_unlock_irqrestore(&port->lock, flags); return; fail: - spin_unlock_irqrestore(&uart->lock, flags); + spin_unlock_irqrestore(&port->lock, flags); printk("ERROR: Conflicting receive handlers for COM%d\n", handle & SERHND_IDX); } -void serial_putc(int handle, unsigned char c) -{ - struct uart *uart = &com[handle & SERHND_IDX]; - - if ( handle == -1 ) - return; - - __serial_putc(uart, handle, c); -} - -void serial_puts(int handle, const char *s) -{ - struct uart *uart = &com[handle & SERHND_IDX]; - - if ( handle == -1 ) - return; - - while ( *s != '\0' ) - __serial_putc(uart, handle, *s++); -} - -/* Returns TRUE if given character (*pc) matches the serial handle. */ -static int byte_matches(int handle, unsigned char *pc) -{ - if ( !(handle & SERHND_HI) ) - { - if ( !(handle & SERHND_LO) || !(*pc & 0x80) ) - return 1; - } - else if ( *pc & 0x80 ) - { - *pc &= 0x7f; - return 1; - } - return 0; -} - -unsigned char irq_serial_getc(int handle) +void serial_force_unlock(int handle) { - struct uart *uart = &com[handle & SERHND_IDX]; - unsigned char c; - - - while ( uart->rxbufp != uart->rxbufc ) - { - c = uart->rxbuf[MASK_RXBUF_IDX(uart->rxbufc++)]; - if ( byte_matches(handle, &c) ) - goto out; - } - - /* We now wait for the UART to receive a suitable character. */ - do { - while ( (inb(uart->io_base + LSR) & LSR_DR) == 0 ) - barrier(); - c = inb(uart->io_base + RBR); - } - while ( !byte_matches(handle, &c) ); - - out: - return c; + struct serial_port *port = &com[handle & SERHND_IDX]; + if ( handle != -1 ) + port->lock = SPIN_LOCK_UNLOCKED; } -unsigned char serial_getc(int handle) +void serial_init_preirq(void) { - struct uart *uart = &com[handle & SERHND_IDX]; - unsigned char c; - unsigned long flags; - - spin_lock_irqsave(&uart->lock, flags); - - while ( uart->rxbufp != uart->rxbufc ) - { - c = uart->rxbuf[MASK_RXBUF_IDX(uart->rxbufc++)]; - if ( byte_matches(handle, &c) ) - goto out; - } - - disable_irq(uart->irq); - - c = irq_serial_getc(handle); - - enable_irq(uart->irq); - out: - spin_unlock_irqrestore(&uart->lock, flags); - return c; + int i; + for ( i = 0; i < ARRAY_SIZE(com); i++ ) + if ( com[i].driver && com[i].driver->init_preirq ) + com[i].driver->init_preirq(&com[i]); } -void serial_force_unlock(int handle) +void serial_init_postirq(void) { - struct uart *uart = &com[handle & SERHND_IDX]; - if ( handle != -1 ) - uart->lock = SPIN_LOCK_UNLOCKED; + int i; + for ( i = 0; i < ARRAY_SIZE(com); i++ ) + if ( com[i].driver && com[i].driver->init_postirq ) + com[i].driver->init_postirq(&com[i]); } void serial_endboot(void) { -#ifdef CONFIG_X86 int i; for ( i = 0; i < ARRAY_SIZE(com); i++ ) - if ( UART_ENABLED(&com[i]) ) - physdev_modify_ioport_access_range(dom0, 0, com[i].io_base, 8); -#endif + if ( com[i].driver && com[i].driver->endboot ) + com[i].driver->endboot(&com[i]); +} + +void serial_register_uart(int idx, struct uart_driver *driver, void *uart) +{ + com[idx].driver = driver; + com[idx].uart = uart; } /* diff --git a/xen/include/asm-ia64/config.h b/xen/include/asm-ia64/config.h index bb7e5f6e16..34603f5418 100644 --- a/xen/include/asm-ia64/config.h +++ b/xen/include/asm-ia64/config.h @@ -160,9 +160,6 @@ void sort_main_extable(void); (likely(sizeof(count) <= 4) /* disallow 64-bit counts */ && \ access_ok(type,addr,count*size)) -// without this, uart_config_stageX does outb's which are non-portable -#define NO_UART_CONFIG_OK - // see drivers/char/console.c #ifndef CONFIG_VTI #define OPT_CONSOLE_STR "com1" diff --git a/xen/include/asm-ia64/serial.h b/xen/include/asm-ia64/serial.h index faeaf526e8..4acf7a77f1 100644 --- a/xen/include/asm-ia64/serial.h +++ b/xen/include/asm-ia64/serial.h @@ -10,21 +10,11 @@ #define arch_serial_putc(_uart, _c) \ ( platform_is_hp_ski() ? (ia64_ssc(c,0,0,0,SSC_PUTCHAR), 1) : \ ( longs_peak_putc(c), 1 )) - -#define OPT_COM1_STR "115200" -#define OPT_COM2_STR "" -#else // CONFIG_VTI +#else #define arch_serial_putc(_uart, _c) \ ( platform_is_hp_ski() ? (ia64_ssc(c,0,0,0,SSC_PUTCHAR), 1) : \ ( (inb((_uart)->io_base + LSR) & LSR_THRE) ? \ (outb((_c), (_uart)->io_base + THR), 1) : 0 )) - -#define OPT_COM1_STR "" -#define OPT_COM2_STR "57600,8n1" -#endif // CONFIG_VTI - -unsigned char irq_serial_getc(int handle); - -void serial_force_unlock(int handle); +#endif #endif /* __ASM_SERIAL_H__ */ diff --git a/xen/include/asm-x86/serial.h b/xen/include/asm-x86/serial.h deleted file mode 100644 index b93d28fb7e..0000000000 --- a/xen/include/asm-x86/serial.h +++ /dev/null @@ -1,22 +0,0 @@ - -#ifndef __ASM_X86_SERIAL_H__ -#define __ASM_X86_SERIAL_H__ - -#define OPT_COM1_STR "" -#define OPT_COM2_STR "" - -#define arch_serial_putc(_uart, _c) \ - ( (inb((_uart)->io_base + LSR) & LSR_THRE) ? \ - (outb((_c), (_uart)->io_base + THR), 1) : 0 ) - -#endif /* __ASM_X86_SERIAL_H__ */ - -/* - * Local variables: - * mode: C - * c-set-style: "BSD" - * c-basic-offset: 4 - * tab-width: 4 - * indent-tabs-mode: nil - * End: - */ diff --git a/xen/include/xen/console.h b/xen/include/xen/console.h index f65633a1af..024840c0ad 100644 --- a/xen/include/xen/console.h +++ b/xen/include/xen/console.h @@ -22,8 +22,4 @@ void console_endboot(int disable_vga); void console_force_unlock(void); void console_force_lock(void); -void console_putc(char c); -int console_getc(void); -int irq_console_getc(void); - #endif /* __CONSOLE_H__ */ diff --git a/xen/include/xen/serial.h b/xen/include/xen/serial.h index 51a0d1a1f2..73daa385f0 100644 --- a/xen/include/xen/serial.h +++ b/xen/include/xen/serial.h @@ -1,9 +1,7 @@ /****************************************************************************** * serial.h * - * Driver for 16550-series UARTs. This driver is to be kept within Xen as - * it permits debugging of seriously-toasted machines (e.g., in situations - * where a device driver within a guest OS would be inaccessible). + * Framework for serial device drivers. * * Copyright (c) 2003-2005, K A Fraser */ @@ -11,94 +9,61 @@ #ifndef __XEN_SERIAL_H__ #define __XEN_SERIAL_H__ -#include <xen/irq.h> -#include <asm/regs.h> -#include <asm/serial.h> - -/* Register offsets */ -#define RBR 0x00 /* receive buffer */ -#define THR 0x00 /* transmit holding */ -#define IER 0x01 /* interrupt enable */ -#define IIR 0x02 /* interrupt identity */ -#define FCR 0x02 /* FIFO control */ -#define LCR 0x03 /* line control */ -#define MCR 0x04 /* Modem control */ -#define LSR 0x05 /* line status */ -#define MSR 0x06 /* Modem status */ -#define DLL 0x00 /* divisor latch (ls) (DLAB=1) */ -#define DLM 0x01 /* divisor latch (ms) (DLAB=1) */ - -/* Interrupt Enable Register */ -#define IER_ERDAI 0x01 /* rx data recv'd */ -#define IER_ETHREI 0x02 /* tx reg. empty */ -#define IER_ELSI 0x04 /* rx line status */ -#define IER_EMSI 0x08 /* MODEM status */ - -/* FIFO control register */ -#define FCR_ENABLE 0x01 /* enable FIFO */ -#define FCR_CLRX 0x02 /* clear Rx FIFO */ -#define FCR_CLTX 0x04 /* clear Tx FIFO */ -#define FCR_DMA 0x10 /* enter DMA mode */ -#define FCR_TRG1 0x00 /* Rx FIFO trig lev 1 */ -#define FCR_TRG4 0x40 /* Rx FIFO trig lev 4 */ -#define FCR_TRG8 0x80 /* Rx FIFO trig lev 8 */ -#define FCR_TRG14 0xc0 /* Rx FIFO trig lev 14 */ - -/* Line control register */ -#define LCR_DLAB 0x80 /* Divisor Latch Access */ - -/* Modem Control Register */ -#define MCR_DTR 0x01 /* Data Terminal Ready */ -#define MCR_RTS 0x02 /* Request to Send */ -#define MCR_OUT2 0x08 /* OUT2: interrupt mask */ - -/* Line Status Register */ -#define LSR_DR 0x01 /* Data ready */ -#define LSR_OE 0x02 /* Overrun */ -#define LSR_PE 0x04 /* Parity error */ -#define LSR_FE 0x08 /* Framing error */ -#define LSR_BI 0x10 /* Break */ -#define LSR_THRE 0x20 /* Xmit hold reg empty */ -#define LSR_TEMT 0x40 /* Xmitter empty */ -#define LSR_ERR 0x80 /* Error */ - -/* These parity settings can be ORed directly into the LCR. */ -#define PARITY_NONE (0<<3) -#define PARITY_ODD (1<<3) -#define PARITY_EVEN (3<<3) -#define PARITY_MARK (5<<3) -#define PARITY_SPACE (7<<3) +struct cpu_user_regs; /* Register a character-receive hook on the specified COM port. */ -typedef void (*serial_rx_fn)(unsigned char, struct cpu_user_regs *); +typedef void (*serial_rx_fn)(char, struct cpu_user_regs *); void serial_set_rx_handler(int handle, serial_rx_fn fn); +/* Number of characters we buffer for a polling receiver. */ #define RXBUFSZ 32 #define MASK_RXBUF_IDX(_i) ((_i)&(RXBUFSZ-1)) -struct uart { - int baud, data_bits, parity, stop_bits, io_base, irq; - serial_rx_fn rx_lo, rx_hi, rx; - spinlock_t lock; - unsigned char rxbuf[RXBUFSZ]; - unsigned int rxbufp, rxbufc; - struct irqaction irqaction; + +struct uart_driver; + +struct serial_port { + /* Uart-driver parameters. */ + struct uart_driver *driver; + void *uart; + /* Receiver callback functions (asynchronous receivers). */ + serial_rx_fn rx_lo, rx_hi, rx; + /* Receive data buffer (polling receivers). */ + char rxbuf[RXBUFSZ]; + unsigned int rxbufp, rxbufc; + /* Serial I/O is concurrency-safe. */ + spinlock_t lock; +}; + +struct uart_driver { + /* Driver initialisation (pre- and post-IRQ subsystem setup). */ + void (*init_preirq)(struct serial_port *); + void (*init_postirq)(struct serial_port *); + /* Hook to clean up after Xen bootstrap (before domain 0 runs). */ + void (*endboot)(struct serial_port *); + /* Put a char onto the serial line. */ + void (*putc)(struct serial_port *, char); + /* Get a char from the serial line: returns FALSE if no char available. */ + int (*getc)(struct serial_port *, char *); }; -/* 'Serial handles' are comprise the following fields. */ +/* 'Serial handles' are composed from the following fields. */ #define SERHND_IDX (1<<0) /* COM1 or COM2? */ #define SERHND_HI (1<<1) /* Mux/demux each transferred char by MSB. */ #define SERHND_LO (1<<2) /* Ditto, except that the MSB is cleared. */ #define SERHND_COOKED (1<<3) /* Newline/carriage-return translation? */ /* Two-stage initialisation (before/after IRQ-subsystem initialisation). */ -void serial_init_stage1(void); -void serial_init_stage2(void); +void serial_init_preirq(void); +void serial_init_postirq(void); + +/* Clean-up hook before domain 0 runs. */ +void serial_endboot(void); /* Takes a config string and creates a numeric handle on the COM port. */ -int parse_serial_handle(char *conf); +int serial_parse_handle(char *conf); /* Transmit a single character via the specified COM port. */ -void serial_putc(int handle, unsigned char c); +void serial_putc(int handle, char c); /* Transmit a NULL-terminated string via the specified COM port. */ void serial_puts(int handle, const char *s); @@ -108,15 +73,21 @@ void serial_puts(int handle, const char *s); * will not return until a character is available. It can safely be * called with interrupts disabled. */ -unsigned char serial_getc(int handle); -/* - * Same as serial_getc but can also be called from interrupt handlers. - */ -unsigned char irq_serial_getc(int handle); +char serial_getc(int handle); +/* Forcibly prevent serial lockup when the system is in a bad way. */ void serial_force_unlock(int handle); -void serial_endboot(void); +/* Register a uart on serial port @idx (e.g., @idx==0 is COM1). */ +void serial_register_uart(int idx, struct uart_driver *driver, void *uart); + +/* Driver helper function: process receive work in interrupt context. */ +void serial_rx_interrupt(struct serial_port *port, struct cpu_user_regs *regs); + +/* + * Initialisers for individual uart drivers. + */ +void ns16550_init(void); #endif /* __XEN_SERIAL_H__ */ |