From 75eb7d11168d9fcf70b9e7f6d861c4809160db7b Mon Sep 17 00:00:00 2001 From: Vladimir 'phcoder' Serbinenko Date: Sun, 18 Jul 2010 01:37:19 +0200 Subject: [PATCH] Restructure serial in order to prepare for usbserial. As a byproduct simultaneous serial consoles are possible --- commands/terminal.c | 15 +- conf/i386.rmk | 2 +- include/grub/ns8250.h | 67 ++++++ include/grub/serial.h | 100 +++++---- include/grub/terminfo.h | 4 +- include/grub/usb.h | 1 + term/ns8250.c | 212 +++++++++++++++++++ term/serial.c | 439 ++++++++++++++++++---------------------- term/terminfo.c | 23 ++- term/usbserial.c | 165 +++++++++++++++ 10 files changed, 733 insertions(+), 295 deletions(-) create mode 100644 include/grub/ns8250.h create mode 100644 term/ns8250.c create mode 100644 term/usbserial.c diff --git a/commands/terminal.c b/commands/terminal.c index d34602a1b..c8b1b6315 100644 --- a/commands/terminal.c +++ b/commands/terminal.c @@ -59,11 +59,17 @@ handle_command (int argc, char **args, struct abstract_terminal **enabled, for (aut = autoloads; aut; aut = aut->next) { for (term = *disabled; term; term = term->next) - if (grub_strcmp (term->name, aut->name) == 0) + if (grub_strcmp (term->name, aut->name) == 0 + || (aut->name[0] && aut->name[grub_strlen (aut->name) - 1] == '*' + && grub_memcmp (term->name, aut->name, + grub_strlen (aut->name) - 1) == 0)) break; if (!term) for (term = *enabled; term; term = term->next) - if (grub_strcmp (term->name, aut->name) == 0) + if (grub_strcmp (term->name, aut->name) == 0 + || (aut->name[0] && aut->name[grub_strlen (aut->name) - 1] == '*' + && grub_memcmp (term->name, aut->name, + grub_strlen (aut->name) - 1) == 0)) break; if (!term) grub_printf ("%s ", aut->name); @@ -98,7 +104,10 @@ handle_command (int argc, char **args, struct abstract_terminal **enabled, return grub_error (GRUB_ERR_BAD_ARGUMENT, "unknown terminal '%s'\n", args[i]); for (aut = autoloads; aut; aut = aut->next) - if (grub_strcmp (args[i], aut->name) == 0) + if (grub_strcmp (args[i], aut->name) == 0 + || (aut->name[0] && aut->name[grub_strlen (aut->name) - 1] == '*' + && grub_memcmp (args[i], aut->name, + grub_strlen (aut->name) - 1) == 0)) { grub_dl_t mod; mod = grub_dl_load (aut->modname); diff --git a/conf/i386.rmk b/conf/i386.rmk index b1df584a6..6bf1b3410 100644 --- a/conf/i386.rmk +++ b/conf/i386.rmk @@ -56,7 +56,7 @@ multiboot2_mod_ASFLAGS = $(COMMON_ASFLAGS) # For serial.mod. pkglib_MODULES += serial.mod -serial_mod_SOURCES = term/serial.c +serial_mod_SOURCES = term/serial.c term/ns8250.c serial_mod_CFLAGS = $(COMMON_CFLAGS) serial_mod_LDFLAGS = $(COMMON_LDFLAGS) diff --git a/include/grub/ns8250.h b/include/grub/ns8250.h new file mode 100644 index 000000000..f21a1a3e3 --- /dev/null +++ b/include/grub/ns8250.h @@ -0,0 +1,67 @@ +/* serial.h - serial device interface */ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2000,2001,2002,2005,2007 Free Software Foundation, Inc. + * + * GRUB is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * GRUB is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GRUB. If not, see . + */ + +#ifndef GRUB_NS8250_HEADER +#define GRUB_NS8250_HEADER 1 + +/* Macros. */ + +/* The offsets of UART registers. */ +#define UART_TX 0 +#define UART_RX 0 +#define UART_DLL 0 +#define UART_IER 1 +#define UART_DLH 1 +#define UART_IIR 2 +#define UART_FCR 2 +#define UART_LCR 3 +#define UART_MCR 4 +#define UART_LSR 5 +#define UART_MSR 6 +#define UART_SR 7 + +/* For LSR bits. */ +#define UART_DATA_READY 0x01 +#define UART_EMPTY_TRANSMITTER 0x20 + +/* The type of parity. */ +#define UART_NO_PARITY 0x00 +#define UART_ODD_PARITY 0x08 +#define UART_EVEN_PARITY 0x18 + +/* The type of the length of stop bit. */ +#define UART_1_STOP_BIT 0x00 +#define UART_2_STOP_BITS 0x04 + +/* the switch of DLAB. */ +#define UART_DLAB 0x80 + +/* Enable the FIFO. */ +#define UART_ENABLE_FIFO_TRIGGER14 0xC7 + +/* Enable the FIFO. */ +#define UART_ENABLE_FIFO_TRIGGER1 0x07 + +/* Turn on DTR, RTS, and OUT2. */ +#define UART_ENABLE_DTRRTS 0x03 + +/* Turn on DTR, RTS, and OUT2. */ +#define UART_ENABLE_OUT2 0x08 + +#endif /* ! GRUB_SERIAL_MACHINE_HEADER */ diff --git a/include/grub/serial.h b/include/grub/serial.h index 758b6fb3e..063282112 100644 --- a/include/grub/serial.h +++ b/include/grub/serial.h @@ -1,7 +1,7 @@ /* serial.h - serial device interface */ /* * GRUB -- GRand Unified Bootloader - * Copyright (C) 2000,2001,2002,2005,2007 Free Software Foundation, Inc. + * Copyright (C) 2010 Free Software Foundation, Inc. * * GRUB is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -20,30 +20,51 @@ #ifndef GRUB_SERIAL_HEADER #define GRUB_SERIAL_HEADER 1 -/* Macros. */ +#include +#include +#include +#include -/* The offsets of UART registers. */ -#define UART_TX 0 -#define UART_RX 0 -#define UART_DLL 0 -#define UART_IER 1 -#define UART_DLH 1 -#define UART_IIR 2 -#define UART_FCR 2 -#define UART_LCR 3 -#define UART_MCR 4 -#define UART_LSR 5 -#define UART_MSR 6 -#define UART_SR 7 +struct grub_serial_port; -/* For LSR bits. */ -#define UART_DATA_READY 0x01 -#define UART_EMPTY_TRANSMITTER 0x20 +struct grub_serial_driver +{ + grub_err_t (*configure) (struct grub_serial_port *port, + unsigned speed, + unsigned short word_len, + unsigned int parity, + unsigned short stop_bits); + int (*fetch) (struct grub_serial_port *port); + void (*put) (struct grub_serial_port *port, const int c); +}; -/* The type of parity. */ -#define UART_NO_PARITY 0x00 -#define UART_ODD_PARITY 0x08 -#define UART_EVEN_PARITY 0x18 +struct grub_serial_port +{ + struct grub_serial_port *next; + char *name; + unsigned speed; + unsigned short word_len; + unsigned int parity; + unsigned short stop_bits; + struct grub_serial_driver *driver; + /* This should be void *data but since serial is useful as an early console + when malloc isn't available it's a union. + */ + union + { + grub_port_t port; + struct + { + grub_usb_device_t dev; + int in_endp; + int out_endp; + }; + }; +}; + +grub_err_t grub_serial_register (struct grub_serial_port *port); + +void grub_serial_unregister (struct grub_serial_port *port); /* The type of word length. */ #define UART_5BITS_WORD 0x00 @@ -51,23 +72,30 @@ #define UART_7BITS_WORD 0x02 #define UART_8BITS_WORD 0x03 +/* The type of parity. */ +#define UART_NO_PARITY 0x00 +#define UART_ODD_PARITY 0x08 +#define UART_EVEN_PARITY 0x18 + /* The type of the length of stop bit. */ #define UART_1_STOP_BIT 0x00 #define UART_2_STOP_BITS 0x04 -/* the switch of DLAB. */ -#define UART_DLAB 0x80 +static inline void +grub_serial_fill_defaults (struct grub_serial_port *port) +{ + /* Set default settings. */ +#ifdef GRUB_MACHINE_MIPS_YEELOONG + port->speed = 115200; +#else + port->speed = 9600; +#endif + port->word_len = UART_8BITS_WORD; + port->parity = UART_NO_PARITY; + port->stop_bits = UART_1_STOP_BIT; +} -/* Enable the FIFO. */ -#define UART_ENABLE_FIFO_TRIGGER14 0xC7 +void grub_ns8250_init (void); +char *grub_serial_ns8250_add_port (grub_port_t port); -/* Enable the FIFO. */ -#define UART_ENABLE_FIFO_TRIGGER1 0x07 - -/* Turn on DTR, RTS, and OUT2. */ -#define UART_ENABLE_DTRRTS 0x03 - -/* Turn on DTR, RTS, and OUT2. */ -#define UART_ENABLE_OUT2 0x08 - -#endif /* ! GRUB_SERIAL_MACHINE_HEADER */ +#endif diff --git a/include/grub/terminfo.h b/include/grub/terminfo.h index d6907d7f0..85ddb5779 100644 --- a/include/grub/terminfo.h +++ b/include/grub/terminfo.h @@ -32,7 +32,7 @@ struct grub_terminfo_input_state { int input_buf[GRUB_TERMINFO_READKEY_MAX_LEN]; int npending; - int (*readkey) (void); + int (*readkey) (struct grub_term_input *term); }; struct grub_terminfo_output_state @@ -51,7 +51,7 @@ struct grub_terminfo_output_state unsigned int xpos, ypos; - void (*put) (const int c); + void (*put) (struct grub_term_output *term, const int c); }; void EXPORT_FUNC(grub_terminfo_gotoxy) (grub_term_output_t term, diff --git a/include/grub/usb.h b/include/grub/usb.h index 3c17318fc..f9c3d61ff 100644 --- a/include/grub/usb.h +++ b/include/grub/usb.h @@ -19,6 +19,7 @@ #ifndef GRUB_USB_H #define GRUB_USB_H 1 +#include #include #include diff --git a/term/ns8250.c b/term/ns8250.c new file mode 100644 index 000000000..4f09c7606 --- /dev/null +++ b/term/ns8250.c @@ -0,0 +1,212 @@ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2000,2001,2002,2003,2004,2005,2007,2008,2009,2010 Free Software Foundation, Inc. + * + * GRUB is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * GRUB is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GRUB. If not, see . + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef GRUB_MACHINE_PCBIOS +static const unsigned short *serial_hw_io_addr = (const unsigned short *) GRUB_MEMORY_MACHINE_BIOS_DATA_AREA_ADDR; +#define GRUB_SERIAL_PORT_NUM 4 +#else +#include +static const grub_port_t serial_hw_io_addr[] = GRUB_MACHINE_SERIAL_PORTS; +#define GRUB_SERIAL_PORT_NUM (ARRAY_SIZE(serial_hw_io_addr)) +#endif + +/* Fetch a key. */ +static int +serial_hw_fetch (struct grub_serial_port *port) +{ + if (grub_inb (port->port + UART_LSR) & UART_DATA_READY) + return grub_inb (port->port + UART_RX); + + return -1; +} + +/* Put a character. */ +static void +serial_hw_put (struct grub_serial_port *port, const int c) +{ + unsigned int timeout = 100000; + + /* Wait until the transmitter holding register is empty. */ + while ((grub_inb (port->port + UART_LSR) & UART_EMPTY_TRANSMITTER) == 0) + { + if (--timeout == 0) + /* There is something wrong. But what can I do? */ + return; + } + + grub_outb (c, port->port + UART_TX); +} + +/* Convert speed to divisor. */ +static unsigned short +serial_get_divisor (unsigned int speed) +{ + unsigned int i; + + /* The structure for speed vs. divisor. */ + struct divisor + { + unsigned int speed; + unsigned short div; + }; + + /* The table which lists common configurations. */ + /* 1843200 / (speed * 16) */ + static struct divisor divisor_tab[] = + { + { 2400, 0x0030 }, + { 4800, 0x0018 }, + { 9600, 0x000C }, + { 19200, 0x0006 }, + { 38400, 0x0003 }, + { 57600, 0x0002 }, + { 115200, 0x0001 } + }; + + /* Set the baud rate. */ + for (i = 0; i < sizeof (divisor_tab) / sizeof (divisor_tab[0]); i++) + if (divisor_tab[i].speed == speed) + /* UART in Yeeloong runs twice the usual rate. */ +#ifdef GRUB_MACHINE_MIPS_YEELOONG + return 2 * divisor_tab[i].div; +#else + return divisor_tab[i].div; +#endif + return 0; +} + +/* Initialize a serial device. PORT is the port number for a serial device. + SPEED is a DTE-DTE speed which must be one of these: 2400, 4800, 9600, + 19200, 38400, 57600 and 115200. WORD_LEN is the word length to be used + for the device. Likewise, PARITY is the type of the parity and + STOP_BIT_LEN is the length of the stop bit. The possible values for + WORD_LEN, PARITY and STOP_BIT_LEN are defined in the header file as + macros. */ +static grub_err_t +serial_hw_configure (struct grub_serial_port *port, + unsigned speed, unsigned short word_len, + unsigned int parity, unsigned short stop_bits) +{ + unsigned char status = 0; + unsigned short divisor; + + divisor = serial_get_divisor (speed); + if (divisor == 0) + return grub_error (GRUB_ERR_BAD_ARGUMENT, "bad speed"); + + port->speed = speed; + port->word_len = word_len; + port->parity = parity; + port->stop_bits = stop_bits; + + /* Turn off the interrupt. */ + grub_outb (0, port->port + UART_IER); + + /* Set DLAB. */ + grub_outb (UART_DLAB, port->port + UART_LCR); + + /* Set the baud rate. */ + grub_outb (divisor & 0xFF, port->port + UART_DLL); + grub_outb (divisor >> 8, port->port + UART_DLH); + + /* Set the line status. */ + status |= (port->parity | port->word_len | port->stop_bits); + grub_outb (status, port->port + UART_LCR); + + /* In Yeeloong serial port has only 3 wires. */ +#ifndef GRUB_MACHINE_MIPS_YEELOONG + /* Enable the FIFO. */ + grub_outb (UART_ENABLE_FIFO_TRIGGER1, port->port + UART_FCR); + + /* Turn on DTR and RTS. */ + grub_outb (UART_ENABLE_DTRRTS, port->port + UART_MCR); +#else + /* Enable the FIFO. */ + grub_outb (UART_ENABLE_FIFO_TRIGGER14, port->port + UART_FCR); + + /* Turn on DTR, RTS, and OUT2. */ + grub_outb (UART_ENABLE_DTRRTS | UART_ENABLE_OUT2, port->port + UART_MCR); +#endif + + /* Drain the input buffer. */ + while (serial_hw_fetch (port) != -1); + + /* FIXME: should check if the serial terminal was found. */ + + return GRUB_ERR_NONE; +} + +static struct grub_serial_driver grub_ns8250_driver = + { + .configure = serial_hw_configure, + .fetch = serial_hw_fetch, + .put = serial_hw_put + }; + +static char com_names[GRUB_SERIAL_PORT_NUM][20]; +static struct grub_serial_port com_ports[GRUB_SERIAL_PORT_NUM]; + +void +grub_ns8250_init (void) +{ + int i; + for (i = 0; i < GRUB_SERIAL_PORT_NUM; i++) + //if (serial_hw_io_addr[i]) + { + grub_snprintf (com_names[i], sizeof (com_names[i]), "com%d", i); + com_ports[i].name = com_names[i]; + com_ports[i].driver = &grub_ns8250_driver; + grub_serial_fill_defaults (&com_ports[i]); + com_ports[i].port = serial_hw_io_addr[i]; + grub_serial_register (&com_ports[i]); + } +} + +char * +grub_serial_ns8250_add_port (grub_port_t port) +{ + struct grub_serial_port *p; + int i; + for (i = 0; i < GRUB_SERIAL_PORT_NUM; i++) + if (com_ports[i].port == port) + return com_names[i]; + p = grub_malloc (sizeof (*p)); + if (!p) + return NULL; + p->name = grub_xasprintf ("port%lx", (unsigned long) port); + if (!p->name) + { + grub_free (p); + return NULL; + } + p->driver = &grub_ns8250_driver; + grub_serial_fill_defaults (p); + p->port = port; + grub_serial_register (p); + + return p->name; +} diff --git a/term/serial.c b/term/serial.c index cf7759ef2..1f54a83ec 100644 --- a/term/serial.c +++ b/term/serial.c @@ -1,6 +1,6 @@ /* * GRUB -- GRand Unified Bootloader - * Copyright (C) 2000,2001,2002,2003,2004,2005,2007,2008,2009 Free Software Foundation, Inc. + * Copyright (C) 2000,2001,2002,2003,2004,2005,2007,2008,2009,2010 Free Software Foundation, Inc. * * GRUB is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -16,7 +16,6 @@ * along with GRUB. If not, see . */ -#include #include #include #include @@ -26,8 +25,9 @@ #include #include #include +#include -static unsigned int registered = 0; +#define FOR_SERIAL_PORTS(var) FOR_LIST_ELEMENTS((var), (grub_serial_ports)) /* Argument options. */ static const struct grub_arg_option options[] = @@ -41,154 +41,19 @@ static const struct grub_arg_option options[] = {0, 0, 0, 0, 0, 0} }; -/* Serial port settings. */ -struct serial_port +struct grub_serial_port *grub_serial_ports; + +struct grub_serial_output_state { - grub_port_t port; - unsigned short divisor; - unsigned short word_len; - unsigned int parity; - unsigned short stop_bits; + struct grub_terminfo_output_state tinfo; + struct grub_serial_port *port; }; -/* Serial port settings. */ -static struct serial_port serial_settings; - -#ifdef GRUB_MACHINE_PCBIOS -static const unsigned short *serial_hw_io_addr = (const unsigned short *) GRUB_MEMORY_MACHINE_BIOS_DATA_AREA_ADDR; -#define GRUB_SERIAL_PORT_NUM 4 -#else -#include -static const grub_port_t serial_hw_io_addr[] = GRUB_MACHINE_SERIAL_PORTS; -#define GRUB_SERIAL_PORT_NUM (ARRAY_SIZE(serial_hw_io_addr)) -#endif - -/* Return the port number for the UNITth serial device. */ -static inline grub_port_t -serial_hw_get_port (const unsigned int unit) +struct grub_serial_input_state { - if (unit < GRUB_SERIAL_PORT_NUM) - return serial_hw_io_addr[unit]; - else - return 0; -} - -/* Fetch a key. */ -static int -serial_hw_fetch (void) -{ - if (grub_inb (serial_settings.port + UART_LSR) & UART_DATA_READY) - return grub_inb (serial_settings.port + UART_RX); - - return -1; -} - -/* Put a character. */ -static void -serial_hw_put (const int c) -{ - unsigned int timeout = 100000; - - /* Wait until the transmitter holding register is empty. */ - while ((grub_inb (serial_settings.port + UART_LSR) & UART_EMPTY_TRANSMITTER) == 0) - { - if (--timeout == 0) - /* There is something wrong. But what can I do? */ - return; - } - - grub_outb (c, serial_settings.port + UART_TX); -} - -/* Convert speed to divisor. */ -static unsigned short -serial_get_divisor (unsigned int speed) -{ - unsigned int i; - - /* The structure for speed vs. divisor. */ - struct divisor - { - unsigned int speed; - unsigned short div; - }; - - /* The table which lists common configurations. */ - /* 1843200 / (speed * 16) */ - static struct divisor divisor_tab[] = - { - { 2400, 0x0030 }, - { 4800, 0x0018 }, - { 9600, 0x000C }, - { 19200, 0x0006 }, - { 38400, 0x0003 }, - { 57600, 0x0002 }, - { 115200, 0x0001 } - }; - - /* Set the baud rate. */ - for (i = 0; i < sizeof (divisor_tab) / sizeof (divisor_tab[0]); i++) - if (divisor_tab[i].speed == speed) - /* UART in Yeeloong runs twice the usual rate. */ -#ifdef GRUB_MACHINE_MIPS_YEELOONG - return 2 * divisor_tab[i].div; -#else - return divisor_tab[i].div; -#endif - return 0; -} - -/* Initialize a serial device. PORT is the port number for a serial device. - SPEED is a DTE-DTE speed which must be one of these: 2400, 4800, 9600, - 19200, 38400, 57600 and 115200. WORD_LEN is the word length to be used - for the device. Likewise, PARITY is the type of the parity and - STOP_BIT_LEN is the length of the stop bit. The possible values for - WORD_LEN, PARITY and STOP_BIT_LEN are defined in the header file as - macros. */ -static grub_err_t -serial_hw_init (void) -{ - unsigned char status = 0; - - /* Turn off the interrupt. */ - grub_outb (0, serial_settings.port + UART_IER); - - /* Set DLAB. */ - grub_outb (UART_DLAB, serial_settings.port + UART_LCR); - - /* Set the baud rate. */ - grub_outb (serial_settings.divisor & 0xFF, serial_settings.port + UART_DLL); - grub_outb (serial_settings.divisor >> 8, serial_settings.port + UART_DLH); - - /* Set the line status. */ - status |= (serial_settings.parity - | serial_settings.word_len - | serial_settings.stop_bits); - grub_outb (status, serial_settings.port + UART_LCR); - - /* In Yeeloong serial port has only 3 wires. */ -#ifndef GRUB_MACHINE_MIPS_YEELOONG - /* Enable the FIFO. */ - grub_outb (UART_ENABLE_FIFO_TRIGGER1, serial_settings.port + UART_FCR); - - /* Turn on DTR and RTS. */ - grub_outb (UART_ENABLE_DTRRTS, serial_settings.port + UART_MCR); -#else - /* Enable the FIFO. */ - grub_outb (UART_ENABLE_FIFO_TRIGGER14, serial_settings.port + UART_FCR); - - /* Turn on DTR, RTS, and OUT2. */ - grub_outb (UART_ENABLE_DTRRTS | UART_ENABLE_OUT2, - serial_settings.port + UART_MCR); -#endif - - /* Drain the input buffer. */ - while (serial_hw_fetch () != -1); - - /* FIXME: should check if the serial terminal was found. */ - - return GRUB_ERR_NONE; -} + struct grub_terminfo_input_state tinfo; + struct grub_serial_port *port; +}; static grub_uint16_t grub_serial_getwh (struct grub_term_output *term __attribute__ ((unused))) @@ -198,16 +63,38 @@ grub_serial_getwh (struct grub_term_output *term __attribute__ ((unused))) return (TEXT_WIDTH << 8) | TEXT_HEIGHT; } -struct grub_terminfo_input_state grub_serial_terminfo_input = +static void +serial_put (grub_term_output_t term, const int c) +{ + struct grub_serial_output_state *data = term->data; + data->port->driver->put (data->port, c); +} + +static int +serial_fetch (grub_term_input_t term) +{ + struct grub_serial_input_state *data = term->data; + return data->port->driver->fetch (data->port); +} + +struct grub_serial_input_state grub_serial_terminfo_input = { - .readkey = serial_hw_fetch + .tinfo = + { + .readkey = serial_fetch + } }; -struct grub_terminfo_output_state grub_serial_terminfo_output = +struct grub_serial_output_state grub_serial_terminfo_output = { - .put = serial_hw_put + .tinfo = + { + .put = serial_put + } }; +int registered = 0; + static struct grub_term_input grub_serial_term_input = { .name = "serial", @@ -235,122 +122,200 @@ static struct grub_term_output grub_serial_term_output = +static struct grub_serial_port * +grub_serial_find (char *name) +{ + struct grub_serial_port *port; + + FOR_SERIAL_PORTS (port) + if (grub_strcmp (port->name, name) == 0) + break; + + if (!port && grub_memcmp (name, "port", sizeof ("port") - 1) == 0 + && grub_isdigit (name [sizeof ("port") - 1])) + { + name = grub_serial_ns8250_add_port (grub_strtoul (&name[sizeof ("port") - 1], + 0, 16)); + if (!name) + return NULL; + + FOR_SERIAL_PORTS (port) + if (grub_strcmp (port->name, name) == 0) + break; + } + + return port; +} + static grub_err_t -grub_cmd_serial (grub_extcmd_t cmd, - int argc __attribute__ ((unused)), - char **args __attribute__ ((unused))) +grub_cmd_serial (grub_extcmd_t cmd, int argc, char **args) { struct grub_arg_list *state = cmd->state; - struct serial_port backup_settings = serial_settings; - grub_err_t hwiniterr; + char pname[40]; + char *name = NULL; + struct grub_serial_port *port; + signed speed = -1; + signed short word_len = -1; + signed int parity = -1; + signed short stop_bits = -1; + grub_err_t err; if (state[0].set) { - unsigned int unit; - - unit = grub_strtoul (state[0].arg, 0, 0); - serial_settings.port = serial_hw_get_port (unit); - if (!serial_settings.port) - return grub_error (GRUB_ERR_BAD_ARGUMENT, "bad unit number"); + grub_snprintf (pname, sizeof (pname), "com%ld", + grub_strtoul (state[0].arg, 0, 0)); + name = pname; } if (state[1].set) - serial_settings.port = (grub_port_t) grub_strtoul (state[1].arg, 0, 0); + { + grub_snprintf (pname, sizeof (pname), "port%lx", + grub_strtoul (state[1].arg, 0, 0)); + name = pname; + } + + if (argc >= 1) + name = args[0]; + + if (!name) + name = "com0"; + + port = grub_serial_find (name); + if (!port) + return grub_error (GRUB_ERR_BAD_ARGUMENT, "unknown serial port"); + + speed = port->speed; + word_len = port->word_len; + parity = port->parity; + stop_bits = port->stop_bits; if (state[2].set) - { - unsigned long speed; - - speed = grub_strtoul (state[2].arg, 0, 0); - serial_settings.divisor = serial_get_divisor ((unsigned int) speed); - if (serial_settings.divisor == 0) - { - serial_settings = backup_settings; - return grub_error (GRUB_ERR_BAD_ARGUMENT, "bad speed"); - } - } + speed = grub_strtoul (state[2].arg, 0, 0); if (state[3].set) { if (! grub_strcmp (state[3].arg, "5")) - serial_settings.word_len = UART_5BITS_WORD; + word_len = UART_5BITS_WORD; else if (! grub_strcmp (state[3].arg, "6")) - serial_settings.word_len = UART_6BITS_WORD; + word_len = UART_6BITS_WORD; else if (! grub_strcmp (state[3].arg, "7")) - serial_settings.word_len = UART_7BITS_WORD; + word_len = UART_7BITS_WORD; else if (! grub_strcmp (state[3].arg, "8")) - serial_settings.word_len = UART_8BITS_WORD; + word_len = UART_8BITS_WORD; else - { - serial_settings = backup_settings; - return grub_error (GRUB_ERR_BAD_ARGUMENT, "bad word length"); - } + return grub_error (GRUB_ERR_BAD_ARGUMENT, "bad word length"); } if (state[4].set) { if (! grub_strcmp (state[4].arg, "no")) - serial_settings.parity = UART_NO_PARITY; + parity = UART_NO_PARITY; else if (! grub_strcmp (state[4].arg, "odd")) - serial_settings.parity = UART_ODD_PARITY; + parity = UART_ODD_PARITY; else if (! grub_strcmp (state[4].arg, "even")) - serial_settings.parity = UART_EVEN_PARITY; + parity = UART_EVEN_PARITY; else - { - serial_settings = backup_settings; - return grub_error (GRUB_ERR_BAD_ARGUMENT, "bad parity"); - } + return grub_error (GRUB_ERR_BAD_ARGUMENT, "bad parity"); } if (state[5].set) { if (! grub_strcmp (state[5].arg, "1")) - serial_settings.stop_bits = UART_1_STOP_BIT; + stop_bits = UART_1_STOP_BIT; else if (! grub_strcmp (state[5].arg, "2")) - serial_settings.stop_bits = UART_2_STOP_BITS; + stop_bits = UART_2_STOP_BITS; else - { - serial_settings = backup_settings; - return grub_error (GRUB_ERR_BAD_ARGUMENT, "bad number of stop bits"); - } + return grub_error (GRUB_ERR_BAD_ARGUMENT, "bad number of stop bits"); } /* Initialize with new settings. */ - hwiniterr = serial_hw_init (); - - if (hwiniterr == GRUB_ERR_NONE) + err = port->driver->configure (port, speed, word_len, parity, stop_bits); + if (err) + return err; + if (!registered) { - /* Register terminal if not yet registered. */ - if (registered == 0) - { - grub_term_register_input ("serial", &grub_serial_term_input); - grub_term_register_output ("serial", &grub_serial_term_output); - grub_terminfo_output_register (&grub_serial_term_output, "vt100"); - registered = 1; - } + grub_term_register_input ("serial", &grub_serial_term_input); + grub_term_register_output ("serial", &grub_serial_term_output); } - else - { - /* Initialization with new settings failed. */ - if (registered == 1) - { - /* If the terminal is registered, attempt to restore previous - settings. */ - serial_settings = backup_settings; - if (serial_hw_init () != GRUB_ERR_NONE) - { - /* If unable to restore settings, unregister terminal. */ - grub_term_unregister_input (&grub_serial_term_input); - grub_term_unregister_output (&grub_serial_term_output); - grub_terminfo_output_unregister (&grub_serial_term_output); - registered = 0; - } - } - } - - return hwiniterr; + grub_serial_terminfo_output.port = port; + grub_serial_terminfo_input.port = port; + registered = 1; + return GRUB_ERR_NONE; } +grub_err_t +grub_serial_register (struct grub_serial_port *port) +{ + struct grub_term_input *in; + struct grub_term_output *out; + struct grub_serial_input_state *indata; + struct grub_serial_output_state *outdata; + + in = grub_malloc (sizeof (*in)); + if (!in) + return grub_errno; + + indata = grub_malloc (sizeof (*indata)); + if (!indata) + { + grub_free (in); + return grub_errno; + } + + grub_memcpy (in, &grub_serial_term_input, sizeof (*in)); + in->data = indata; + in->name = grub_xasprintf ("serial_%s", port->name); + grub_memcpy (indata, &grub_serial_terminfo_input, sizeof (*indata)); + + if (!in->name) + { + grub_free (in); + grub_free (indata); + return grub_errno; + } + + out = grub_malloc (sizeof (*out)); + if (!out) + { + grub_free (in); + grub_free (indata); + grub_free ((char *) in->name); + return grub_errno; + } + + outdata = grub_malloc (sizeof (*outdata)); + if (!outdata) + { + grub_free (in); + grub_free (indata); + grub_free ((char *) in->name); + grub_free (out); + return grub_errno; + } + + grub_memcpy (out, &grub_serial_term_output, sizeof (*out)); + out->data = outdata; + out->name = in->name; + grub_memcpy (outdata, &grub_serial_terminfo_output, sizeof (*outdata)); + + grub_list_push (GRUB_AS_LIST_P (&grub_serial_ports), GRUB_AS_LIST (port)); + ((struct grub_serial_input_state *) in->data)->port = port; + ((struct grub_serial_output_state *) out->data)->port = port; + grub_term_register_input ("serial_*", in); + grub_term_register_output ("serial_*", out); + grub_terminfo_output_register (out, "vt100"); + + return GRUB_ERR_NONE; +} + +void +grub_serial_unregister (struct grub_serial_port *port) +{ + grub_list_remove (GRUB_AS_LIST_P (&grub_serial_ports), GRUB_AS_LIST (port)); + /* FIXME */ +} + + static grub_extcmd_t cmd; GRUB_MOD_INIT(serial) @@ -360,21 +325,13 @@ GRUB_MOD_INIT(serial) N_("[OPTIONS...]"), N_("Configure serial port."), options); - /* Set default settings. */ - serial_settings.port = serial_hw_get_port (0); -#ifdef GRUB_MACHINE_MIPS_YEELOONG - serial_settings.divisor = serial_get_divisor (115200); -#else - serial_settings.divisor = serial_get_divisor (9600); -#endif - serial_settings.word_len = UART_8BITS_WORD; - serial_settings.parity = UART_NO_PARITY; - serial_settings.stop_bits = UART_1_STOP_BIT; + grub_ns8250_init (); #ifdef GRUB_MACHINE_MIPS_YEELOONG { grub_err_t hwiniterr; - hwiniterr = serial_hw_init (); + hwiniterr = grub_ns8250_driver.init ("com0", &serial_settings); + serial_settings.driver = &grub_ns8250_driver; if (hwiniterr == GRUB_ERR_NONE) { @@ -389,11 +346,7 @@ GRUB_MOD_INIT(serial) GRUB_MOD_FINI(serial) { + while (grub_serial_ports) + grub_serial_unregister (grub_serial_ports); grub_unregister_extcmd (cmd); - if (registered == 1) /* Unregister terminal only if registered. */ - { - grub_term_unregister_input (&grub_serial_term_input); - grub_term_unregister_output (&grub_serial_term_output); - grub_terminfo_output_unregister (&grub_serial_term_output); - } } diff --git a/term/terminfo.c b/term/terminfo.c index ff54e5dba..5691b9cc6 100644 --- a/term/terminfo.c +++ b/term/terminfo.c @@ -195,7 +195,7 @@ putstr (struct grub_term_output *term, const char *str) struct grub_terminfo_output_state *data = (struct grub_terminfo_output_state *) term->data; while (*str) - data->put (*str++); + data->put (term, *str++); } grub_uint16_t @@ -225,7 +225,7 @@ grub_terminfo_gotoxy (struct grub_term_output *term, else { if ((y == data->ypos) && (x == data->xpos - 1)) - data->put ('\b'); + data->put (term, '\b'); } data->xpos = x; @@ -348,20 +348,21 @@ grub_terminfo_putchar (struct grub_term_output *term, data->xpos = 0; if (data->ypos < grub_term_height (term) - 1) data->ypos++; - data->put ('\r'); - data->put ('\n'); + data->put (term, '\r'); + data->put (term, '\n'); } data->xpos += c->estimated_width; break; } - data->put (c->base); + data->put (term, c->base); } #define ANSI_C0 0x9b static void -grub_terminfo_readkey (int *keys, int *len, int (*readkey) (void)) +grub_terminfo_readkey (struct grub_term_input *term, int *keys, int *len, + int (*readkey) (struct grub_term_input *term)) { int c; @@ -371,7 +372,7 @@ grub_terminfo_readkey (int *keys, int *len, int (*readkey) (void)) /* On 9600 we have to wait up to 12 milliseconds. */ \ start = grub_get_time_ms (); \ do \ - c = readkey (); \ + c = readkey (term); \ while (c == -1 && grub_get_time_ms () - start < 12); \ if (c == -1) \ return; \ @@ -380,7 +381,7 @@ grub_terminfo_readkey (int *keys, int *len, int (*readkey) (void)) (*len)++; \ } - c = readkey (); + c = readkey (term); if (c < 0) { *len = 0; @@ -475,7 +476,8 @@ grub_terminfo_checkkey (struct grub_term_input *termi) if (data->npending) return data->input_buf[0]; - grub_terminfo_readkey (data->input_buf, &data->npending, data->readkey); + grub_terminfo_readkey (termi, data->input_buf, + &data->npending, data->readkey); if (data->npending) return data->input_buf[0]; @@ -491,7 +493,8 @@ grub_terminfo_getkey (struct grub_term_input *termi) = (struct grub_terminfo_input_state *) (termi->data); int ret; while (! data->npending) - grub_terminfo_readkey (data->input_buf, &data->npending, data->readkey); + grub_terminfo_readkey (termi, data->input_buf, &data->npending, + data->readkey); ret = data->input_buf[0]; data->npending--; diff --git a/term/usbserial.c b/term/usbserial.c new file mode 100644 index 000000000..5a63348fb --- /dev/null +++ b/term/usbserial.c @@ -0,0 +1,165 @@ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2000,2001,2002,2003,2004,2005,2007,2008,2009 Free Software Foundation, Inc. + * + * GRUB is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * GRUB is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GRUB. If not, see . + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static unsigned int registered = 0; + +/* Argument options. */ +static const struct grub_arg_option options[] = +{ + {"unit", 'u', 0, N_("Set the serial unit."), 0, ARG_TYPE_INT}, + {"speed", 's', 0, N_("Set the serial port speed."), 0, ARG_TYPE_INT}, + {"word", 'w', 0, N_("Set the serial port word length."), 0, ARG_TYPE_INT}, + {"parity", 'r', 0, N_("Set the serial port parity."), 0, ARG_TYPE_STRING}, + {"stop", 't', 0, N_("Set the serial port stop bits."), 0, ARG_TYPE_INT}, + {0, 0, 0, 0, 0, 0} +}; + +/* Serial port settings. */ +struct usbserial_port +{ + grub_usb_device_t usbdev; + int in_endp; + int out_endp; + unsigned short divisor; + unsigned short word_len; + unsigned int parity; + unsigned short stop_bits; +}; + +/* Serial port settings. */ +static struct serial_port serial_settings; + +/* Fetch a key. */ +static int +serial_hw_fetch (void) +{ + /* FIXME */ + return -1; +} + +/* Put a character. */ +static void +usbserial_hw_put (struct usbserial_port *port, const int c) +{ + char cc = c; + grub_usb_bulk_write (port->usbdev, port->out_endp, 1, &cc); +} + +static grub_uint16_t +grub_serial_getwh (struct grub_term_output *term __attribute__ ((unused))) +{ + const grub_uint8_t TEXT_WIDTH = 80; + const grub_uint8_t TEXT_HEIGHT = 24; + return (TEXT_WIDTH << 8) | TEXT_HEIGHT; +} + +struct grub_terminfo_input_state grub_serial_terminfo_input = + { + .readkey = serial_hw_fetch + }; + +struct grub_terminfo_output_state grub_serial_terminfo_output = + { + .put = usbserial_hw_put + }; + +static struct grub_term_input grub_serial_term_input = +{ + .name = "usbserial", + .init = grub_terminfo_input_init, + .checkkey = grub_terminfo_checkkey, + .getkey = grub_terminfo_getkey, + .data = &grub_serial_terminfo_input +}; + +static struct grub_term_output grub_serial_term_output = +{ + .name = "usbserial", + .putchar = grub_terminfo_putchar, + .getwh = grub_serial_getwh, + .getxy = grub_terminfo_getxy, + .gotoxy = grub_terminfo_gotoxy, + .cls = grub_terminfo_cls, + .setcolorstate = grub_terminfo_setcolorstate, + .setcursor = grub_terminfo_setcursor, + .flags = GRUB_TERM_CODE_TYPE_ASCII, + .data = &grub_serial_terminfo_output, + .normal_color = GRUB_TERM_DEFAULT_NORMAL_COLOR, + .highlight_color = GRUB_TERM_DEFAULT_HIGHLIGHT_COLOR, +}; + + + + +static grub_extcmd_t cmd; + +GRUB_MOD_INIT(serial) +{ + cmd = grub_register_extcmd ("serial", grub_cmd_serial, + GRUB_COMMAND_FLAG_BOTH, + N_("[OPTIONS...]"), + N_("Configure serial port."), options); + + /* Set default settings. */ + serial_settings.port = serial_hw_get_port (0); +#ifdef GRUB_MACHINE_MIPS_YEELOONG + serial_settings.divisor = serial_get_divisor (115200); +#else + serial_settings.divisor = serial_get_divisor (9600); +#endif + serial_settings.word_len = UART_8BITS_WORD; + serial_settings.parity = UART_NO_PARITY; + serial_settings.stop_bits = UART_1_STOP_BIT; + +#ifdef GRUB_MACHINE_MIPS_YEELOONG + { + grub_err_t hwiniterr; + hwiniterr = serial_hw_init (); + + if (hwiniterr == GRUB_ERR_NONE) + { + grub_term_register_input_active ("serial", &grub_serial_term_input); + grub_term_register_output_active ("serial", &grub_serial_term_output); + + registered = 1; + } + } +#endif +} + +GRUB_MOD_FINI(serial) +{ + grub_unregister_extcmd (cmd); + if (registered == 1) /* Unregister terminal only if registered. */ + { + grub_term_unregister_input (&grub_serial_term_input); + grub_term_unregister_output (&grub_serial_term_output); + grub_terminfo_output_unregister (&grub_serial_term_output); + } +}