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