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); }