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
#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)
.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);
{
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);
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. */
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;
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");
}
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");
}
#include <grub/mm.h>
#include <grub/usb.h>
+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;
}
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;