From 1da07b142b14c60c5527c5bbdd644335fd3a76ee Mon Sep 17 00:00:00 2001 From: Vladimir 'phcoder' Serbinenko Date: Sun, 18 Jul 2010 14:43:23 +0200 Subject: [PATCH] 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;