]> git.ipfire.org Git - thirdparty/grub.git/commitdiff
usbserial basic support. Works in qemu
authorVladimir 'phcoder' Serbinenko <phcoder@gmail.com>
Sun, 18 Jul 2010 10:53:35 +0000 (12:53 +0200)
committerVladimir 'phcoder' Serbinenko <phcoder@gmail.com>
Sun, 18 Jul 2010 10:53:35 +0000 (12:53 +0200)
bus/usb/usb.c
conf/i386-pc.rmk
include/grub/serial.h
term/ns8250.c
term/serial.c
term/usbserial.c

index 804cbaff04049dc8ea2806bb6e741a5cfb370e5e..ba052e5eeff80756d1d0a8ff80694f4d7a67f32f 100644 (file)
@@ -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;
        }
     }
 }
index c157aaf01e9b2f3d12ee1bdaa1a33937c690f517..e1cfe1468df6102fdbf09ddb46a1ebe2200a164f 100644 (file)
@@ -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)
index 06328211211b5c473b7215e9e1e10ff594d736ce..97e97004cd4f99f3b743e9bd511c1adc87b7c7aa 100644 (file)
@@ -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);
index ee2eeb8f51650763eddbd6360a402866e643e9b2..53e94b89977513dca85ea298c9d806ad0dd0db8e 100644 (file)
@@ -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 (divisor == 0)
-    return grub_error (GRUB_ERR_BAD_ARGUMENT, "bad speed");
+  if (port->configured)
+    return;
 
-  port->speed = speed;
-  port->word_len = word_len;
-  port->parity = parity;
-  port->stop_bits = stop_bits;
+  divisor = serial_get_divisor (port->speed);
+  /* Shouldn't happen.  */
+  if (divisor == 0)
+    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);  
 
index 1f54a83ecfa083c3cc234a48ed55be725e76acf5..d3b2ba1431b53cd8742963960b144327188926e2 100644 (file)
@@ -315,7 +315,6 @@ grub_serial_unregister (struct grub_serial_port *port)
   /* FIXME */
 }
 
-
 static grub_extcmd_t cmd;
 
 GRUB_MOD_INIT(serial)
index 5a63348fb152652506c26c1628aceda16e53b158..e250845485b4e9fdd188ae3817b4b8b9454f1570 100644 (file)
@@ -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
  *  along with GRUB.  If not, see <http://www.gnu.org/licenses/>.
  */
 
-#include <grub/machine/memory.h>
 #include <grub/serial.h>
-#include <grub/term.h>
 #include <grub/types.h>
 #include <grub/dl.h>
 #include <grub/misc.h>
-#include <grub/terminfo.h>
-#include <grub/cpu/io.h>
-#include <grub/extcmd.h>
-#include <grub/i18n.h>
-
-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 <grub/mm.h>
+#include <grub/usb.h>
 
 /* 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;
 
-struct grub_terminfo_input_state grub_serial_terminfo_input =
-  {
-    .readkey = serial_hw_fetch
-  };
+  port->configured = 0;
 
-struct grub_terminfo_output_state grub_serial_terminfo_output =
+  return GRUB_ERR_NONE;
+}
+
+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,
-};
+  port = grub_malloc (sizeof (*port));
+  if (!port)
+    {
+      grub_print_error ();
+      return 0;
+    }
 
-\f
+  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;
+    }
 
-static grub_extcmd_t cmd;
+  grub_serial_config_defaults (port);
+  grub_serial_register (port);
 
-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
+  return 1;
 }
 
-GRUB_MOD_FINI(serial)
+struct grub_usb_attach_desc attach_hook =
 {
-  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);
-    }
+  .class = 0xff,
+  .hook = grub_usbserial_attach
+};
+
+GRUB_MOD_INIT(usbserial)
+{
+  grub_usb_register_attach_hook_class (&attach_hook);
 }