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