Commit 16a28ef2 authored by Gary Jennejohn's avatar Gary Jennejohn Committed by Wolfgang Denk

IOMUX: Add console multiplexing support.

Modifications to support console multiplexing.  This is controlled using
CONFIG_SYS_CONSOLE_MUX in the board configuration file.

This allows a user to specify multiple console devices in the environment
with a command like this: setenv stdin serial,nc.  As a result, the user can
enter text on both the serial and netconsole interfaces.

All devices - stdin, stdout and stderr - can be set in this manner.

1) common/iomux.c and include/iomux.h contain the environment setting
implementation.
2) doc/README.iomux contains a somewhat more detailed description.
3) The implementation in (1) is called from common/cmd_nvedit.c to
handle setenv and from common/console.c to handle initialization of
input/output devices at boot time.
4) common/console.c also contains the code needed to poll multiple console
devices for input and send output to all devices registered for output.
5) include/common.h includes iomux.h and common/Makefile generates iomux.o
when CONFIG_SYS_CONSOLE_MUX is set.
Signed-off-by: default avatarGary Jennejohn <garyj@denx.de>
parent 774ce720
......@@ -142,6 +142,7 @@ COBJS-$(CONFIG_VFD) += cmd_vfd.o
# others
COBJS-$(CONFIG_DDR_SPD) += ddr_spd.o
COBJS-$(CONFIG_CMD_DOC) += docecc.o
COBJS-$(CONFIG_CONSOLE_MUX) += iomux.o
COBJS-y += flash.o
COBJS-y += kgdb.o
COBJS-$(CONFIG_LCD) += lcd.o
......
......@@ -213,6 +213,11 @@ int _do_setenv (int flag, int argc, char *argv[])
return 1;
}
#ifdef CONFIG_CONSOLE_MUX
i = iomux_doenv(console, argv[2]);
if (i)
return i;
#else
/* Try assigning specified device */
if (console_assign (console, argv[2]) < 0)
return 1;
......@@ -221,6 +226,7 @@ int _do_setenv (int flag, int argc, char *argv[])
if (serial_assign (argv[2]) < 0)
return 1;
#endif
#endif /* CONFIG_CONSOLE_MUX */
}
/*
......
......@@ -93,6 +93,76 @@ static int console_setfile (int file, device_t * dev)
return error;
}
#if defined(CONFIG_CONSOLE_MUX)
/** Console I/O multiplexing *******************************************/
static device_t *tstcdev;
device_t **console_devices[MAX_FILES];
int cd_count[MAX_FILES];
/*
* This depends on tstc() always being called before getc().
* This is guaranteed to be true because this routine is called
* only from fgetc() which assures it.
* No attempt is made to demultiplex multiple input sources.
*/
static int iomux_getc(void)
{
unsigned char ret;
/* This is never called with testcdev == NULL */
ret = tstcdev->getc();
tstcdev = NULL;
return ret;
}
static int iomux_tstc(int file)
{
int i, ret;
device_t *dev;
disable_ctrlc(1);
for (i = 0; i < cd_count[file]; i++) {
dev = console_devices[file][i];
if (dev->tstc != NULL) {
ret = dev->tstc();
if (ret > 0) {
tstcdev = dev;
disable_ctrlc(0);
return ret;
}
}
}
disable_ctrlc(0);
return 0;
}
static void iomux_putc(int file, const char c)
{
int i;
device_t *dev;
for (i = 0; i < cd_count[file]; i++) {
dev = console_devices[file][i];
if (dev->putc != NULL)
dev->putc(c);
}
}
static void iomux_puts(int file, const char *s)
{
int i;
device_t *dev;
for (i = 0; i < cd_count[file]; i++) {
dev = console_devices[file][i];
if (dev->puts != NULL)
dev->puts(s);
}
}
#endif /* defined(CONFIG_CONSOLE_MUX) */
/** U-Boot INITIAL CONSOLE-NOT COMPATIBLE FUNCTIONS *************************/
void serial_printf (const char *fmt, ...)
......@@ -114,8 +184,31 @@ void serial_printf (const char *fmt, ...)
int fgetc (int file)
{
if (file < MAX_FILES)
if (file < MAX_FILES) {
#if defined(CONFIG_CONSOLE_MUX)
/*
* Effectively poll for input wherever it may be available.
*/
for (;;) {
/*
* Upper layer may have already called tstc() so
* check for that first.
*/
if (tstcdev != NULL)
return iomux_getc();
iomux_tstc(file);
#ifdef CONFIG_WATCHDOG
/*
* If the watchdog must be rate-limited then it should
* already be handled in board-specific code.
*/
udelay(1);
#endif
}
#else
return stdio_devices[file]->getc ();
#endif
}
return -1;
}
......@@ -123,7 +216,11 @@ int fgetc (int file)
int ftstc (int file)
{
if (file < MAX_FILES)
#if defined(CONFIG_CONSOLE_MUX)
return iomux_tstc(file);
#else
return stdio_devices[file]->tstc ();
#endif
return -1;
}
......@@ -131,13 +228,21 @@ int ftstc (int file)
void fputc (int file, const char c)
{
if (file < MAX_FILES)
#if defined(CONFIG_CONSOLE_MUX)
iomux_putc(file, c);
#else
stdio_devices[file]->putc (c);
#endif
}
void fputs (int file, const char *s)
{
if (file < MAX_FILES)
#if defined(CONFIG_CONSOLE_MUX)
iomux_puts(file, s);
#else
stdio_devices[file]->puts (s);
#endif
}
void fprintf (int file, const char *fmt, ...)
......@@ -407,6 +512,9 @@ int console_init_r (void)
#ifdef CONFIG_SYS_CONSOLE_ENV_OVERWRITE
int i;
#endif /* CONFIG_SYS_CONSOLE_ENV_OVERWRITE */
#ifdef CONFIG_CONSOLE_MUX
int iomux_err = 0;
#endif
/* set default handlers at first */
gd->jt[XF_getc] = serial_getc;
......@@ -425,6 +533,14 @@ int console_init_r (void)
inputdev = search_device (DEV_FLAGS_INPUT, stdinname);
outputdev = search_device (DEV_FLAGS_OUTPUT, stdoutname);
errdev = search_device (DEV_FLAGS_OUTPUT, stderrname);
#ifdef CONFIG_CONSOLE_MUX
iomux_err = iomux_doenv(stdin, stdinname);
iomux_err += iomux_doenv(stdout, stdoutname);
iomux_err += iomux_doenv(stderr, stderrname);
if (!iomux_err)
/* Successful, so skip all the code below. */
goto done;
#endif
}
/* if the devices are overwritten or not found, use default device */
if (inputdev == NULL) {
......@@ -438,15 +554,34 @@ int console_init_r (void)
}
/* Initializes output console first */
if (outputdev != NULL) {
#ifdef CONFIG_CONSOLE_MUX
/* need to set a console if not done above. */
iomux_doenv(stdout, outputdev->name);
#else
console_setfile (stdout, outputdev);
#endif
}
if (errdev != NULL) {
#ifdef CONFIG_CONSOLE_MUX
/* need to set a console if not done above. */
iomux_doenv(stderr, errdev->name);
#else
console_setfile (stderr, errdev);
#endif
}
if (inputdev != NULL) {
#ifdef CONFIG_CONSOLE_MUX
/* need to set a console if not done above. */
iomux_doenv(stdin, inputdev->name);
#else
console_setfile (stdin, inputdev);
#endif
}
#ifdef CONFIG_CONSOLE_MUX
done:
#endif
gd->flags |= GD_FLG_DEVINIT; /* device initialization completed */
#ifndef CONFIG_SYS_CONSOLE_INFO_QUIET
......@@ -455,21 +590,33 @@ int console_init_r (void)
if (stdio_devices[stdin] == NULL) {
puts ("No input devices available!\n");
} else {
#ifdef CONFIG_CONSOLE_MUX
iomux_printdevs(stdin);
#else
printf ("%s\n", stdio_devices[stdin]->name);
#endif
}
puts ("Out: ");
if (stdio_devices[stdout] == NULL) {
puts ("No output devices available!\n");
} else {
#ifdef CONFIG_CONSOLE_MUX
iomux_printdevs(stdout);
#else
printf ("%s\n", stdio_devices[stdout]->name);
#endif
}
puts ("Err: ");
if (stdio_devices[stderr] == NULL) {
puts ("No error devices available!\n");
} else {
#ifdef CONFIG_CONSOLE_MUX
iomux_printdevs(stderr);
#else
printf ("%s\n", stdio_devices[stderr]->name);
#endif
}
#endif /* CONFIG_SYS_CONSOLE_INFO_QUIET */
......@@ -524,11 +671,18 @@ int console_init_r (void)
if (outputdev != NULL) {
console_setfile (stdout, outputdev);
console_setfile (stderr, outputdev);
#ifdef CONFIG_CONSOLE_MUX
console_devices[stdout][0] = outputdev;
console_devices[stderr][0] = outputdev;
#endif
}
/* Initializes input console */
if (inputdev != NULL) {
console_setfile (stdin, inputdev);
#ifdef CONFIG_CONSOLE_MUX
console_devices[stdin][0] = inputdev;
#endif
}
gd->flags |= GD_FLG_DEVINIT; /* device initialization completed */
......
/*
* (C) Copyright 2008
* Gary Jennejohn, DENX Software Engineering GmbH, garyj@denx.de.
*
* See file CREDITS for list of people who contributed to this
* project.
*
* This program 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 2 of
* the License, or (at your option) any later version.
*
* This program 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 this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston,
* MA 02111-1307 USA
*/
#include <common.h>
#include <serial.h>
#include <malloc.h>
#ifdef CONFIG_CONSOLE_MUX
void iomux_printdevs(const int console)
{
int i;
device_t *dev;
for (i = 0; i < cd_count[console]; i++) {
dev = console_devices[console][i];
printf("%s ", dev->name);
}
printf("\n");
}
/* This tries to preserve the old list if an error occurs. */
int iomux_doenv(const int console, const char *arg)
{
char *console_args, *temp, **start;
int i, j, k, io_flag, cs_idx, repeat;
device_t *dev;
device_t **cons_set;
console_args = strdup(arg);
if (console_args == NULL)
return 1;
/*
* Check whether a comma separated list of devices was
* entered and count how many devices were entered.
* The array start[] has pointers to the beginning of
* each device name (up to MAX_CONSARGS devices).
*
* Have to do this twice - once to count the number of
* commas and then again to populate start.
*/
i = 0;
temp = console_args;
for (;;) {
temp = strchr(temp, ',');
if (temp != NULL) {
i++;
temp++;
continue;
}
/* There's always one entry more than the number of commas. */
i++;
break;
}
start = (char **)malloc(i * sizeof(char *));
if (start == NULL) {
free(console_args);
return 1;
}
i = 0;
start[0] = console_args;
for (;;) {
temp = strchr(start[i++], ',');
if (temp == NULL)
break;
*temp = '\0';
start[i] = temp + 1;
}
cons_set = (device_t **)calloc(i, sizeof(device_t *));
if (cons_set == NULL) {
free(start);
free(console_args);
return 1;
}
switch (console) {
case stdin:
io_flag = DEV_FLAGS_INPUT;
break;
case stdout:
case stderr:
io_flag = DEV_FLAGS_OUTPUT;
break;
default:
free(start);
free(console_args);
free(cons_set);
return 1;
}
cs_idx = 0;
for (j = 0; j < i; j++) {
/*
* Check whether the device exists and is valid.
* console_assign() also calls search_device(),
* but I need the pointer to the device.
*/
dev = search_device(io_flag, start[j]);
if (dev == NULL)
continue;
/*
* Prevent multiple entries for a device.
*/
repeat = 0;
for (k = 0; k < cs_idx; k++) {
if (dev == cons_set[k]) {
repeat++;
break;
}
}
if (repeat)
continue;
/*
* Try assigning the specified device.
* This could screw up the console settings for apps.
*/
if (console_assign(console, start[j]) < 0)
continue;
#ifdef CONFIG_SERIAL_MULTI
/*
* This was taken from common/cmd_nvedit.c.
* This will never work because serial_assign() returns
* 1 upon error, not -1.
* This would almost always return an error anyway because
* serial_assign() expects the name of a serial device, like
* serial_smc, but the user generally only wants to set serial.
*/
if (serial_assign(start[j]) < 0)
continue;
#endif
cons_set[cs_idx++] = dev;
}
free(console_args);
free(start);
/* failed to set any console */
if (cs_idx == 0) {
free(cons_set);
return 1;
} else {
/* Works even if console_devices[console] is NULL. */
console_devices[console] =
(device_t **)realloc(console_devices[console],
cs_idx * sizeof(device_t *));
if (console_devices[console] == NULL) {
free(cons_set);
return 1;
}
memcpy(console_devices[console], cons_set, cs_idx *
sizeof(device_t *));
cd_count[console] = cs_idx;
}
free(cons_set);
return 0;
}
#endif /* CONFIG_CONSOLE_MUX */
/*
* (C) Copyright 2008
* Gary Jennejohn, DENX Software Engineering GmbH <garyj@denx.de>
*
* See file CREDITS for list of people who contributed to this
* project.
*
* This program 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 2 of
* the License, or (at your option) any later version.
*
* This program 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 this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston,
* MA 02111-1307 USA
*/
U-Boot console multiplexing
===========================
HOW CONSOLE MULTIPLEXING WORKS
------------------------------
This functionality is controlled with CONFIG_CONSOLE_MUX in the board
configuration file.
Two new files, common/iomux.c and include/iomux.h, contain the heart
(iomux_doenv()) of the environment setting implementation.
iomux_doenv() is called in common/cmd_nvedit.c to handle setenv and in
common/console.c in console_init_r() during bootup to initialize
stdio_devices[].
A user can use a comma-separated list of devices to set stdin, stdout
and stderr. For example: "setenv stdin serial,nc". NOTE: No spaces
are allowed around the comma(s)!
The length of the list is limited by malloc(), since the array used
is allocated and freed dynamically.
It should be possible to specify any device which console_assign()
finds acceptable, but the code has only been tested with serial and
nc.
iomux_doenv() prevents multiple use of the same device, e.g. "setenv
stdin nc,nc,serial" will discard the second nc. iomux_doenv() is
not able to modify the environment, however, so that "pri stdin" still
shows "nc,nc,serial".
The major change in common/console.c was to modify fgetc() to call
the iomux_tstc() routine in a for-loop. iomux_tstc() in turn calls
the tstc() routine for every registered device, but exits immediately
when one of them returns true. fgetc() then calls iomux_getc(),
which calls the corresponding getc() routine. fgetc() hangs in
the for-loop until iomux_tstc() returns true and the input can be
retrieved.
Thus, a user can type into any device registered for stdin. No effort
has been made to demulitplex simultaneous input from multiple stdin
devices.
fputc() and fputs() have been modified to call iomux_putc() and
iomux_puts() respectively, which call the corresponding output
routines for every registered device.
Thus, a user can see the ouput for any device registered for stdout
or stderr on all devices registered for stdout or stderr. As an
example, if stdin=serial,nc and stdout=serial,nc then all output
for serial, e.g. echos of input on serial, will appear on serial and nc.
Just as with the old console code, this statement is still true:
If not defined in the environment, the first input device is assigned
to the 'stdin' file, the first output one to 'stdout' and 'stderr'.
If CONFIG_SYS_CONSOLE_IS_IN_ENV is defined then multiple input/output
devices can be set at boot time if defined in the environment.
CAVEATS
-------
Note that common/iomux.c calls console_assign() for every registered
device as it is discovered. This means that the environment settings
for application consoles will be set to the last device in the list.
On a slow machine, such as MPC852T clocked at 66MHz, the overhead associated
with calling tstc() and then getc() means that copy&paste will normally not
work, even when stdin=stdout=stderr=serial.
On a faster machine, such as a sequoia, cut&paste of longer (about 80
characters) lines works fine when serial is the only device used.
Using nc as a stdin device results in even more overhead because nc_tstc()
is quite slow. Even on a sequoia cut&paste does not work on the serial
interface when nc is added to stdin, although there is no character loss using
the ethernet interface for input. In this test case stdin=serial,nc and
stdout=serial.
In addition, the overhead associated with sending to two devices, when one of
them is nc, also causes problems. Even on a sequoia cut&paste does not work
on the serial interface (stdin=serial) when nc is added to stdout (stdout=
serial,nc).
......@@ -678,6 +678,13 @@ void fputc(int file, const char c);
int ftstc(int file);
int fgetc(int file);
/*
* CONSOLE multiplexing.
*/
#ifdef CONFIG_CONSOLE_MUX
#include <iomux.h>
#endif
int pcmcia_init (void);
#ifdef CONFIG_STATUS_LED
......
/*
* (C) Copyright 2008
* Gary Jennejohn, DENX Software Engineering GmbH, garyj@denx.de.
*
* See file CREDITS for list of people who contributed to this
* project.
*
* This program 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 2 of
* the License, or (at your option) any later version.
*
*This program 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 this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston,
* MA 02111-1307 USA
*/
#ifndef _IO_MUX_H
#define _IO_MUX_H
#include <devices.h>
/*
* Stuff required to support console multiplexing.
*/
/*
* Pointers to devices used for each file type. Defined in console.c
* but storage is allocated in iomux.c.
*/
extern device_t **console_devices[MAX_FILES];
/*
* The count of devices assigned to each FILE. Defined in console.c
* and populated in iomux.c.
*/
extern int cd_count[MAX_FILES];
int iomux_doenv(const int, const char *);
void iomux_printdevs(const int);
device_t *search_device(int, char *);
#endif /* _IO_MUX_H */
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment