From 3a2537696721bc0bf6a90bdc27b327768e79e635 Mon Sep 17 00:00:00 2001 From: Vladimir 'phcoder' Serbinenko Date: Sat, 17 Jul 2010 03:57:59 +0200 Subject: [PATCH 01/37] Detect usb keyboard properly, support keyboard hotpluanad multiple keyboards --- term/usb_keyboard.c | 116 +++++++++++++++++++++++++++++++------------- 1 file changed, 82 insertions(+), 34 deletions(-) diff --git a/term/usb_keyboard.c b/term/usb_keyboard.c index ae9c41035..f010aa9e4 100644 --- a/term/usb_keyboard.c +++ b/term/usb_keyboard.c @@ -54,7 +54,6 @@ static char keyboard_map_shift[128] = '?' }; -static grub_usb_device_t usbdev; /* Valid values for bmRequestType. See HID definition version 1.11 section 7.2. */ @@ -69,36 +68,64 @@ static grub_usb_device_t usbdev; #define USB_HID_SET_IDLE 0x0A #define USB_HID_SET_PROTOCOL 0x0B +static int grub_usb_keyboard_checkkey (struct grub_term_input *term); +static int grub_usb_keyboard_getkey (struct grub_term_input *term); +static int grub_usb_keyboard_getkeystatus (struct grub_term_input *term); + +static struct grub_term_input grub_usb_keyboard_term = + { + .checkkey = grub_usb_keyboard_checkkey, + .getkey = grub_usb_keyboard_getkey, + .getkeystatus = grub_usb_keyboard_getkeystatus, + .next = 0 + }; + +static struct grub_term_input grub_usb_keyboards[16]; + static void -grub_usb_hid (void) +grub_usb_keyboard_detach (grub_usb_device_t usbdev, + int config __attribute__ ((unused)), + int interface __attribute__ ((unused))) { - struct grub_usb_desc_device *descdev; + unsigned i; + for (i = 0; i < ARRAY_SIZE (grub_usb_keyboards); i++) + if (grub_usb_keyboards[i].data && grub_usb_keyboards[i].data == usbdev) + { + grub_term_unregister_input (&grub_usb_keyboards[i]); + grub_free ((char *) grub_usb_keyboards[i].name); + grub_usb_keyboards[i].name = NULL; + grub_usb_keyboards[i].data = 0; + } +} - auto int usb_iterate (grub_usb_device_t dev); - int usb_iterate (grub_usb_device_t dev) - { - descdev = &dev->descdev; +static int +grub_usb_keyboard_attach (grub_usb_device_t usbdev, int configno, int interfno) +{ + unsigned curnum; - grub_dprintf ("usb_keyboard", "%x %x %x\n", - descdev->class, descdev->subclass, descdev->protocol); + grub_dprintf ("usb_keyboard", "%x %x %x %d %d\n", + usbdev->descdev.class, usbdev->descdev.subclass, + usbdev->descdev.protocol, configno, interfno); + + for (curnum = 0; curnum < ARRAY_SIZE (grub_usb_keyboards); curnum++) + if (!grub_usb_keyboards[curnum].data) + break; + + if (curnum == ARRAY_SIZE (grub_usb_keyboards)) + return 0; #if 0 - if (descdev->class != 0x09 - || descdev->subclass == 0x01 - || descdev->protocol != 0x02) + if (descdev->class != 0x09 + || descdev->subclass == 0x01 + || descdev->protocol != 0x02) return 0; #endif - if (descdev->class != 0 || descdev->subclass != 0 || descdev->protocol != 0) - return 0; + if (usbdev->descdev.class != 0 + || usbdev->descdev.subclass != 0 || usbdev->descdev.protocol != 0) + return 0; - grub_printf ("HID found!\n"); - - usbdev = dev; - - return 1; - } - grub_usb_iterate (usb_iterate); + grub_printf ("HID found!\n"); /* Place the device in boot mode. */ grub_usb_control_msg (usbdev, USB_HID_HOST_TO_DEVICE, USB_HID_SET_PROTOCOL, @@ -107,6 +134,19 @@ grub_usb_hid (void) /* Reports every time an event occurs and not more often than that. */ grub_usb_control_msg (usbdev, USB_HID_HOST_TO_DEVICE, USB_HID_SET_IDLE, 0<<8, 0, 0, 0); + + grub_memcpy (&grub_usb_keyboards[curnum], &grub_usb_keyboard_term, + sizeof (grub_usb_keyboards[curnum])); + grub_usb_keyboards[curnum].data = usbdev; + usbdev->config[configno].interf[interfno].detach_hook + = grub_usb_keyboard_detach; + grub_usb_keyboards[curnum].name = grub_xasprintf ("usb_keyboard%d", curnum); + if (!grub_usb_keyboards[curnum].name) + return 0; + grub_term_register_input_active ("usb_keyboard", &grub_usb_keyboards[curnum]); + + + return 1; } static grub_err_t @@ -119,13 +159,14 @@ grub_usb_keyboard_getreport (grub_usb_device_t dev, grub_uint8_t *report) static int -grub_usb_keyboard_checkkey (struct grub_term_input *term __attribute__ ((unused))) +grub_usb_keyboard_checkkey (struct grub_term_input *term) { grub_uint8_t data[8]; int key; grub_err_t err; grub_uint64_t currtime; int timeout = 50; + grub_usb_device_t usbdev = term->data; data[2] = 0; currtime = grub_get_time_ms (); @@ -196,6 +237,7 @@ grub_usb_keyboard_getkey (struct grub_term_input *term) grub_uint64_t currtime; int timeout; static grub_usb_keyboard_repeat_t repeat = GRUB_HIDBOOT_REPEAT_NONE; + grub_usb_device_t usbdev = term->data; again: @@ -253,13 +295,14 @@ grub_usb_keyboard_getkey (struct grub_term_input *term) } static int -grub_usb_keyboard_getkeystatus (struct grub_term_input *term __attribute__ ((unused))) +grub_usb_keyboard_getkeystatus (struct grub_term_input *term) { grub_uint8_t data[8]; int mods = 0; grub_err_t err; grub_uint64_t currtime; int timeout = 50; + grub_usb_device_t usbdev = term->data; /* Set idle time to the minimum offered by the spec (4 milliseconds) so that we can find out the current state. */ @@ -307,22 +350,27 @@ grub_usb_keyboard_getkeystatus (struct grub_term_input *term __attribute__ ((unu return mods; } -static struct grub_term_input grub_usb_keyboard_term = - { - .name = "usb_keyboard", - .checkkey = grub_usb_keyboard_checkkey, - .getkey = grub_usb_keyboard_getkey, - .getkeystatus = grub_usb_keyboard_getkeystatus, - .next = 0 - }; +struct grub_usb_attach_desc attach_hook = +{ + .class = GRUB_USB_CLASS_HID, + .hook = grub_usb_keyboard_attach +}; GRUB_MOD_INIT(usb_keyboard) { - grub_usb_hid (); - grub_term_register_input ("usb_keyboard", &grub_usb_keyboard_term); + grub_usb_register_attach_hook_class (&attach_hook); } GRUB_MOD_FINI(usb_keyboard) { - grub_term_unregister_input (&grub_usb_keyboard_term); + unsigned i; + for (i = 0; i < ARRAY_SIZE (grub_usb_keyboards); i++) + if (grub_usb_keyboards[i].data) + { + grub_term_unregister_input (&grub_usb_keyboards[i]); + grub_free ((char *) grub_usb_keyboards[i].name); + grub_usb_keyboards[i].name = NULL; + grub_usb_keyboards[i].data = 0; + } + grub_usb_unregister_attach_hook_class (&attach_hook); } From a17b90f0ec5802967577ed71ab825c915d7e2ab6 Mon Sep 17 00:00:00 2001 From: Vladimir 'phcoder' Serbinenko Date: Sat, 17 Jul 2010 03:58:23 +0200 Subject: [PATCH 02/37] Support USB device drivers autoloading --- bus/usb/usb.c | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/bus/usb/usb.c b/bus/usb/usb.c index f6a0a8b56..804cbaff0 100644 --- a/bus/usb/usb.c +++ b/bus/usb/usb.c @@ -22,6 +22,7 @@ #include #include #include +#include static grub_usb_controller_dev_t grub_usb_list; struct grub_usb_attach_desc *attach_hooks; @@ -256,6 +257,19 @@ void grub_usb_device_attach (grub_usb_device_t dev) for (desc = attach_hooks; desc; desc = desc->next) if (interf->class == desc->class && desc->hook (dev, 0, i)) dev->config[0].interf[i].attached = 1; + + if (dev->config[0].interf[i].attached) + continue; + + switch (interf->class) + { + case GRUB_USB_CLASS_MASS_STORAGE: + grub_dl_load ("usbms"); + break; + case GRUB_USB_CLASS_HID: + grub_dl_load ("usb_keyboard"); + break; + } } } From 03f286ea9fc4336eaf4b56f3412c794f78238a65 Mon Sep 17 00:00:00 2001 From: Vladimir 'phcoder' Serbinenko Date: Sun, 18 Jul 2010 01:35:55 +0200 Subject: [PATCH 03/37] Always show class --- commands/usbtest.c | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/commands/usbtest.c b/commands/usbtest.c index 213288b52..7f00c8856 100644 --- a/commands/usbtest.c +++ b/commands/usbtest.c @@ -29,11 +29,11 @@ static const char *usb_classes[] = { - "", + "Unknown", "Audio", "Communication Interface", "HID", - "", + "Unknown", "Physical", "Image", "Printer", @@ -138,10 +138,10 @@ usb_iterate (grub_usb_device_t dev) usb_print_str ("Vendor", dev, descdev->strvendor); usb_print_str ("Serial", dev, descdev->strserial); - if (descdev->class > 0 && descdev->class <= 0x0E) - grub_printf ("Class: (0x%02x) %s, Subclass: 0x%02x, Protocol: 0x%02x\n", - descdev->class, usb_classes[descdev->class], - descdev->subclass, descdev->protocol); + grub_printf ("Class: (0x%02x) %s, Subclass: 0x%02x, Protocol: 0x%02x\n", + descdev->class, descdev->class < ARRAY_SIZE (usb_classes) + ? usb_classes[descdev->class] : "Unknown", + descdev->subclass, descdev->protocol); grub_printf ("USB version %d.%d, VendorID: 0x%02x, ProductID: 0x%02x, #conf: %d\n", descdev->usbrel >> 8, (descdev->usbrel >> 4) & 0x0F, descdev->vendorid, descdev->prodid, descdev->configcnt); @@ -164,10 +164,10 @@ usb_iterate (grub_usb_device_t dev) grub_printf ("Interface #%d: #Endpoints: %d ", i, interf->endpointcnt); - if (interf->class > 0 && interf->class <= 0x0E) - grub_printf ("Class: (0x%02x) %s, Subclass: 0x%02x, Protocol: 0x%02x\n", - interf->class, usb_classes[interf->class], - interf->subclass, interf->protocol); + grub_printf ("Class: (0x%02x) %s, Subclass: 0x%02x, Protocol: 0x%02x\n", + interf->class, interf->class < ARRAY_SIZE (usb_classes) + ? usb_classes[interf->class] : "Unknown", + interf->subclass, interf->protocol); usb_print_str ("Interface", dev, interf->strif); From 75eb7d11168d9fcf70b9e7f6d861c4809160db7b Mon Sep 17 00:00:00 2001 From: Vladimir 'phcoder' Serbinenko Date: Sun, 18 Jul 2010 01:37:19 +0200 Subject: [PATCH 04/37] 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); + } +} From 7b5502f30b0e8a110f91fc2be6286bc303269649 Mon Sep 17 00:00:00 2001 From: Vladimir 'phcoder' Serbinenko Date: Sun, 18 Jul 2010 01:41:08 +0200 Subject: [PATCH 05/37] Remove debug comment-out --- term/ns8250.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/term/ns8250.c b/term/ns8250.c index 4f09c7606..ee2eeb8f5 100644 --- a/term/ns8250.c +++ b/term/ns8250.c @@ -175,7 +175,7 @@ grub_ns8250_init (void) { int i; for (i = 0; i < GRUB_SERIAL_PORT_NUM; i++) - //if (serial_hw_io_addr[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]; From cbf41813b307e62fcb8ca424429d26a2084c8704 Mon Sep 17 00:00:00 2001 From: Vladimir 'phcoder' Serbinenko Date: Sun, 18 Jul 2010 12:53:08 +0200 Subject: [PATCH 06/37] Don't check transaction active flag as it's not updated and creates problems for usbserial --- bus/usb/uhci.c | 51 +++++++++++++++++++++++--------------------------- 1 file changed, 23 insertions(+), 28 deletions(-) diff --git a/bus/usb/uhci.c b/bus/usb/uhci.c index efdf3aceb..5b4432018 100644 --- a/bus/usb/uhci.c +++ b/bus/usb/uhci.c @@ -512,42 +512,37 @@ grub_uhci_transfer (grub_usb_controller_t dev, grub_dprintf ("uhci", "t status=0x%02x\n", errtd->ctrl_status); - /* Check if the TD is not longer active. */ - if (! (errtd->ctrl_status & (1 << 23))) - { - grub_dprintf ("uhci", ">>t status=0x%02x\n", errtd->ctrl_status); + /* Check if the endpoint is stalled. */ + if (errtd->ctrl_status & (1 << 22)) + err = GRUB_USB_ERR_STALL; - /* Check if the endpoint is stalled. */ - if (errtd->ctrl_status & (1 << 22)) - err = GRUB_USB_ERR_STALL; + /* Check if an error related to the data buffer occurred. */ + if (errtd->ctrl_status & (1 << 21)) + err = GRUB_USB_ERR_DATA; - /* Check if an error related to the data buffer occurred. */ - if (errtd->ctrl_status & (1 << 21)) - err = GRUB_USB_ERR_DATA; + /* Check if a babble error occurred. */ + if (errtd->ctrl_status & (1 << 20)) + err = GRUB_USB_ERR_BABBLE; - /* Check if a babble error occurred. */ - if (errtd->ctrl_status & (1 << 20)) - err = GRUB_USB_ERR_BABBLE; + /* Check if a NAK occurred. */ + if (errtd->ctrl_status & (1 << 19)) + err = GRUB_USB_ERR_NAK; - /* Check if a NAK occurred. */ - if (errtd->ctrl_status & (1 << 19)) - err = GRUB_USB_ERR_NAK; + /* Check if a timeout occurred. */ + if (errtd->ctrl_status & (1 << 18)) + err = GRUB_USB_ERR_TIMEOUT; - /* Check if a timeout occurred. */ - if (errtd->ctrl_status & (1 << 18)) - err = GRUB_USB_ERR_TIMEOUT; + /* Check if a bitstuff error occurred. */ + if (errtd->ctrl_status & (1 << 17)) + err = GRUB_USB_ERR_BITSTUFF; - /* Check if a bitstuff error occurred. */ - if (errtd->ctrl_status & (1 << 17)) - err = GRUB_USB_ERR_BITSTUFF; + if (err) + goto fail; - if (err) - goto fail; + /* Fall through, no errors occurred, so the QH might be + updated. */ + grub_dprintf ("uhci", "transaction fallthrough\n"); - /* Fall through, no errors occurred, so the QH might be - updated. */ - grub_dprintf ("uhci", "transaction fallthrough\n"); - } if (grub_get_time_ms () > endtime) { err = GRUB_USB_ERR_STALL; From 34364df689ec64226f3d5de06e08a1844166a926 Mon Sep 17 00:00:00 2001 From: Vladimir 'phcoder' Serbinenko Date: Sun, 18 Jul 2010 12:53:35 +0200 Subject: [PATCH 07/37] usbserial basic support. Works in qemu --- bus/usb/usb.c | 3 + conf/i386-pc.rmk | 6 ++ include/grub/serial.h | 24 ++--- term/ns8250.c | 124 ++++++++++++++---------- term/serial.c | 1 - term/usbserial.c | 212 ++++++++++++++++++------------------------ 6 files changed, 189 insertions(+), 181 deletions(-) diff --git a/bus/usb/usb.c b/bus/usb/usb.c index 804cbaff0..ba052e5ee 100644 --- a/bus/usb/usb.c +++ b/bus/usb/usb.c @@ -269,6 +269,9 @@ void grub_usb_device_attach (grub_usb_device_t dev) case GRUB_USB_CLASS_HID: grub_dl_load ("usb_keyboard"); break; + case 0xff: + grub_dl_load ("usbserial"); + break; } } } diff --git a/conf/i386-pc.rmk b/conf/i386-pc.rmk index c157aaf01..e1cfe1468 100644 --- a/conf/i386-pc.rmk +++ b/conf/i386-pc.rmk @@ -195,6 +195,12 @@ usb_mod_SOURCES = bus/usb/usb.c bus/usb/usbtrans.c bus/usb/usbhub.c usb_mod_CFLAGS = $(COMMON_CFLAGS) usb_mod_LDFLAGS = $(COMMON_LDFLAGS) +# For serial.mod. +pkglib_MODULES += usbserial.mod +usbserial_mod_SOURCES = term/usbserial.c +usbserial_mod_CFLAGS = $(COMMON_CFLAGS) +usbserial_mod_LDFLAGS = $(COMMON_LDFLAGS) + # For usbtest.mod usbtest_mod_SOURCES = commands/usbtest.c usbtest_mod_CFLAGS = $(COMMON_CFLAGS) diff --git a/include/grub/serial.h b/include/grub/serial.h index 063282112..97e97004c 100644 --- a/include/grub/serial.h +++ b/include/grub/serial.h @@ -47,6 +47,7 @@ struct grub_serial_port unsigned int parity; unsigned short stop_bits; struct grub_serial_driver *driver; + int configured; /* This should be void *data but since serial is useful as an early console when malloc isn't available it's a union. */ @@ -55,9 +56,9 @@ struct grub_serial_port grub_port_t port; struct { - grub_usb_device_t dev; - int in_endp; - int out_endp; + grub_usb_device_t usbdev; + struct grub_usb_desc_endp *in_endp; + struct grub_usb_desc_endp *out_endp; }; }; }; @@ -81,18 +82,19 @@ void grub_serial_unregister (struct grub_serial_port *port); #define UART_1_STOP_BIT 0x00 #define UART_2_STOP_BITS 0x04 -static inline void -grub_serial_fill_defaults (struct grub_serial_port *port) -{ /* Set default settings. */ +static inline grub_err_t +grub_serial_config_defaults (struct grub_serial_port *port) +{ + return port->driver->configure (port, + #ifdef GRUB_MACHINE_MIPS_YEELOONG - port->speed = 115200; + 115200, #else - port->speed = 9600; + 9600, #endif - port->word_len = UART_8BITS_WORD; - port->parity = UART_NO_PARITY; - port->stop_bits = UART_1_STOP_BIT; + UART_8BITS_WORD, UART_NO_PARITY, + UART_1_STOP_BIT); } void grub_ns8250_init (void); diff --git a/term/ns8250.c b/term/ns8250.c index ee2eeb8f5..53e94b899 100644 --- a/term/ns8250.c +++ b/term/ns8250.c @@ -34,33 +34,6 @@ 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) @@ -99,29 +72,19 @@ serial_get_divisor (unsigned int speed) 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) +static void +do_real_config (struct grub_serial_port *port) { + int divisor; unsigned char status = 0; - unsigned short divisor; - divisor = serial_get_divisor (speed); + if (port->configured) + return; + + divisor = serial_get_divisor (port->speed); + /* Shouldn't happen. */ 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; + return; /* Turn off the interrupt. */ grub_outb (0, port->port + UART_IER); @@ -153,7 +116,66 @@ serial_hw_configure (struct grub_serial_port *port, #endif /* Drain the input buffer. */ - while (serial_hw_fetch (port) != -1); + while (grub_inb (port->port + UART_LSR) & UART_DATA_READY) + grub_inb (port->port + UART_RX); + + port->configured = 1; +} + +/* Fetch a key. */ +static int +serial_hw_fetch (struct grub_serial_port *port) +{ + do_real_config (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; + + do_real_config (port); + + /* 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); +} + +/* 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 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; + + port->configured = 0; /* FIXME: should check if the serial terminal was found. */ @@ -177,11 +199,15 @@ grub_ns8250_init (void) for (i = 0; i < GRUB_SERIAL_PORT_NUM; i++) if (serial_hw_io_addr[i]) { + grub_err_t err; 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]; + err = grub_serial_config_defaults (&com_ports[i]); + if (err) + grub_print_error (); + grub_serial_register (&com_ports[i]); } } @@ -204,7 +230,7 @@ grub_serial_ns8250_add_port (grub_port_t port) return NULL; } p->driver = &grub_ns8250_driver; - grub_serial_fill_defaults (p); + grub_serial_config_defaults (p); p->port = port; grub_serial_register (p); diff --git a/term/serial.c b/term/serial.c index 1f54a83ec..d3b2ba143 100644 --- a/term/serial.c +++ b/term/serial.c @@ -315,7 +315,6 @@ grub_serial_unregister (struct grub_serial_port *port) /* FIXME */ } - static grub_extcmd_t cmd; GRUB_MOD_INIT(serial) diff --git a/term/usbserial.c b/term/usbserial.c index 5a63348fb..e25084548 100644 --- a/term/usbserial.c +++ b/term/usbserial.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,150 +16,122 @@ * 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; +#include +#include /* Fetch a key. */ static int -serial_hw_fetch (void) +usbserial_hw_fetch (struct grub_serial_port *port) { - /* FIXME */ - return -1; + char cc[3]; + grub_usb_err_t err; + err = grub_usb_bulk_read (port->usbdev, port->in_endp->endp_addr, 2, cc); + if (err != GRUB_USB_ERR_NAK) + return -1; + + err = grub_usb_bulk_read (port->usbdev, port->in_endp->endp_addr, 3, cc); + if (err != GRUB_USB_ERR_NONE) + return -1; + + return cc[2]; } /* Put a character. */ static void -usbserial_hw_put (struct usbserial_port *port, const int c) +usbserial_hw_put (struct grub_serial_port *port, const int c) { char cc = c; - grub_usb_bulk_write (port->usbdev, port->out_endp, 1, &cc); + grub_usb_bulk_write (port->usbdev, port->out_endp->endp_addr, 1, &cc); } -static grub_uint16_t -grub_serial_getwh (struct grub_term_output *term __attribute__ ((unused))) +/* FIXME */ +static grub_err_t +usbserial_hw_configure (struct grub_serial_port *port, + unsigned speed, unsigned short word_len, + unsigned int parity, unsigned short stop_bits) { - const grub_uint8_t TEXT_WIDTH = 80; - const grub_uint8_t TEXT_HEIGHT = 24; - return (TEXT_WIDTH << 8) | TEXT_HEIGHT; + port->speed = speed; + port->word_len = word_len; + port->parity = parity; + port->stop_bits = stop_bits; + + port->configured = 0; + + return GRUB_ERR_NONE; } -struct grub_terminfo_input_state grub_serial_terminfo_input = - { - .readkey = serial_hw_fetch - }; - -struct grub_terminfo_output_state grub_serial_terminfo_output = +static struct grub_serial_driver grub_usbserial_driver = { + .configure = usbserial_hw_configure, + .fetch = usbserial_hw_fetch, .put = usbserial_hw_put }; -static struct grub_term_input grub_serial_term_input = +static int +grub_usbserial_attach (grub_usb_device_t usbdev, int configno, int interfno) { - .name = "usbserial", - .init = grub_terminfo_input_init, - .checkkey = grub_terminfo_checkkey, - .getkey = grub_terminfo_getkey, - .data = &grub_serial_terminfo_input -}; + static struct grub_serial_port *port; + int j; + struct grub_usb_desc_if *interf + = usbdev->config[configno].interf[interfno].descif; -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. */ + port = grub_malloc (sizeof (*port)); + if (!port) { - grub_term_unregister_input (&grub_serial_term_input); - grub_term_unregister_output (&grub_serial_term_output); - grub_terminfo_output_unregister (&grub_serial_term_output); + grub_print_error (); + return 0; } + + port->name = grub_xasprintf ("usb%d", usbdev->addr); + if (!port->name) + { + grub_free (port); + grub_print_error (); + return 0; + } + + port->usbdev = usbdev; + port->driver = &grub_usbserial_driver; + for (j = 0; j < interf->endpointcnt; j++) + { + struct grub_usb_desc_endp *endp; + endp = &usbdev->config[0].interf[interfno].descendp[j]; + + if ((endp->endp_addr & 128) && (endp->attrib & 3) == 2) + { + /* Bulk IN endpoint. */ + port->in_endp = endp; + } + else if (!(endp->endp_addr & 128) && (endp->attrib & 3) == 2) + { + /* Bulk OUT endpoint. */ + port->out_endp = endp; + } + } + if (!port->out_endp || !port->in_endp) + { + grub_free (port->name); + grub_free (port); + return 0; + } + + grub_serial_config_defaults (port); + grub_serial_register (port); + + return 1; +} + +struct grub_usb_attach_desc attach_hook = +{ + .class = 0xff, + .hook = grub_usbserial_attach +}; + +GRUB_MOD_INIT(usbserial) +{ + grub_usb_register_attach_hook_class (&attach_hook); } From 8c8e2699068c433c069004431354d4f3361924f5 Mon Sep 17 00:00:00 2001 From: Vladimir 'phcoder' Serbinenko Date: Sun, 18 Jul 2010 13:40:48 +0200 Subject: [PATCH 08/37] Encapsulate serial config in dedicated structure --- include/grub/serial.h | 35 +++++++++++++++++++++-------------- include/grub/usb.h | 5 +++++ term/ns8250.c | 16 ++++++---------- term/serial.c | 32 +++++++++++++------------------- term/usbserial.c | 24 +++++++++++++++++------- 5 files changed, 62 insertions(+), 50 deletions(-) diff --git a/include/grub/serial.h b/include/grub/serial.h index 97e97004c..7828a7c98 100644 --- a/include/grub/serial.h +++ b/include/grub/serial.h @@ -26,27 +26,30 @@ #include struct grub_serial_port; +struct grub_serial_config; 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); + struct grub_serial_config *config); int (*fetch) (struct grub_serial_port *port); void (*put) (struct grub_serial_port *port, const int c); }; +struct grub_serial_config +{ + unsigned speed; + unsigned short word_len; + unsigned int parity; + unsigned short stop_bits; +}; + 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; + struct grub_serial_config config; int configured; /* This should be void *data but since serial is useful as an early console when malloc isn't available it's a union. @@ -86,15 +89,19 @@ void grub_serial_unregister (struct grub_serial_port *port); static inline grub_err_t grub_serial_config_defaults (struct grub_serial_port *port) { - return port->driver->configure (port, - + struct grub_serial_config config = + { #ifdef GRUB_MACHINE_MIPS_YEELOONG - 115200, + .speed = 115200, #else - 9600, + .speed = 9600, #endif - UART_8BITS_WORD, UART_NO_PARITY, - UART_1_STOP_BIT); + .word_len = UART_8BITS_WORD, + .parity = UART_NO_PARITY, + .stop_bits = UART_1_STOP_BIT + }; + + return port->driver->configure (port, &config); } void grub_ns8250_init (void); diff --git a/include/grub/usb.h b/include/grub/usb.h index f9c3d61ff..b3acd3c5e 100644 --- a/include/grub/usb.h +++ b/include/grub/usb.h @@ -48,6 +48,11 @@ typedef enum GRUB_USB_SPEED_HIGH } grub_usb_speed_t; +enum + { + GRUB_USB_REQTYPE_VENDOR_OUT = 0x40 + }; + /* Call HOOK with each device, until HOOK returns non-zero. */ int grub_usb_iterate (int (*hook) (grub_usb_device_t dev)); diff --git a/term/ns8250.c b/term/ns8250.c index 53e94b899..c63bc6494 100644 --- a/term/ns8250.c +++ b/term/ns8250.c @@ -81,7 +81,7 @@ do_real_config (struct grub_serial_port *port) if (port->configured) return; - divisor = serial_get_divisor (port->speed); + divisor = serial_get_divisor (port->config.speed); /* Shouldn't happen. */ if (divisor == 0) return; @@ -97,7 +97,8 @@ do_real_config (struct grub_serial_port *port) grub_outb (divisor >> 8, port->port + UART_DLH); /* Set the line status. */ - status |= (port->parity | port->word_len | port->stop_bits); + status |= (port->config.parity | port->config.word_len + | port->config.stop_bits); grub_outb (status, port->port + UART_LCR); /* In Yeeloong serial port has only 3 wires. */ @@ -161,20 +162,15 @@ serial_hw_put (struct grub_serial_port *port, const int c) 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) + struct grub_serial_config *config) { unsigned short divisor; - divisor = serial_get_divisor (speed); + divisor = serial_get_divisor (config->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; - + port->config = *config; port->configured = 0; /* FIXME: should check if the serial terminal was found. */ diff --git a/term/serial.c b/term/serial.c index d3b2ba143..00214cb4c 100644 --- a/term/serial.c +++ b/term/serial.c @@ -154,10 +154,7 @@ grub_cmd_serial (grub_extcmd_t cmd, int argc, char **args) 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; + struct grub_serial_config config; grub_err_t err; if (state[0].set) @@ -184,24 +181,21 @@ grub_cmd_serial (grub_extcmd_t cmd, int argc, char **args) 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; + config = port->config; if (state[2].set) - speed = grub_strtoul (state[2].arg, 0, 0); + config.speed = grub_strtoul (state[2].arg, 0, 0); if (state[3].set) { if (! grub_strcmp (state[3].arg, "5")) - word_len = UART_5BITS_WORD; + config.word_len = UART_5BITS_WORD; else if (! grub_strcmp (state[3].arg, "6")) - word_len = UART_6BITS_WORD; + config.word_len = UART_6BITS_WORD; else if (! grub_strcmp (state[3].arg, "7")) - word_len = UART_7BITS_WORD; + config.word_len = UART_7BITS_WORD; else if (! grub_strcmp (state[3].arg, "8")) - word_len = UART_8BITS_WORD; + config.word_len = UART_8BITS_WORD; else return grub_error (GRUB_ERR_BAD_ARGUMENT, "bad word length"); } @@ -209,11 +203,11 @@ grub_cmd_serial (grub_extcmd_t cmd, int argc, char **args) if (state[4].set) { if (! grub_strcmp (state[4].arg, "no")) - parity = UART_NO_PARITY; + config.parity = UART_NO_PARITY; else if (! grub_strcmp (state[4].arg, "odd")) - parity = UART_ODD_PARITY; + config.parity = UART_ODD_PARITY; else if (! grub_strcmp (state[4].arg, "even")) - parity = UART_EVEN_PARITY; + config.parity = UART_EVEN_PARITY; else return grub_error (GRUB_ERR_BAD_ARGUMENT, "bad parity"); } @@ -221,15 +215,15 @@ grub_cmd_serial (grub_extcmd_t cmd, int argc, char **args) if (state[5].set) { if (! grub_strcmp (state[5].arg, "1")) - stop_bits = UART_1_STOP_BIT; + config.stop_bits = UART_1_STOP_BIT; else if (! grub_strcmp (state[5].arg, "2")) - stop_bits = UART_2_STOP_BITS; + config.stop_bits = UART_2_STOP_BITS; else return grub_error (GRUB_ERR_BAD_ARGUMENT, "bad number of stop bits"); } /* Initialize with new settings. */ - err = port->driver->configure (port, speed, word_len, parity, stop_bits); + err = port->driver->configure (port, &config); if (err) return err; if (!registered) diff --git a/term/usbserial.c b/term/usbserial.c index e25084548..258187ff9 100644 --- a/term/usbserial.c +++ b/term/usbserial.c @@ -23,12 +23,24 @@ #include #include +static void +real_config (struct grub_serial_port *port) +{ + if (port->configured) + return; + + port->configured = 1; +} + /* Fetch a key. */ static int usbserial_hw_fetch (struct grub_serial_port *port) { char cc[3]; grub_usb_err_t err; + + real_config (port); + err = grub_usb_bulk_read (port->usbdev, port->in_endp->endp_addr, 2, cc); if (err != GRUB_USB_ERR_NAK) return -1; @@ -45,20 +57,18 @@ static void usbserial_hw_put (struct grub_serial_port *port, const int c) { char cc = c; + + real_config (port); + grub_usb_bulk_write (port->usbdev, port->out_endp->endp_addr, 1, &cc); } /* FIXME */ static grub_err_t usbserial_hw_configure (struct grub_serial_port *port, - unsigned speed, unsigned short word_len, - unsigned int parity, unsigned short stop_bits) + struct grub_serial_config *config) { - port->speed = speed; - port->word_len = word_len; - port->parity = parity; - port->stop_bits = stop_bits; - + port->config = *config; port->configured = 0; return GRUB_ERR_NONE; From 1da07b142b14c60c5527c5bbdd644335fd3a76ee Mon Sep 17 00:00:00 2001 From: Vladimir 'phcoder' Serbinenko Date: Sun, 18 Jul 2010 14:43:23 +0200 Subject: [PATCH 09/37] some serial config support --- include/grub/serial.h | 31 +++++++++------- term/ns8250.c | 25 ++++++++++--- term/serial.c | 10 +++--- term/usbserial.c | 82 ++++++++++++++++++++++++++++++++++++++++++- 4 files changed, 124 insertions(+), 24 deletions(-) diff --git a/include/grub/serial.h b/include/grub/serial.h index 7828a7c98..5afc1f172 100644 --- a/include/grub/serial.h +++ b/include/grub/serial.h @@ -36,12 +36,26 @@ struct grub_serial_driver void (*put) (struct grub_serial_port *port, const int c); }; +/* The type of parity. */ +typedef enum + { + GRUB_SERIAL_PARITY_NONE, + GRUB_SERIAL_PARITY_ODD, + GRUB_SERIAL_PARITY_EVEN, + } grub_serial_parity_t; + +typedef enum + { + GRUB_SERIAL_STOP_BITS_1, + GRUB_SERIAL_STOP_BITS_2, + } grub_serial_stop_bits_t; + struct grub_serial_config { unsigned speed; unsigned short word_len; - unsigned int parity; - unsigned short stop_bits; + grub_serial_parity_t parity; + grub_serial_stop_bits_t stop_bits; }; struct grub_serial_port @@ -76,15 +90,6 @@ void grub_serial_unregister (struct grub_serial_port *port); #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 - /* Set default settings. */ static inline grub_err_t grub_serial_config_defaults (struct grub_serial_port *port) @@ -97,8 +102,8 @@ grub_serial_config_defaults (struct grub_serial_port *port) .speed = 9600, #endif .word_len = UART_8BITS_WORD, - .parity = UART_NO_PARITY, - .stop_bits = UART_1_STOP_BIT + .parity = GRUB_SERIAL_PARITY_NONE, + .stop_bits = GRUB_SERIAL_STOP_BITS_1 }; return port->driver->configure (port, &config); diff --git a/term/ns8250.c b/term/ns8250.c index c63bc6494..a69c683f2 100644 --- a/term/ns8250.c +++ b/term/ns8250.c @@ -77,14 +77,20 @@ do_real_config (struct grub_serial_port *port) { int divisor; unsigned char status = 0; + const unsigned char parities[] = { + [GRUB_SERIAL_PARITY_NONE] = UART_NO_PARITY, + [GRUB_SERIAL_PARITY_ODD] = UART_ODD_PARITY, + [GRUB_SERIAL_PARITY_EVEN] = UART_EVEN_PARITY + }; + const unsigned char stop_bits[] = { + [GRUB_SERIAL_STOP_BITS_1] = UART_1_STOP_BIT, + [GRUB_SERIAL_STOP_BITS_2] = UART_2_STOP_BITS, + }; if (port->configured) return; divisor = serial_get_divisor (port->config.speed); - /* Shouldn't happen. */ - if (divisor == 0) - return; /* Turn off the interrupt. */ grub_outb (0, port->port + UART_IER); @@ -97,8 +103,8 @@ do_real_config (struct grub_serial_port *port) grub_outb (divisor >> 8, port->port + UART_DLH); /* Set the line status. */ - status |= (port->config.parity | port->config.word_len - | port->config.stop_bits); + status |= (parities[port->config.parity] + | port->config.word_len | stop_bits[port->config.stop_bits]); grub_outb (status, port->port + UART_LCR); /* In Yeeloong serial port has only 3 wires. */ @@ -170,6 +176,15 @@ serial_hw_configure (struct grub_serial_port *port, if (divisor == 0) return grub_error (GRUB_ERR_BAD_ARGUMENT, "bad speed"); + if (config->parity != GRUB_SERIAL_PARITY_NONE + && config->parity != GRUB_SERIAL_PARITY_ODD + && config->parity != GRUB_SERIAL_PARITY_EVEN) + return grub_error (GRUB_ERR_BAD_ARGUMENT, "unsupported parity"); + + if (config->stop_bits != GRUB_SERIAL_STOP_BITS_1 + && config->stop_bits != GRUB_SERIAL_STOP_BITS_2) + return grub_error (GRUB_ERR_BAD_ARGUMENT, "unsupported stop bits"); + port->config = *config; port->configured = 0; diff --git a/term/serial.c b/term/serial.c index 00214cb4c..5f27b16b6 100644 --- a/term/serial.c +++ b/term/serial.c @@ -203,11 +203,11 @@ grub_cmd_serial (grub_extcmd_t cmd, int argc, char **args) if (state[4].set) { if (! grub_strcmp (state[4].arg, "no")) - config.parity = UART_NO_PARITY; + config.parity = GRUB_SERIAL_PARITY_NONE; else if (! grub_strcmp (state[4].arg, "odd")) - config.parity = UART_ODD_PARITY; + config.parity = GRUB_SERIAL_PARITY_ODD; else if (! grub_strcmp (state[4].arg, "even")) - config.parity = UART_EVEN_PARITY; + config.parity = GRUB_SERIAL_PARITY_EVEN; else return grub_error (GRUB_ERR_BAD_ARGUMENT, "bad parity"); } @@ -215,9 +215,9 @@ grub_cmd_serial (grub_extcmd_t cmd, int argc, char **args) if (state[5].set) { if (! grub_strcmp (state[5].arg, "1")) - config.stop_bits = UART_1_STOP_BIT; + config.stop_bits = GRUB_SERIAL_STOP_BITS_1; else if (! grub_strcmp (state[5].arg, "2")) - config.stop_bits = UART_2_STOP_BITS; + config.stop_bits = GRUB_SERIAL_STOP_BITS_2; else return grub_error (GRUB_ERR_BAD_ARGUMENT, "bad number of stop bits"); } diff --git a/term/usbserial.c b/term/usbserial.c index 258187ff9..b2a89d56c 100644 --- a/term/usbserial.c +++ b/term/usbserial.c @@ -23,12 +23,78 @@ #include #include +enum + { + GRUB_USBSERIAL_MODEM_CTRL = 0x01, + GRUB_USBSERIAL_FLOW_CTRL = 0x02, + GRUB_USBSERIAL_SPEED_CTRL = 0x03, + GRUB_USBSERIAL_DATA_CTRL = 0x04 + }; + +#define GRUB_USBSERIAL_MODEM_CTRL_DTRRTS 3 +#define GRUB_USBSERIAL_FLOW_CTRL_DTRRTS 3 + +/* Convert speed to divisor. */ +static grub_uint32_t +get_divisor (unsigned int speed) +{ + unsigned int i; + + /* The structure for speed vs. divisor. */ + struct divisor + { + unsigned int speed; + grub_uint32_t div; + }; + + /* The table which lists common configurations. */ + static struct divisor divisor_tab[] = + { + { 9600, 0x4138 }, + }; + + /* 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; +} + static void real_config (struct grub_serial_port *port) { + grub_uint32_t divisor; + const grub_uint16_t parities[] = { + [GRUB_SERIAL_PARITY_NONE] = 0x0000, + [GRUB_SERIAL_PARITY_ODD] = 0x0100, + [GRUB_SERIAL_PARITY_EVEN] = 0x0200 + }; + const grub_uint16_t stop_bits[] = { + [GRUB_SERIAL_STOP_BITS_1] = 0x0000, + [GRUB_SERIAL_STOP_BITS_2] = 0x1000, + }; + if (port->configured) return; + grub_usb_control_msg (port->usbdev, GRUB_USB_REQTYPE_VENDOR_OUT, + GRUB_USBSERIAL_MODEM_CTRL, + GRUB_USBSERIAL_MODEM_CTRL_DTRRTS, 0, 0, 0); + + grub_usb_control_msg (port->usbdev, GRUB_USB_REQTYPE_VENDOR_OUT, + GRUB_USBSERIAL_FLOW_CTRL, + GRUB_USBSERIAL_FLOW_CTRL_DTRRTS, 0, 0, 0); + + divisor = get_divisor (port->config.speed); + grub_usb_control_msg (port->usbdev, GRUB_USB_REQTYPE_VENDOR_OUT, + GRUB_USBSERIAL_SPEED_CTRL, + divisor & 0xffff, divisor >> 16, 0, 0); + + grub_usb_control_msg (port->usbdev, GRUB_USB_REQTYPE_VENDOR_OUT, + GRUB_USBSERIAL_DATA_CTRL, + parities[port->config.parity] + | stop_bits[port->config.stop_bits] , 0, 0, 0); + port->configured = 1; } @@ -63,11 +129,25 @@ usbserial_hw_put (struct grub_serial_port *port, const int c) grub_usb_bulk_write (port->usbdev, port->out_endp->endp_addr, 1, &cc); } -/* FIXME */ static grub_err_t usbserial_hw_configure (struct grub_serial_port *port, struct grub_serial_config *config) { + grub_uint16_t divisor; + + divisor = get_divisor (config->speed); + if (divisor == 0) + return grub_error (GRUB_ERR_BAD_ARGUMENT, "bad speed"); + + if (config->parity != GRUB_SERIAL_PARITY_NONE + && config->parity != GRUB_SERIAL_PARITY_ODD + && config->parity != GRUB_SERIAL_PARITY_EVEN) + return grub_error (GRUB_ERR_BAD_ARGUMENT, "unsupported parity"); + + if (config->stop_bits != GRUB_SERIAL_STOP_BITS_1 + && config->stop_bits != GRUB_SERIAL_STOP_BITS_2) + return grub_error (GRUB_ERR_BAD_ARGUMENT, "unsupported stop bits"); + port->config = *config; port->configured = 0; From 91d135a12c6be53cd67f9b45b7c4d9c6f957aea7 Mon Sep 17 00:00:00 2001 From: Vladimir 'phcoder' Serbinenko Date: Sun, 18 Jul 2010 15:00:50 +0200 Subject: [PATCH 10/37] Support variable speed --- term/ns8250.c | 2 +- term/usbserial.c | 9 ++++++++- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/term/ns8250.c b/term/ns8250.c index a69c683f2..9677913be 100644 --- a/term/ns8250.c +++ b/term/ns8250.c @@ -61,7 +61,7 @@ serial_get_divisor (unsigned int speed) }; /* Set the baud rate. */ - for (i = 0; i < sizeof (divisor_tab) / sizeof (divisor_tab[0]); i++) + for (i = 0; i < ARRAY_SIZE (divisor_tab); i++) if (divisor_tab[i].speed == speed) /* UART in Yeeloong runs twice the usual rate. */ #ifdef GRUB_MACHINE_MIPS_YEELOONG diff --git a/term/usbserial.c b/term/usbserial.c index b2a89d56c..7a5aead2c 100644 --- a/term/usbserial.c +++ b/term/usbserial.c @@ -48,13 +48,20 @@ get_divisor (unsigned int speed) }; /* The table which lists common configurations. */ + /* Computed with a division formula with 3MHz as base frequency. */ static struct divisor divisor_tab[] = { + { 2400, 0x04e2 }, + { 4800, 0x0271 }, { 9600, 0x4138 }, + { 19200, 0x809c }, + { 38400, 0xc04e }, + { 57600, 0xc034 }, + { 115200, 0x001a } }; /* Set the baud rate. */ - for (i = 0; i < sizeof (divisor_tab) / sizeof (divisor_tab[0]); i++) + for (i = 0; i < ARRAY_SIZE (divisor_tab); i++) if (divisor_tab[i].speed == speed) return divisor_tab[i].div; return 0; From fd5b6637939816e80ed8bb7fc6efb68b9f174a7b Mon Sep 17 00:00:00 2001 From: Vladimir 'phcoder' Serbinenko Date: Sun, 18 Jul 2010 15:07:59 +0200 Subject: [PATCH 11/37] Configure word length --- include/grub/ns8250.h | 6 ++++++ include/grub/serial.h | 10 ++-------- term/ns8250.c | 6 +++++- term/serial.c | 13 +------------ term/usbserial.c | 6 +++++- 5 files changed, 19 insertions(+), 22 deletions(-) diff --git a/include/grub/ns8250.h b/include/grub/ns8250.h index f21a1a3e3..f8b9c3a8c 100644 --- a/include/grub/ns8250.h +++ b/include/grub/ns8250.h @@ -45,6 +45,12 @@ #define UART_ODD_PARITY 0x08 #define UART_EVEN_PARITY 0x18 +/* The type of word length. */ +#define UART_5BITS_WORD 0x00 +#define UART_6BITS_WORD 0x01 +#define UART_7BITS_WORD 0x02 +#define UART_8BITS_WORD 0x03 + /* The type of the length of stop bit. */ #define UART_1_STOP_BIT 0x00 #define UART_2_STOP_BITS 0x04 diff --git a/include/grub/serial.h b/include/grub/serial.h index 5afc1f172..e66dcf80d 100644 --- a/include/grub/serial.h +++ b/include/grub/serial.h @@ -53,7 +53,7 @@ typedef enum struct grub_serial_config { unsigned speed; - unsigned short word_len; + int word_len; grub_serial_parity_t parity; grub_serial_stop_bits_t stop_bits; }; @@ -84,12 +84,6 @@ 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 -#define UART_6BITS_WORD 0x01 -#define UART_7BITS_WORD 0x02 -#define UART_8BITS_WORD 0x03 - /* Set default settings. */ static inline grub_err_t grub_serial_config_defaults (struct grub_serial_port *port) @@ -101,7 +95,7 @@ grub_serial_config_defaults (struct grub_serial_port *port) #else .speed = 9600, #endif - .word_len = UART_8BITS_WORD, + .word_len = 8, .parity = GRUB_SERIAL_PARITY_NONE, .stop_bits = GRUB_SERIAL_STOP_BITS_1 }; diff --git a/term/ns8250.c b/term/ns8250.c index 9677913be..93a5e215e 100644 --- a/term/ns8250.c +++ b/term/ns8250.c @@ -104,7 +104,8 @@ do_real_config (struct grub_serial_port *port) /* Set the line status. */ status |= (parities[port->config.parity] - | port->config.word_len | stop_bits[port->config.stop_bits]); + | (port->config.word_len - 5) + | stop_bits[port->config.stop_bits]); grub_outb (status, port->port + UART_LCR); /* In Yeeloong serial port has only 3 wires. */ @@ -185,6 +186,9 @@ serial_hw_configure (struct grub_serial_port *port, && config->stop_bits != GRUB_SERIAL_STOP_BITS_2) return grub_error (GRUB_ERR_BAD_ARGUMENT, "unsupported stop bits"); + if (config->word_len < 5 || config->word_len > 8) + return grub_error (GRUB_ERR_BAD_ARGUMENT, "unsupported word length"); + port->config = *config; port->configured = 0; diff --git a/term/serial.c b/term/serial.c index 5f27b16b6..aeaec5c88 100644 --- a/term/serial.c +++ b/term/serial.c @@ -187,18 +187,7 @@ grub_cmd_serial (grub_extcmd_t cmd, int argc, char **args) config.speed = grub_strtoul (state[2].arg, 0, 0); if (state[3].set) - { - if (! grub_strcmp (state[3].arg, "5")) - config.word_len = UART_5BITS_WORD; - else if (! grub_strcmp (state[3].arg, "6")) - config.word_len = UART_6BITS_WORD; - else if (! grub_strcmp (state[3].arg, "7")) - config.word_len = UART_7BITS_WORD; - else if (! grub_strcmp (state[3].arg, "8")) - config.word_len = UART_8BITS_WORD; - else - return grub_error (GRUB_ERR_BAD_ARGUMENT, "bad word length"); - } + config.word_len = grub_strtoul (state[3].arg, 0, 0); if (state[4].set) { diff --git a/term/usbserial.c b/term/usbserial.c index 7a5aead2c..b27df8ae2 100644 --- a/term/usbserial.c +++ b/term/usbserial.c @@ -100,7 +100,8 @@ real_config (struct grub_serial_port *port) grub_usb_control_msg (port->usbdev, GRUB_USB_REQTYPE_VENDOR_OUT, GRUB_USBSERIAL_DATA_CTRL, parities[port->config.parity] - | stop_bits[port->config.stop_bits] , 0, 0, 0); + | stop_bits[port->config.stop_bits] + | port->config.word_len, 0, 0, 0); port->configured = 1; } @@ -155,6 +156,9 @@ usbserial_hw_configure (struct grub_serial_port *port, && config->stop_bits != GRUB_SERIAL_STOP_BITS_2) return grub_error (GRUB_ERR_BAD_ARGUMENT, "unsupported stop bits"); + if (config->word_len < 5 || config->word_len > 8) + return grub_error (GRUB_ERR_BAD_ARGUMENT, "unsupported word length"); + port->config = *config; port->configured = 0; From dd20a7868b291fd5fe6cfc8625f5eca733bf1b74 Mon Sep 17 00:00:00 2001 From: Vladimir 'phcoder' Serbinenko Date: Sun, 18 Jul 2010 16:31:42 +0200 Subject: [PATCH 12/37] Rename usbserial to usbserial_ftdi --- term/usbserial.c => bus/usb/serial/ftdi.c | 32 ++++++++++++++++++++--- bus/usb/usb.c | 3 ++- conf/i386-pc.rmk | 8 +++--- 3 files changed, 34 insertions(+), 9 deletions(-) rename term/usbserial.c => bus/usb/serial/ftdi.c (90%) diff --git a/term/usbserial.c b/bus/usb/serial/ftdi.c similarity index 90% rename from term/usbserial.c rename to bus/usb/serial/ftdi.c index b27df8ae2..c7a7554ee 100644 --- a/term/usbserial.c +++ b/bus/usb/serial/ftdi.c @@ -172,13 +172,22 @@ static struct grub_serial_driver grub_usbserial_driver = .put = usbserial_hw_put }; +static const struct +{ + grub_uint16_t vendor, product; +} products[] = + { + {0x0403, 0x6001} /* QEMU virtual USBserial. */ + }; + static int -grub_usbserial_attach (grub_usb_device_t usbdev, int configno, int interfno) +grub_ftdi_attach (grub_usb_device_t usbdev, int configno, int interfno) { static struct grub_serial_port *port; int j; - struct grub_usb_desc_if *interf - = usbdev->config[configno].interf[interfno].descif; + struct grub_usb_desc_if *interf; + + interf = usbdev->config[configno].interf[interfno].descif; port = grub_malloc (sizeof (*port)); if (!port) @@ -226,13 +235,28 @@ grub_usbserial_attach (grub_usb_device_t usbdev, int configno, int interfno) return 1; } +static int +grub_usbserial_attach (grub_usb_device_t usbdev, int configno, int interfno) +{ + unsigned j; + + for (j = 0; j < ARRAY_SIZE (products); j++) + if (usbdev->descdev.vendorid == products[j].vendor + && usbdev->descdev.prodid == products[j].product) + break; + if (j == ARRAY_SIZE (products)) + return 0; + + return grub_ftdi_attach (usbdev, configno, interfno); +} + struct grub_usb_attach_desc attach_hook = { .class = 0xff, .hook = grub_usbserial_attach }; -GRUB_MOD_INIT(usbserial) +GRUB_MOD_INIT(usbserial_ftdi) { grub_usb_register_attach_hook_class (&attach_hook); } diff --git a/bus/usb/usb.c b/bus/usb/usb.c index ba052e5ee..b49caacdb 100644 --- a/bus/usb/usb.c +++ b/bus/usb/usb.c @@ -270,7 +270,8 @@ void grub_usb_device_attach (grub_usb_device_t dev) grub_dl_load ("usb_keyboard"); break; case 0xff: - grub_dl_load ("usbserial"); + /* FIXME: don't load useless modules. */ + grub_dl_load ("usbserial_ftdi"); break; } } diff --git a/conf/i386-pc.rmk b/conf/i386-pc.rmk index e1cfe1468..2e95133f3 100644 --- a/conf/i386-pc.rmk +++ b/conf/i386-pc.rmk @@ -196,10 +196,10 @@ usb_mod_CFLAGS = $(COMMON_CFLAGS) usb_mod_LDFLAGS = $(COMMON_LDFLAGS) # For serial.mod. -pkglib_MODULES += usbserial.mod -usbserial_mod_SOURCES = term/usbserial.c -usbserial_mod_CFLAGS = $(COMMON_CFLAGS) -usbserial_mod_LDFLAGS = $(COMMON_LDFLAGS) +pkglib_MODULES += usbserial_ftdi.mod +usbserial_ftdi_mod_SOURCES = bus/usb/serial/ftdi.c +usbserial_ftdi_mod_CFLAGS = $(COMMON_CFLAGS) +usbserial_ftdi_mod_LDFLAGS = $(COMMON_LDFLAGS) # For usbtest.mod usbtest_mod_SOURCES = commands/usbtest.c From 24494d478adad49868af2a75d7afa307379e6547 Mon Sep 17 00:00:00 2001 From: Vladimir 'phcoder' Serbinenko Date: Sun, 18 Jul 2010 17:40:42 +0200 Subject: [PATCH 13/37] Add fini routines for usbserial and rename grub_usbserial to grub_ftdi --- bus/usb/serial/ftdi.c | 87 ++++++++++++++++++++++++++++--------------- include/grub/serial.h | 8 ++++ include/grub/usb.h | 5 +-- term/ns8250.c | 2 +- term/serial.c | 74 +++++++++++++++++++++++------------- 5 files changed, 118 insertions(+), 58 deletions(-) diff --git a/bus/usb/serial/ftdi.c b/bus/usb/serial/ftdi.c index c7a7554ee..4495b055c 100644 --- a/bus/usb/serial/ftdi.c +++ b/bus/usb/serial/ftdi.c @@ -25,14 +25,14 @@ enum { - GRUB_USBSERIAL_MODEM_CTRL = 0x01, - GRUB_USBSERIAL_FLOW_CTRL = 0x02, - GRUB_USBSERIAL_SPEED_CTRL = 0x03, - GRUB_USBSERIAL_DATA_CTRL = 0x04 + GRUB_FTDI_MODEM_CTRL = 0x01, + GRUB_FTDI_FLOW_CTRL = 0x02, + GRUB_FTDI_SPEED_CTRL = 0x03, + GRUB_FTDI_DATA_CTRL = 0x04 }; -#define GRUB_USBSERIAL_MODEM_CTRL_DTRRTS 3 -#define GRUB_USBSERIAL_FLOW_CTRL_DTRRTS 3 +#define GRUB_FTDI_MODEM_CTRL_DTRRTS 3 +#define GRUB_FTDI_FLOW_CTRL_DTRRTS 3 /* Convert speed to divisor. */ static grub_uint32_t @@ -85,20 +85,20 @@ real_config (struct grub_serial_port *port) return; grub_usb_control_msg (port->usbdev, GRUB_USB_REQTYPE_VENDOR_OUT, - GRUB_USBSERIAL_MODEM_CTRL, - GRUB_USBSERIAL_MODEM_CTRL_DTRRTS, 0, 0, 0); + GRUB_FTDI_MODEM_CTRL, + GRUB_FTDI_MODEM_CTRL_DTRRTS, 0, 0, 0); grub_usb_control_msg (port->usbdev, GRUB_USB_REQTYPE_VENDOR_OUT, - GRUB_USBSERIAL_FLOW_CTRL, - GRUB_USBSERIAL_FLOW_CTRL_DTRRTS, 0, 0, 0); + GRUB_FTDI_FLOW_CTRL, + GRUB_FTDI_FLOW_CTRL_DTRRTS, 0, 0, 0); divisor = get_divisor (port->config.speed); grub_usb_control_msg (port->usbdev, GRUB_USB_REQTYPE_VENDOR_OUT, - GRUB_USBSERIAL_SPEED_CTRL, + GRUB_FTDI_SPEED_CTRL, divisor & 0xffff, divisor >> 16, 0, 0); grub_usb_control_msg (port->usbdev, GRUB_USB_REQTYPE_VENDOR_OUT, - GRUB_USBSERIAL_DATA_CTRL, + GRUB_FTDI_DATA_CTRL, parities[port->config.parity] | stop_bits[port->config.stop_bits] | port->config.word_len, 0, 0, 0); @@ -108,17 +108,13 @@ real_config (struct grub_serial_port *port) /* Fetch a key. */ static int -usbserial_hw_fetch (struct grub_serial_port *port) +ftdi_hw_fetch (struct grub_serial_port *port) { char cc[3]; grub_usb_err_t err; real_config (port); - err = grub_usb_bulk_read (port->usbdev, port->in_endp->endp_addr, 2, cc); - if (err != GRUB_USB_ERR_NAK) - return -1; - err = grub_usb_bulk_read (port->usbdev, port->in_endp->endp_addr, 3, cc); if (err != GRUB_USB_ERR_NONE) return -1; @@ -128,7 +124,7 @@ usbserial_hw_fetch (struct grub_serial_port *port) /* Put a character. */ static void -usbserial_hw_put (struct grub_serial_port *port, const int c) +ftdi_hw_put (struct grub_serial_port *port, const int c) { char cc = c; @@ -138,7 +134,7 @@ usbserial_hw_put (struct grub_serial_port *port, const int c) } static grub_err_t -usbserial_hw_configure (struct grub_serial_port *port, +ftdi_hw_configure (struct grub_serial_port *port, struct grub_serial_config *config) { grub_uint16_t divisor; @@ -165,11 +161,19 @@ usbserial_hw_configure (struct grub_serial_port *port, return GRUB_ERR_NONE; } -static struct grub_serial_driver grub_usbserial_driver = +static void +ftdi_fini (struct grub_serial_port *port) +{ + port->usbdev->config[port->configno].interf[port->interfno].detach_hook = 0; + port->usbdev->config[port->configno].interf[port->interfno].attached = 0; +} + +static struct grub_serial_driver grub_ftdi_driver = { - .configure = usbserial_hw_configure, - .fetch = usbserial_hw_fetch, - .put = usbserial_hw_put + .configure = ftdi_hw_configure, + .fetch = ftdi_hw_fetch, + .put = ftdi_hw_put, + .fini = ftdi_fini }; static const struct @@ -180,8 +184,19 @@ static const struct {0x0403, 0x6001} /* QEMU virtual USBserial. */ }; +static int ftdinum = 0; + +static void +ftdi_detach (grub_usb_device_t usbdev, int configno, int interfno) +{ + static struct grub_serial_port *port; + port = usbdev->config[configno].interf[interfno].detach_data; + + grub_serial_unregister (port); +} + static int -grub_ftdi_attach (grub_usb_device_t usbdev, int configno, int interfno) +grub_ftdi_attach_real (grub_usb_device_t usbdev, int configno, int interfno) { static struct grub_serial_port *port; int j; @@ -196,7 +211,7 @@ grub_ftdi_attach (grub_usb_device_t usbdev, int configno, int interfno) return 0; } - port->name = grub_xasprintf ("usb%d", usbdev->addr); + port->name = grub_xasprintf ("ftdi%d", ftdinum++); if (!port->name) { grub_free (port); @@ -205,7 +220,7 @@ grub_ftdi_attach (grub_usb_device_t usbdev, int configno, int interfno) } port->usbdev = usbdev; - port->driver = &grub_usbserial_driver; + port->driver = &grub_ftdi_driver; for (j = 0; j < interf->endpointcnt; j++) { struct grub_usb_desc_endp *endp; @@ -229,14 +244,22 @@ grub_ftdi_attach (grub_usb_device_t usbdev, int configno, int interfno) return 0; } + port->configno = configno; + port->interfno = interfno; + grub_serial_config_defaults (port); grub_serial_register (port); + port->usbdev->config[port->configno].interf[port->interfno].detach_hook + = ftdi_detach; + port->usbdev->config[port->configno].interf[port->interfno].detach_data + = port; + return 1; } static int -grub_usbserial_attach (grub_usb_device_t usbdev, int configno, int interfno) +grub_ftdi_attach (grub_usb_device_t usbdev, int configno, int interfno) { unsigned j; @@ -247,16 +270,22 @@ grub_usbserial_attach (grub_usb_device_t usbdev, int configno, int interfno) if (j == ARRAY_SIZE (products)) return 0; - return grub_ftdi_attach (usbdev, configno, interfno); + return grub_ftdi_attach_real (usbdev, configno, interfno); } struct grub_usb_attach_desc attach_hook = { .class = 0xff, - .hook = grub_usbserial_attach + .hook = grub_ftdi_attach }; GRUB_MOD_INIT(usbserial_ftdi) { grub_usb_register_attach_hook_class (&attach_hook); } + +GRUB_MOD_FINI(usbserial_ftdi) +{ + grub_serial_unregister_driver (&grub_ftdi_driver); + grub_usb_unregister_attach_hook_class (&attach_hook); +} diff --git a/include/grub/serial.h b/include/grub/serial.h index e66dcf80d..fd601a6d9 100644 --- a/include/grub/serial.h +++ b/include/grub/serial.h @@ -24,6 +24,7 @@ #include #include #include +#include struct grub_serial_port; struct grub_serial_config; @@ -34,6 +35,7 @@ struct grub_serial_driver struct grub_serial_config *config); int (*fetch) (struct grub_serial_port *port); void (*put) (struct grub_serial_port *port, const int c); + void (*fini) (struct grub_serial_port *port); }; /* The type of parity. */ @@ -74,10 +76,14 @@ struct grub_serial_port struct { grub_usb_device_t usbdev; + int configno; + int interfno; struct grub_usb_desc_endp *in_endp; struct grub_usb_desc_endp *out_endp; }; }; + grub_term_output_t term_out; + grub_term_input_t term_in; }; grub_err_t grub_serial_register (struct grub_serial_port *port); @@ -105,5 +111,7 @@ grub_serial_config_defaults (struct grub_serial_port *port) void grub_ns8250_init (void); char *grub_serial_ns8250_add_port (grub_port_t port); +extern struct grub_serial_driver grub_ns8250_driver; +void grub_serial_unregister_driver (struct grub_serial_driver *driver); #endif diff --git a/include/grub/usb.h b/include/grub/usb.h index b3acd3c5e..595cbd6d3 100644 --- a/include/grub/usb.h +++ b/include/grub/usb.h @@ -138,6 +138,8 @@ struct grub_usb_interface int attached; void (*detach_hook) (struct grub_usb_device *dev, int config, int interface); + + void *detach_data; }; struct grub_usb_configuration @@ -171,9 +173,6 @@ struct grub_usb_device /* Data toggle values (used for bulk transfers only). */ int toggle[256]; - - /* Device-specific data. */ - void *data; }; diff --git a/term/ns8250.c b/term/ns8250.c index 93a5e215e..6bf8c5b59 100644 --- a/term/ns8250.c +++ b/term/ns8250.c @@ -197,7 +197,7 @@ serial_hw_configure (struct grub_serial_port *port, return GRUB_ERR_NONE; } -static struct grub_serial_driver grub_ns8250_driver = +struct grub_serial_driver grub_ns8250_driver = { .configure = serial_hw_configure, .fetch = serial_hw_fetch, diff --git a/term/serial.c b/term/serial.c index aeaec5c88..20dec7347 100644 --- a/term/serial.c +++ b/term/serial.c @@ -215,14 +215,18 @@ grub_cmd_serial (grub_extcmd_t cmd, int argc, char **args) err = port->driver->configure (port, &config); if (err) return err; - if (!registered) + /* Compatibility kludge. */ + if (port->driver == &grub_ns8250_driver) { - grub_term_register_input ("serial", &grub_serial_term_input); - grub_term_register_output ("serial", &grub_serial_term_output); + if (!registered) + { + grub_term_register_input ("serial", &grub_serial_term_input); + grub_term_register_output ("serial", &grub_serial_term_output); + } + grub_serial_terminfo_output.port = port; + grub_serial_terminfo_input.port = port; + registered = 1; } - grub_serial_terminfo_output.port = port; - grub_serial_terminfo_input.port = port; - registered = 1; return GRUB_ERR_NONE; } @@ -284,9 +288,21 @@ grub_serial_register (struct grub_serial_port *port) 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); + port->term_in = in; + port->term_out = out; grub_terminfo_output_register (out, "vt100"); +#ifdef GRUB_MACHINE_MIPS_YEELOONG + if (grub_strcmp (port->name, "com0") == 0) + { + grub_term_register_input_active ("serial_*", in); + grub_term_register_output_active ("serial_*", out); + } + else +#endif + { + grub_term_register_input ("serial_*", in); + grub_term_register_output ("serial_*", out); + } return GRUB_ERR_NONE; } @@ -294,8 +310,27 @@ grub_serial_register (struct grub_serial_port *port) void grub_serial_unregister (struct grub_serial_port *port) { + if (port->driver->fini) + port->driver->fini (port); + + if (port->term_in) + grub_term_unregister_input (port->term_in); + if (port->term_out) + grub_term_unregister_output (port->term_out); + grub_list_remove (GRUB_AS_LIST_P (&grub_serial_ports), GRUB_AS_LIST (port)); - /* FIXME */ +} + +void +grub_serial_unregister_driver (struct grub_serial_driver *driver) +{ + struct grub_serial_port *port, *next; + for (port = grub_serial_ports; port; port = next) + { + next = port->next; + if (port->driver == driver) + grub_serial_unregister (port); + } } static grub_extcmd_t cmd; @@ -308,27 +343,16 @@ GRUB_MOD_INIT(serial) N_("Configure serial port."), options); grub_ns8250_init (); - -#ifdef GRUB_MACHINE_MIPS_YEELOONG - { - grub_err_t hwiniterr; - hwiniterr = grub_ns8250_driver.init ("com0", &serial_settings); - serial_settings.driver = &grub_ns8250_driver; - - 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) { while (grub_serial_ports) grub_serial_unregister (grub_serial_ports); + if (registered) + { + grub_term_unregister_input (&grub_serial_term_input); + grub_term_unregister_output (&grub_serial_term_output); + } grub_unregister_extcmd (cmd); } From 44e7b8cb491aafc0e1b238b2aedb671e40ef9947 Mon Sep 17 00:00:00 2001 From: Vladimir 'phcoder' Serbinenko Date: Sun, 18 Jul 2010 19:09:54 +0200 Subject: [PATCH 14/37] account for absence of NS8250 on emu --- term/serial.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/term/serial.c b/term/serial.c index 20dec7347..2268788af 100644 --- a/term/serial.c +++ b/term/serial.c @@ -131,6 +131,7 @@ grub_serial_find (char *name) if (grub_strcmp (port->name, name) == 0) break; +#ifndef GRUB_MACHINE_EMU if (!port && grub_memcmp (name, "port", sizeof ("port") - 1) == 0 && grub_isdigit (name [sizeof ("port") - 1])) { @@ -143,6 +144,7 @@ grub_serial_find (char *name) if (grub_strcmp (port->name, name) == 0) break; } +#endif return port; } @@ -215,6 +217,7 @@ grub_cmd_serial (grub_extcmd_t cmd, int argc, char **args) err = port->driver->configure (port, &config); if (err) return err; +#ifndef GRUB_MACHINE_EMU /* Compatibility kludge. */ if (port->driver == &grub_ns8250_driver) { @@ -227,6 +230,7 @@ grub_cmd_serial (grub_extcmd_t cmd, int argc, char **args) grub_serial_terminfo_input.port = port; registered = 1; } +#endif return GRUB_ERR_NONE; } @@ -341,8 +345,9 @@ GRUB_MOD_INIT(serial) GRUB_COMMAND_FLAG_BOTH, N_("[OPTIONS...]"), N_("Configure serial port."), options); - +#ifndef GRUB_MACHINE_EMU grub_ns8250_init (); +#endif } GRUB_MOD_FINI(serial) From a531fd134de9e809444af4d5dad09d793621a029 Mon Sep 17 00:00:00 2001 From: Vladimir 'phcoder' Serbinenko Date: Sun, 18 Jul 2010 19:10:57 +0200 Subject: [PATCH 15/37] Split common usbserial function. PL2303 skeleton --- bus/usb/serial/common.c | 102 +++++++++++++++++++ bus/usb/serial/ftdi.c | 89 +--------------- bus/usb/serial/pl2303.c | 212 +++++++++++++++++++++++++++++++++++++++ conf/i386-pc.rmk | 12 +++ include/grub/usbserial.h | 31 ++++++ 5 files changed, 362 insertions(+), 84 deletions(-) create mode 100644 bus/usb/serial/common.c create mode 100644 bus/usb/serial/pl2303.c create mode 100644 include/grub/usbserial.h diff --git a/bus/usb/serial/common.c b/bus/usb/serial/common.c new file mode 100644 index 000000000..1da1c5bcf --- /dev/null +++ b/bus/usb/serial/common.c @@ -0,0 +1,102 @@ +/* + * 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 + +void +grub_usbserial_fini (struct grub_serial_port *port) +{ + port->usbdev->config[port->configno].interf[port->interfno].detach_hook = 0; + port->usbdev->config[port->configno].interf[port->interfno].attached = 0; +} + +void +grub_usbserial_detach (grub_usb_device_t usbdev, int configno, int interfno) +{ + static struct grub_serial_port *port; + port = usbdev->config[configno].interf[interfno].detach_data; + + grub_serial_unregister (port); +} + +static int usbnum = 0; + +int +grub_usbserial_attach (grub_usb_device_t usbdev, int configno, int interfno, + struct grub_serial_driver *driver) +{ + struct grub_serial_port *port; + int j; + struct grub_usb_desc_if *interf; + + interf = usbdev->config[configno].interf[interfno].descif; + + port = grub_malloc (sizeof (*port)); + if (!port) + { + grub_print_error (); + return 0; + } + + port->name = grub_xasprintf ("usb%d", usbnum++); + if (!port->name) + { + grub_free (port); + grub_print_error (); + return 0; + } + + port->usbdev = usbdev; + port->driver = driver; + for (j = 0; j < interf->endpointcnt; j++) + { + struct grub_usb_desc_endp *endp; + endp = &usbdev->config[0].interf[interfno].descendp[j]; + + if ((endp->endp_addr & 128) && (endp->attrib & 3) == 2) + { + /* Bulk IN endpoint. */ + port->in_endp = endp; + } + else if (!(endp->endp_addr & 128) && (endp->attrib & 3) == 2) + { + /* Bulk OUT endpoint. */ + port->out_endp = endp; + } + } + if (!port->out_endp || !port->in_endp) + { + grub_free (port->name); + grub_free (port); + return 0; + } + + port->configno = configno; + port->interfno = interfno; + + grub_serial_config_defaults (port); + grub_serial_register (port); + + port->usbdev->config[port->configno].interf[port->interfno].detach_hook + = grub_usbserial_detach; + port->usbdev->config[port->configno].interf[port->interfno].detach_data + = port; + + return 1; +} diff --git a/bus/usb/serial/ftdi.c b/bus/usb/serial/ftdi.c index 4495b055c..a4d88dbae 100644 --- a/bus/usb/serial/ftdi.c +++ b/bus/usb/serial/ftdi.c @@ -22,6 +22,7 @@ #include #include #include +#include enum { @@ -161,19 +162,12 @@ ftdi_hw_configure (struct grub_serial_port *port, return GRUB_ERR_NONE; } -static void -ftdi_fini (struct grub_serial_port *port) -{ - port->usbdev->config[port->configno].interf[port->interfno].detach_hook = 0; - port->usbdev->config[port->configno].interf[port->interfno].attached = 0; -} - static struct grub_serial_driver grub_ftdi_driver = { .configure = ftdi_hw_configure, .fetch = ftdi_hw_fetch, .put = ftdi_hw_put, - .fini = ftdi_fini + .fini = grub_usbserial_fini }; static const struct @@ -184,80 +178,6 @@ static const struct {0x0403, 0x6001} /* QEMU virtual USBserial. */ }; -static int ftdinum = 0; - -static void -ftdi_detach (grub_usb_device_t usbdev, int configno, int interfno) -{ - static struct grub_serial_port *port; - port = usbdev->config[configno].interf[interfno].detach_data; - - grub_serial_unregister (port); -} - -static int -grub_ftdi_attach_real (grub_usb_device_t usbdev, int configno, int interfno) -{ - static struct grub_serial_port *port; - int j; - struct grub_usb_desc_if *interf; - - interf = usbdev->config[configno].interf[interfno].descif; - - port = grub_malloc (sizeof (*port)); - if (!port) - { - grub_print_error (); - return 0; - } - - port->name = grub_xasprintf ("ftdi%d", ftdinum++); - if (!port->name) - { - grub_free (port); - grub_print_error (); - return 0; - } - - port->usbdev = usbdev; - port->driver = &grub_ftdi_driver; - for (j = 0; j < interf->endpointcnt; j++) - { - struct grub_usb_desc_endp *endp; - endp = &usbdev->config[0].interf[interfno].descendp[j]; - - if ((endp->endp_addr & 128) && (endp->attrib & 3) == 2) - { - /* Bulk IN endpoint. */ - port->in_endp = endp; - } - else if (!(endp->endp_addr & 128) && (endp->attrib & 3) == 2) - { - /* Bulk OUT endpoint. */ - port->out_endp = endp; - } - } - if (!port->out_endp || !port->in_endp) - { - grub_free (port->name); - grub_free (port); - return 0; - } - - port->configno = configno; - port->interfno = interfno; - - grub_serial_config_defaults (port); - grub_serial_register (port); - - port->usbdev->config[port->configno].interf[port->interfno].detach_hook - = ftdi_detach; - port->usbdev->config[port->configno].interf[port->interfno].detach_data - = port; - - return 1; -} - static int grub_ftdi_attach (grub_usb_device_t usbdev, int configno, int interfno) { @@ -270,10 +190,11 @@ grub_ftdi_attach (grub_usb_device_t usbdev, int configno, int interfno) if (j == ARRAY_SIZE (products)) return 0; - return grub_ftdi_attach_real (usbdev, configno, interfno); + return grub_usbserial_attach (usbdev, configno, interfno, + &grub_ftdi_driver); } -struct grub_usb_attach_desc attach_hook = +static struct grub_usb_attach_desc attach_hook = { .class = 0xff, .hook = grub_ftdi_attach diff --git a/bus/usb/serial/pl2303.c b/bus/usb/serial/pl2303.c new file mode 100644 index 000000000..eeb53402e --- /dev/null +++ b/bus/usb/serial/pl2303.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 + +enum + { + GRUB_FTDI_MODEM_CTRL = 0x01, + GRUB_FTDI_FLOW_CTRL = 0x02, + GRUB_FTDI_SPEED_CTRL = 0x03, + GRUB_FTDI_DATA_CTRL = 0x04 + }; + +#define GRUB_FTDI_MODEM_CTRL_DTRRTS 3 +#define GRUB_FTDI_FLOW_CTRL_DTRRTS 3 + +/* Convert speed to divisor. */ +static grub_uint32_t +get_divisor (unsigned int speed) +{ + unsigned int i; + + /* The structure for speed vs. divisor. */ + struct divisor + { + unsigned int speed; + grub_uint32_t div; + }; + + /* The table which lists common configurations. */ + /* Computed with a division formula with 3MHz as base frequency. */ + static struct divisor divisor_tab[] = + { + { 2400, 0x04e2 }, + { 4800, 0x0271 }, + { 9600, 0x4138 }, + { 19200, 0x809c }, + { 38400, 0xc04e }, + { 57600, 0xc034 }, + { 115200, 0x001a } + }; + + /* Set the baud rate. */ + for (i = 0; i < ARRAY_SIZE (divisor_tab); i++) + if (divisor_tab[i].speed == speed) + return divisor_tab[i].div; + return 0; +} + +static void +real_config (struct grub_serial_port *port) +{ + grub_uint32_t divisor; + const grub_uint16_t parities[] = { + [GRUB_SERIAL_PARITY_NONE] = 0x0000, + [GRUB_SERIAL_PARITY_ODD] = 0x0100, + [GRUB_SERIAL_PARITY_EVEN] = 0x0200 + }; + const grub_uint16_t stop_bits[] = { + [GRUB_SERIAL_STOP_BITS_1] = 0x0000, + [GRUB_SERIAL_STOP_BITS_2] = 0x1000, + }; + + // if (port->configured) + return; + + grub_usb_control_msg (port->usbdev, GRUB_USB_REQTYPE_VENDOR_OUT, + GRUB_FTDI_MODEM_CTRL, + GRUB_FTDI_MODEM_CTRL_DTRRTS, 0, 0, 0); + + grub_usb_control_msg (port->usbdev, GRUB_USB_REQTYPE_VENDOR_OUT, + GRUB_FTDI_FLOW_CTRL, + GRUB_FTDI_FLOW_CTRL_DTRRTS, 0, 0, 0); + + divisor = get_divisor (port->config.speed); + grub_usb_control_msg (port->usbdev, GRUB_USB_REQTYPE_VENDOR_OUT, + GRUB_FTDI_SPEED_CTRL, + divisor & 0xffff, divisor >> 16, 0, 0); + + grub_usb_control_msg (port->usbdev, GRUB_USB_REQTYPE_VENDOR_OUT, + GRUB_FTDI_DATA_CTRL, + parities[port->config.parity] + | stop_bits[port->config.stop_bits] + | port->config.word_len, 0, 0, 0); + + port->configured = 1; +} + +/* Fetch a key. */ +static int +pl2303_hw_fetch (struct grub_serial_port *port) +{ + char cc; + grub_usb_err_t err; + + real_config (port); + + err = grub_usb_bulk_read (port->usbdev, port->in_endp->endp_addr, 1, &cc); + if (err != GRUB_USB_ERR_NONE) + return -1; + + return cc; +} + +/* Put a character. */ +static void +pl2303_hw_put (struct grub_serial_port *port, const int c) +{ + char cc = c; + + real_config (port); + + grub_usb_bulk_write (port->usbdev, port->out_endp->endp_addr, 1, &cc); +} + +static grub_err_t +pl2303_hw_configure (struct grub_serial_port *port, + struct grub_serial_config *config) +{ + grub_uint16_t divisor; + + divisor = get_divisor (config->speed); + if (divisor == 0) + return grub_error (GRUB_ERR_BAD_ARGUMENT, "bad speed"); + + if (config->parity != GRUB_SERIAL_PARITY_NONE + && config->parity != GRUB_SERIAL_PARITY_ODD + && config->parity != GRUB_SERIAL_PARITY_EVEN) + return grub_error (GRUB_ERR_BAD_ARGUMENT, "unsupported parity"); + + if (config->stop_bits != GRUB_SERIAL_STOP_BITS_1 + && config->stop_bits != GRUB_SERIAL_STOP_BITS_2) + return grub_error (GRUB_ERR_BAD_ARGUMENT, "unsupported stop bits"); + + if (config->word_len < 5 || config->word_len > 8) + return grub_error (GRUB_ERR_BAD_ARGUMENT, "unsupported word length"); + + port->config = *config; + port->configured = 0; + + return GRUB_ERR_NONE; +} + +static struct grub_serial_driver grub_pl2303_driver = + { + .configure = pl2303_hw_configure, + .fetch = pl2303_hw_fetch, + .put = pl2303_hw_put, + .fini = grub_usbserial_fini + }; + +static const struct +{ + grub_uint16_t vendor, product; +} products[] = + { + {0x067b, 0x2303} + }; + +static int +grub_pl2303_attach (grub_usb_device_t usbdev, int configno, int interfno) +{ + unsigned j; + + for (j = 0; j < ARRAY_SIZE (products); j++) + if (usbdev->descdev.vendorid == products[j].vendor + && usbdev->descdev.prodid == products[j].product) + break; + if (j == ARRAY_SIZE (products)) + return 0; + + return grub_usbserial_attach (usbdev, configno, interfno, + &grub_pl2303_driver); +} + +static struct grub_usb_attach_desc attach_hook = +{ + .class = 0xff, + .hook = grub_pl2303_attach +}; + +GRUB_MOD_INIT(usbserial_pl2303) +{ + grub_usb_register_attach_hook_class (&attach_hook); +} + +GRUB_MOD_FINI(usbserial_pl2303) +{ + grub_serial_unregister_driver (&grub_pl2303_driver); + grub_usb_unregister_attach_hook_class (&attach_hook); +} diff --git a/conf/i386-pc.rmk b/conf/i386-pc.rmk index 2e95133f3..9c77a9d1d 100644 --- a/conf/i386-pc.rmk +++ b/conf/i386-pc.rmk @@ -195,6 +195,18 @@ usb_mod_SOURCES = bus/usb/usb.c bus/usb/usbtrans.c bus/usb/usbhub.c usb_mod_CFLAGS = $(COMMON_CFLAGS) usb_mod_LDFLAGS = $(COMMON_LDFLAGS) +# For serial.mod. +pkglib_MODULES += usbserial_common.mod +usbserial_common_mod_SOURCES = bus/usb/serial/common.c +usbserial_common_mod_CFLAGS = $(COMMON_CFLAGS) +usbserial_common_mod_LDFLAGS = $(COMMON_LDFLAGS) + +# For serial.mod. +pkglib_MODULES += usbserial_pl2303.mod +usbserial_pl2303_mod_SOURCES = bus/usb/serial/pl2303.c +usbserial_pl2303_mod_CFLAGS = $(COMMON_CFLAGS) +usbserial_pl2303_mod_LDFLAGS = $(COMMON_LDFLAGS) + # For serial.mod. pkglib_MODULES += usbserial_ftdi.mod usbserial_ftdi_mod_SOURCES = bus/usb/serial/ftdi.c diff --git a/include/grub/usbserial.h b/include/grub/usbserial.h new file mode 100644 index 000000000..786eff7fe --- /dev/null +++ b/include/grub/usbserial.h @@ -0,0 +1,31 @@ +/* serial.h - serial device interface */ +/* + * GRUB -- GRand Unified Bootloader + * 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 + * 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_USBSERIAL_HEADER +#define GRUB_USBSERIAL_HEADER 1 + +void grub_usbserial_fini (struct grub_serial_port *port); + +void grub_usbserial_detach (grub_usb_device_t usbdev, int configno, + int interfno); + +int +grub_usbserial_attach (grub_usb_device_t usbdev, int configno, int interfno, + struct grub_serial_driver *driver); +#endif From aa86530e3897931e90011e1d4bf388155d701361 Mon Sep 17 00:00:00 2001 From: Vladimir 'phcoder' Serbinenko Date: Sun, 18 Jul 2010 19:11:09 +0200 Subject: [PATCH 16/37] enable usbserial on grub-emu --- conf/any-emu.rmk | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/conf/any-emu.rmk b/conf/any-emu.rmk index 958da2bee..8272bf25e 100644 --- a/conf/any-emu.rmk +++ b/conf/any-emu.rmk @@ -70,6 +70,30 @@ usbms_mod_SOURCES = disk/usbms.c usbms_mod_CFLAGS = $(COMMON_CFLAGS) usbms_mod_LDFLAGS = $(COMMON_LDFLAGS) +# For serial.mod. +pkglib_MODULES += usbserial_common.mod +usbserial_common_mod_SOURCES = bus/usb/serial/common.c +usbserial_common_mod_CFLAGS = $(COMMON_CFLAGS) +usbserial_common_mod_LDFLAGS = $(COMMON_LDFLAGS) + +# For serial.mod. +pkglib_MODULES += usbserial_pl2303.mod +usbserial_pl2303_mod_SOURCES = bus/usb/serial/pl2303.c +usbserial_pl2303_mod_CFLAGS = $(COMMON_CFLAGS) +usbserial_pl2303_mod_LDFLAGS = $(COMMON_LDFLAGS) + +# For serial.mod. +pkglib_MODULES += usbserial_ftdi.mod +usbserial_ftdi_mod_SOURCES = bus/usb/serial/ftdi.c +usbserial_ftdi_mod_CFLAGS = $(COMMON_CFLAGS) +usbserial_ftdi_mod_LDFLAGS = $(COMMON_LDFLAGS) + +# For serial.mod. +pkglib_MODULES += serial.mod +serial_mod_SOURCES = term/serial.c +serial_mod_CFLAGS = $(COMMON_CFLAGS) +serial_mod_LDFLAGS = $(COMMON_LDFLAGS) + grub_emu_LDFLAGS += $(LIBUSB) endif From d5562777511a3718d43a03eb86954d06acd012ce Mon Sep 17 00:00:00 2001 From: Vladimir 'phcoder' Serbinenko Date: Sun, 18 Jul 2010 21:35:22 +0200 Subject: [PATCH 17/37] Add possibility of bulk reading with short timeout --- bus/usb/ohci.c | 4 ++-- bus/usb/uhci.c | 5 +++-- bus/usb/usbtrans.c | 19 ++++++++++++++----- include/grub/usb.h | 8 +++++++- 4 files changed, 26 insertions(+), 10 deletions(-) diff --git a/bus/usb/ohci.c b/bus/usb/ohci.c index 57ca24065..73e3e01e8 100644 --- a/bus/usb/ohci.c +++ b/bus/usb/ohci.c @@ -654,7 +654,7 @@ grub_ohci_transaction (grub_ohci_td_t td, static grub_usb_err_t grub_ohci_transfer (grub_usb_controller_t dev, - grub_usb_transfer_t transfer) + grub_usb_transfer_t transfer, int timeout) { struct grub_ohci *o = (struct grub_ohci *) dev->data; grub_ohci_ed_t ed_virt; @@ -832,7 +832,7 @@ grub_ohci_transfer (grub_usb_controller_t dev, } /* Safety measure to avoid a hang. */ - maxtime = grub_get_time_ms () + 1000; + maxtime = grub_get_time_ms () + timeout; /* Wait until the transfer is completed or STALLs. */ do diff --git a/bus/usb/uhci.c b/bus/usb/uhci.c index 5b4432018..d211de369 100644 --- a/bus/usb/uhci.c +++ b/bus/usb/uhci.c @@ -436,7 +436,8 @@ grub_uhci_transaction (struct grub_uhci *u, unsigned int endp, static grub_usb_err_t grub_uhci_transfer (grub_usb_controller_t dev, - grub_usb_transfer_t transfer) + grub_usb_transfer_t transfer, + int timeout) { struct grub_uhci *u = (struct grub_uhci *) dev->data; grub_uhci_qh_t qh; @@ -496,7 +497,7 @@ grub_uhci_transfer (grub_usb_controller_t dev, /* Wait until either the transaction completed or an error occurred. */ - endtime = grub_get_time_ms () + 1000; + endtime = grub_get_time_ms () + timeout; for (;;) { grub_uhci_td_t errtd; diff --git a/bus/usb/usbtrans.c b/bus/usb/usbtrans.c index 4a55cab11..9dfef509f 100644 --- a/bus/usb/usbtrans.c +++ b/bus/usb/usbtrans.c @@ -145,7 +145,7 @@ grub_usb_control_msg (grub_usb_device_t dev, transfer->transactions[datablocks + 1].toggle = 1; - err = dev->controller.dev->transfer (&dev->controller, transfer); + err = dev->controller.dev->transfer (&dev->controller, transfer, 1000); grub_dprintf ("usb", "control: err=%d\n", err); grub_free (transfer->transactions); @@ -162,7 +162,7 @@ grub_usb_control_msg (grub_usb_device_t dev, static grub_usb_err_t grub_usb_bulk_readwrite (grub_usb_device_t dev, int endpoint, grub_size_t size0, char *data_in, - grub_transfer_type_t type) + grub_transfer_type_t type, int timeout) { int i; grub_usb_transfer_t transfer; @@ -243,7 +243,7 @@ grub_usb_bulk_readwrite (grub_usb_device_t dev, size -= tr->size; } - err = dev->controller.dev->transfer (&dev->controller, transfer); + err = dev->controller.dev->transfer (&dev->controller, transfer, timeout); /* We must remember proper toggle value even if some transactions * were not processed - correct value should be inversion of last * processed transaction (TD). */ @@ -269,7 +269,7 @@ grub_usb_bulk_write (grub_usb_device_t dev, int endpoint, grub_size_t size, char *data) { return grub_usb_bulk_readwrite (dev, endpoint, size, data, - GRUB_USB_TRANSFER_TYPE_OUT); + GRUB_USB_TRANSFER_TYPE_OUT, 1000); } grub_usb_err_t @@ -277,5 +277,14 @@ grub_usb_bulk_read (grub_usb_device_t dev, int endpoint, grub_size_t size, char *data) { return grub_usb_bulk_readwrite (dev, endpoint, size, data, - GRUB_USB_TRANSFER_TYPE_IN); + GRUB_USB_TRANSFER_TYPE_IN, 1000); +} + +grub_usb_err_t +grub_usb_bulk_read_timeout (grub_usb_device_t dev, + int endpoint, grub_size_t size, char *data, + int timeout) +{ + return grub_usb_bulk_readwrite (dev, endpoint, size, data, + GRUB_USB_TRANSFER_TYPE_IN, timeout); } diff --git a/include/grub/usb.h b/include/grub/usb.h index 595cbd6d3..aba2fccc7 100644 --- a/include/grub/usb.h +++ b/include/grub/usb.h @@ -50,6 +50,7 @@ typedef enum enum { + GRUB_USB_REQTYPE_CLASS_INTERFACE_OUT = 0x21, GRUB_USB_REQTYPE_VENDOR_OUT = 0x40 }; @@ -103,7 +104,8 @@ struct grub_usb_controller_dev int (*iterate) (int (*hook) (grub_usb_controller_t dev)); grub_usb_err_t (*transfer) (grub_usb_controller_t dev, - grub_usb_transfer_t transfer); + grub_usb_transfer_t transfer, + int timeout); int (*hubports) (grub_usb_controller_t dev); @@ -235,5 +237,9 @@ void grub_usb_unregister_attach_hook_class (struct grub_usb_attach_desc *desc); void grub_usb_poll_devices (void); void grub_usb_device_attach (grub_usb_device_t dev); +grub_usb_err_t +grub_usb_bulk_read_timeout (grub_usb_device_t dev, + int endpoint, grub_size_t size, char *data, + int timeout); #endif /* GRUB_USB_H */ From 9edd681bbc114fbcf22e78de10092b650ead9653 Mon Sep 17 00:00:00 2001 From: Vladimir 'phcoder' Serbinenko Date: Sun, 18 Jul 2010 21:36:00 +0200 Subject: [PATCH 18/37] Somewhat working although a lot of hardcoding pl2303 --- bus/usb/serial/pl2303.c | 59 +++++++++++++++-------------------------- bus/usb/usb.c | 1 + 2 files changed, 23 insertions(+), 37 deletions(-) diff --git a/bus/usb/serial/pl2303.c b/bus/usb/serial/pl2303.c index eeb53402e..b7b25daf3 100644 --- a/bus/usb/serial/pl2303.c +++ b/bus/usb/serial/pl2303.c @@ -24,17 +24,6 @@ #include #include -enum - { - GRUB_FTDI_MODEM_CTRL = 0x01, - GRUB_FTDI_FLOW_CTRL = 0x02, - GRUB_FTDI_SPEED_CTRL = 0x03, - GRUB_FTDI_DATA_CTRL = 0x04 - }; - -#define GRUB_FTDI_MODEM_CTRL_DTRRTS 3 -#define GRUB_FTDI_FLOW_CTRL_DTRRTS 3 - /* Convert speed to divisor. */ static grub_uint32_t get_divisor (unsigned int speed) @@ -68,41 +57,36 @@ get_divisor (unsigned int speed) return 0; } +#define GRUB_PL2303_REQUEST_CONFIG 1 + static void real_config (struct grub_serial_port *port) { - grub_uint32_t divisor; - const grub_uint16_t parities[] = { - [GRUB_SERIAL_PARITY_NONE] = 0x0000, - [GRUB_SERIAL_PARITY_ODD] = 0x0100, - [GRUB_SERIAL_PARITY_EVEN] = 0x0200 - }; - const grub_uint16_t stop_bits[] = { - [GRUB_SERIAL_STOP_BITS_1] = 0x0000, - [GRUB_SERIAL_STOP_BITS_2] = 0x1000, - }; + struct req20 + { + char data[7]; + } req20; - // if (port->configured) + if (port->configured) return; grub_usb_control_msg (port->usbdev, GRUB_USB_REQTYPE_VENDOR_OUT, - GRUB_FTDI_MODEM_CTRL, - GRUB_FTDI_MODEM_CTRL_DTRRTS, 0, 0, 0); - + GRUB_PL2303_REQUEST_CONFIG, 0, 0x61, 0, 0); grub_usb_control_msg (port->usbdev, GRUB_USB_REQTYPE_VENDOR_OUT, - GRUB_FTDI_FLOW_CTRL, - GRUB_FTDI_FLOW_CTRL_DTRRTS, 0, 0, 0); - - divisor = get_divisor (port->config.speed); + GRUB_PL2303_REQUEST_CONFIG, 1, 0, 0, 0); grub_usb_control_msg (port->usbdev, GRUB_USB_REQTYPE_VENDOR_OUT, - GRUB_FTDI_SPEED_CTRL, - divisor & 0xffff, divisor >> 16, 0, 0); - + GRUB_PL2303_REQUEST_CONFIG, 2, 0x44, 0, 0); grub_usb_control_msg (port->usbdev, GRUB_USB_REQTYPE_VENDOR_OUT, - GRUB_FTDI_DATA_CTRL, - parities[port->config.parity] - | stop_bits[port->config.stop_bits] - | port->config.word_len, 0, 0, 0); + GRUB_PL2303_REQUEST_CONFIG, 8, 0, 0, 0); + grub_usb_control_msg (port->usbdev, GRUB_USB_REQTYPE_VENDOR_OUT, + GRUB_PL2303_REQUEST_CONFIG, 9, 0, 0, 0); + + grub_memset (&req20, 0, sizeof (req20)); + req20.data[6] = 8; + grub_usb_control_msg (port->usbdev, GRUB_USB_REQTYPE_CLASS_INTERFACE_OUT, + 0x20, 0, 0, sizeof (req20), (char *) &req20); + grub_usb_control_msg (port->usbdev, GRUB_USB_REQTYPE_CLASS_INTERFACE_OUT, + 0x22, 3, 0, 0, 0); port->configured = 1; } @@ -116,7 +100,8 @@ pl2303_hw_fetch (struct grub_serial_port *port) real_config (port); - err = grub_usb_bulk_read (port->usbdev, port->in_endp->endp_addr, 1, &cc); + err = grub_usb_bulk_read_timeout (port->usbdev, port->in_endp->endp_addr, + 1, &cc, 10); if (err != GRUB_USB_ERR_NONE) return -1; diff --git a/bus/usb/usb.c b/bus/usb/usb.c index b49caacdb..a961e0b48 100644 --- a/bus/usb/usb.c +++ b/bus/usb/usb.c @@ -272,6 +272,7 @@ void grub_usb_device_attach (grub_usb_device_t dev) case 0xff: /* FIXME: don't load useless modules. */ grub_dl_load ("usbserial_ftdi"); + grub_dl_load ("usbserial_pl2303"); break; } } From 9685412782b19c0a771151d9057a302baa3a0c39 Mon Sep 17 00:00:00 2001 From: Vladimir 'phcoder' Serbinenko Date: Sun, 18 Jul 2010 23:12:08 +0200 Subject: [PATCH 19/37] PL2303 works and is configurable. But sometime input is lost --- bus/usb/serial/pl2303.c | 121 +++++++++++++++++++++++++--------------- include/grub/usb.h | 3 +- 2 files changed, 77 insertions(+), 47 deletions(-) diff --git a/bus/usb/serial/pl2303.c b/bus/usb/serial/pl2303.c index b7b25daf3..ae1a4c1b3 100644 --- a/bus/usb/serial/pl2303.c +++ b/bus/usb/serial/pl2303.c @@ -26,68 +26,100 @@ /* Convert speed to divisor. */ static grub_uint32_t -get_divisor (unsigned int speed) +is_speed_supported (unsigned int speed) { unsigned int i; + unsigned int supported[] = { 2400, 4800, 9600, 19200, 38400, 57600, 115200}; - /* The structure for speed vs. divisor. */ - struct divisor - { - unsigned int speed; - grub_uint32_t div; - }; - - /* The table which lists common configurations. */ - /* Computed with a division formula with 3MHz as base frequency. */ - static struct divisor divisor_tab[] = - { - { 2400, 0x04e2 }, - { 4800, 0x0271 }, - { 9600, 0x4138 }, - { 19200, 0x809c }, - { 38400, 0xc04e }, - { 57600, 0xc034 }, - { 115200, 0x001a } - }; - - /* Set the baud rate. */ - for (i = 0; i < ARRAY_SIZE (divisor_tab); i++) - if (divisor_tab[i].speed == speed) - return divisor_tab[i].div; + for (i = 0; i < ARRAY_SIZE (supported); i++) + if (supported[i] == speed) + return 1; return 0; } -#define GRUB_PL2303_REQUEST_CONFIG 1 +#define GRUB_PL2303_REQUEST_SET_CONFIG 0x20 +#define GRUB_PL2303_STOP_BITS_1 0x0 +#define GRUB_PL2303_STOP_BITS_2 0x2 + +#define GRUB_PL2303_PARITY_NONE 0 +#define GRUB_PL2303_PARITY_ODD 1 +#define GRUB_PL2303_PARITY_EVEN 2 + +struct grub_pl2303_config +{ + grub_uint32_t speed; + grub_uint8_t stop_bits; + grub_uint8_t parity; + grub_uint8_t word_len; +} __attribute__ ((packed)); static void real_config (struct grub_serial_port *port) { - struct req20 - { - char data[7]; - } req20; + struct grub_pl2303_config config_pl2303; + char xx; if (port->configured) return; + grub_usb_control_msg (port->usbdev, GRUB_USB_REQTYPE_VENDOR_IN, + 1, 0x8484, 0, 1, &xx); grub_usb_control_msg (port->usbdev, GRUB_USB_REQTYPE_VENDOR_OUT, - GRUB_PL2303_REQUEST_CONFIG, 0, 0x61, 0, 0); - grub_usb_control_msg (port->usbdev, GRUB_USB_REQTYPE_VENDOR_OUT, - GRUB_PL2303_REQUEST_CONFIG, 1, 0, 0, 0); - grub_usb_control_msg (port->usbdev, GRUB_USB_REQTYPE_VENDOR_OUT, - GRUB_PL2303_REQUEST_CONFIG, 2, 0x44, 0, 0); - grub_usb_control_msg (port->usbdev, GRUB_USB_REQTYPE_VENDOR_OUT, - GRUB_PL2303_REQUEST_CONFIG, 8, 0, 0, 0); - grub_usb_control_msg (port->usbdev, GRUB_USB_REQTYPE_VENDOR_OUT, - GRUB_PL2303_REQUEST_CONFIG, 9, 0, 0, 0); + 1, 0x0404, 0, 0, 0); - grub_memset (&req20, 0, sizeof (req20)); - req20.data[6] = 8; + grub_usb_control_msg (port->usbdev, GRUB_USB_REQTYPE_VENDOR_IN, + 1, 0x8484, 0, 1, &xx); + grub_usb_control_msg (port->usbdev, GRUB_USB_REQTYPE_VENDOR_IN, + 1, 0x8383, 0, 1, &xx); + grub_usb_control_msg (port->usbdev, GRUB_USB_REQTYPE_VENDOR_IN, + 1, 0x8484, 0, 1, &xx); + grub_usb_control_msg (port->usbdev, GRUB_USB_REQTYPE_VENDOR_OUT, + 1, 0x0404, 1, 0, 0); + + grub_usb_control_msg (port->usbdev, GRUB_USB_REQTYPE_VENDOR_IN, + 1, 0x8484, 0, 1, &xx); + grub_usb_control_msg (port->usbdev, GRUB_USB_REQTYPE_VENDOR_IN, + 1, 0x8383, 0, 1, &xx); + + grub_usb_control_msg (port->usbdev, GRUB_USB_REQTYPE_VENDOR_OUT, + 1, 0, 1, 0, 0); + grub_usb_control_msg (port->usbdev, GRUB_USB_REQTYPE_VENDOR_OUT, + 1, 1, 0, 0, 0); + grub_usb_control_msg (port->usbdev, GRUB_USB_REQTYPE_VENDOR_OUT, + 1, 2, 0x44, 0, 0); + grub_usb_control_msg (port->usbdev, GRUB_USB_REQTYPE_VENDOR_OUT, + 1, 8, 0, 0, 0); + grub_usb_control_msg (port->usbdev, GRUB_USB_REQTYPE_VENDOR_OUT, + 1, 9, 0, 0, 0); + + if (port->config.stop_bits == GRUB_SERIAL_STOP_BITS_2) + config_pl2303.stop_bits = GRUB_PL2303_STOP_BITS_2; + else + config_pl2303.stop_bits = GRUB_PL2303_STOP_BITS_1; + + switch (port->config.parity) + { + case GRUB_SERIAL_PARITY_NONE: + config_pl2303.parity = GRUB_PL2303_PARITY_NONE; + break; + case GRUB_SERIAL_PARITY_ODD: + config_pl2303.parity = GRUB_PL2303_PARITY_ODD; + break; + case GRUB_SERIAL_PARITY_EVEN: + config_pl2303.parity = GRUB_PL2303_PARITY_EVEN; + break; + } + + config_pl2303.word_len = port->config.word_len; + config_pl2303.speed = port->config.speed; grub_usb_control_msg (port->usbdev, GRUB_USB_REQTYPE_CLASS_INTERFACE_OUT, - 0x20, 0, 0, sizeof (req20), (char *) &req20); + GRUB_PL2303_REQUEST_SET_CONFIG, 0, 0, + sizeof (config_pl2303), (char *) &config_pl2303); grub_usb_control_msg (port->usbdev, GRUB_USB_REQTYPE_CLASS_INTERFACE_OUT, 0x22, 3, 0, 0, 0); + grub_usb_control_msg (port->usbdev, GRUB_USB_REQTYPE_VENDOR_OUT, + 1, 0, 0x61, 0, 0); port->configured = 1; } @@ -123,10 +155,7 @@ static grub_err_t pl2303_hw_configure (struct grub_serial_port *port, struct grub_serial_config *config) { - grub_uint16_t divisor; - - divisor = get_divisor (config->speed); - if (divisor == 0) + if (!is_speed_supported (config->speed)) return grub_error (GRUB_ERR_BAD_ARGUMENT, "bad speed"); if (config->parity != GRUB_SERIAL_PARITY_NONE diff --git a/include/grub/usb.h b/include/grub/usb.h index aba2fccc7..8b54e869f 100644 --- a/include/grub/usb.h +++ b/include/grub/usb.h @@ -51,7 +51,8 @@ typedef enum enum { GRUB_USB_REQTYPE_CLASS_INTERFACE_OUT = 0x21, - GRUB_USB_REQTYPE_VENDOR_OUT = 0x40 + GRUB_USB_REQTYPE_VENDOR_OUT = 0x40, + GRUB_USB_REQTYPE_VENDOR_IN = 0xc0 }; /* Call HOOK with each device, until HOOK returns non-zero. */ From ac2534273bd10fde16fcd367a65e9fe575e20872 Mon Sep 17 00:00:00 2001 From: Vladimir 'phcoder' Serbinenko Date: Mon, 19 Jul 2010 00:12:59 +0200 Subject: [PATCH 20/37] fix losing pl2303 input at the price of losing some input bytes sometimes. --- bus/usb/serial/pl2303.c | 15 ++++++++++++--- include/grub/serial.h | 2 ++ 2 files changed, 14 insertions(+), 3 deletions(-) diff --git a/bus/usb/serial/pl2303.c b/bus/usb/serial/pl2303.c index ae1a4c1b3..e2dcd606e 100644 --- a/bus/usb/serial/pl2303.c +++ b/bus/usb/serial/pl2303.c @@ -127,17 +127,26 @@ real_config (struct grub_serial_port *port) static int pl2303_hw_fetch (struct grub_serial_port *port) { - char cc; grub_usb_err_t err; real_config (port); + if (port->bufstart < port->bufend) + return port->buf[port->bufstart++]; + err = grub_usb_bulk_read_timeout (port->usbdev, port->in_endp->endp_addr, - 1, &cc, 10); + sizeof (port->buf), port->buf, 10); if (err != GRUB_USB_ERR_NONE) return -1; - return cc; + port->bufstart = 0; + /* FIXME: nearly always only one byte is transfered. + It happens however that more are transfered. + Setting sizeof (port->buf) to 1 leads code to stop reading after + such transfer. */ + port->bufend = 1; + + return port->buf[port->bufstart++]; } /* Put a character. */ diff --git a/include/grub/serial.h b/include/grub/serial.h index fd601a6d9..a7d37c7de 100644 --- a/include/grub/serial.h +++ b/include/grub/serial.h @@ -67,6 +67,8 @@ struct grub_serial_port struct grub_serial_driver *driver; struct grub_serial_config config; int configured; + char buf[64]; + int bufstart, bufend; /* This should be void *data but since serial is useful as an early console when malloc isn't available it's a union. */ From 824e1447accec3af43d6b0f31adab4a99523b8cb Mon Sep 17 00:00:00 2001 From: Vladimir 'phcoder' Serbinenko Date: Mon, 19 Jul 2010 00:13:06 +0200 Subject: [PATCH 21/37] Use generic description of HID endpoints --- include/grub/usb.h | 1 + term/usb_keyboard.c | 27 +++++++++++---------------- 2 files changed, 12 insertions(+), 16 deletions(-) diff --git a/include/grub/usb.h b/include/grub/usb.h index 8b54e869f..0ebb39478 100644 --- a/include/grub/usb.h +++ b/include/grub/usb.h @@ -52,6 +52,7 @@ enum { GRUB_USB_REQTYPE_CLASS_INTERFACE_OUT = 0x21, GRUB_USB_REQTYPE_VENDOR_OUT = 0x40, + GRUB_USB_REQTYPE_CLASS_INTERFACE_IN = 0xa1, GRUB_USB_REQTYPE_VENDOR_IN = 0xc0 }; diff --git a/term/usb_keyboard.c b/term/usb_keyboard.c index f010aa9e4..1c0ce228f 100644 --- a/term/usb_keyboard.c +++ b/term/usb_keyboard.c @@ -55,11 +55,6 @@ static char keyboard_map_shift[128] = }; -/* Valid values for bmRequestType. See HID definition version 1.11 section - 7.2. */ -#define USB_HID_HOST_TO_DEVICE 0x21 -#define USB_HID_DEVICE_TO_HOST 0xA1 - /* Valid values for bRequest. See HID definition version 1.11 section 7.2. */ #define USB_HID_GET_REPORT 0x01 #define USB_HID_GET_IDLE 0x02 @@ -128,12 +123,12 @@ grub_usb_keyboard_attach (grub_usb_device_t usbdev, int configno, int interfno) grub_printf ("HID found!\n"); /* Place the device in boot mode. */ - grub_usb_control_msg (usbdev, USB_HID_HOST_TO_DEVICE, USB_HID_SET_PROTOCOL, - 0, 0, 0, 0); + grub_usb_control_msg (usbdev, GRUB_USB_REQTYPE_CLASS_INTERFACE_OUT, + USB_HID_SET_PROTOCOL, 0, 0, 0, 0); /* Reports every time an event occurs and not more often than that. */ - grub_usb_control_msg (usbdev, USB_HID_HOST_TO_DEVICE, USB_HID_SET_IDLE, - 0<<8, 0, 0, 0); + grub_usb_control_msg (usbdev, GRUB_USB_REQTYPE_CLASS_INTERFACE_OUT, + USB_HID_SET_IDLE, 0<<8, 0, 0, 0); grub_memcpy (&grub_usb_keyboards[curnum], &grub_usb_keyboard_term, sizeof (grub_usb_keyboards[curnum])); @@ -152,8 +147,8 @@ grub_usb_keyboard_attach (grub_usb_device_t usbdev, int configno, int interfno) static grub_err_t grub_usb_keyboard_getreport (grub_usb_device_t dev, grub_uint8_t *report) { - return grub_usb_control_msg (dev, USB_HID_DEVICE_TO_HOST, USB_HID_GET_REPORT, - 0, 0, 8, (char *) report); + return grub_usb_control_msg (dev, GRUB_USB_REQTYPE_CLASS_INTERFACE_IN, + USB_HID_GET_REPORT, 0, 0, 8, (char *) report); } @@ -205,7 +200,7 @@ grub_usb_keyboard_checkkey (struct grub_term_input *term) /* Wait until the key is released. */ while (!err && data[2]) { - err = grub_usb_control_msg (usbdev, USB_HID_DEVICE_TO_HOST, + err = grub_usb_control_msg (usbdev, GRUB_USB_REQTYPE_CLASS_INTERFACE_IN, USB_HID_GET_REPORT, 0, 0, sizeof (data), (char *) data); grub_dprintf ("usb_keyboard", @@ -306,8 +301,8 @@ grub_usb_keyboard_getkeystatus (struct grub_term_input *term) /* Set idle time to the minimum offered by the spec (4 milliseconds) so that we can find out the current state. */ - grub_usb_control_msg (usbdev, USB_HID_HOST_TO_DEVICE, USB_HID_SET_IDLE, - 0<<8, 0, 0, 0); + grub_usb_control_msg (usbdev, GRUB_USB_REQTYPE_CLASS_INTERFACE_OUT, + USB_HID_SET_IDLE, 0<<8, 0, 0, 0); currtime = grub_get_time_ms (); do @@ -323,8 +318,8 @@ grub_usb_keyboard_getkeystatus (struct grub_term_input *term) /* Go back to reporting every time an event occurs and not more often than that. */ - grub_usb_control_msg (usbdev, USB_HID_HOST_TO_DEVICE, USB_HID_SET_IDLE, - 0<<8, 0, 0, 0); + grub_usb_control_msg (usbdev, GRUB_USB_REQTYPE_CLASS_INTERFACE_OUT, + USB_HID_SET_IDLE, 0<<8, 0, 0, 0); /* We allowed a while for modifiers to show up in the report, but it is not an error if they never did. */ From 34787305df83999507efdab0cc9279de8f243f47 Mon Sep 17 00:00:00 2001 From: Vladimir 'phcoder' Serbinenko Date: Mon, 19 Jul 2010 08:43:01 +0200 Subject: [PATCH 22/37] Allow psartial transfers and use them for usbserial --- bus/usb/ohci.c | 23 ++++++++++++++--------- bus/usb/serial/common.c | 23 +++++++++++++++++++++++ bus/usb/serial/ftdi.c | 9 +-------- bus/usb/serial/pl2303.c | 19 +------------------ bus/usb/uhci.c | 23 ++++++++++++++++------- bus/usb/usbtrans.c | 39 ++++++++++++++++++++++++++++----------- include/grub/serial.h | 4 ++-- include/grub/usb.h | 8 ++++---- include/grub/usbserial.h | 3 +++ include/grub/usbtrans.h | 1 + 10 files changed, 93 insertions(+), 59 deletions(-) diff --git a/bus/usb/ohci.c b/bus/usb/ohci.c index 73e3e01e8..d8abb343a 100644 --- a/bus/usb/ohci.c +++ b/bus/usb/ohci.c @@ -654,7 +654,8 @@ grub_ohci_transaction (grub_ohci_td_t td, static grub_usb_err_t grub_ohci_transfer (grub_usb_controller_t dev, - grub_usb_transfer_t transfer, int timeout) + grub_usb_transfer_t transfer, int timeout, + grub_size_t *actual) { struct grub_ohci *o = (struct grub_ohci *) dev->data; grub_ohci_ed_t ed_virt; @@ -680,6 +681,8 @@ grub_ohci_transfer (grub_usb_controller_t dev, int err_unrec = 0; grub_uint32_t intstatus; + *actual = 0; + /* Pre-set target for ED - we need it to find proper ED */ /* Set the device address. */ target = transfer->devaddr; @@ -1078,12 +1081,14 @@ grub_ohci_transfer (grub_usb_controller_t dev, case 9: /* XXX: Data underrun error. */ - err = GRUB_USB_ERR_DATA; grub_dprintf ("ohci", "Underrun, failed TD address: %p, index: %d\n", tderr_virt, tderr_virt->tr_index); - grub_dprintf ("ohci", "Underrun, number of not transferred bytes: %d\n", - 1 + grub_le_to_cpu32 (tderr_virt->buffer_end) - - grub_le_to_cpu32 (tderr_virt->buffer)); + if (transfer->last_trans == -1) + break; + *actual = transfer->transactions[transfer->last_trans].size + - (grub_le_to_cpu32 (tderr_virt->buffer_end) + - grub_le_to_cpu32 (tderr_virt->buffer)) + + transfer->transactions[transfer->last_trans].preceding; break; case 10: @@ -1172,12 +1177,12 @@ grub_ohci_transfer (grub_usb_controller_t dev, transfer->last_trans = tderr_virt->tr_index; else transfer->last_trans = -1; - } + else + *actual = transfer->size; - /* Set empty ED - set HEAD = TAIL = last (not processed) TD */ - ed_virt->td_head = grub_cpu_to_le32 ( grub_le_to_cpu32 ( - ed_virt->td_tail) & ~0xf); + /* Set empty ED - set HEAD = TAIL = last (not processed) TD */ + ed_virt->td_head = grub_cpu_to_le32 (grub_le_to_cpu32 (ed_virt->td_tail) & ~0xf); /* At this point always should be: * ED has skip bit set and halted or empty or after next SOF, diff --git a/bus/usb/serial/common.c b/bus/usb/serial/common.c index 1da1c5bcf..6b5b90059 100644 --- a/bus/usb/serial/common.c +++ b/bus/usb/serial/common.c @@ -100,3 +100,26 @@ grub_usbserial_attach (grub_usb_device_t usbdev, int configno, int interfno, return 1; } + +int +grub_usbserial_fetch (struct grub_serial_port *port, grub_size_t header_size) +{ + grub_usb_err_t err; + grub_size_t actual; + + if (port->bufstart < port->bufend) + return port->buf[port->bufstart++]; + + err = grub_usb_bulk_read_extended (port->usbdev, port->in_endp->endp_addr, + sizeof (port->buf), port->buf, 10, + &actual); + if (err != GRUB_USB_ERR_NONE) + return -1; + + port->bufstart = header_size; + port->bufend = actual; + if (port->bufstart >= port->bufend) + return -1; + + return port->buf[port->bufstart++]; +} diff --git a/bus/usb/serial/ftdi.c b/bus/usb/serial/ftdi.c index a4d88dbae..bd1713b27 100644 --- a/bus/usb/serial/ftdi.c +++ b/bus/usb/serial/ftdi.c @@ -111,16 +111,9 @@ real_config (struct grub_serial_port *port) static int ftdi_hw_fetch (struct grub_serial_port *port) { - char cc[3]; - grub_usb_err_t err; - real_config (port); - err = grub_usb_bulk_read (port->usbdev, port->in_endp->endp_addr, 3, cc); - if (err != GRUB_USB_ERR_NONE) - return -1; - - return cc[2]; + return grub_usbserial_fetch (port, 2); } /* Put a character. */ diff --git a/bus/usb/serial/pl2303.c b/bus/usb/serial/pl2303.c index e2dcd606e..9e3b9ae7e 100644 --- a/bus/usb/serial/pl2303.c +++ b/bus/usb/serial/pl2303.c @@ -127,26 +127,9 @@ real_config (struct grub_serial_port *port) static int pl2303_hw_fetch (struct grub_serial_port *port) { - grub_usb_err_t err; - real_config (port); - if (port->bufstart < port->bufend) - return port->buf[port->bufstart++]; - - err = grub_usb_bulk_read_timeout (port->usbdev, port->in_endp->endp_addr, - sizeof (port->buf), port->buf, 10); - if (err != GRUB_USB_ERR_NONE) - return -1; - - port->bufstart = 0; - /* FIXME: nearly always only one byte is transfered. - It happens however that more are transfered. - Setting sizeof (port->buf) to 1 leads code to stop reading after - such transfer. */ - port->bufend = 1; - - return port->buf[port->bufstart++]; + return grub_usbserial_fetch (port, 0); } /* Put a character. */ diff --git a/bus/usb/uhci.c b/bus/usb/uhci.c index d211de369..69fae60fd 100644 --- a/bus/usb/uhci.c +++ b/bus/usb/uhci.c @@ -75,8 +75,10 @@ struct grub_uhci_td This is GRUB specific. */ grub_uint32_t linkptr2; - /* 3 additional 32 bits words reserved for the Host Controller Driver. */ - grub_uint32_t data[3]; + grub_uint32_t buffer0; + + /* 2 additional 32 bits words reserved for the Host Controller Driver. */ + grub_uint32_t data[2]; } __attribute__ ((packed)); typedef volatile struct grub_uhci_td *grub_uhci_td_t; @@ -333,9 +335,11 @@ grub_free_td (struct grub_uhci *u, grub_uhci_td_t td) static void grub_free_queue (struct grub_uhci *u, grub_uhci_td_t td, - grub_usb_transfer_t transfer) + grub_usb_transfer_t transfer, grub_size_t *actual) { int i; /* Index of TD in transfer */ + + *actual = 0; /* Free the TDs in this queue and set last_trans. */ for (i=0; td; i++) @@ -345,6 +349,8 @@ grub_free_queue (struct grub_uhci *u, grub_uhci_td_t td, /* Check state of TD and possibly set last_trans */ if (transfer && (td->linkptr & 1)) transfer->last_trans = i; + + *actual += (td->ctrl_status + 1) & 0x7ff; /* Unlink the queue. */ tdprev = td; @@ -430,6 +436,7 @@ grub_uhci_transaction (struct grub_uhci *u, unsigned int endp, | (addr << 8) | tf[type]); td->buffer = data; + td->buffer0 = data; return td; } @@ -437,7 +444,7 @@ grub_uhci_transaction (struct grub_uhci *u, unsigned int endp, static grub_usb_err_t grub_uhci_transfer (grub_usb_controller_t dev, grub_usb_transfer_t transfer, - int timeout) + int timeout, grub_size_t *actual) { struct grub_uhci *u = (struct grub_uhci *) dev->data; grub_uhci_qh_t qh; @@ -448,10 +455,12 @@ grub_uhci_transfer (grub_usb_controller_t dev, int i; grub_uint64_t endtime; + *actual = 0; + /* Allocate a queue head for the transfer queue. */ qh = grub_alloc_qh (u, GRUB_USB_TRANSACTION_TYPE_CONTROL); if (! qh) - return grub_errno; + return GRUB_USB_ERR_INTERNAL; grub_dprintf ("uhci", "transfer, iobase:%08x\n", u->iobase); @@ -469,7 +478,7 @@ grub_uhci_transfer (grub_usb_controller_t dev, td_prev->linkptr = 1; if (td_first) - grub_free_queue (u, td_first, NULL); + grub_free_queue (u, td_first, NULL, actual); return GRUB_USB_ERR_INTERNAL; } @@ -563,7 +572,7 @@ grub_uhci_transfer (grub_usb_controller_t dev, /* Place the QH back in the free list and deallocate the associated TDs. */ qh->elinkptr = 1; - grub_free_queue (u, td_first, transfer); + grub_free_queue (u, td_first, transfer, actual); return err; } diff --git a/bus/usb/usbtrans.c b/bus/usb/usbtrans.c index 9dfef509f..db2ec097a 100644 --- a/bus/usb/usbtrans.c +++ b/bus/usb/usbtrans.c @@ -43,6 +43,7 @@ grub_usb_control_msg (grub_usb_device_t dev, volatile char *data; grub_uint32_t data_addr; grub_size_t size = size0; + grub_size_t actual; /* FIXME: avoid allocation any kind of buffer in a first place. */ data_chunk = grub_memalign_dma32 (128, size ? : 16); @@ -132,6 +133,7 @@ grub_usb_control_msg (grub_usb_device_t dev, else tr->pid = GRUB_USB_TRANSFER_TYPE_OUT; tr->data = data_addr + i * max; + tr->preceding = i * max; size -= max; } @@ -145,7 +147,8 @@ grub_usb_control_msg (grub_usb_device_t dev, transfer->transactions[datablocks + 1].toggle = 1; - err = dev->controller.dev->transfer (&dev->controller, transfer, 1000); + err = dev->controller.dev->transfer (&dev->controller, transfer, + 1000, &actual); grub_dprintf ("usb", "control: err=%d\n", err); grub_free (transfer->transactions); @@ -162,7 +165,8 @@ grub_usb_control_msg (grub_usb_device_t dev, static grub_usb_err_t grub_usb_bulk_readwrite (grub_usb_device_t dev, int endpoint, grub_size_t size0, char *data_in, - grub_transfer_type_t type, int timeout) + grub_transfer_type_t type, int timeout, + grub_size_t *actual) { int i; grub_usb_transfer_t transfer; @@ -240,10 +244,12 @@ grub_usb_bulk_readwrite (grub_usb_device_t dev, toggle = toggle ? 0 : 1; tr->pid = type; tr->data = data_addr + i * max; + tr->preceding = i * max; size -= tr->size; } - err = dev->controller.dev->transfer (&dev->controller, transfer, timeout); + err = dev->controller.dev->transfer (&dev->controller, transfer, timeout, + actual); /* We must remember proper toggle value even if some transactions * were not processed - correct value should be inversion of last * processed transaction (TD). */ @@ -268,23 +274,34 @@ grub_usb_err_t grub_usb_bulk_write (grub_usb_device_t dev, int endpoint, grub_size_t size, char *data) { - return grub_usb_bulk_readwrite (dev, endpoint, size, data, - GRUB_USB_TRANSFER_TYPE_OUT, 1000); + grub_size_t actual; + grub_usb_err_t err; + + err = grub_usb_bulk_readwrite (dev, endpoint, size, data, + GRUB_USB_TRANSFER_TYPE_OUT, 1000, &actual); + if (!err && actual != size) + err = GRUB_USB_ERR_DATA; + return err; } grub_usb_err_t grub_usb_bulk_read (grub_usb_device_t dev, int endpoint, grub_size_t size, char *data) { - return grub_usb_bulk_readwrite (dev, endpoint, size, data, - GRUB_USB_TRANSFER_TYPE_IN, 1000); + grub_size_t actual; + grub_usb_err_t err; + err = grub_usb_bulk_readwrite (dev, endpoint, size, data, + GRUB_USB_TRANSFER_TYPE_IN, 1000, &actual); + if (!err && actual != size) + err = GRUB_USB_ERR_DATA; + return err; } grub_usb_err_t -grub_usb_bulk_read_timeout (grub_usb_device_t dev, - int endpoint, grub_size_t size, char *data, - int timeout) +grub_usb_bulk_read_extended (grub_usb_device_t dev, + int endpoint, grub_size_t size, char *data, + int timeout, grub_size_t *actual) { return grub_usb_bulk_readwrite (dev, endpoint, size, data, - GRUB_USB_TRANSFER_TYPE_IN, timeout); + GRUB_USB_TRANSFER_TYPE_IN, timeout, actual); } diff --git a/include/grub/serial.h b/include/grub/serial.h index a7d37c7de..68cec6fdf 100644 --- a/include/grub/serial.h +++ b/include/grub/serial.h @@ -67,8 +67,6 @@ struct grub_serial_port struct grub_serial_driver *driver; struct grub_serial_config config; int configured; - char buf[64]; - int bufstart, bufend; /* This should be void *data but since serial is useful as an early console when malloc isn't available it's a union. */ @@ -80,6 +78,8 @@ struct grub_serial_port grub_usb_device_t usbdev; int configno; int interfno; + char buf[64]; + int bufstart, bufend; struct grub_usb_desc_endp *in_endp; struct grub_usb_desc_endp *out_endp; }; diff --git a/include/grub/usb.h b/include/grub/usb.h index 0ebb39478..b2dc77ce4 100644 --- a/include/grub/usb.h +++ b/include/grub/usb.h @@ -107,7 +107,7 @@ struct grub_usb_controller_dev grub_usb_err_t (*transfer) (grub_usb_controller_t dev, grub_usb_transfer_t transfer, - int timeout); + int timeout, grub_size_t *actual); int (*hubports) (grub_usb_controller_t dev); @@ -240,8 +240,8 @@ void grub_usb_poll_devices (void); void grub_usb_device_attach (grub_usb_device_t dev); grub_usb_err_t -grub_usb_bulk_read_timeout (grub_usb_device_t dev, - int endpoint, grub_size_t size, char *data, - int timeout); +grub_usb_bulk_read_extended (grub_usb_device_t dev, + int endpoint, grub_size_t size, char *data, + int timeout, grub_size_t *actual); #endif /* GRUB_USB_H */ diff --git a/include/grub/usbserial.h b/include/grub/usbserial.h index 786eff7fe..74201256e 100644 --- a/include/grub/usbserial.h +++ b/include/grub/usbserial.h @@ -28,4 +28,7 @@ void grub_usbserial_detach (grub_usb_device_t usbdev, int configno, int grub_usbserial_attach (grub_usb_device_t usbdev, int configno, int interfno, struct grub_serial_driver *driver); +int +grub_usbserial_fetch (struct grub_serial_port *port, grub_size_t header_size); + #endif diff --git a/include/grub/usbtrans.h b/include/grub/usbtrans.h index e68698c1d..a5bb2e8b2 100644 --- a/include/grub/usbtrans.h +++ b/include/grub/usbtrans.h @@ -38,6 +38,7 @@ struct grub_usb_transaction int toggle; grub_transfer_type_t pid; grub_uint32_t data; + grub_size_t preceding; }; typedef struct grub_usb_transaction *grub_usb_transaction_t; From 443a6c4b21198a80b9723c2aff7b787fad6c291c Mon Sep 17 00:00:00 2001 From: Vladimir 'phcoder' Serbinenko Date: Sun, 1 Aug 2010 23:08:03 +0200 Subject: [PATCH 23/37] Skip unexpected descriptors --- bus/usb/usb.c | 13 +++++++++++-- include/grub/usbdesc.h | 6 ++++++ 2 files changed, 17 insertions(+), 2 deletions(-) diff --git a/bus/usb/usb.c b/bus/usb/usb.c index a961e0b48..b3eaeba0e 100644 --- a/bus/usb/usb.c +++ b/bus/usb/usb.c @@ -209,14 +209,23 @@ grub_usb_device_initialize (grub_usb_device_t dev) goto fail; /* Skip the configuration descriptor. */ - pos = sizeof (struct grub_usb_desc_config); + pos = dev->config[i].descconf->length; /* Read all interfaces. */ for (currif = 0; currif < dev->config[i].descconf->numif; currif++) { + while (pos < config.totallen + && ((struct grub_usb_desc *)&data[pos])->type + != GRUB_USB_DESCRIPTOR_INTERFACE) + pos += ((struct grub_usb_desc *)&data[pos])->length; dev->config[i].interf[currif].descif = (struct grub_usb_desc_if *) &data[pos]; - pos += sizeof (struct grub_usb_desc_if); + pos += dev->config[i].interf[currif].descif->length; + + while (pos < config.totallen + && ((struct grub_usb_desc *)&data[pos])->type + != GRUB_USB_DESCRIPTOR_ENDPOINT) + pos += ((struct grub_usb_desc *)&data[pos])->length; /* Point to the first endpoint. */ dev->config[i].interf[currif].descendp diff --git a/include/grub/usbdesc.h b/include/grub/usbdesc.h index 2f711d755..84b723a62 100644 --- a/include/grub/usbdesc.h +++ b/include/grub/usbdesc.h @@ -31,6 +31,12 @@ typedef enum { GRUB_USB_DESCRIPTOR_HUB = 0x29 } grub_usb_descriptor_t; +struct grub_usb_desc +{ + grub_uint8_t length; + grub_uint8_t type; +} __attribute__ ((packed)); + struct grub_usb_desc_device { grub_uint8_t length; From 9c98ae89113d5ccfa1aedf3277805c82f2b5909e Mon Sep 17 00:00:00 2001 From: Vladimir 'phcoder' Serbinenko Date: Sun, 1 Aug 2010 23:08:33 +0200 Subject: [PATCH 24/37] Skip non-boot usb_keyboard interface --- term/usb_keyboard.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/term/usb_keyboard.c b/term/usb_keyboard.c index 1c0ce228f..f2d74d71c 100644 --- a/term/usb_keyboard.c +++ b/term/usb_keyboard.c @@ -63,6 +63,9 @@ static char keyboard_map_shift[128] = #define USB_HID_SET_IDLE 0x0A #define USB_HID_SET_PROTOCOL 0x0B +#define USB_HID_BOOT_SUBCLASS 0x01 +#define USB_HID_KBD_PROTOCOL 0x01 + static int grub_usb_keyboard_checkkey (struct grub_term_input *term); static int grub_usb_keyboard_getkey (struct grub_term_input *term); static int grub_usb_keyboard_getkeystatus (struct grub_term_input *term); @@ -120,6 +123,12 @@ grub_usb_keyboard_attach (grub_usb_device_t usbdev, int configno, int interfno) || usbdev->descdev.subclass != 0 || usbdev->descdev.protocol != 0) return 0; + if (usbdev->config[configno].interf[interfno].descif->subclass + != USB_HID_BOOT_SUBCLASS + || usbdev->config[configno].interf[interfno].descif->protocol + != USB_HID_KBD_PROTOCOL) + return 0; + grub_printf ("HID found!\n"); /* Place the device in boot mode. */ From 537f3753211bfe432032aaa060824a64000f04e8 Mon Sep 17 00:00:00 2001 From: Vladimir 'phcoder' Serbinenko Date: Fri, 20 Aug 2010 12:22:23 +0200 Subject: [PATCH 25/37] Fix control msg type --- term/usb_keyboard.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/term/usb_keyboard.c b/term/usb_keyboard.c index f2d74d71c..2157e26d7 100644 --- a/term/usb_keyboard.c +++ b/term/usb_keyboard.c @@ -133,11 +133,11 @@ grub_usb_keyboard_attach (grub_usb_device_t usbdev, int configno, int interfno) /* Place the device in boot mode. */ grub_usb_control_msg (usbdev, GRUB_USB_REQTYPE_CLASS_INTERFACE_OUT, - USB_HID_SET_PROTOCOL, 0, 0, 0, 0); + USB_HID_SET_PROTOCOL, 0, 0, 0, 0); /* Reports every time an event occurs and not more often than that. */ grub_usb_control_msg (usbdev, GRUB_USB_REQTYPE_CLASS_INTERFACE_OUT, - USB_HID_SET_IDLE, 0<<8, 0, 0, 0); + USB_HID_SET_IDLE, 0<<8, 0, 0, 0); grub_memcpy (&grub_usb_keyboards[curnum], &grub_usb_keyboard_term, sizeof (grub_usb_keyboards[curnum])); @@ -157,7 +157,7 @@ static grub_err_t grub_usb_keyboard_getreport (grub_usb_device_t dev, grub_uint8_t *report) { return grub_usb_control_msg (dev, GRUB_USB_REQTYPE_CLASS_INTERFACE_IN, - USB_HID_GET_REPORT, 0, 0, 8, (char *) report); + USB_HID_GET_REPORT, 0x0100, 0, 8, (char *) report); } From a17e3c978b05b40124b0f1501f6865be8756384f Mon Sep 17 00:00:00 2001 From: Vladimir 'phcoder' Serbinenko Date: Fri, 20 Aug 2010 14:36:29 +0200 Subject: [PATCH 26/37] Use GetReport only at initialisation as specified in the USBHID spec --- term/usb_keyboard.c | 233 ++++++++++++++------------------------------ 1 file changed, 73 insertions(+), 160 deletions(-) diff --git a/term/usb_keyboard.c b/term/usb_keyboard.c index 2157e26d7..d318a04ff 100644 --- a/term/usb_keyboard.c +++ b/term/usb_keyboard.c @@ -78,6 +78,13 @@ static struct grub_term_input grub_usb_keyboard_term = .next = 0 }; +struct grub_usb_keyboard_data +{ + grub_usb_device_t usbdev; + grub_uint8_t status; + int key; +}; + static struct grub_term_input grub_usb_keyboards[16]; static void @@ -92,6 +99,7 @@ grub_usb_keyboard_detach (grub_usb_device_t usbdev, grub_term_unregister_input (&grub_usb_keyboards[i]); grub_free ((char *) grub_usb_keyboards[i].name); grub_usb_keyboards[i].name = NULL; + grub_free (grub_usb_keyboards[i].data); grub_usb_keyboards[i].data = 0; } } @@ -100,6 +108,7 @@ static int grub_usb_keyboard_attach (grub_usb_device_t usbdev, int configno, int interfno) { unsigned curnum; + struct grub_usb_keyboard_data *data; grub_dprintf ("usb_keyboard", "%x %x %x %d %d\n", usbdev->descdev.class, usbdev->descdev.subclass, @@ -112,13 +121,6 @@ grub_usb_keyboard_attach (grub_usb_device_t usbdev, int configno, int interfno) if (curnum == ARRAY_SIZE (grub_usb_keyboards)) return 0; -#if 0 - if (descdev->class != 0x09 - || descdev->subclass == 0x01 - || descdev->protocol != 0x02) - return 0; -#endif - if (usbdev->descdev.class != 0 || usbdev->descdev.subclass != 0 || usbdev->descdev.protocol != 0) return 0; @@ -131,6 +133,15 @@ grub_usb_keyboard_attach (grub_usb_device_t usbdev, int configno, int interfno) grub_printf ("HID found!\n"); + data = grub_malloc (sizeof (*data)); + if (!data) + { + grub_print_error (); + return 0; + } + + data->usbdev = usbdev; + /* Place the device in boot mode. */ grub_usb_control_msg (usbdev, GRUB_USB_REQTYPE_CLASS_INTERFACE_OUT, USB_HID_SET_PROTOCOL, 0, 0, 0, 0); @@ -141,51 +152,64 @@ grub_usb_keyboard_attach (grub_usb_device_t usbdev, int configno, int interfno) grub_memcpy (&grub_usb_keyboards[curnum], &grub_usb_keyboard_term, sizeof (grub_usb_keyboards[curnum])); - grub_usb_keyboards[curnum].data = usbdev; + grub_usb_keyboards[curnum].data = data; usbdev->config[configno].interf[interfno].detach_hook = grub_usb_keyboard_detach; grub_usb_keyboards[curnum].name = grub_xasprintf ("usb_keyboard%d", curnum); if (!grub_usb_keyboards[curnum].name) - return 0; + { + grub_print_error (); + return 0; + } + + { + grub_uint8_t report[8]; + grub_usb_err_t err; + grub_memset (report, 0, sizeof (report)); + err = grub_usb_control_msg (usbdev, GRUB_USB_REQTYPE_CLASS_INTERFACE_IN, + USB_HID_GET_REPORT, 0x0000, interfno, + sizeof (report), (char *) report); + if (err) + { + data->status = 0; + data->key = -1; + } + else + { + data->status = report[0]; + data->key = report[2] ? : -1; + } + } + grub_term_register_input_active ("usb_keyboard", &grub_usb_keyboards[curnum]); - return 1; } -static grub_err_t -grub_usb_keyboard_getreport (grub_usb_device_t dev, grub_uint8_t *report) -{ - return grub_usb_control_msg (dev, GRUB_USB_REQTYPE_CLASS_INTERFACE_IN, - USB_HID_GET_REPORT, 0x0100, 0, 8, (char *) report); -} - static int grub_usb_keyboard_checkkey (struct grub_term_input *term) { grub_uint8_t data[8]; - int key; - grub_err_t err; - grub_uint64_t currtime; - int timeout = 50; - grub_usb_device_t usbdev = term->data; + grub_usb_err_t err; + struct grub_usb_keyboard_data *termdata = term->data; + grub_size_t actual; + + if (termdata->key != -1) + return termdata->key; data[2] = 0; - currtime = grub_get_time_ms (); - do - { - /* Get_Report. */ - err = grub_usb_keyboard_getreport (usbdev, data); + /* Poll interrupt pipe. */ + err = grub_usb_bulk_read_extended (termdata->usbdev, 1, sizeof (data), + (char *) data, 1, &actual); - /* Implement a timeout. */ - if (grub_get_time_ms () > currtime + timeout) - break; - } - while (err || !data[2]); + if (err) + return -1; - if (err || !data[2]) + termdata->status = data[0]; + + if (actual < 3 || !data[2]) return -1; grub_dprintf ("usb_keyboard", @@ -196,161 +220,50 @@ grub_usb_keyboard_checkkey (struct grub_term_input *term) /* Check if the Control or Shift key was pressed. */ if (data[0] & 0x01 || data[0] & 0x10) - key = keyboard_map[data[2]] - 'a' + 1; + termdata->key = keyboard_map[data[2]] - 'a' + 1; else if (data[0] & 0x02 || data[0] & 0x20) - key = keyboard_map_shift[data[2]]; + termdata->key = keyboard_map_shift[data[2]]; else - key = keyboard_map[data[2]]; + termdata->key = keyboard_map[data[2]]; - if (key == 0) + if (termdata->key == 0) grub_printf ("Unknown key 0x%x detected\n", data[2]); -#if 0 - /* Wait until the key is released. */ - while (!err && data[2]) - { - err = grub_usb_control_msg (usbdev, GRUB_USB_REQTYPE_CLASS_INTERFACE_IN, - USB_HID_GET_REPORT, 0, 0, - sizeof (data), (char *) data); - grub_dprintf ("usb_keyboard", - "report2: 0x%02x 0x%02x 0x%02x 0x%02x" - " 0x%02x 0x%02x 0x%02x 0x%02x\n", - data[0], data[1], data[2], data[3], - data[4], data[5], data[6], data[7]); - } -#endif - grub_errno = GRUB_ERR_NONE; - return key; + return termdata->key; } -typedef enum -{ - GRUB_HIDBOOT_REPEAT_NONE, - GRUB_HIDBOOT_REPEAT_FIRST, - GRUB_HIDBOOT_REPEAT -} grub_usb_keyboard_repeat_t; - static int grub_usb_keyboard_getkey (struct grub_term_input *term) { - int key; - grub_err_t err; - grub_uint8_t data[8]; - grub_uint64_t currtime; - int timeout; - static grub_usb_keyboard_repeat_t repeat = GRUB_HIDBOOT_REPEAT_NONE; - grub_usb_device_t usbdev = term->data; + int ret; + struct grub_usb_keyboard_data *termdata = term->data; - again: + while (termdata->key == -1) + grub_usb_keyboard_checkkey (term); - do - { - key = grub_usb_keyboard_checkkey (term); - } while (key == -1); + ret = termdata->key; - data[2] = !0; /* Or whatever. */ - err = 0; + termdata->key = -1; - switch (repeat) - { - case GRUB_HIDBOOT_REPEAT_FIRST: - timeout = 500; - break; - case GRUB_HIDBOOT_REPEAT: - timeout = 50; - break; - default: - timeout = 100; - break; - } - - /* Wait until the key is released. */ - currtime = grub_get_time_ms (); - while (!err && data[2]) - { - /* Implement a timeout. */ - if (grub_get_time_ms () > currtime + timeout) - { - if (repeat == 0) - repeat = 1; - else - repeat = 2; - - grub_errno = GRUB_ERR_NONE; - return key; - } - - err = grub_usb_keyboard_getreport (usbdev, data); - } - - if (repeat) - { - repeat = 0; - goto again; - } - - repeat = 0; - - grub_errno = GRUB_ERR_NONE; - - return key; + return ret; } static int grub_usb_keyboard_getkeystatus (struct grub_term_input *term) { - grub_uint8_t data[8]; + struct grub_usb_keyboard_data *termdata = term->data; int mods = 0; - grub_err_t err; - grub_uint64_t currtime; - int timeout = 50; - grub_usb_device_t usbdev = term->data; - - /* Set idle time to the minimum offered by the spec (4 milliseconds) so - that we can find out the current state. */ - grub_usb_control_msg (usbdev, GRUB_USB_REQTYPE_CLASS_INTERFACE_OUT, - USB_HID_SET_IDLE, 0<<8, 0, 0, 0); - - currtime = grub_get_time_ms (); - do - { - /* Get_Report. */ - err = grub_usb_keyboard_getreport (usbdev, data); - - /* Implement a timeout. */ - if (grub_get_time_ms () > currtime + timeout) - break; - } - while (err || !data[0]); - - /* Go back to reporting every time an event occurs and not more often than - that. */ - grub_usb_control_msg (usbdev, GRUB_USB_REQTYPE_CLASS_INTERFACE_OUT, - USB_HID_SET_IDLE, 0<<8, 0, 0, 0); - - /* We allowed a while for modifiers to show up in the report, but it is - not an error if they never did. */ - if (err) - return -1; - - grub_dprintf ("usb_keyboard", - "report: 0x%02x 0x%02x 0x%02x 0x%02x" - " 0x%02x 0x%02x 0x%02x 0x%02x\n", - data[0], data[1], data[2], data[3], - data[4], data[5], data[6], data[7]); /* Check Shift, Control, and Alt status. */ - if (data[0] & 0x02 || data[0] & 0x20) + if (termdata->status & 0x02 || termdata->status & 0x20) mods |= GRUB_TERM_STATUS_SHIFT; - if (data[0] & 0x01 || data[0] & 0x10) + if (termdata->status & 0x01 || termdata->status & 0x10) mods |= GRUB_TERM_STATUS_CTRL; - if (data[0] & 0x04 || data[0] & 0x40) + if (termdata->status & 0x04 || termdata->status & 0x40) mods |= GRUB_TERM_STATUS_ALT; - grub_errno = GRUB_ERR_NONE; - return mods; } From 9ba74de61a9c6adfe14bf51a3226111e645a6c18 Mon Sep 17 00:00:00 2001 From: Vladimir 'phcoder' Serbinenko Date: Fri, 20 Aug 2010 16:34:34 +0200 Subject: [PATCH 27/37] Scan descriptor rather than elying on hardcoded endpoint number --- include/grub/usb.h | 14 ++++++++++++++ term/usb_keyboard.c | 21 +++++++++++++++++++-- 2 files changed, 33 insertions(+), 2 deletions(-) diff --git a/include/grub/usb.h b/include/grub/usb.h index b2dc77ce4..5d0eb2082 100644 --- a/include/grub/usb.h +++ b/include/grub/usb.h @@ -181,6 +181,20 @@ struct grub_usb_device +typedef enum grub_usb_ep_type + { + GRUB_USB_EP_CONTROL, + GRUB_USB_EP_ISOCHRONOUS, + GRUB_USB_EP_BULK, + GRUB_USB_EP_INTERRUPT + } grub_usb_ep_type_t; + +static inline enum grub_usb_ep_type +grub_usb_get_ep_type (struct grub_usb_desc_endp *ep) +{ + return ep->attrib & 3; +} + typedef enum { GRUB_USB_CLASS_NOTHERE, diff --git a/term/usb_keyboard.c b/term/usb_keyboard.c index d318a04ff..5fcb570b7 100644 --- a/term/usb_keyboard.c +++ b/term/usb_keyboard.c @@ -83,6 +83,7 @@ struct grub_usb_keyboard_data grub_usb_device_t usbdev; grub_uint8_t status; int key; + struct grub_usb_desc_endp *endp; }; static struct grub_term_input grub_usb_keyboards[16]; @@ -109,6 +110,8 @@ grub_usb_keyboard_attach (grub_usb_device_t usbdev, int configno, int interfno) { unsigned curnum; struct grub_usb_keyboard_data *data; + struct grub_usb_desc_endp *endp = NULL; + int j; grub_dprintf ("usb_keyboard", "%x %x %x %d %d\n", usbdev->descdev.class, usbdev->descdev.subclass, @@ -131,6 +134,18 @@ grub_usb_keyboard_attach (grub_usb_device_t usbdev, int configno, int interfno) != USB_HID_KBD_PROTOCOL) return 0; + for (j = 0; j < usbdev->config[configno].interf[interfno].descif->endpointcnt; + j++) + { + endp = &usbdev->config[configno].interf[interfno].descendp[j]; + + if ((endp->endp_addr & 128) && grub_usb_get_ep_type(endp) + == GRUB_USB_EP_INTERRUPT) + break; + } + if (j == usbdev->config[configno].interf[interfno].descif->endpointcnt) + return 0; + grub_printf ("HID found!\n"); data = grub_malloc (sizeof (*data)); @@ -141,6 +156,7 @@ grub_usb_keyboard_attach (grub_usb_device_t usbdev, int configno, int interfno) } data->usbdev = usbdev; + data->endp = endp; /* Place the device in boot mode. */ grub_usb_control_msg (usbdev, GRUB_USB_REQTYPE_CLASS_INTERFACE_OUT, @@ -201,8 +217,9 @@ grub_usb_keyboard_checkkey (struct grub_term_input *term) data[2] = 0; /* Poll interrupt pipe. */ - err = grub_usb_bulk_read_extended (termdata->usbdev, 1, sizeof (data), - (char *) data, 1, &actual); + err = grub_usb_bulk_read_extended (termdata->usbdev, + termdata->endp->endp_addr, sizeof (data), + (char *) data, 10, &actual); if (err) return -1; From 15bd1f9ccfa4405626b9e733505497ed3899758b Mon Sep 17 00:00:00 2001 From: Vladimir 'phcoder' Serbinenko Date: Fri, 20 Aug 2010 16:49:24 +0200 Subject: [PATCH 28/37] Don't update status on 0 message --- term/usb_keyboard.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/term/usb_keyboard.c b/term/usb_keyboard.c index 5fcb570b7..62f5df352 100644 --- a/term/usb_keyboard.c +++ b/term/usb_keyboard.c @@ -220,8 +220,7 @@ grub_usb_keyboard_checkkey (struct grub_term_input *term) err = grub_usb_bulk_read_extended (termdata->usbdev, termdata->endp->endp_addr, sizeof (data), (char *) data, 10, &actual); - - if (err) + if (err || actual < 1) return -1; termdata->status = data[0]; From 24582ab39b8353f4acae89f2a377f3f8eca60977 Mon Sep 17 00:00:00 2001 From: Vladimir 'phcoder' Serbinenko Date: Fri, 20 Aug 2010 16:56:03 +0200 Subject: [PATCH 29/37] Correct *actual counting in OHCI --- bus/usb/ohci.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/bus/usb/ohci.c b/bus/usb/ohci.c index aa96ed5e6..39e47ffee 100644 --- a/bus/usb/ohci.c +++ b/bus/usb/ohci.c @@ -989,6 +989,7 @@ grub_ohci_transfer (grub_usb_controller_t dev, transfer->last_trans = tderr_virt->tr_index; else transfer->last_trans = -1; + *actual = transfer->size; } else if (err_halt) /* error, ED is halted by OHCI, i.e. can be modified */ @@ -1178,8 +1179,6 @@ grub_ohci_transfer (grub_usb_controller_t dev, else transfer->last_trans = -1; } - else - *actual = transfer->size; /* Set empty ED - set HEAD = TAIL = last (not processed) TD */ ed_virt->td_head = grub_cpu_to_le32 (grub_le_to_cpu32 (ed_virt->td_tail) & ~0xf); From 60c1ffdfdc325c571bab525e2608a943f87343b5 Mon Sep 17 00:00:00 2001 From: Vladimir 'phcoder' Serbinenko Date: Fri, 20 Aug 2010 19:33:44 +0200 Subject: [PATCH 30/37] Fix OHCI error message --- bus/usb/ohci.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bus/usb/ohci.c b/bus/usb/ohci.c index 39e47ffee..974bed6cd 100644 --- a/bus/usb/ohci.c +++ b/bus/usb/ohci.c @@ -1036,7 +1036,7 @@ grub_ohci_transfer (grub_usb_controller_t dev, { case 0: /* XXX: Should not happen! */ - grub_error (GRUB_ERR_IO, "OHCI without reporting the reason"); + grub_error (GRUB_ERR_IO, "OHCI failed without reporting the reason"); err = GRUB_USB_ERR_INTERNAL; break; From 52d8255d2021ce28ce89a351750c50b27b273cf6 Mon Sep 17 00:00:00 2001 From: Vladimir 'phcoder' Serbinenko Date: Fri, 20 Aug 2010 19:34:29 +0200 Subject: [PATCH 31/37] Support hot unplugging --- bus/usb/usb.c | 13 +++++- bus/usb/usbhub.c | 107 +++++++++++++++++++++++++++++--------------- disk/usbms.c | 10 ++--- include/grub/term.h | 1 + include/grub/usb.h | 6 +++ kern/term.c | 11 +++++ term/usb_keyboard.c | 23 ++++++---- 7 files changed, 120 insertions(+), 51 deletions(-) diff --git a/bus/usb/usb.c b/bus/usb/usb.c index b3eaeba0e..2bd805ef2 100644 --- a/bus/usb/usb.c +++ b/bus/usb/usb.c @@ -22,7 +22,7 @@ #include #include #include -#include +#include static grub_usb_controller_dev_t grub_usb_list; struct grub_usb_attach_desc *attach_hooks; @@ -334,3 +334,14 @@ grub_usb_unregister_attach_hook_class (struct grub_usb_attach_desc *desc) { grub_list_remove (GRUB_AS_LIST_P (&attach_hooks), GRUB_AS_LIST (desc)); } + + +GRUB_MOD_INIT(usb) +{ + grub_term_poll_usb = grub_usb_poll_devices; +} + +GRUB_MOD_FINI(usb) +{ + grub_term_poll_usb = NULL; +} diff --git a/bus/usb/usbhub.c b/bus/usb/usbhub.c index 6c14f4b8d..7f2c8d24b 100644 --- a/bus/usb/usbhub.c +++ b/bus/usb/usbhub.c @@ -33,7 +33,7 @@ struct grub_usb_hub struct grub_usb_hub *next; grub_usb_controller_t controller; int nports; - grub_usb_speed_t *speed; + struct grub_usb_device **devices; grub_usb_device_t dev; }; @@ -104,7 +104,7 @@ grub_usb_hub_add_dev (grub_usb_controller_t controller, grub_usb_speed_t speed) } -static grub_err_t +static grub_usb_err_t grub_usb_add_hub (grub_usb_device_t dev) { struct grub_usb_usb_hubdesc hubdesc; @@ -112,6 +112,7 @@ grub_usb_add_hub (grub_usb_device_t dev) int i; grub_uint64_t timeout; grub_usb_device_t next_dev; + grub_usb_device_t *attached_devices; err = grub_usb_control_msg (dev, (GRUB_USB_REQTYPE_IN | GRUB_USB_REQTYPE_CLASS @@ -130,6 +131,13 @@ grub_usb_add_hub (grub_usb_device_t dev) grub_dprintf ("usb", "Hub set configuration\n"); grub_usb_set_configuration (dev, 1); + attached_devices = grub_zalloc (hubdesc.portcnt + * sizeof (attached_devices[0])); + if (!attached_devices) + return GRUB_USB_ERR_INTERNAL; + dev->children = attached_devices; + dev->nports = hubdesc.portcnt; + /* Power on all Hub ports. */ for (i = 1; i <= hubdesc.portcnt; i++) { @@ -236,6 +244,8 @@ grub_usb_add_hub (grub_usb_device_t dev) if (! next_dev) continue; + attached_devices[i - 1] = next_dev; + /* If the device is a Hub, scan it for more devices. */ if (next_dev->descdev.class == 0x09) grub_usb_add_hub (next_dev); @@ -246,27 +256,29 @@ grub_usb_add_hub (grub_usb_device_t dev) } static void -attach_root_port (grub_usb_controller_t controller, int portno, +attach_root_port (struct grub_usb_hub *hub, int portno, grub_usb_speed_t speed) { grub_usb_device_t dev; grub_err_t err; /* Disable the port. XXX: Why? */ - err = controller->dev->portstatus (controller, portno, 0); + err = hub->controller->dev->portstatus (hub->controller, portno, 0); if (err) return; /* Enable the port. */ - err = controller->dev->portstatus (controller, portno, 1); + err = hub->controller->dev->portstatus (hub->controller, portno, 1); if (err) return; /* Enable the port and create a device. */ - dev = grub_usb_hub_add_dev (controller, speed); + dev = grub_usb_hub_add_dev (hub->controller, speed); if (! dev) return; + hub->devices[portno] = dev; + /* If the device is a Hub, scan it for more devices. */ if (dev->descdev.class == 0x09) grub_usb_add_hub (dev); @@ -297,8 +309,8 @@ grub_usb_root_hub (grub_usb_controller_t controller) /* Query the number of ports the root Hub has. */ hub->nports = controller->dev->hubports (controller); - hub->speed = grub_malloc (sizeof (hub->speed[0]) * hub->nports); - if (!hub->speed) + hub->devices = grub_zalloc (sizeof (hub->devices[0]) * hub->nports); + if (!hub->devices) { grub_free (hub->controller); grub_free (hub); @@ -307,36 +319,54 @@ grub_usb_root_hub (grub_usb_controller_t controller) for (i = 0; i < hub->nports; i++) { - hub->speed[i] = controller->dev->detect_dev (hub->controller, i, - &changed); + grub_usb_speed_t speed; + speed = controller->dev->detect_dev (hub->controller, i, + &changed); - if (hub->speed[i] != GRUB_USB_SPEED_NONE) - attach_root_port (hub->controller, i, hub->speed[i]); + if (speed != GRUB_USB_SPEED_NONE) + attach_root_port (hub, i, speed); } return GRUB_USB_ERR_NONE; } +static void detach_device (grub_usb_device_t dev); + +static void +detach_device (grub_usb_device_t dev) +{ + unsigned i; + int k; + if (!dev) + return; + if (dev->descdev.class == GRUB_USB_CLASS_HUB) + { + for (i = 0; i < dev->nports; i++) + detach_device (dev->children[i]); + grub_free (dev->children); + } + for (i = 0; i < ARRAY_SIZE (dev->config); i++) + if (dev->config[i].descconf) + for (k = 0; k < dev->config[i].descconf->numif; k++) + { + struct grub_usb_interface *inter = &dev->config[i].interf[k]; + if (inter && inter->detach_hook) + inter->detach_hook (dev, i, k); + } + grub_usb_devs[dev->addr] = 0; +} + static void poll_nonroot_hub (grub_usb_device_t dev) { - struct grub_usb_usb_hubdesc hubdesc; grub_err_t err; - int i; + unsigned i; grub_uint64_t timeout; grub_usb_device_t next_dev; - - err = grub_usb_control_msg (dev, (GRUB_USB_REQTYPE_IN - | GRUB_USB_REQTYPE_CLASS - | GRUB_USB_REQTYPE_TARGET_DEV), - GRUB_USB_REQ_GET_DESCRIPTOR, - (GRUB_USB_DESCRIPTOR_HUB << 8) | 0, - 0, sizeof (hubdesc), (char *) &hubdesc); - if (err) - return; - + grub_usb_device_t *attached_devices = dev->children; + /* Iterate over the Hub ports. */ - for (i = 1; i <= hubdesc.portcnt; i++) + for (i = 1; i <= dev->nports; i++) { grub_uint32_t status; @@ -350,6 +380,12 @@ poll_nonroot_hub (grub_usb_device_t dev) status. */ if (err) continue; + + if (status & GRUB_USB_HUB_STATUS_C_CONNECTED) + { + detach_device (attached_devices[i-1]); + attached_devices[i - 1] = NULL; + } /* Connected and status of connection changed ? */ if ((status & GRUB_USB_HUB_STATUS_CONNECTED) @@ -417,6 +453,8 @@ poll_nonroot_hub (grub_usb_device_t dev) if (! next_dev) continue; + attached_devices[i - 1] = next_dev; + /* If the device is a Hub, scan it for more devices. */ if (next_dev->descdev.class == 0x09) grub_usb_add_hub (next_dev); @@ -434,26 +472,23 @@ grub_usb_poll_devices (void) for (hub = hubs; hub; hub = hub->next) { - int changed=0; /* Do we have to recheck number of ports? */ /* No, it should be never changed, it should be constant. */ for (i = 0; i < hub->nports; i++) { grub_usb_speed_t speed; + int changed = 0; speed = hub->controller->dev->detect_dev (hub->controller, i, &changed); - if (speed != GRUB_USB_SPEED_NONE) + if (changed) { - if (changed) - attach_root_port (hub->controller, i, speed); + detach_device (hub->devices[i]); + hub->devices[i] = NULL; + if (speed != GRUB_USB_SPEED_NONE) + attach_root_port (hub, i, speed); } - - /* XXX: There should be also handling - * of disconnected devices. */ - - hub->speed[i] = speed; } } @@ -463,9 +498,7 @@ grub_usb_poll_devices (void) grub_usb_device_t dev = grub_usb_devs[i]; if (dev && dev->descdev.class == 0x09) - { - poll_nonroot_hub (dev); - } + poll_nonroot_hub (dev); } } diff --git a/disk/usbms.c b/disk/usbms.c index 225761e0f..e63105ccc 100644 --- a/disk/usbms.c +++ b/disk/usbms.c @@ -65,6 +65,7 @@ typedef struct grub_usbms_dev *grub_usbms_dev_t; /* FIXME: remove limit. */ #define MAX_USBMS_DEVICES 128 static grub_usbms_dev_t grub_usbms_devices[MAX_USBMS_DEVICES]; +static int first_available_slot = 0; static grub_err_t grub_usbms_reset (grub_usb_device_t dev, int interface) @@ -96,13 +97,12 @@ grub_usbms_attach (grub_usb_device_t usbdev, int configno, int interfno) unsigned curnum; grub_usb_err_t err; - for (curnum = 0; curnum < ARRAY_SIZE (grub_usbms_devices); curnum++) - if (!grub_usbms_devices[curnum]) - break; - - if (curnum == ARRAY_SIZE (grub_usbms_devices)) + if (first_available_slot == ARRAY_SIZE (grub_usbms_devices)) return 0; + curnum = first_available_slot; + first_available_slot++; + interf = usbdev->config[configno].interf[interfno].descif; if ((interf->subclass != GRUB_USBMS_SUBCLASS_BULK diff --git a/include/grub/term.h b/include/grub/term.h index f40d935e4..734e4ab17 100644 --- a/include/grub/term.h +++ b/include/grub/term.h @@ -459,6 +459,7 @@ grub_print_spaces (struct grub_term_output *term, int number_spaces) grub_putcode (' ', term); } +extern void (*EXPORT_VAR (grub_term_poll_usb)) (void); /* For convenience. */ #define GRUB_TERM_ASCII_CHAR(c) ((c) & 0xff) diff --git a/include/grub/usb.h b/include/grub/usb.h index 5d0eb2082..674f7ec46 100644 --- a/include/grub/usb.h +++ b/include/grub/usb.h @@ -177,6 +177,12 @@ struct grub_usb_device /* Data toggle values (used for bulk transfers only). */ int toggle[256]; + + /* Array of children for a hub. */ + grub_usb_device_t *children; + + /* Number of hub ports. */ + unsigned nports; }; diff --git a/kern/term.c b/kern/term.c index 47bd426fd..360539e50 100644 --- a/kern/term.c +++ b/kern/term.c @@ -28,6 +28,8 @@ struct grub_term_input *grub_term_inputs_disabled; struct grub_term_output *grub_term_outputs; struct grub_term_input *grub_term_inputs; +void (*grub_term_poll_usb) (void) = NULL; + /* Put a Unicode character. */ static void grub_putcode_dumb (grub_uint32_t code, @@ -85,6 +87,9 @@ grub_getkey (void) while (1) { + if (grub_term_poll_usb) + grub_term_poll_usb (); + FOR_ACTIVE_TERM_INPUTS(term) { int key = term->checkkey (term); @@ -101,6 +106,9 @@ grub_checkkey (void) { grub_term_input_t term; + if (grub_term_poll_usb) + grub_term_poll_usb (); + FOR_ACTIVE_TERM_INPUTS(term) { int key = term->checkkey (term); @@ -117,6 +125,9 @@ grub_getkeystatus (void) int status = 0; grub_term_input_t term; + if (grub_term_poll_usb) + grub_term_poll_usb (); + FOR_ACTIVE_TERM_INPUTS(term) { if (term->getkeystatus) diff --git a/term/usb_keyboard.c b/term/usb_keyboard.c index 62f5df352..b6bc05dbe 100644 --- a/term/usb_keyboard.c +++ b/term/usb_keyboard.c @@ -95,14 +95,21 @@ grub_usb_keyboard_detach (grub_usb_device_t usbdev, { unsigned i; for (i = 0; i < ARRAY_SIZE (grub_usb_keyboards); i++) - if (grub_usb_keyboards[i].data && grub_usb_keyboards[i].data == usbdev) - { - grub_term_unregister_input (&grub_usb_keyboards[i]); - grub_free ((char *) grub_usb_keyboards[i].name); - grub_usb_keyboards[i].name = NULL; - grub_free (grub_usb_keyboards[i].data); - grub_usb_keyboards[i].data = 0; - } + { + struct grub_usb_keyboard_data *data = grub_usb_keyboards[i].data; + + if (!data) + continue; + + if (data->usbdev != usbdev) + continue; + + grub_term_unregister_input (&grub_usb_keyboards[i]); + grub_free ((char *) grub_usb_keyboards[i].name); + grub_usb_keyboards[i].name = NULL; + grub_free (grub_usb_keyboards[i].data); + grub_usb_keyboards[i].data = 0; + } } static int From 2823526d28c3467accc7965fa8bd9fcce8df5fec Mon Sep 17 00:00:00 2001 From: Vladimir 'phcoder' Serbinenko Date: Fri, 20 Aug 2010 19:54:40 +0200 Subject: [PATCH 32/37] Acount for transfer->size being size-1 when counting *actual --- bus/usb/ohci.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bus/usb/ohci.c b/bus/usb/ohci.c index 974bed6cd..7f757485c 100644 --- a/bus/usb/ohci.c +++ b/bus/usb/ohci.c @@ -989,7 +989,7 @@ grub_ohci_transfer (grub_usb_controller_t dev, transfer->last_trans = tderr_virt->tr_index; else transfer->last_trans = -1; - *actual = transfer->size; + *actual = transfer->size + 1; } else if (err_halt) /* error, ED is halted by OHCI, i.e. can be modified */ From ccedc09bc702c4c7a1110b5527ebd2ae9ffac71d Mon Sep 17 00:00:00 2001 From: Vladimir 'phcoder' Serbinenko Date: Fri, 20 Aug 2010 20:26:57 +0200 Subject: [PATCH 33/37] Make HID found dprintf instead of printf --- term/usb_keyboard.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/term/usb_keyboard.c b/term/usb_keyboard.c index b6bc05dbe..d875ac00a 100644 --- a/term/usb_keyboard.c +++ b/term/usb_keyboard.c @@ -153,7 +153,7 @@ grub_usb_keyboard_attach (grub_usb_device_t usbdev, int configno, int interfno) if (j == usbdev->config[configno].interf[interfno].descif->endpointcnt) return 0; - grub_printf ("HID found!\n"); + grub_dprintf ("usb_keyboard", "HID found!\n"); data = grub_malloc (sizeof (*data)); if (!data) From 1420c1d54a33ae75a8bd6b902fc8f9cb961d804d Mon Sep 17 00:00:00 2001 From: Vladimir 'phcoder' Serbinenko Date: Fri, 20 Aug 2010 21:25:20 +0200 Subject: [PATCH 34/37] Remove unused buffer0 --- bus/usb/uhci.c | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/bus/usb/uhci.c b/bus/usb/uhci.c index 69fae60fd..0bba24b54 100644 --- a/bus/usb/uhci.c +++ b/bus/usb/uhci.c @@ -75,10 +75,8 @@ struct grub_uhci_td This is GRUB specific. */ grub_uint32_t linkptr2; - grub_uint32_t buffer0; - - /* 2 additional 32 bits words reserved for the Host Controller Driver. */ - grub_uint32_t data[2]; + /* 3 additional 32 bits words reserved for the Host Controller Driver. */ + grub_uint32_t data[3]; } __attribute__ ((packed)); typedef volatile struct grub_uhci_td *grub_uhci_td_t; @@ -436,7 +434,6 @@ grub_uhci_transaction (struct grub_uhci *u, unsigned int endp, | (addr << 8) | tf[type]); td->buffer = data; - td->buffer0 = data; return td; } From 41e46ae6484ba99a0a94965e393977fe941b681c Mon Sep 17 00:00:00 2001 From: Vladimir 'phcoder' Serbinenko Date: Fri, 20 Aug 2010 21:26:04 +0200 Subject: [PATCH 35/37] Enable usbserial on yeeloong --- conf/mips-yeeloong.rmk | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/conf/mips-yeeloong.rmk b/conf/mips-yeeloong.rmk index 365ca1ca5..90949866c 100644 --- a/conf/mips-yeeloong.rmk +++ b/conf/mips-yeeloong.rmk @@ -97,6 +97,24 @@ usb_mod_SOURCES = bus/usb/usb.c bus/usb/usbtrans.c bus/usb/usbhub.c usb_mod_CFLAGS = $(COMMON_CFLAGS) usb_mod_LDFLAGS = $(COMMON_LDFLAGS) +# For serial.mod. +pkglib_MODULES += usbserial_common.mod +usbserial_common_mod_SOURCES = bus/usb/serial/common.c +usbserial_common_mod_CFLAGS = $(COMMON_CFLAGS) +usbserial_common_mod_LDFLAGS = $(COMMON_LDFLAGS) + +# For serial.mod. +pkglib_MODULES += usbserial_pl2303.mod +usbserial_pl2303_mod_SOURCES = bus/usb/serial/pl2303.c +usbserial_pl2303_mod_CFLAGS = $(COMMON_CFLAGS) +usbserial_pl2303_mod_LDFLAGS = $(COMMON_LDFLAGS) + +# For serial.mod. +pkglib_MODULES += usbserial_ftdi.mod +usbserial_ftdi_mod_SOURCES = bus/usb/serial/ftdi.c +usbserial_ftdi_mod_CFLAGS = $(COMMON_CFLAGS) +usbserial_ftdi_mod_LDFLAGS = $(COMMON_LDFLAGS) + # For usbtest.mod pkglib_MODULES += usbtest.mod usbtest_mod_SOURCES = commands/usbtest.c From fb1d7b7975e3a6dde8e01cbbad43ba32c8f4b3e7 Mon Sep 17 00:00:00 2001 From: Vladimir 'phcoder' Serbinenko Date: Fri, 20 Aug 2010 21:26:27 +0200 Subject: [PATCH 36/37] Add ChangeLog --- ChangeLog | 79 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 79 insertions(+) diff --git a/ChangeLog b/ChangeLog index 7d82e08fb..72a8d3601 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,82 @@ +2010-08-20 Vladimir Serbinenko + + USB hotunplugging and USB serial support. + + * bus/usb/ohci.c (grub_ohci_transfer): Fill *actual and respect timeout. + * bus/usb/uhci.c (grub_free_queue): Compute *actual. + (grub_uhci_transfer): Respect timeout and set *actual. + * bus/usb/usb.c (grub_usb_device_initialize): Correctly skip fields of + non-standard length. + (grub_usb_device_attach): Autoload modules. + (GRUB_MOD_INIT): Set grub_term_poll_usb. + (GRUB_MOD_FINI): Unset grub_term_poll_usb. + * bus/usb/usbhub.c (grub_usb_hub): Replace speed with devices. All + users updated. + (grub_usb_add_hub): Fill nports and children. + (attach_root_port): Receive hub instead of controller. + All users updated. Fill hub->devices. + (grub_usb_root_hub): Allocate hub->devices. + (detach_device): New function. + (poll_nonroot_hub): Fill children and detach devices. + * bus/usb/usbtrans.c (grub_usb_bulk_readwrite): Accept timeout and + actual arguments. All users updated. + (grub_usb_bulk_read_extended): New function. + * bus/usb/serial/common.c: New file. + * bus/usb/serial/ftdi.c: Likewise. + * bus/usb/serial/pl2303.c: Likewise. + * commands/terminal.c (handle_command): Support wildcard. + * commands/usbtest.c: Output "Unknown" instead of empty string. + * conf/any-emu.rmk (pkglib_MODULES): Add usbserial_common.mod. + (usbserial_common_mod_SOURCES): New variable. + (usbserial_common_mod_CFLAGS): Likewise. + (usbserial_common_mod_LDFLAGS): Likewise. + (pkglib_MODULES): Add usbserial_pl2303.mod. + (usbserial_pl2303_mod_SOURCES): New variable. + (usbserial_pl2303_mod_CFLAGS): Likewise. + (usbserial_pl2303_mod_LDFLAGS): Likewise. + (pkglib_MODULES): Add usbserial_ftdi.mod. + (usbserial_ftdi_mod_SOURCES): New variable. + (usbserial_ftdi_mod_CFLAGS): Likewise. + (usbserial_ftdi_mod_LDFLAGS): Likewise. + (pkglib_MODULES): Add serial.mod. + (serial_mod_SOURCES): New variable. + (serial_mod_CFLAGS): Likewise. + (serial_mod_LDFLAGS): Likewise. + * conf/i386-pc.rmk: Likewise. + * conf/mips-yeeloong.rmk: Likewise. + * conf/i386.rmk (serial_mod_SOURCES): Add term/ns8250.c. + * conf/mips-yeeloong.rmk (kernel_img_SOURCES): Likewise. + * disk/usbms.c (first_available_slot): New variable. + (grub_usbms_attach): Don't reuse free slots due to potential cache + problems. + * include/grub/serial.h: Moved to .. + * include/grub/ns8250.h: ...this. + * include/grub/serial.h: New file. + * include/grub/term.h (grub_term_poll_usb): New variable. + * include/grub/terminfo.h (grub_terminfo_input_state): Pass term to + readkey. All users updated. + (grub_terminfo_output_state): Pass term to put. + * include/grub/usb.h (GRUB_USB_REQTYPE): New enum. + (grub_usb_controller_dev): Add timeout and actual arguments to + transfer. All users updated. + (grub_usb_interface): New field detach_data. + (grub_usb_device): New fields children and nports. + (grub_usb_ep_type_t): New type. + (grub_usb_get_ep_type): New function. + (grub_usb_bulk_read_extended): Likewise. + * include/grub/usbdesc.h (grub_usb_desc): New type. + * include/grub/usbserial.h: New file. + * include/grub/usbtrans.h (grub_usb_transaction): New field preceding. + * kern/term.c (grub_term_poll_usb): New variable. + (grub_getkey): Call grub_term_poll_usb if set. + (grub_checkkey): Likewise. + (grub_getkeystatus): Likewise. + * term/serial.c: Moved controller-specific parts to ... + * term/ns8250.c: ... here. + * term/serial.c: Mostly rewritten. + * term/usb_keyboard.c: Reorganised to use GET_REPORT only on attaching + according to spec. + 2010-08-20 Colin Watson * commands/i386/pc/sendkey.c (keysym_table): Rename "numlock" to From d6f66ca2a0c9f4934f9512601da3b251d09dcfd0 Mon Sep 17 00:00:00 2001 From: Vladimir 'phcoder' Serbinenko Date: Fri, 20 Aug 2010 21:31:33 +0200 Subject: [PATCH 37/37] Export serial-related functions from kernel --- conf/mips-yeeloong.rmk | 2 +- include/grub/serial.h | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/conf/mips-yeeloong.rmk b/conf/mips-yeeloong.rmk index 90949866c..9cbbdf472 100644 --- a/conf/mips-yeeloong.rmk +++ b/conf/mips-yeeloong.rmk @@ -5,7 +5,7 @@ COMMON_CFLAGS += -march=mips3 COMMON_ASFLAGS += -march=mips3 kernel_img_HEADERS += pci.h bitmap.h video.h gfxterm.h font.h \ - bitmap_scale.h bufio.h cs5536.h machine/pci.h + bitmap_scale.h bufio.h cs5536.h machine/pci.h serial.h include $(srcdir)/conf/mips.mk diff --git a/include/grub/serial.h b/include/grub/serial.h index 68cec6fdf..652268b2e 100644 --- a/include/grub/serial.h +++ b/include/grub/serial.h @@ -88,9 +88,9 @@ struct grub_serial_port grub_term_input_t term_in; }; -grub_err_t grub_serial_register (struct grub_serial_port *port); +grub_err_t EXPORT_FUNC(grub_serial_register) (struct grub_serial_port *port); -void grub_serial_unregister (struct grub_serial_port *port); +void EXPORT_FUNC(grub_serial_unregister) (struct grub_serial_port *port); /* Set default settings. */ static inline grub_err_t @@ -114,6 +114,6 @@ grub_serial_config_defaults (struct grub_serial_port *port) void grub_ns8250_init (void); char *grub_serial_ns8250_add_port (grub_port_t port); extern struct grub_serial_driver grub_ns8250_driver; -void grub_serial_unregister_driver (struct grub_serial_driver *driver); +void EXPORT_FUNC(grub_serial_unregister_driver) (struct grub_serial_driver *driver); #endif