vaseboot/VasEBoot-core/term/serial.c

464 lines
13 KiB
C
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/*
* VasEBoot -- GRand Unified Bootloader
* Copyright (C) 2000,2001,2002,2003,2004,2005,2007,2008,2009,2010 Free Software Foundation, Inc.
*
* VasEBoot 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.
*
* VasEBoot 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 VasEBoot. If not, see <http://www.gnu.org/licenses/>.
*/
#include <VasEBoot/serial.h>
#include <VasEBoot/term.h>
#include <VasEBoot/types.h>
#include <VasEBoot/dl.h>
#include <VasEBoot/misc.h>
#include <VasEBoot/terminfo.h>
#if !defined (VasEBoot_MACHINE_EMU) && (defined(__mips__) || defined (__i386__) || defined (__x86_64__))
#include <VasEBoot/cpu/io.h>
#endif
#include <VasEBoot/extcmd.h>
#include <VasEBoot/i18n.h>
#include <VasEBoot/list.h>
#ifdef VasEBoot_MACHINE_MIPS_LOONGSON
#include <VasEBoot/machine/kernel.h>
#endif
#ifdef VasEBoot_MACHINE_IEEE1275
#include <VasEBoot/ieee1275/console.h>
#endif
VasEBoot_MOD_LICENSE ("GPLv3+");
#define FOR_SERIAL_PORTS(var) FOR_LIST_ELEMENTS((var), (VasEBoot_serial_ports))
enum
{
OPTION_UNIT,
OPTION_PORT,
OPTION_SPEED,
OPTION_WORD,
OPTION_PARITY,
OPTION_STOP,
OPTION_BASE_CLOCK,
OPTION_RTSCTS
};
/* Argument options. */
static const struct VasEBoot_arg_option options[] =
{
{"unit", 'u', 0, N_("Set the serial unit."), 0, ARG_TYPE_INT},
{"port", 'p', 0, N_("Set the serial port address."), 0, ARG_TYPE_STRING},
{"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},
{"base-clock", 'b', 0, N_("Set the base frequency."), 0, ARG_TYPE_STRING},
{"rtscts", 'f', 0, N_("Enable/disable RTS/CTS."), "on|off", ARG_TYPE_STRING},
{0, 0, 0, 0, 0, 0}
};
static struct VasEBoot_serial_port *VasEBoot_serial_ports;
struct VasEBoot_serial_output_state
{
struct VasEBoot_terminfo_output_state tinfo;
struct VasEBoot_serial_port *port;
};
struct VasEBoot_serial_input_state
{
struct VasEBoot_terminfo_input_state tinfo;
struct VasEBoot_serial_port *port;
};
static void
serial_put (VasEBoot_term_output_t term, const int c)
{
struct VasEBoot_serial_output_state *data = term->data;
data->port->driver->put (data->port, c);
}
static int
serial_fetch (VasEBoot_term_input_t term)
{
struct VasEBoot_serial_input_state *data = term->data;
return data->port->driver->fetch (data->port);
}
static const struct VasEBoot_serial_input_state VasEBoot_serial_terminfo_input_template =
{
.tinfo =
{
.readkey = serial_fetch
}
};
static const struct VasEBoot_serial_output_state VasEBoot_serial_terminfo_output_template =
{
.tinfo =
{
.put = serial_put,
.size = { 80, 24 }
}
};
static struct VasEBoot_serial_input_state VasEBoot_serial_terminfo_input;
static struct VasEBoot_serial_output_state VasEBoot_serial_terminfo_output;
static int registered = 0;
static struct VasEBoot_term_input VasEBoot_serial_term_input =
{
.name = "serial",
.init = VasEBoot_terminfo_input_init,
.getkey = VasEBoot_terminfo_getkey,
.data = &VasEBoot_serial_terminfo_input
};
static struct VasEBoot_term_output VasEBoot_serial_term_output =
{
.name = "serial",
.init = VasEBoot_terminfo_output_init,
.putchar = VasEBoot_terminfo_putchar,
.getwh = VasEBoot_terminfo_getwh,
.getxy = VasEBoot_terminfo_getxy,
.gotoxy = VasEBoot_terminfo_gotoxy,
.cls = VasEBoot_terminfo_cls,
.setcolorstate = VasEBoot_terminfo_setcolorstate,
.setcursor = VasEBoot_terminfo_setcursor,
.flags = VasEBoot_TERM_CODE_TYPE_ASCII,
.data = &VasEBoot_serial_terminfo_output,
.progress_update_divisor = VasEBoot_PROGRESS_SLOW
};
struct VasEBoot_serial_port *
VasEBoot_serial_find (const char *name)
{
struct VasEBoot_serial_port *port;
FOR_SERIAL_PORTS (port)
if (VasEBoot_strcmp (port->name, name) == 0)
break;
#if (defined(__mips__) || defined (__i386__) || defined (__x86_64__)) && !defined(VasEBoot_MACHINE_EMU) && !defined(VasEBoot_MACHINE_ARC)
if (!port && VasEBoot_memcmp (name, "port", sizeof ("port") - 1) == 0
&& VasEBoot_isxdigit (name [sizeof ("port") - 1]))
{
name = VasEBoot_serial_ns8250_add_port (VasEBoot_strtoul (&name[sizeof ("port") - 1],
0, 16));
if (!name)
return NULL;
FOR_SERIAL_PORTS (port)
if (VasEBoot_strcmp (port->name, name) == 0)
break;
}
#endif
#ifdef VasEBoot_MACHINE_IEEE1275
if (!port && VasEBoot_memcmp (name, "ieee1275/", sizeof ("ieee1275/") - 1) == 0)
{
name = VasEBoot_ofserial_add_port (&name[sizeof ("ieee1275/") - 1]);
if (!name)
return NULL;
FOR_SERIAL_PORTS (port)
if (VasEBoot_strcmp (port->name, name) == 0)
break;
}
#endif
return port;
}
static VasEBoot_err_t
VasEBoot_cmd_serial (VasEBoot_extcmd_context_t ctxt, int argc, char **args)
{
struct VasEBoot_arg_list *state = ctxt->state;
char pname[40];
const char *name = NULL;
struct VasEBoot_serial_port *port;
struct VasEBoot_serial_config config;
VasEBoot_err_t err;
if (state[OPTION_UNIT].set)
{
VasEBoot_snprintf (pname, sizeof (pname), "com%ld",
VasEBoot_strtoul (state[0].arg, 0, 0));
name = pname;
}
if (state[OPTION_PORT].set)
{
VasEBoot_snprintf (pname, sizeof (pname), "port%lx",
VasEBoot_strtoul (state[1].arg, 0, 0));
name = pname;
}
if (argc >= 1)
name = args[0];
if (!name)
name = "com0";
port = VasEBoot_serial_find (name);
if (!port)
return VasEBoot_error (VasEBoot_ERR_BAD_ARGUMENT,
N_("serial port `%s' isn't found"),
name);
config = port->config;
if (state[OPTION_SPEED].set) {
config.speed = VasEBoot_strtoul (state[OPTION_SPEED].arg, 0, 0);
if (config.speed == 0)
return VasEBoot_error (VasEBoot_ERR_BAD_ARGUMENT,
N_("unsupported serial port parity"));
}
if (state[OPTION_WORD].set)
config.word_len = VasEBoot_strtoul (state[OPTION_WORD].arg, 0, 0);
if (state[OPTION_PARITY].set)
{
if (! VasEBoot_strcmp (state[OPTION_PARITY].arg, "no"))
config.parity = VasEBoot_SERIAL_PARITY_NONE;
else if (! VasEBoot_strcmp (state[OPTION_PARITY].arg, "odd"))
config.parity = VasEBoot_SERIAL_PARITY_ODD;
else if (! VasEBoot_strcmp (state[OPTION_PARITY].arg, "even"))
config.parity = VasEBoot_SERIAL_PARITY_EVEN;
else
return VasEBoot_error (VasEBoot_ERR_BAD_ARGUMENT,
N_("unsupported serial port parity"));
}
if (state[OPTION_RTSCTS].set)
{
if (VasEBoot_strcmp (state[OPTION_RTSCTS].arg, "on") == 0)
config.rtscts = 1;
else if (VasEBoot_strcmp (state[OPTION_RTSCTS].arg, "off") == 0)
config.rtscts = 0;
else
return VasEBoot_error (VasEBoot_ERR_BAD_ARGUMENT,
N_("unsupported serial port flow control"));
}
if (state[OPTION_STOP].set)
{
if (! VasEBoot_strcmp (state[OPTION_STOP].arg, "1"))
config.stop_bits = VasEBoot_SERIAL_STOP_BITS_1;
else if (! VasEBoot_strcmp (state[OPTION_STOP].arg, "2"))
config.stop_bits = VasEBoot_SERIAL_STOP_BITS_2;
else if (! VasEBoot_strcmp (state[OPTION_STOP].arg, "1.5"))
config.stop_bits = VasEBoot_SERIAL_STOP_BITS_1_5;
else
return VasEBoot_error (VasEBoot_ERR_BAD_ARGUMENT,
N_("unsupported serial port stop bits number"));
}
if (state[OPTION_BASE_CLOCK].set)
{
char *ptr;
config.base_clock = VasEBoot_strtoull (state[OPTION_BASE_CLOCK].arg, &ptr, 0);
if (VasEBoot_errno)
return VasEBoot_errno;
if (ptr && *ptr == 'M')
config.base_clock *= 1000000;
if (ptr && (*ptr == 'k' || *ptr == 'K'))
config.base_clock *= 1000;
}
if (config.speed == 0)
config.speed = 9600;
/* Initialize with new settings. */
err = port->driver->configure (port, &config);
if (err)
return err;
#if !defined (VasEBoot_MACHINE_EMU) && !defined(VasEBoot_MACHINE_ARC) && (defined(__mips__) || defined (__i386__) || defined (__x86_64__))
/* Compatibility kludge. */
if (port->driver == &VasEBoot_ns8250_driver)
{
if (!registered)
{
VasEBoot_terminfo_output_register (&VasEBoot_serial_term_output, "vt100");
VasEBoot_term_register_input ("serial", &VasEBoot_serial_term_input);
VasEBoot_term_register_output ("serial", &VasEBoot_serial_term_output);
}
VasEBoot_serial_terminfo_output.port = port;
VasEBoot_serial_terminfo_input.port = port;
registered = 1;
}
#endif
return VasEBoot_ERR_NONE;
}
#ifdef VasEBoot_MACHINE_MIPS_LOONGSON
const char loongson_defserial[][6] =
{
[VasEBoot_ARCH_MACHINE_YEELOONG] = "com0",
[VasEBoot_ARCH_MACHINE_FULOONG2F] = "com2",
[VasEBoot_ARCH_MACHINE_FULOONG2E] = "com1"
};
#endif
VasEBoot_err_t
VasEBoot_serial_register (struct VasEBoot_serial_port *port)
{
struct VasEBoot_term_input *in;
struct VasEBoot_term_output *out;
struct VasEBoot_serial_input_state *indata;
struct VasEBoot_serial_output_state *outdata;
in = VasEBoot_malloc (sizeof (*in));
if (!in)
return VasEBoot_errno;
indata = VasEBoot_malloc (sizeof (*indata));
if (!indata)
{
VasEBoot_free (in);
return VasEBoot_errno;
}
VasEBoot_memcpy (in, &VasEBoot_serial_term_input, sizeof (*in));
in->data = indata;
in->name = VasEBoot_xasprintf ("serial_%s", port->name);
VasEBoot_memcpy (indata, &VasEBoot_serial_terminfo_input, sizeof (*indata));
if (!in->name)
{
VasEBoot_free (in);
VasEBoot_free (indata);
return VasEBoot_errno;
}
out = VasEBoot_zalloc (sizeof (*out));
if (!out)
{
VasEBoot_free (indata);
VasEBoot_free ((char *) in->name);
VasEBoot_free (in);
return VasEBoot_errno;
}
outdata = VasEBoot_malloc (sizeof (*outdata));
if (!outdata)
{
VasEBoot_free (indata);
VasEBoot_free ((char *) in->name);
VasEBoot_free (out);
VasEBoot_free (in);
return VasEBoot_errno;
}
VasEBoot_memcpy (out, &VasEBoot_serial_term_output, sizeof (*out));
out->data = outdata;
out->name = in->name;
VasEBoot_memcpy (outdata, &VasEBoot_serial_terminfo_output, sizeof (*outdata));
VasEBoot_list_push (VasEBoot_AS_LIST_P (&VasEBoot_serial_ports), VasEBoot_AS_LIST (port));
((struct VasEBoot_serial_input_state *) in->data)->port = port;
((struct VasEBoot_serial_output_state *) out->data)->port = port;
port->term_in = in;
port->term_out = out;
VasEBoot_terminfo_output_register (out, "vt100");
#ifdef VasEBoot_MACHINE_MIPS_LOONGSON
if (VasEBoot_strcmp (port->name, loongson_defserial[VasEBoot_arch_machine]) == 0)
{
VasEBoot_term_register_input_active ("serial_*", in);
VasEBoot_term_register_output_active ("serial_*", out);
}
else
{
VasEBoot_term_register_input_inactive ("serial_*", in);
VasEBoot_term_register_output_inactive ("serial_*", out);
}
#else
VasEBoot_term_register_input ("serial_*", in);
VasEBoot_term_register_output ("serial_*", out);
#endif
return VasEBoot_ERR_NONE;
}
void
VasEBoot_serial_unregister (struct VasEBoot_serial_port *port)
{
if (port->driver->fini)
port->driver->fini (port);
if (port->term_in)
VasEBoot_term_unregister_input (port->term_in);
if (port->term_out)
VasEBoot_term_unregister_output (port->term_out);
VasEBoot_list_remove (VasEBoot_AS_LIST (port));
}
void
VasEBoot_serial_unregister_driver (struct VasEBoot_serial_driver *driver)
{
struct VasEBoot_serial_port *port, *next;
for (port = VasEBoot_serial_ports; port; port = next)
{
next = port->next;
if (port->driver == driver)
VasEBoot_serial_unregister (port);
}
}
static VasEBoot_extcmd_t cmd;
VasEBoot_MOD_INIT(serial)
{
cmd = VasEBoot_register_extcmd ("serial", VasEBoot_cmd_serial, 0,
N_("[OPTIONS...]"),
N_("Configure serial port."), options);
VasEBoot_memcpy (&VasEBoot_serial_terminfo_output,
&VasEBoot_serial_terminfo_output_template,
sizeof (VasEBoot_serial_terminfo_output));
VasEBoot_memcpy (&VasEBoot_serial_terminfo_input,
&VasEBoot_serial_terminfo_input_template,
sizeof (VasEBoot_serial_terminfo_input));
#if !defined (VasEBoot_MACHINE_EMU) && !defined(VasEBoot_MACHINE_ARC) && (defined(__mips__) || defined (__i386__) || defined (__x86_64__))
VasEBoot_ns8250_init ();
#endif
#ifdef VasEBoot_MACHINE_IEEE1275
VasEBoot_ofserial_init ();
#endif
#ifdef VasEBoot_MACHINE_EFI
VasEBoot_efiserial_init ();
#endif
#ifdef VasEBoot_MACHINE_ARC
VasEBoot_arcserial_init ();
#endif
}
VasEBoot_MOD_FINI(serial)
{
while (VasEBoot_serial_ports)
VasEBoot_serial_unregister (VasEBoot_serial_ports);
if (registered)
{
VasEBoot_term_unregister_input (&VasEBoot_serial_term_input);
VasEBoot_term_unregister_output (&VasEBoot_serial_term_output);
}
VasEBoot_unregister_extcmd (cmd);
}