On Thu, Nov 13, 2008 at 08:05:37PM +0200, Vesa J??skel?inen wrote: > Hi Don, > > Thanks for the patch! >
Here is a new patch incorporating your comments. (Still waiting on legal about the copyright assignment, I'm sure it'll happen - someday.) Signed-off-by: Donald D. Dugger <[EMAIL PROTECTED]> -- Don Dugger "Censeo Toto nos in Kansa esse decisse." - D. Gale [EMAIL PROTECTED] Ph: 303/443-3786 diffstat pci_serial-grub-1113.patch ChangeLog | 14 + include/grub/i386/pc/serial.h | 10 + include/grub/i386/pc/serial_table.h | 86 ++++++++++ include/grub/i386/pci.h | 16 ++ include/grub/pci.h | 6 include/grub/pci_ids.h | 29 +++ term/i386/pc/serial.c | 288 +++++++++++++++++++++++++----------- 7 files changed, 368 insertions(+), 81 deletions(-) Index: include/grub/i386/pci.h =================================================================== --- include/grub/i386/pci.h (revision 1911) +++ include/grub/i386/pci.h (working copy) @@ -32,4 +32,20 @@ return grub_inl (GRUB_PCI_DATA_REG); } +static inline grub_uint32_t +grub_pci_read_config(unsigned int bus, unsigned int dev, unsigned int func, unsigned int off) +{ + grub_pci_address_t addr; + + addr = grub_pci_make_address (bus, dev, func, off); + return grub_pci_read (addr); +} + +static inline grub_uint32_t +grub_pci_get_bar(unsigned int bus, unsigned int dev, unsigned int func, unsigned int bar) +{ + + return grub_pci_read_config(bus, dev, func, 4 + bar) & ~3; +} + #endif /* GRUB_CPU_PCI_H */ Index: include/grub/i386/pc/serial.h =================================================================== --- include/grub/i386/pc/serial.h (revision 1911) +++ include/grub/i386/pc/serial.h (working copy) @@ -40,6 +40,9 @@ #define UART_DATA_READY 0x01 #define UART_EMPTY_TRANSMITTER 0x20 +/* Default base baud */ +#define UART_BASE_BAUD 115200 + /* The type of parity. */ #define UART_NO_PARITY 0x00 #define UART_ODD_PARITY 0x08 @@ -64,4 +67,11 @@ /* Turn on DTR, RTS, and OUT2. */ #define UART_ENABLE_MODEM 0x0B +/* Serial device types */ +#define SERIAL_LEGACY 0 +#define SERIAL_PCI 1 +#define SERIAL_USB 2 + +void grub_serial_add(int type, unsigned int id, unsigned int base, unsigned int port); + #endif /* ! GRUB_SERIAL_MACHINE_HEADER */ Index: include/grub/i386/pc/serial_table.h =================================================================== --- include/grub/i386/pc/serial_table.h (revision 0) +++ include/grub/i386/pc/serial_table.h (revision 0) @@ -0,0 +1,86 @@ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2008 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 <http://www.gnu.org/licenses/>. + */ + +static void +titan(unsigned int bus, unsigned int dev, unsigned int func, unsigned int bbaud) +{ + unsigned int port; + + port = grub_pci_get_bar(bus, dev, func, 1); + grub_serial_add(SERIAL_PCI, GRUB_PCI_BDF(bus, dev, func), bbaud, port); + return; +} + +static void +netmos(unsigned int bus, unsigned int dev, unsigned int func, unsigned int bbaud) +{ + unsigned int port; + + port = grub_pci_get_bar(bus, dev, func, 0); + grub_serial_add(SERIAL_PCI, GRUB_PCI_BDF(bus, dev, func), bbaud, port); + return; +} + +static void +generic(unsigned int bus, unsigned int dev, unsigned int func, unsigned int bbaud) +{ + + grub_serial_add(SERIAL_PCI, GRUB_PCI_BDF(bus, dev, func), bbaud, 0); + return; +} + +/* + * Table to map PCI ID to config routine. Currently, the config routine + * only sets the base baud but, utltimately, it should identify which + * I/O port is associated with which serial port. + */ +static struct pci_device_id { + unsigned int vendor_id; + unsigned int device_id; + unsigned int ss_vendor; + unsigned int ss_device; + unsigned int dev_class; + unsigned int dev_class_mask; + unsigned int base_baud; + void (*dev_config)(unsigned int bus, unsigned int dev, + unsigned int func, unsigned int bbaud); +} serial_pci_id[] = { + { PCI_VENDOR_ID_TITAN, PCI_ANY_ID, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + 921600, titan }, + { PCI_VENDOR_ID_NETMOS, PCI_ANY_ID, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + 115200, netmos }, + /* + * Generic entries that define the defaults + */ + { PCI_ANY_ID, PCI_ANY_ID, + PCI_ANY_ID, PCI_ANY_ID, + PCI_CLASS_COMM_SERIAL << 8, 0xffff00, + 115200, generic }, + { PCI_ANY_ID, PCI_ANY_ID, + PCI_ANY_ID, PCI_ANY_ID, + PCI_CLASS_COMM_SERIAL_16450 << 8, 0xffff00, + 115200, generic }, + { PCI_ANY_ID, PCI_ANY_ID, + PCI_ANY_ID, PCI_ANY_ID, + PCI_CLASS_COMM_SERIAL_16550 << 8, 0xffff00, + 115200, generic }, + { + 0, 0, 0, 0, 0, 0, 0, generic }, +}; Index: include/grub/pci_ids.h =================================================================== --- include/grub/pci_ids.h (revision 0) +++ include/grub/pci_ids.h (revision 0) @@ -0,0 +1,29 @@ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2008 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 <http://www.gnu.org/licenses/>. + */ + +#define PCI_ANY_ID ((unsigned int)(~0)) + +#define PCI_CLASS_COMM_SERIAL 0x0700 +#define PCI_CLASS_COMM_SERIAL_16450 0x0702 +#define PCI_CLASS_COMM_SERIAL_16550 0x0703 + +#define PCI_VENDOR_ID_TITAN 0x14D2 +#define PCI_DEVICE_ID_TITAN_100L 0x8010 + +#define PCI_VENDOR_ID_NETMOS 0x9710 +#define PCI_DEVICE_ID_NETMOS_9835 0x9835 Index: include/grub/pci.h =================================================================== --- include/grub/pci.h (revision 1911) +++ include/grub/pci.h (working copy) @@ -35,6 +35,12 @@ #define GRUB_PCI_ADDR_MEM_MASK ~0xf #define GRUB_PCI_ADDR_IO_MASK ~0x03 +#define GRUB_PCI_BDF(b,d,f) ((b << 8) | (d << 3) | f) + +#define GRUB_PCI_VENDOR(id) (id & 0xffff) +#define GRUB_PCI_DEVICE(id) ((id >> 16) & 0xffff) +#define GRUB_PCI_DEVICE_CLASS(c) ((c >> 16) | ((c >> 8) & 0xff)) + typedef grub_uint32_t grub_pci_id_t; typedef int (*grub_pci_iteratefunc_t) (int bus, int device, int func, grub_pci_id_t pciid); Index: ChangeLog =================================================================== --- ChangeLog (revision 1911) +++ ChangeLog (working copy) @@ -1,3 +1,17 @@ +2008-11-03 Don Dugger <[EMAIL PROTECTED]> + + * term/i386/pc/serial.c: major changes to support multiple serial + devices, also add `--base' parameter to serial command allowing + user to specify base baud for those UARTs that don't follow + the PC standard and are not recognized + * include/grub/i386/pc/serial.h: define default base baud value + of 115200 (default for PCs) and serial device types. + * include/grub/i386/pc/serial_table.h: define base bauds for known + serial devices + * include/grub/pci_ids.h: start at a PCI IDs table + * include/grub/pci.h: add some helper functions + * include/grub/i386/pci.h: add some helper functions + 2008-11-12 Robert Millan <[EMAIL PROTECTED]> * conf/i386-pc.rmk (kernel_img_SOURCES): Add `term/i386/vga_common.c'. Index: term/i386/pc/serial.c =================================================================== --- term/i386/pc/serial.c (revision 1911) +++ term/i386/pc/serial.c (working copy) @@ -27,6 +27,10 @@ #include <grub/arg.h> #include <grub/terminfo.h> #include <grub/cpu/io.h> +#include <grub/mm.h> +#include <grub/pci.h> +#include <grub/pci_ids.h> +#include <grub/machine/serial_table.h> #define TEXT_WIDTH 80 #define TEXT_HEIGHT 25 @@ -48,22 +52,24 @@ {"word", 'w', 0, "Set the serial port word length", 0, ARG_TYPE_INT}, {"parity", 'r', 0, "Set the serial port parity", 0, ARG_TYPE_STRING}, {"stop", 't', 0, "Set the serial port stop bits", 0, ARG_TYPE_INT}, + {"base", 'b', 0, "Set the serial port base baud", 0, ARG_TYPE_INT}, {0, 0, 0, 0, 0, 0} }; -/* Serial port settings. */ -struct serial_port -{ +struct serial_dev { + int type; + int id; unsigned short port; - unsigned short divisor; + unsigned int speed; + unsigned int base; unsigned short word_len; unsigned int parity; unsigned short stop_bits; }; +static struct serial_dev *serial_devices = (struct serial_dev *)0; +static struct serial_dev *serial_dev; +static int num_dev = 0; -/* Serial port settings. */ -static struct serial_port serial_settings; - #ifdef GRUB_MACHINE_PCBIOS /* The BIOS data area. */ static const unsigned short *serial_hw_io_addr = (const unsigned short *) 0x0400; @@ -73,6 +79,94 @@ #define GRUB_SERIAL_PORT_NUM (sizeof(serial_hw_io_addr)/(serial_hw_io_addr[0])) #endif +static const char *serial_types[] = { + "legacy", + "pci", + "usb" +}; + +static void +serial_pr_type(int i, struct serial_dev *p) +{ + + grub_printf("%c%2d: %6.6s ", (p == serial_dev) ? '*' : ' ', + i, serial_types[p->type]); + switch (p->type) { + + case SERIAL_LEGACY: + grub_printf(" COM%d", p->id + 1); + break; + + case SERIAL_PCI: + { + unsigned int b, d, f; + + b = p->id >> 8; + d = (p->id >> 3) & 0x1f; + f = p->id & 7; + grub_printf("%d:%02x.%d", b, d, f); + break; + } + + case SERIAL_USB: + grub_printf(" %2d", p->id); + break; + + } + + return; +} + +static const char parity[] = { + 'N', + 'O', + '?', + 'E' +}; + +static void +serial_pr(void) +{ + int i; + struct serial_dev *p; + + grub_printf("Available serial units:\n"); + p = serial_devices; + for (i = 0; i < num_dev; i++) { + serial_pr_type(i, p); + grub_printf(" 0x%04x %6d/%-7d %d%c%d\n", p->port, p->speed, p->base, + p->word_len + 5, + parity[p->parity >> 3], + (p->stop_bits >> 2) + 1); + p++; + } +} + +void +grub_serial_add(int type, unsigned int id, unsigned int base, unsigned int port) +{ + int idx, unit; + + unit = serial_dev - serial_devices; + idx = num_dev++; + if ((serial_devices = grub_realloc(serial_devices, num_dev * (sizeof(*serial_devices)))) == (struct serial_dev *)0) { + grub_error (GRUB_ERR_OUT_OF_MEMORY, "realloc of %d bytes failed\n", num_dev * (sizeof(*serial_devices))); + return; + } + + serial_devices[idx].type = type; + serial_devices[idx].id = id; + serial_devices[idx].base = base; + serial_devices[idx].port = port; + serial_devices[idx].speed = 9600; + serial_devices[idx].word_len = UART_8BITS_WORD; + serial_devices[idx].parity = UART_NO_PARITY; + serial_devices[idx].stop_bits = UART_1_STOP_BIT; + serial_dev = &serial_devices[unit]; + + return; +} + /* Return the port number for the UNITth serial device. */ static inline unsigned short serial_hw_get_port (const unsigned int unit) @@ -87,8 +181,8 @@ 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); + if (grub_inb (serial_dev->port + UART_LSR) & UART_DATA_READY) + return grub_inb (serial_dev->port + UART_RX); return -1; } @@ -100,14 +194,14 @@ unsigned int timeout = 100000; /* Wait until the transmitter holding register is empty. */ - while ((grub_inb (serial_settings.port + UART_LSR) & UART_EMPTY_TRANSMITTER) == 0) + while ((grub_inb (serial_dev->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); + grub_outb (c, serial_dev->port + UART_TX); } static void @@ -210,35 +304,9 @@ /* Convert speed to divisor. */ static unsigned short -serial_get_divisor (unsigned int speed) +serial_get_divisor (unsigned int speed, unsigned int base) { - 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) - return divisor_tab[i].div; - return 0; + return ((base << 4) + (speed << 3)) / (speed << 4); } /* The serial version of checkkey. */ @@ -274,31 +342,36 @@ WORD_LEN, PARITY and STOP_BIT_LEN are defined in the header file as macros. */ static grub_err_t -serial_hw_init (void) +serial_hw_init (struct serial_dev *dev) { unsigned char status = 0; + unsigned short divisor; + if (dev->port == 0) + return GRUB_ERR_OUT_OF_RANGE; + /* Turn off the interrupt. */ - grub_outb (0, serial_settings.port + UART_IER); + grub_outb (0, dev->port + UART_IER); /* Set DLAB. */ - grub_outb (UART_DLAB, serial_settings.port + UART_LCR); + grub_outb (UART_DLAB, dev->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); + divisor = serial_get_divisor(dev->speed, dev->base); + grub_outb (divisor & 0xFF, dev->port + UART_DLL); + grub_outb (divisor >> 8, dev->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); + status |= (dev->parity + | dev->word_len + | dev->stop_bits); + grub_outb (status, dev->port + UART_LCR); /* Enable the FIFO. */ - grub_outb (UART_ENABLE_FIFO, serial_settings.port + UART_FCR); + grub_outb (UART_ENABLE_FIFO, dev->port + UART_FCR); /* Turn on DTR, RTS, and OUT2. */ - grub_outb (UART_ENABLE_MODEM, serial_settings.port + UART_MCR); + grub_outb (UART_ENABLE_MODEM, dev->port + UART_MCR); /* Drain the input buffer. */ while (grub_serial_checkkey () != -1) @@ -495,31 +568,41 @@ int argc __attribute__ ((unused)), char **args __attribute__ ((unused))) { - struct serial_port backup_settings = serial_settings; + int unit; + struct serial_dev dev; grub_err_t hwiniterr; + if ((state[0].set == 0) && (state[1].set == 0) && (state[2].set == 0) && + (state[3].set == 0) && (state[4].set == 0) && (state[5].set == 0) && + (state[6].set == 0)) { + serial_pr(); + return GRUB_ERR_NONE; + } + + dev = *serial_dev; + unit = serial_dev - serial_devices; 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."); + if (unit >= num_dev) + { + return grub_error (GRUB_ERR_BAD_ARGUMENT, "bad unit number."); + } + dev = serial_devices[unit]; } if (state[1].set) - serial_settings.port = (unsigned short) grub_strtoul (state[1].arg, 0, 0); + { + dev.port = (unsigned short) grub_strtoul (state[1].arg, 0, 0); + } 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) + dev.speed = (unsigned int )grub_strtoul (state[2].arg, 0, 0); + if (dev.speed == 0) { - serial_settings = backup_settings; return grub_error (GRUB_ERR_BAD_ARGUMENT, "bad speed"); } } @@ -527,16 +610,15 @@ if (state[3].set) { if (! grub_strcmp (state[3].arg, "5")) - serial_settings.word_len = UART_5BITS_WORD; + dev.word_len = UART_5BITS_WORD; else if (! grub_strcmp (state[3].arg, "6")) - serial_settings.word_len = UART_6BITS_WORD; + dev.word_len = UART_6BITS_WORD; else if (! grub_strcmp (state[3].arg, "7")) - serial_settings.word_len = UART_7BITS_WORD; + dev.word_len = UART_7BITS_WORD; else if (! grub_strcmp (state[3].arg, "8")) - serial_settings.word_len = UART_8BITS_WORD; + dev.word_len = UART_8BITS_WORD; else { - serial_settings = backup_settings; return grub_error (GRUB_ERR_BAD_ARGUMENT, "bad word length"); } } @@ -544,14 +626,13 @@ if (state[4].set) { if (! grub_strcmp (state[4].arg, "no")) - serial_settings.parity = UART_NO_PARITY; + dev.parity = UART_NO_PARITY; else if (! grub_strcmp (state[4].arg, "odd")) - serial_settings.parity = UART_ODD_PARITY; + dev.parity = UART_ODD_PARITY; else if (! grub_strcmp (state[4].arg, "even")) - serial_settings.parity = UART_EVEN_PARITY; + dev.parity = UART_EVEN_PARITY; else { - serial_settings = backup_settings; return grub_error (GRUB_ERR_BAD_ARGUMENT, "bad parity"); } } @@ -559,21 +640,32 @@ if (state[5].set) { if (! grub_strcmp (state[5].arg, "1")) - serial_settings.stop_bits = UART_1_STOP_BIT; + dev.stop_bits = UART_1_STOP_BIT; else if (! grub_strcmp (state[5].arg, "2")) - serial_settings.stop_bits = UART_2_STOP_BITS; + dev.stop_bits = UART_2_STOP_BITS; else { - serial_settings = backup_settings; return grub_error (GRUB_ERR_BAD_ARGUMENT, "bad number of stop bits"); } } + if (state[6].set) + { + + dev.base = grub_strtoul (state[6].arg, 0, 0); + if (dev.base == 0) + { + return grub_error (GRUB_ERR_BAD_ARGUMENT, "bad base baud"); + } + } + /* Initialize with new settings. */ - hwiniterr = serial_hw_init (); + hwiniterr = serial_hw_init (&dev); if (hwiniterr == GRUB_ERR_NONE) { + serial_dev = &serial_devices[unit]; + *serial_dev = dev; /* Register terminal if not yet registered. */ if (registered == 0) { @@ -584,13 +676,13 @@ } else { + grub_error(GRUB_ERR_BAD_ARGUMENT, "Bad settings, revert to prior device"); /* 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 (serial_hw_init (serial_dev) != GRUB_ERR_NONE) { /* If unable to restore settings, unregister terminal. */ grub_term_unregister_input (&grub_serial_term_input); @@ -603,21 +695,55 @@ return hwiniterr; } +static int +serial_pci_scan (int bus, int dev, int func, grub_pci_id_t pci_id) +{ + struct pci_device_id *p; + unsigned int w, vid, did, ss_vid, ss_did, class; + + vid = GRUB_PCI_VENDOR(pci_id); + did = GRUB_PCI_DEVICE(pci_id); + class = GRUB_PCI_DEVICE_CLASS(grub_pci_read_config(bus, dev, func, 2)); + w = grub_pci_read_config (bus, dev, func, 11); + ss_vid = GRUB_PCI_VENDOR(w); + ss_did = GRUB_PCI_DEVICE(w); + for (p = serial_pci_id; p->vendor_id; p++) { + if ((p->vendor_id == PCI_ANY_ID || p->vendor_id == vid) && + (p->device_id == PCI_ANY_ID || p->device_id == did) && + (p->ss_vendor == PCI_ANY_ID || p->ss_vendor == ss_vid) && + (p->ss_device == PCI_ANY_ID || p->ss_device == ss_did) && + !((p->dev_class ^ (class << 8)) & p->dev_class_mask)) { + (p->dev_config)(bus, dev, func, p->base_baud); + break; + } + } + return 0; +} + GRUB_MOD_INIT(serial) { + int i; + unsigned int port; + (void) mod; /* To stop warning. */ + grub_errno = 0; grub_register_command ("serial", grub_cmd_serial, GRUB_COMMAND_FLAG_BOTH, "serial [OPTIONS...]", "Configure serial port.", options); /* Set default settings. */ - serial_settings.port = serial_hw_get_port (0); - serial_settings.divisor = serial_get_divisor (9600); - serial_settings.word_len = UART_8BITS_WORD; - serial_settings.parity = UART_NO_PARITY; - serial_settings.stop_bits = UART_1_STOP_BIT; + for (i = 0; i < GRUB_SERIAL_PORT_NUM; i++) + if ((port = serial_hw_get_port(i)) != 0) + grub_serial_add(SERIAL_LEGACY, i, UART_BASE_BAUD, port); + serial_dev = &serial_devices[0]; + + /* + * Check for PCI serial card, set defaults appropriately if one exists + */ + grub_pci_iterate (serial_pci_scan); } GRUB_MOD_FINI(serial) { + grub_free (serial_devices); grub_unregister_command ("serial"); if (registered == 1) /* Unregister terminal only if registered. */ { _______________________________________________ Grub-devel mailing list Grub-devel@gnu.org http://lists.gnu.org/mailman/listinfo/grub-devel