Restructure serial in order to prepare for usbserial. As a byproduct simultaneous serial consoles are possible
This commit is contained in:
parent
03f286ea9f
commit
75eb7d1116
10 changed files with 733 additions and 295 deletions
|
@ -59,11 +59,17 @@ handle_command (int argc, char **args, struct abstract_terminal **enabled,
|
||||||
for (aut = autoloads; aut; aut = aut->next)
|
for (aut = autoloads; aut; aut = aut->next)
|
||||||
{
|
{
|
||||||
for (term = *disabled; term; term = term->next)
|
for (term = *disabled; term; term = term->next)
|
||||||
if (grub_strcmp (term->name, aut->name) == 0)
|
if (grub_strcmp (term->name, aut->name) == 0
|
||||||
|
|| (aut->name[0] && aut->name[grub_strlen (aut->name) - 1] == '*'
|
||||||
|
&& grub_memcmp (term->name, aut->name,
|
||||||
|
grub_strlen (aut->name) - 1) == 0))
|
||||||
break;
|
break;
|
||||||
if (!term)
|
if (!term)
|
||||||
for (term = *enabled; term; term = term->next)
|
for (term = *enabled; term; term = term->next)
|
||||||
if (grub_strcmp (term->name, aut->name) == 0)
|
if (grub_strcmp (term->name, aut->name) == 0
|
||||||
|
|| (aut->name[0] && aut->name[grub_strlen (aut->name) - 1] == '*'
|
||||||
|
&& grub_memcmp (term->name, aut->name,
|
||||||
|
grub_strlen (aut->name) - 1) == 0))
|
||||||
break;
|
break;
|
||||||
if (!term)
|
if (!term)
|
||||||
grub_printf ("%s ", aut->name);
|
grub_printf ("%s ", aut->name);
|
||||||
|
@ -98,7 +104,10 @@ handle_command (int argc, char **args, struct abstract_terminal **enabled,
|
||||||
return grub_error (GRUB_ERR_BAD_ARGUMENT, "unknown terminal '%s'\n",
|
return grub_error (GRUB_ERR_BAD_ARGUMENT, "unknown terminal '%s'\n",
|
||||||
args[i]);
|
args[i]);
|
||||||
for (aut = autoloads; aut; aut = aut->next)
|
for (aut = autoloads; aut; aut = aut->next)
|
||||||
if (grub_strcmp (args[i], aut->name) == 0)
|
if (grub_strcmp (args[i], aut->name) == 0
|
||||||
|
|| (aut->name[0] && aut->name[grub_strlen (aut->name) - 1] == '*'
|
||||||
|
&& grub_memcmp (args[i], aut->name,
|
||||||
|
grub_strlen (aut->name) - 1) == 0))
|
||||||
{
|
{
|
||||||
grub_dl_t mod;
|
grub_dl_t mod;
|
||||||
mod = grub_dl_load (aut->modname);
|
mod = grub_dl_load (aut->modname);
|
||||||
|
|
|
@ -56,7 +56,7 @@ multiboot2_mod_ASFLAGS = $(COMMON_ASFLAGS)
|
||||||
|
|
||||||
# For serial.mod.
|
# For serial.mod.
|
||||||
pkglib_MODULES += serial.mod
|
pkglib_MODULES += serial.mod
|
||||||
serial_mod_SOURCES = term/serial.c
|
serial_mod_SOURCES = term/serial.c term/ns8250.c
|
||||||
serial_mod_CFLAGS = $(COMMON_CFLAGS)
|
serial_mod_CFLAGS = $(COMMON_CFLAGS)
|
||||||
serial_mod_LDFLAGS = $(COMMON_LDFLAGS)
|
serial_mod_LDFLAGS = $(COMMON_LDFLAGS)
|
||||||
|
|
||||||
|
|
67
include/grub/ns8250.h
Normal file
67
include/grub/ns8250.h
Normal file
|
@ -0,0 +1,67 @@
|
||||||
|
/* serial.h - serial device interface */
|
||||||
|
/*
|
||||||
|
* GRUB -- GRand Unified Bootloader
|
||||||
|
* Copyright (C) 2000,2001,2002,2005,2007 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
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* GRUB is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with GRUB. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef GRUB_NS8250_HEADER
|
||||||
|
#define GRUB_NS8250_HEADER 1
|
||||||
|
|
||||||
|
/* Macros. */
|
||||||
|
|
||||||
|
/* The offsets of UART registers. */
|
||||||
|
#define UART_TX 0
|
||||||
|
#define UART_RX 0
|
||||||
|
#define UART_DLL 0
|
||||||
|
#define UART_IER 1
|
||||||
|
#define UART_DLH 1
|
||||||
|
#define UART_IIR 2
|
||||||
|
#define UART_FCR 2
|
||||||
|
#define UART_LCR 3
|
||||||
|
#define UART_MCR 4
|
||||||
|
#define UART_LSR 5
|
||||||
|
#define UART_MSR 6
|
||||||
|
#define UART_SR 7
|
||||||
|
|
||||||
|
/* For LSR bits. */
|
||||||
|
#define UART_DATA_READY 0x01
|
||||||
|
#define UART_EMPTY_TRANSMITTER 0x20
|
||||||
|
|
||||||
|
/* 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
|
||||||
|
|
||||||
|
/* the switch of DLAB. */
|
||||||
|
#define UART_DLAB 0x80
|
||||||
|
|
||||||
|
/* Enable the FIFO. */
|
||||||
|
#define UART_ENABLE_FIFO_TRIGGER14 0xC7
|
||||||
|
|
||||||
|
/* Enable the FIFO. */
|
||||||
|
#define UART_ENABLE_FIFO_TRIGGER1 0x07
|
||||||
|
|
||||||
|
/* Turn on DTR, RTS, and OUT2. */
|
||||||
|
#define UART_ENABLE_DTRRTS 0x03
|
||||||
|
|
||||||
|
/* Turn on DTR, RTS, and OUT2. */
|
||||||
|
#define UART_ENABLE_OUT2 0x08
|
||||||
|
|
||||||
|
#endif /* ! GRUB_SERIAL_MACHINE_HEADER */
|
|
@ -1,7 +1,7 @@
|
||||||
/* serial.h - serial device interface */
|
/* serial.h - serial device interface */
|
||||||
/*
|
/*
|
||||||
* GRUB -- GRand Unified Bootloader
|
* GRUB -- GRand Unified Bootloader
|
||||||
* Copyright (C) 2000,2001,2002,2005,2007 Free Software Foundation, Inc.
|
* Copyright (C) 2010 Free Software Foundation, Inc.
|
||||||
*
|
*
|
||||||
* GRUB is free software: you can redistribute it and/or modify
|
* GRUB is free software: you can redistribute it and/or modify
|
||||||
* it under the terms of the GNU General Public License as published by
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
@ -20,30 +20,51 @@
|
||||||
#ifndef GRUB_SERIAL_HEADER
|
#ifndef GRUB_SERIAL_HEADER
|
||||||
#define GRUB_SERIAL_HEADER 1
|
#define GRUB_SERIAL_HEADER 1
|
||||||
|
|
||||||
/* Macros. */
|
#include <grub/types.h>
|
||||||
|
#include <grub/cpu/io.h>
|
||||||
|
#include <grub/usb.h>
|
||||||
|
#include <grub/list.h>
|
||||||
|
|
||||||
/* The offsets of UART registers. */
|
struct grub_serial_port;
|
||||||
#define UART_TX 0
|
|
||||||
#define UART_RX 0
|
|
||||||
#define UART_DLL 0
|
|
||||||
#define UART_IER 1
|
|
||||||
#define UART_DLH 1
|
|
||||||
#define UART_IIR 2
|
|
||||||
#define UART_FCR 2
|
|
||||||
#define UART_LCR 3
|
|
||||||
#define UART_MCR 4
|
|
||||||
#define UART_LSR 5
|
|
||||||
#define UART_MSR 6
|
|
||||||
#define UART_SR 7
|
|
||||||
|
|
||||||
/* For LSR bits. */
|
struct grub_serial_driver
|
||||||
#define UART_DATA_READY 0x01
|
{
|
||||||
#define UART_EMPTY_TRANSMITTER 0x20
|
grub_err_t (*configure) (struct grub_serial_port *port,
|
||||||
|
unsigned speed,
|
||||||
|
unsigned short word_len,
|
||||||
|
unsigned int parity,
|
||||||
|
unsigned short stop_bits);
|
||||||
|
int (*fetch) (struct grub_serial_port *port);
|
||||||
|
void (*put) (struct grub_serial_port *port, const int c);
|
||||||
|
};
|
||||||
|
|
||||||
/* The type of parity. */
|
struct grub_serial_port
|
||||||
#define UART_NO_PARITY 0x00
|
{
|
||||||
#define UART_ODD_PARITY 0x08
|
struct grub_serial_port *next;
|
||||||
#define UART_EVEN_PARITY 0x18
|
char *name;
|
||||||
|
unsigned speed;
|
||||||
|
unsigned short word_len;
|
||||||
|
unsigned int parity;
|
||||||
|
unsigned short stop_bits;
|
||||||
|
struct grub_serial_driver *driver;
|
||||||
|
/* This should be void *data but since serial is useful as an early console
|
||||||
|
when malloc isn't available it's a union.
|
||||||
|
*/
|
||||||
|
union
|
||||||
|
{
|
||||||
|
grub_port_t port;
|
||||||
|
struct
|
||||||
|
{
|
||||||
|
grub_usb_device_t dev;
|
||||||
|
int in_endp;
|
||||||
|
int out_endp;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
grub_err_t grub_serial_register (struct grub_serial_port *port);
|
||||||
|
|
||||||
|
void grub_serial_unregister (struct grub_serial_port *port);
|
||||||
|
|
||||||
/* The type of word length. */
|
/* The type of word length. */
|
||||||
#define UART_5BITS_WORD 0x00
|
#define UART_5BITS_WORD 0x00
|
||||||
|
@ -51,23 +72,30 @@
|
||||||
#define UART_7BITS_WORD 0x02
|
#define UART_7BITS_WORD 0x02
|
||||||
#define UART_8BITS_WORD 0x03
|
#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. */
|
/* The type of the length of stop bit. */
|
||||||
#define UART_1_STOP_BIT 0x00
|
#define UART_1_STOP_BIT 0x00
|
||||||
#define UART_2_STOP_BITS 0x04
|
#define UART_2_STOP_BITS 0x04
|
||||||
|
|
||||||
/* the switch of DLAB. */
|
static inline void
|
||||||
#define UART_DLAB 0x80
|
grub_serial_fill_defaults (struct grub_serial_port *port)
|
||||||
|
{
|
||||||
|
/* Set default settings. */
|
||||||
|
#ifdef GRUB_MACHINE_MIPS_YEELOONG
|
||||||
|
port->speed = 115200;
|
||||||
|
#else
|
||||||
|
port->speed = 9600;
|
||||||
|
#endif
|
||||||
|
port->word_len = UART_8BITS_WORD;
|
||||||
|
port->parity = UART_NO_PARITY;
|
||||||
|
port->stop_bits = UART_1_STOP_BIT;
|
||||||
|
}
|
||||||
|
|
||||||
/* Enable the FIFO. */
|
void grub_ns8250_init (void);
|
||||||
#define UART_ENABLE_FIFO_TRIGGER14 0xC7
|
char *grub_serial_ns8250_add_port (grub_port_t port);
|
||||||
|
|
||||||
/* Enable the FIFO. */
|
#endif
|
||||||
#define UART_ENABLE_FIFO_TRIGGER1 0x07
|
|
||||||
|
|
||||||
/* Turn on DTR, RTS, and OUT2. */
|
|
||||||
#define UART_ENABLE_DTRRTS 0x03
|
|
||||||
|
|
||||||
/* Turn on DTR, RTS, and OUT2. */
|
|
||||||
#define UART_ENABLE_OUT2 0x08
|
|
||||||
|
|
||||||
#endif /* ! GRUB_SERIAL_MACHINE_HEADER */
|
|
||||||
|
|
|
@ -32,7 +32,7 @@ struct grub_terminfo_input_state
|
||||||
{
|
{
|
||||||
int input_buf[GRUB_TERMINFO_READKEY_MAX_LEN];
|
int input_buf[GRUB_TERMINFO_READKEY_MAX_LEN];
|
||||||
int npending;
|
int npending;
|
||||||
int (*readkey) (void);
|
int (*readkey) (struct grub_term_input *term);
|
||||||
};
|
};
|
||||||
|
|
||||||
struct grub_terminfo_output_state
|
struct grub_terminfo_output_state
|
||||||
|
@ -51,7 +51,7 @@ struct grub_terminfo_output_state
|
||||||
|
|
||||||
unsigned int xpos, ypos;
|
unsigned int xpos, ypos;
|
||||||
|
|
||||||
void (*put) (const int c);
|
void (*put) (struct grub_term_output *term, const int c);
|
||||||
};
|
};
|
||||||
|
|
||||||
void EXPORT_FUNC(grub_terminfo_gotoxy) (grub_term_output_t term,
|
void EXPORT_FUNC(grub_terminfo_gotoxy) (grub_term_output_t term,
|
||||||
|
|
|
@ -19,6 +19,7 @@
|
||||||
#ifndef GRUB_USB_H
|
#ifndef GRUB_USB_H
|
||||||
#define GRUB_USB_H 1
|
#define GRUB_USB_H 1
|
||||||
|
|
||||||
|
#include <grub/err.h>
|
||||||
#include <grub/usbdesc.h>
|
#include <grub/usbdesc.h>
|
||||||
#include <grub/usbtrans.h>
|
#include <grub/usbtrans.h>
|
||||||
|
|
||||||
|
|
212
term/ns8250.c
Normal file
212
term/ns8250.c
Normal file
|
@ -0,0 +1,212 @@
|
||||||
|
/*
|
||||||
|
* GRUB -- GRand Unified Bootloader
|
||||||
|
* 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
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* GRUB is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with GRUB. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <grub/machine/memory.h>
|
||||||
|
#include <grub/serial.h>
|
||||||
|
#include <grub/ns8250.h>
|
||||||
|
#include <grub/types.h>
|
||||||
|
#include <grub/dl.h>
|
||||||
|
#include <grub/misc.h>
|
||||||
|
#include <grub/cpu/io.h>
|
||||||
|
#include <grub/mm.h>
|
||||||
|
|
||||||
|
#ifdef GRUB_MACHINE_PCBIOS
|
||||||
|
static const unsigned short *serial_hw_io_addr = (const unsigned short *) GRUB_MEMORY_MACHINE_BIOS_DATA_AREA_ADDR;
|
||||||
|
#define GRUB_SERIAL_PORT_NUM 4
|
||||||
|
#else
|
||||||
|
#include <grub/machine/serial.h>
|
||||||
|
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)
|
||||||
|
{
|
||||||
|
unsigned int i;
|
||||||
|
|
||||||
|
/* The structure for speed vs. divisor. */
|
||||||
|
struct divisor
|
||||||
|
{
|
||||||
|
unsigned int speed;
|
||||||
|
unsigned short div;
|
||||||
|
};
|
||||||
|
|
||||||
|
/* The table which lists common configurations. */
|
||||||
|
/* 1843200 / (speed * 16) */
|
||||||
|
static struct divisor divisor_tab[] =
|
||||||
|
{
|
||||||
|
{ 2400, 0x0030 },
|
||||||
|
{ 4800, 0x0018 },
|
||||||
|
{ 9600, 0x000C },
|
||||||
|
{ 19200, 0x0006 },
|
||||||
|
{ 38400, 0x0003 },
|
||||||
|
{ 57600, 0x0002 },
|
||||||
|
{ 115200, 0x0001 }
|
||||||
|
};
|
||||||
|
|
||||||
|
/* Set the baud rate. */
|
||||||
|
for (i = 0; i < sizeof (divisor_tab) / sizeof (divisor_tab[0]); i++)
|
||||||
|
if (divisor_tab[i].speed == speed)
|
||||||
|
/* UART in Yeeloong runs twice the usual rate. */
|
||||||
|
#ifdef GRUB_MACHINE_MIPS_YEELOONG
|
||||||
|
return 2 * divisor_tab[i].div;
|
||||||
|
#else
|
||||||
|
return divisor_tab[i].div;
|
||||||
|
#endif
|
||||||
|
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)
|
||||||
|
{
|
||||||
|
unsigned char status = 0;
|
||||||
|
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;
|
||||||
|
|
||||||
|
/* Turn off the interrupt. */
|
||||||
|
grub_outb (0, port->port + UART_IER);
|
||||||
|
|
||||||
|
/* Set DLAB. */
|
||||||
|
grub_outb (UART_DLAB, port->port + UART_LCR);
|
||||||
|
|
||||||
|
/* Set the baud rate. */
|
||||||
|
grub_outb (divisor & 0xFF, port->port + UART_DLL);
|
||||||
|
grub_outb (divisor >> 8, port->port + UART_DLH);
|
||||||
|
|
||||||
|
/* Set the line status. */
|
||||||
|
status |= (port->parity | port->word_len | port->stop_bits);
|
||||||
|
grub_outb (status, port->port + UART_LCR);
|
||||||
|
|
||||||
|
/* In Yeeloong serial port has only 3 wires. */
|
||||||
|
#ifndef GRUB_MACHINE_MIPS_YEELOONG
|
||||||
|
/* Enable the FIFO. */
|
||||||
|
grub_outb (UART_ENABLE_FIFO_TRIGGER1, port->port + UART_FCR);
|
||||||
|
|
||||||
|
/* Turn on DTR and RTS. */
|
||||||
|
grub_outb (UART_ENABLE_DTRRTS, port->port + UART_MCR);
|
||||||
|
#else
|
||||||
|
/* Enable the FIFO. */
|
||||||
|
grub_outb (UART_ENABLE_FIFO_TRIGGER14, port->port + UART_FCR);
|
||||||
|
|
||||||
|
/* Turn on DTR, RTS, and OUT2. */
|
||||||
|
grub_outb (UART_ENABLE_DTRRTS | UART_ENABLE_OUT2, port->port + UART_MCR);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* Drain the input buffer. */
|
||||||
|
while (serial_hw_fetch (port) != -1);
|
||||||
|
|
||||||
|
/* FIXME: should check if the serial terminal was found. */
|
||||||
|
|
||||||
|
return GRUB_ERR_NONE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct grub_serial_driver grub_ns8250_driver =
|
||||||
|
{
|
||||||
|
.configure = serial_hw_configure,
|
||||||
|
.fetch = serial_hw_fetch,
|
||||||
|
.put = serial_hw_put
|
||||||
|
};
|
||||||
|
|
||||||
|
static char com_names[GRUB_SERIAL_PORT_NUM][20];
|
||||||
|
static struct grub_serial_port com_ports[GRUB_SERIAL_PORT_NUM];
|
||||||
|
|
||||||
|
void
|
||||||
|
grub_ns8250_init (void)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
for (i = 0; i < GRUB_SERIAL_PORT_NUM; i++)
|
||||||
|
//if (serial_hw_io_addr[i])
|
||||||
|
{
|
||||||
|
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];
|
||||||
|
grub_serial_register (&com_ports[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
char *
|
||||||
|
grub_serial_ns8250_add_port (grub_port_t port)
|
||||||
|
{
|
||||||
|
struct grub_serial_port *p;
|
||||||
|
int i;
|
||||||
|
for (i = 0; i < GRUB_SERIAL_PORT_NUM; i++)
|
||||||
|
if (com_ports[i].port == port)
|
||||||
|
return com_names[i];
|
||||||
|
p = grub_malloc (sizeof (*p));
|
||||||
|
if (!p)
|
||||||
|
return NULL;
|
||||||
|
p->name = grub_xasprintf ("port%lx", (unsigned long) port);
|
||||||
|
if (!p->name)
|
||||||
|
{
|
||||||
|
grub_free (p);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
p->driver = &grub_ns8250_driver;
|
||||||
|
grub_serial_fill_defaults (p);
|
||||||
|
p->port = port;
|
||||||
|
grub_serial_register (p);
|
||||||
|
|
||||||
|
return p->name;
|
||||||
|
}
|
439
term/serial.c
439
term/serial.c
|
@ -1,6 +1,6 @@
|
||||||
/*
|
/*
|
||||||
* GRUB -- GRand Unified Bootloader
|
* 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
|
* GRUB is free software: you can redistribute it and/or modify
|
||||||
* it under the terms of the GNU General Public License as published by
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
@ -16,7 +16,6 @@
|
||||||
* along with GRUB. If not, see <http://www.gnu.org/licenses/>.
|
* along with GRUB. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <grub/machine/memory.h>
|
|
||||||
#include <grub/serial.h>
|
#include <grub/serial.h>
|
||||||
#include <grub/term.h>
|
#include <grub/term.h>
|
||||||
#include <grub/types.h>
|
#include <grub/types.h>
|
||||||
|
@ -26,8 +25,9 @@
|
||||||
#include <grub/cpu/io.h>
|
#include <grub/cpu/io.h>
|
||||||
#include <grub/extcmd.h>
|
#include <grub/extcmd.h>
|
||||||
#include <grub/i18n.h>
|
#include <grub/i18n.h>
|
||||||
|
#include <grub/list.h>
|
||||||
|
|
||||||
static unsigned int registered = 0;
|
#define FOR_SERIAL_PORTS(var) FOR_LIST_ELEMENTS((var), (grub_serial_ports))
|
||||||
|
|
||||||
/* Argument options. */
|
/* Argument options. */
|
||||||
static const struct grub_arg_option options[] =
|
static const struct grub_arg_option options[] =
|
||||||
|
@ -41,154 +41,19 @@ static const struct grub_arg_option options[] =
|
||||||
{0, 0, 0, 0, 0, 0}
|
{0, 0, 0, 0, 0, 0}
|
||||||
};
|
};
|
||||||
|
|
||||||
/* Serial port settings. */
|
struct grub_serial_port *grub_serial_ports;
|
||||||
struct serial_port
|
|
||||||
|
struct grub_serial_output_state
|
||||||
{
|
{
|
||||||
grub_port_t port;
|
struct grub_terminfo_output_state tinfo;
|
||||||
unsigned short divisor;
|
struct grub_serial_port *port;
|
||||||
unsigned short word_len;
|
|
||||||
unsigned int parity;
|
|
||||||
unsigned short stop_bits;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/* Serial port settings. */
|
struct grub_serial_input_state
|
||||||
static struct serial_port serial_settings;
|
|
||||||
|
|
||||||
#ifdef GRUB_MACHINE_PCBIOS
|
|
||||||
static const unsigned short *serial_hw_io_addr = (const unsigned short *) GRUB_MEMORY_MACHINE_BIOS_DATA_AREA_ADDR;
|
|
||||||
#define GRUB_SERIAL_PORT_NUM 4
|
|
||||||
#else
|
|
||||||
#include <grub/machine/serial.h>
|
|
||||||
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
|
|
||||||
|
|
||||||
/* Return the port number for the UNITth serial device. */
|
|
||||||
static inline grub_port_t
|
|
||||||
serial_hw_get_port (const unsigned int unit)
|
|
||||||
{
|
{
|
||||||
if (unit < GRUB_SERIAL_PORT_NUM)
|
struct grub_terminfo_input_state tinfo;
|
||||||
return serial_hw_io_addr[unit];
|
struct grub_serial_port *port;
|
||||||
else
|
};
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Fetch a key. */
|
|
||||||
static int
|
|
||||||
serial_hw_fetch (void)
|
|
||||||
{
|
|
||||||
if (grub_inb (serial_settings.port + UART_LSR) & UART_DATA_READY)
|
|
||||||
return grub_inb (serial_settings.port + UART_RX);
|
|
||||||
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Put a character. */
|
|
||||||
static void
|
|
||||||
serial_hw_put (const int c)
|
|
||||||
{
|
|
||||||
unsigned int timeout = 100000;
|
|
||||||
|
|
||||||
/* Wait until the transmitter holding register is empty. */
|
|
||||||
while ((grub_inb (serial_settings.port + UART_LSR) & UART_EMPTY_TRANSMITTER) == 0)
|
|
||||||
{
|
|
||||||
if (--timeout == 0)
|
|
||||||
/* There is something wrong. But what can I do? */
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
grub_outb (c, serial_settings.port + UART_TX);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Convert speed to divisor. */
|
|
||||||
static unsigned short
|
|
||||||
serial_get_divisor (unsigned int speed)
|
|
||||||
{
|
|
||||||
unsigned int i;
|
|
||||||
|
|
||||||
/* The structure for speed vs. divisor. */
|
|
||||||
struct divisor
|
|
||||||
{
|
|
||||||
unsigned int speed;
|
|
||||||
unsigned short div;
|
|
||||||
};
|
|
||||||
|
|
||||||
/* The table which lists common configurations. */
|
|
||||||
/* 1843200 / (speed * 16) */
|
|
||||||
static struct divisor divisor_tab[] =
|
|
||||||
{
|
|
||||||
{ 2400, 0x0030 },
|
|
||||||
{ 4800, 0x0018 },
|
|
||||||
{ 9600, 0x000C },
|
|
||||||
{ 19200, 0x0006 },
|
|
||||||
{ 38400, 0x0003 },
|
|
||||||
{ 57600, 0x0002 },
|
|
||||||
{ 115200, 0x0001 }
|
|
||||||
};
|
|
||||||
|
|
||||||
/* Set the baud rate. */
|
|
||||||
for (i = 0; i < sizeof (divisor_tab) / sizeof (divisor_tab[0]); i++)
|
|
||||||
if (divisor_tab[i].speed == speed)
|
|
||||||
/* UART in Yeeloong runs twice the usual rate. */
|
|
||||||
#ifdef GRUB_MACHINE_MIPS_YEELOONG
|
|
||||||
return 2 * divisor_tab[i].div;
|
|
||||||
#else
|
|
||||||
return divisor_tab[i].div;
|
|
||||||
#endif
|
|
||||||
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_init (void)
|
|
||||||
{
|
|
||||||
unsigned char status = 0;
|
|
||||||
|
|
||||||
/* Turn off the interrupt. */
|
|
||||||
grub_outb (0, serial_settings.port + UART_IER);
|
|
||||||
|
|
||||||
/* Set DLAB. */
|
|
||||||
grub_outb (UART_DLAB, serial_settings.port + UART_LCR);
|
|
||||||
|
|
||||||
/* Set the baud rate. */
|
|
||||||
grub_outb (serial_settings.divisor & 0xFF, serial_settings.port + UART_DLL);
|
|
||||||
grub_outb (serial_settings.divisor >> 8, serial_settings.port + UART_DLH);
|
|
||||||
|
|
||||||
/* Set the line status. */
|
|
||||||
status |= (serial_settings.parity
|
|
||||||
| serial_settings.word_len
|
|
||||||
| serial_settings.stop_bits);
|
|
||||||
grub_outb (status, serial_settings.port + UART_LCR);
|
|
||||||
|
|
||||||
/* In Yeeloong serial port has only 3 wires. */
|
|
||||||
#ifndef GRUB_MACHINE_MIPS_YEELOONG
|
|
||||||
/* Enable the FIFO. */
|
|
||||||
grub_outb (UART_ENABLE_FIFO_TRIGGER1, serial_settings.port + UART_FCR);
|
|
||||||
|
|
||||||
/* Turn on DTR and RTS. */
|
|
||||||
grub_outb (UART_ENABLE_DTRRTS, serial_settings.port + UART_MCR);
|
|
||||||
#else
|
|
||||||
/* Enable the FIFO. */
|
|
||||||
grub_outb (UART_ENABLE_FIFO_TRIGGER14, serial_settings.port + UART_FCR);
|
|
||||||
|
|
||||||
/* Turn on DTR, RTS, and OUT2. */
|
|
||||||
grub_outb (UART_ENABLE_DTRRTS | UART_ENABLE_OUT2,
|
|
||||||
serial_settings.port + UART_MCR);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/* Drain the input buffer. */
|
|
||||||
while (serial_hw_fetch () != -1);
|
|
||||||
|
|
||||||
/* FIXME: should check if the serial terminal was found. */
|
|
||||||
|
|
||||||
return GRUB_ERR_NONE;
|
|
||||||
}
|
|
||||||
|
|
||||||
static grub_uint16_t
|
static grub_uint16_t
|
||||||
grub_serial_getwh (struct grub_term_output *term __attribute__ ((unused)))
|
grub_serial_getwh (struct grub_term_output *term __attribute__ ((unused)))
|
||||||
|
@ -198,16 +63,38 @@ grub_serial_getwh (struct grub_term_output *term __attribute__ ((unused)))
|
||||||
return (TEXT_WIDTH << 8) | TEXT_HEIGHT;
|
return (TEXT_WIDTH << 8) | TEXT_HEIGHT;
|
||||||
}
|
}
|
||||||
|
|
||||||
struct grub_terminfo_input_state grub_serial_terminfo_input =
|
static void
|
||||||
|
serial_put (grub_term_output_t term, const int c)
|
||||||
|
{
|
||||||
|
struct grub_serial_output_state *data = term->data;
|
||||||
|
data->port->driver->put (data->port, c);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
serial_fetch (grub_term_input_t term)
|
||||||
|
{
|
||||||
|
struct grub_serial_input_state *data = term->data;
|
||||||
|
return data->port->driver->fetch (data->port);
|
||||||
|
}
|
||||||
|
|
||||||
|
struct grub_serial_input_state grub_serial_terminfo_input =
|
||||||
{
|
{
|
||||||
.readkey = serial_hw_fetch
|
.tinfo =
|
||||||
|
{
|
||||||
|
.readkey = serial_fetch
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
struct grub_terminfo_output_state grub_serial_terminfo_output =
|
struct grub_serial_output_state grub_serial_terminfo_output =
|
||||||
{
|
{
|
||||||
.put = serial_hw_put
|
.tinfo =
|
||||||
|
{
|
||||||
|
.put = serial_put
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
int registered = 0;
|
||||||
|
|
||||||
static struct grub_term_input grub_serial_term_input =
|
static struct grub_term_input grub_serial_term_input =
|
||||||
{
|
{
|
||||||
.name = "serial",
|
.name = "serial",
|
||||||
|
@ -235,122 +122,200 @@ static struct grub_term_output grub_serial_term_output =
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
static struct grub_serial_port *
|
||||||
|
grub_serial_find (char *name)
|
||||||
|
{
|
||||||
|
struct grub_serial_port *port;
|
||||||
|
|
||||||
|
FOR_SERIAL_PORTS (port)
|
||||||
|
if (grub_strcmp (port->name, name) == 0)
|
||||||
|
break;
|
||||||
|
|
||||||
|
if (!port && grub_memcmp (name, "port", sizeof ("port") - 1) == 0
|
||||||
|
&& grub_isdigit (name [sizeof ("port") - 1]))
|
||||||
|
{
|
||||||
|
name = grub_serial_ns8250_add_port (grub_strtoul (&name[sizeof ("port") - 1],
|
||||||
|
0, 16));
|
||||||
|
if (!name)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
FOR_SERIAL_PORTS (port)
|
||||||
|
if (grub_strcmp (port->name, name) == 0)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return port;
|
||||||
|
}
|
||||||
|
|
||||||
static grub_err_t
|
static grub_err_t
|
||||||
grub_cmd_serial (grub_extcmd_t cmd,
|
grub_cmd_serial (grub_extcmd_t cmd, int argc, char **args)
|
||||||
int argc __attribute__ ((unused)),
|
|
||||||
char **args __attribute__ ((unused)))
|
|
||||||
{
|
{
|
||||||
struct grub_arg_list *state = cmd->state;
|
struct grub_arg_list *state = cmd->state;
|
||||||
struct serial_port backup_settings = serial_settings;
|
char pname[40];
|
||||||
grub_err_t hwiniterr;
|
char *name = NULL;
|
||||||
|
struct grub_serial_port *port;
|
||||||
|
signed speed = -1;
|
||||||
|
signed short word_len = -1;
|
||||||
|
signed int parity = -1;
|
||||||
|
signed short stop_bits = -1;
|
||||||
|
grub_err_t err;
|
||||||
|
|
||||||
if (state[0].set)
|
if (state[0].set)
|
||||||
{
|
{
|
||||||
unsigned int unit;
|
grub_snprintf (pname, sizeof (pname), "com%ld",
|
||||||
|
grub_strtoul (state[0].arg, 0, 0));
|
||||||
unit = grub_strtoul (state[0].arg, 0, 0);
|
name = pname;
|
||||||
serial_settings.port = serial_hw_get_port (unit);
|
|
||||||
if (!serial_settings.port)
|
|
||||||
return grub_error (GRUB_ERR_BAD_ARGUMENT, "bad unit number");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (state[1].set)
|
if (state[1].set)
|
||||||
serial_settings.port = (grub_port_t) grub_strtoul (state[1].arg, 0, 0);
|
{
|
||||||
|
grub_snprintf (pname, sizeof (pname), "port%lx",
|
||||||
|
grub_strtoul (state[1].arg, 0, 0));
|
||||||
|
name = pname;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (argc >= 1)
|
||||||
|
name = args[0];
|
||||||
|
|
||||||
|
if (!name)
|
||||||
|
name = "com0";
|
||||||
|
|
||||||
|
port = grub_serial_find (name);
|
||||||
|
if (!port)
|
||||||
|
return grub_error (GRUB_ERR_BAD_ARGUMENT, "unknown serial port");
|
||||||
|
|
||||||
|
speed = port->speed;
|
||||||
|
word_len = port->word_len;
|
||||||
|
parity = port->parity;
|
||||||
|
stop_bits = port->stop_bits;
|
||||||
|
|
||||||
if (state[2].set)
|
if (state[2].set)
|
||||||
{
|
speed = grub_strtoul (state[2].arg, 0, 0);
|
||||||
unsigned long speed;
|
|
||||||
|
|
||||||
speed = grub_strtoul (state[2].arg, 0, 0);
|
|
||||||
serial_settings.divisor = serial_get_divisor ((unsigned int) speed);
|
|
||||||
if (serial_settings.divisor == 0)
|
|
||||||
{
|
|
||||||
serial_settings = backup_settings;
|
|
||||||
return grub_error (GRUB_ERR_BAD_ARGUMENT, "bad speed");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (state[3].set)
|
if (state[3].set)
|
||||||
{
|
{
|
||||||
if (! grub_strcmp (state[3].arg, "5"))
|
if (! grub_strcmp (state[3].arg, "5"))
|
||||||
serial_settings.word_len = UART_5BITS_WORD;
|
word_len = UART_5BITS_WORD;
|
||||||
else if (! grub_strcmp (state[3].arg, "6"))
|
else if (! grub_strcmp (state[3].arg, "6"))
|
||||||
serial_settings.word_len = UART_6BITS_WORD;
|
word_len = UART_6BITS_WORD;
|
||||||
else if (! grub_strcmp (state[3].arg, "7"))
|
else if (! grub_strcmp (state[3].arg, "7"))
|
||||||
serial_settings.word_len = UART_7BITS_WORD;
|
word_len = UART_7BITS_WORD;
|
||||||
else if (! grub_strcmp (state[3].arg, "8"))
|
else if (! grub_strcmp (state[3].arg, "8"))
|
||||||
serial_settings.word_len = UART_8BITS_WORD;
|
word_len = UART_8BITS_WORD;
|
||||||
else
|
else
|
||||||
{
|
return grub_error (GRUB_ERR_BAD_ARGUMENT, "bad word length");
|
||||||
serial_settings = backup_settings;
|
|
||||||
return grub_error (GRUB_ERR_BAD_ARGUMENT, "bad word length");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (state[4].set)
|
if (state[4].set)
|
||||||
{
|
{
|
||||||
if (! grub_strcmp (state[4].arg, "no"))
|
if (! grub_strcmp (state[4].arg, "no"))
|
||||||
serial_settings.parity = UART_NO_PARITY;
|
parity = UART_NO_PARITY;
|
||||||
else if (! grub_strcmp (state[4].arg, "odd"))
|
else if (! grub_strcmp (state[4].arg, "odd"))
|
||||||
serial_settings.parity = UART_ODD_PARITY;
|
parity = UART_ODD_PARITY;
|
||||||
else if (! grub_strcmp (state[4].arg, "even"))
|
else if (! grub_strcmp (state[4].arg, "even"))
|
||||||
serial_settings.parity = UART_EVEN_PARITY;
|
parity = UART_EVEN_PARITY;
|
||||||
else
|
else
|
||||||
{
|
return grub_error (GRUB_ERR_BAD_ARGUMENT, "bad parity");
|
||||||
serial_settings = backup_settings;
|
|
||||||
return grub_error (GRUB_ERR_BAD_ARGUMENT, "bad parity");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (state[5].set)
|
if (state[5].set)
|
||||||
{
|
{
|
||||||
if (! grub_strcmp (state[5].arg, "1"))
|
if (! grub_strcmp (state[5].arg, "1"))
|
||||||
serial_settings.stop_bits = UART_1_STOP_BIT;
|
stop_bits = UART_1_STOP_BIT;
|
||||||
else if (! grub_strcmp (state[5].arg, "2"))
|
else if (! grub_strcmp (state[5].arg, "2"))
|
||||||
serial_settings.stop_bits = UART_2_STOP_BITS;
|
stop_bits = UART_2_STOP_BITS;
|
||||||
else
|
else
|
||||||
{
|
return grub_error (GRUB_ERR_BAD_ARGUMENT, "bad number of stop bits");
|
||||||
serial_settings = backup_settings;
|
|
||||||
return grub_error (GRUB_ERR_BAD_ARGUMENT, "bad number of stop bits");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Initialize with new settings. */
|
/* Initialize with new settings. */
|
||||||
hwiniterr = serial_hw_init ();
|
err = port->driver->configure (port, speed, word_len, parity, stop_bits);
|
||||||
|
if (err)
|
||||||
if (hwiniterr == GRUB_ERR_NONE)
|
return err;
|
||||||
|
if (!registered)
|
||||||
{
|
{
|
||||||
/* Register terminal if not yet registered. */
|
grub_term_register_input ("serial", &grub_serial_term_input);
|
||||||
if (registered == 0)
|
grub_term_register_output ("serial", &grub_serial_term_output);
|
||||||
{
|
|
||||||
grub_term_register_input ("serial", &grub_serial_term_input);
|
|
||||||
grub_term_register_output ("serial", &grub_serial_term_output);
|
|
||||||
grub_terminfo_output_register (&grub_serial_term_output, "vt100");
|
|
||||||
registered = 1;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
else
|
grub_serial_terminfo_output.port = port;
|
||||||
{
|
grub_serial_terminfo_input.port = port;
|
||||||
/* Initialization with new settings failed. */
|
registered = 1;
|
||||||
if (registered == 1)
|
return GRUB_ERR_NONE;
|
||||||
{
|
|
||||||
/* If the terminal is registered, attempt to restore previous
|
|
||||||
settings. */
|
|
||||||
serial_settings = backup_settings;
|
|
||||||
if (serial_hw_init () != GRUB_ERR_NONE)
|
|
||||||
{
|
|
||||||
/* If unable to restore settings, unregister terminal. */
|
|
||||||
grub_term_unregister_input (&grub_serial_term_input);
|
|
||||||
grub_term_unregister_output (&grub_serial_term_output);
|
|
||||||
grub_terminfo_output_unregister (&grub_serial_term_output);
|
|
||||||
registered = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return hwiniterr;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
grub_err_t
|
||||||
|
grub_serial_register (struct grub_serial_port *port)
|
||||||
|
{
|
||||||
|
struct grub_term_input *in;
|
||||||
|
struct grub_term_output *out;
|
||||||
|
struct grub_serial_input_state *indata;
|
||||||
|
struct grub_serial_output_state *outdata;
|
||||||
|
|
||||||
|
in = grub_malloc (sizeof (*in));
|
||||||
|
if (!in)
|
||||||
|
return grub_errno;
|
||||||
|
|
||||||
|
indata = grub_malloc (sizeof (*indata));
|
||||||
|
if (!indata)
|
||||||
|
{
|
||||||
|
grub_free (in);
|
||||||
|
return grub_errno;
|
||||||
|
}
|
||||||
|
|
||||||
|
grub_memcpy (in, &grub_serial_term_input, sizeof (*in));
|
||||||
|
in->data = indata;
|
||||||
|
in->name = grub_xasprintf ("serial_%s", port->name);
|
||||||
|
grub_memcpy (indata, &grub_serial_terminfo_input, sizeof (*indata));
|
||||||
|
|
||||||
|
if (!in->name)
|
||||||
|
{
|
||||||
|
grub_free (in);
|
||||||
|
grub_free (indata);
|
||||||
|
return grub_errno;
|
||||||
|
}
|
||||||
|
|
||||||
|
out = grub_malloc (sizeof (*out));
|
||||||
|
if (!out)
|
||||||
|
{
|
||||||
|
grub_free (in);
|
||||||
|
grub_free (indata);
|
||||||
|
grub_free ((char *) in->name);
|
||||||
|
return grub_errno;
|
||||||
|
}
|
||||||
|
|
||||||
|
outdata = grub_malloc (sizeof (*outdata));
|
||||||
|
if (!outdata)
|
||||||
|
{
|
||||||
|
grub_free (in);
|
||||||
|
grub_free (indata);
|
||||||
|
grub_free ((char *) in->name);
|
||||||
|
grub_free (out);
|
||||||
|
return grub_errno;
|
||||||
|
}
|
||||||
|
|
||||||
|
grub_memcpy (out, &grub_serial_term_output, sizeof (*out));
|
||||||
|
out->data = outdata;
|
||||||
|
out->name = in->name;
|
||||||
|
grub_memcpy (outdata, &grub_serial_terminfo_output, sizeof (*outdata));
|
||||||
|
|
||||||
|
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);
|
||||||
|
grub_terminfo_output_register (out, "vt100");
|
||||||
|
|
||||||
|
return GRUB_ERR_NONE;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
grub_serial_unregister (struct grub_serial_port *port)
|
||||||
|
{
|
||||||
|
grub_list_remove (GRUB_AS_LIST_P (&grub_serial_ports), GRUB_AS_LIST (port));
|
||||||
|
/* FIXME */
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
static grub_extcmd_t cmd;
|
static grub_extcmd_t cmd;
|
||||||
|
|
||||||
GRUB_MOD_INIT(serial)
|
GRUB_MOD_INIT(serial)
|
||||||
|
@ -360,21 +325,13 @@ GRUB_MOD_INIT(serial)
|
||||||
N_("[OPTIONS...]"),
|
N_("[OPTIONS...]"),
|
||||||
N_("Configure serial port."), options);
|
N_("Configure serial port."), options);
|
||||||
|
|
||||||
/* Set default settings. */
|
grub_ns8250_init ();
|
||||||
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
|
#ifdef GRUB_MACHINE_MIPS_YEELOONG
|
||||||
{
|
{
|
||||||
grub_err_t hwiniterr;
|
grub_err_t hwiniterr;
|
||||||
hwiniterr = serial_hw_init ();
|
hwiniterr = grub_ns8250_driver.init ("com0", &serial_settings);
|
||||||
|
serial_settings.driver = &grub_ns8250_driver;
|
||||||
|
|
||||||
if (hwiniterr == GRUB_ERR_NONE)
|
if (hwiniterr == GRUB_ERR_NONE)
|
||||||
{
|
{
|
||||||
|
@ -389,11 +346,7 @@ GRUB_MOD_INIT(serial)
|
||||||
|
|
||||||
GRUB_MOD_FINI(serial)
|
GRUB_MOD_FINI(serial)
|
||||||
{
|
{
|
||||||
|
while (grub_serial_ports)
|
||||||
|
grub_serial_unregister (grub_serial_ports);
|
||||||
grub_unregister_extcmd (cmd);
|
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);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -195,7 +195,7 @@ putstr (struct grub_term_output *term, const char *str)
|
||||||
struct grub_terminfo_output_state *data
|
struct grub_terminfo_output_state *data
|
||||||
= (struct grub_terminfo_output_state *) term->data;
|
= (struct grub_terminfo_output_state *) term->data;
|
||||||
while (*str)
|
while (*str)
|
||||||
data->put (*str++);
|
data->put (term, *str++);
|
||||||
}
|
}
|
||||||
|
|
||||||
grub_uint16_t
|
grub_uint16_t
|
||||||
|
@ -225,7 +225,7 @@ grub_terminfo_gotoxy (struct grub_term_output *term,
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
if ((y == data->ypos) && (x == data->xpos - 1))
|
if ((y == data->ypos) && (x == data->xpos - 1))
|
||||||
data->put ('\b');
|
data->put (term, '\b');
|
||||||
}
|
}
|
||||||
|
|
||||||
data->xpos = x;
|
data->xpos = x;
|
||||||
|
@ -348,20 +348,21 @@ grub_terminfo_putchar (struct grub_term_output *term,
|
||||||
data->xpos = 0;
|
data->xpos = 0;
|
||||||
if (data->ypos < grub_term_height (term) - 1)
|
if (data->ypos < grub_term_height (term) - 1)
|
||||||
data->ypos++;
|
data->ypos++;
|
||||||
data->put ('\r');
|
data->put (term, '\r');
|
||||||
data->put ('\n');
|
data->put (term, '\n');
|
||||||
}
|
}
|
||||||
data->xpos += c->estimated_width;
|
data->xpos += c->estimated_width;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
data->put (c->base);
|
data->put (term, c->base);
|
||||||
}
|
}
|
||||||
|
|
||||||
#define ANSI_C0 0x9b
|
#define ANSI_C0 0x9b
|
||||||
|
|
||||||
static void
|
static void
|
||||||
grub_terminfo_readkey (int *keys, int *len, int (*readkey) (void))
|
grub_terminfo_readkey (struct grub_term_input *term, int *keys, int *len,
|
||||||
|
int (*readkey) (struct grub_term_input *term))
|
||||||
{
|
{
|
||||||
int c;
|
int c;
|
||||||
|
|
||||||
|
@ -371,7 +372,7 @@ grub_terminfo_readkey (int *keys, int *len, int (*readkey) (void))
|
||||||
/* On 9600 we have to wait up to 12 milliseconds. */ \
|
/* On 9600 we have to wait up to 12 milliseconds. */ \
|
||||||
start = grub_get_time_ms (); \
|
start = grub_get_time_ms (); \
|
||||||
do \
|
do \
|
||||||
c = readkey (); \
|
c = readkey (term); \
|
||||||
while (c == -1 && grub_get_time_ms () - start < 12); \
|
while (c == -1 && grub_get_time_ms () - start < 12); \
|
||||||
if (c == -1) \
|
if (c == -1) \
|
||||||
return; \
|
return; \
|
||||||
|
@ -380,7 +381,7 @@ grub_terminfo_readkey (int *keys, int *len, int (*readkey) (void))
|
||||||
(*len)++; \
|
(*len)++; \
|
||||||
}
|
}
|
||||||
|
|
||||||
c = readkey ();
|
c = readkey (term);
|
||||||
if (c < 0)
|
if (c < 0)
|
||||||
{
|
{
|
||||||
*len = 0;
|
*len = 0;
|
||||||
|
@ -475,7 +476,8 @@ grub_terminfo_checkkey (struct grub_term_input *termi)
|
||||||
if (data->npending)
|
if (data->npending)
|
||||||
return data->input_buf[0];
|
return data->input_buf[0];
|
||||||
|
|
||||||
grub_terminfo_readkey (data->input_buf, &data->npending, data->readkey);
|
grub_terminfo_readkey (termi, data->input_buf,
|
||||||
|
&data->npending, data->readkey);
|
||||||
|
|
||||||
if (data->npending)
|
if (data->npending)
|
||||||
return data->input_buf[0];
|
return data->input_buf[0];
|
||||||
|
@ -491,7 +493,8 @@ grub_terminfo_getkey (struct grub_term_input *termi)
|
||||||
= (struct grub_terminfo_input_state *) (termi->data);
|
= (struct grub_terminfo_input_state *) (termi->data);
|
||||||
int ret;
|
int ret;
|
||||||
while (! data->npending)
|
while (! data->npending)
|
||||||
grub_terminfo_readkey (data->input_buf, &data->npending, data->readkey);
|
grub_terminfo_readkey (termi, data->input_buf, &data->npending,
|
||||||
|
data->readkey);
|
||||||
|
|
||||||
ret = data->input_buf[0];
|
ret = data->input_buf[0];
|
||||||
data->npending--;
|
data->npending--;
|
||||||
|
|
165
term/usbserial.c
Normal file
165
term/usbserial.c
Normal file
|
@ -0,0 +1,165 @@
|
||||||
|
/*
|
||||||
|
* GRUB -- GRand Unified Bootloader
|
||||||
|
* Copyright (C) 2000,2001,2002,2003,2004,2005,2007,2008,2009 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
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* GRUB is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* 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;
|
||||||
|
|
||||||
|
/* Fetch a key. */
|
||||||
|
static int
|
||||||
|
serial_hw_fetch (void)
|
||||||
|
{
|
||||||
|
/* FIXME */
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Put a character. */
|
||||||
|
static void
|
||||||
|
usbserial_hw_put (struct usbserial_port *port, const int c)
|
||||||
|
{
|
||||||
|
char cc = c;
|
||||||
|
grub_usb_bulk_write (port->usbdev, port->out_endp, 1, &cc);
|
||||||
|
}
|
||||||
|
|
||||||
|
static grub_uint16_t
|
||||||
|
grub_serial_getwh (struct grub_term_output *term __attribute__ ((unused)))
|
||||||
|
{
|
||||||
|
const grub_uint8_t TEXT_WIDTH = 80;
|
||||||
|
const grub_uint8_t TEXT_HEIGHT = 24;
|
||||||
|
return (TEXT_WIDTH << 8) | TEXT_HEIGHT;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct grub_terminfo_input_state grub_serial_terminfo_input =
|
||||||
|
{
|
||||||
|
.readkey = serial_hw_fetch
|
||||||
|
};
|
||||||
|
|
||||||
|
struct grub_terminfo_output_state grub_serial_terminfo_output =
|
||||||
|
{
|
||||||
|
.put = usbserial_hw_put
|
||||||
|
};
|
||||||
|
|
||||||
|
static struct grub_term_input grub_serial_term_input =
|
||||||
|
{
|
||||||
|
.name = "usbserial",
|
||||||
|
.init = grub_terminfo_input_init,
|
||||||
|
.checkkey = grub_terminfo_checkkey,
|
||||||
|
.getkey = grub_terminfo_getkey,
|
||||||
|
.data = &grub_serial_terminfo_input
|
||||||
|
};
|
||||||
|
|
||||||
|
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. */
|
||||||
|
{
|
||||||
|
grub_term_unregister_input (&grub_serial_term_input);
|
||||||
|
grub_term_unregister_output (&grub_serial_term_output);
|
||||||
|
grub_terminfo_output_unregister (&grub_serial_term_output);
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in a new issue