Date: Wed, 3 Dec 1997 23:13:09 +0100
From: Miquel van Smoorenburg <miquels@cistron.nl>
To: Linus Torvalds <torvalds@transmeta.com>
Cc: Roman.Hodek@informatik.uni-erlangen.de,
        Geert.Uytterhoeven@cs.kuleuven.ac.be
Subject: Extended serial console patch [was: Re: Linux 2.1.69..]
References: <199712012308.AAA07549@defiant.cistron.nl> <Pine.LNX.3.95.971201151017.478E-100000@penguin.transmeta.com>
In-Reply-To: <Pine.LNX.3.95.971201151017.478E-100000@penguin.transmeta.com>; from Linus Torvalds on Mon, Dec 01, 1997 at 03:11:06PM -0800
X-Ncc-Regid: nl.cistron

According to Linus Torvalds:
> On Tue, 2 Dec 1997, Miquel van Smoorenburg wrote:
> > 
> > Any chance you'll accept my extended serial console stuff if I send
> > you the patch? It's working and being used on ppc/m68k/alpha/i386 ..
> 
> Do dumm.. I've seen it many times, but I've always had something more
> pressing going on. Can you send me a short description of what it does and
> why along with the patch, and maybe I could finally take a closer look..

Okay Linus, here we go.

What does the extended serial console patch do?

1. It adds a kernel command line argument "console=". It is possible
   to specify multiple arguments. With this you can select the device(s)
   that print(s) the console messages.
2. The first device named in "console=" will become the real system console.
   For this we have defined a new character device, /dev/console c 5 1
   This device defaults to the video card, but if there is no video card
   in the system or no support for it compiled in the kernel it will
   default to the first serial port.
3. /dev/tty0 is now seperate from /dev/console, BUT there it total
   backwards compatibility. Eg you can still use it as /dev/console and
   TIOCCONS still works too. This means there are NO compatibility problems
   if one doesn't want to (or can't) use the new console device.
4. The other way around too; if you use the new /dev/console, and have
   selected the video card as console (default) everything will still
   run, even X and SvgaLib (which open /dev/console instead of the
   proper /dev/tty0)
5. c 5 1 has been "unofficially" assigned by H. Peter Anvin already to me.


Why do we want to have this?

1. For remote systems. Lots of people (including Cistron Internet) have
   Linux machines at remote locations, with some kind of access to the
   serial port. Usually these machines do not even have a video card
   installed. It's great to have a _real_ serial console in case of trouble;
   it has saved me a 35 KM trip several times (esp. with the 2.0.30 .. 32
   series!)
2. For machines without a video card, such as some Alphas, some Sparcs etc.
3. FreeBSD has had it for years now :)


Breakdown of the patch per file:

Documentation/Configure.help		Help
Documentation/serial-console.txt	Docs
drivers/char/Config.in			Add config option
drivers/char/console.c			Extra test for presence of VGA card,
					extended register_console
drivers/char/keyboard.c			keyboard_wait_for_keypress now
					returns int instead of void
drivers/char/n_tty.c			Reward both CONSOLE_DEV (tty0) and
					SYSCONS_DEV (console)
drivers/char/serial.c			Input/output routines, setup for
					serial console.
drivers/char/tga.c			Add con_is_present() function
drivers/char/tty_io.c			Like n_tty.c, also tty_register_driver
					of SYSTEM_TYPE_CONSOLE
drivers/char/vga.c			Add con_is_present() function
fs/proc/proc_tty.c			Add subtype SYSTEM_TYPE_CONSOLE
include/linux/console.h			Extend struct console, add
					struct console_cmdline
include/linux/tty_driver.h		Add SYSCONS definitions
init/main.c				Add console_setup()
kernel/printk.c				Add console_setup(), extend
					register_console()

And here is the diff:
[ftp://ftp.cistron.nl/pub/people/miquels/kernel/serial-console-2.1.70.diff]

diff -ruN linux-2.1.70.orig/Documentation/Configure.help linux-2.1.70/Documentation/Configure.help
--- linux-2.1.70.orig/Documentation/Configure.help	Wed Dec  3 12:09:26 1997
+++ linux-2.1.70/Documentation/Configure.help	Wed Dec  3 12:10:08 1997
@@ -4899,11 +4899,10 @@
 
 Console on virtual terminal
 CONFIG_VT_CONSOLE
-  If you enable this option, all kernel messages will be sent to the
-  device /dev/tty which corresponds to the virtual terminal you have
-  visible on your display. You should say N here only if you have some
-  other console device, in which case you probably want to say Y to
-  "Console on serial port", below. If unsure, say N.
+  If you enable this option, by default all kernel messages will be sent
+  to the device /dev/tty0 which corresponds to the virtual terminal you
+  have visible on your display. You should say Y here unless you only
+  want to have the kernel messages output on a serial port.
 
 Software generated cursor
 CONFIG_SOFTCURSOR
@@ -4932,15 +4931,12 @@
 
 Console on serial port
 CONFIG_SERIAL_CONSOLE
-  If you enable this option, all kernel messages will be sent to the
-  device /dev/ttyS0 which corresponds to a serial port; this could be
-  useful if you attached a terminal or printer to that port. (You can
-  change the number of the serial port used from 0 to something else
-  by setting the variable CONFIG_SERIAL_CONSOLE_PORT.) You can use
-  this option in combination with the option "Console on virtual
-  terminal" above, in which case you get the output on both the serial
-  port and on your display. Most people say N here so that they can
-  use the serial port for modem, mouse or some other device.
+  If you enable this option, it is possible to use a serial port as the
+  console. By default still the virtual console will be used at the
+  system console but you can alter that using a kernel command line
+  option. If you don't have a VGA card installed the kernel will
+  automatically use /dev/ttyS0 as system console if this option is
+  enabled.
 
 Comtrol Rocketport support
 CONFIG_ROCKETPORT
diff -ruN linux-2.1.70.orig/Documentation/serial-console.txt linux-2.1.70/Documentation/serial-console.txt
--- linux-2.1.70.orig/Documentation/serial-console.txt	Wed May 14 07:41:00 1997
+++ linux-2.1.70/Documentation/serial-console.txt	Wed Dec  3 22:14:39 1997
@@ -1,48 +1,67 @@
                        Linux Serial Console
 
-These examples are valid if you want to use /dev/ttyS1 (COM2)
-as the serial console. Replace as needed.
+It is possible to specify multiple devices for console output. You can
+define a new kernel command line option to select which device(s) to
+use for console output.
 
-1. Tell LILO to use the serial port.
+The format of this option is:
+
+	console=device,options
+
+	device:		tty0 for the foreground virtual console
+			ttyX for any other virtual console
+			ttySx for a serial port
+
+	options:	depend on the driver. For the serial port this
+			defines the baudrate/parity/bits of the port,
+			in the format BBBBPN, where BBBB is the speed,
+			P is parity (n/o/e), and N is bits. Default is
+			9600n8. The maximum baudrate is 115200.
+
+You can specify multiple console= options on the kernel command line.
+Output will appear on all of them. The first device will be used when
+you open /dev/console. So, for example:
+
+	console=tty0 console=ttyS1,9600
+
+defines that opening /dev/console will get you the current foreground
+virtual console, and kernel messages will appear on both the VGA
+console and the 2nd serial port (ttyS1 or COM2) at 9600 baud.
+
+Note that you can only define one console per device type (serial, video).
+
+If no console device is specified, the first device found capable of
+acting as a system console will be used. At this time, the system
+first looks for a VGA card and then for a serial port. So if you don't
+have a VGA card in your system the first serial port will automatically
+become the console.
+
+You will need to create a new device to use /dev/console. The official
+/dev/console is now character device 5,1.
+
+Here's an example that will use /dev/ttyS1 (COM2) as the console.
+Replace the sample values as needed.
+
+1. Create /dev/console (real console) and /dev/tty0 (master virtual
+   console):
+
+   cd /dev
+   rm -f console tty0
+   mknod -m 622 console c 5 1
+   mknod -m 622 tty0 c 4 0
+
+2. LILO can also take input from a serial device. This is a very
+   useful option. To tell LILO to use the serial port:
    In lilo.conf (global section): 
 
    serial  = 1,9600n8 (ttyS1, 9600 bd, no parity, 8 bits)
 
-2. Adjust to kernel flags for the new kernel,
+3. Adjust to kernel flags for the new kernel,
    again in lilo.conf (kernel section)
 
-   append = "console=1,9600,n8" 
-
-   (Note the extra comma needed if you want to supply parity/framing 
-   information.)
-
-3. Link /dev/console to the serial port.
+   append = "console=ttyS1,9600" 
 
-   Your probably want to save your old /dev/console (the "master" virtual
-   console). Check if it is a symbolic link first. If not, `mv' it to
-   `/dev/tty0':
-
-   ls -l /dev/console
-   mv /dev/console /dev/tty0
-
-   Now link the serial port you are going to use as the console to
-   /dev/console, for example ttyS1:
-
-   ln -s /dev/ttyS1 /dev/console
-
-   On some systems you might want to edit your bootup scripts to make sure
-   they don't reset this arrangement on boot. (On Debian, check
-   /etc/rc.boot/console and /etc/default/console). You probably also want
-   to put a getty on either /dev/console or /dev/ttyS1.
-
-4. Init and /dev/console.
-   Sysvinit will open /dev/console on boot. If this does not point
-   to the serial console device, the startup messages will be printed
-   to the wrong device. The kernel also passes the environment variable
-   "CONSOLE" to the init program. sysvinit-2.64 reckognizes this, and
-   opens that device instead. Boot scripts as mentioned in (3) can
-   also check this variable to see what device the system console is.
-   If CONSOLE is not set you can assume the console is /dev/tty0.
+4. Init and /etc/ioctl.save
 
    Sysvinit remembers its stty settings in a file in /etc, called
    `/etc/ioctl.save'. REMOVE THIS FILE before using the serial
@@ -51,28 +70,25 @@
 
 5. /dev/console and X
    Programs that want to do something with the virtual console usually
-   open /dev/console. XF86 does this, and probably SVGALIB as well.
-   IMO this is wrong; they should open /dev/tty0.
-   I have binary patched /usr/bin/X11/XF86_SVGA to use "tty0"
-   instead of "console".
-
-6. Notes.
-
-   If you compile the next little program, you will be able
-   to really "halt" the system. It will enter a little
-   monitor :)
-
-   main() { reboot(0xfee1dead, 672274793, 0xCDEF0123); }
-
-   This is just a call to the new "halt" function that later
-   kernels have. This is included the "halt" command of
-   the recent sysvinit versions.
-
-   The monitor will also be entered at a kernel panic, or
-   when you press "break". That last function does not
-   work at the moment I think, but it would be useful for
-   kernel debugging.  You don't have alt-scrollock on a serial
-   console to find out the current EIP...
+   open /dev/console. If you have created the new /dev/console device,
+   and your console is NOT the virtual console some programs will fail.
+   Those are programs that want to access the VT interface, and use
+   /dev/console instead of /dev/tty0. Some of those programs are:
+
+   Xfree86, svgalib, gpm, SVGATextMode
+
+   I have binary patched the above mentioned programs to use "tty0"
+   instead of "console".  This will be reported to the maintainers of
+   said programs.
+
+   Note that if you boot without a console= option (or with
+   console=/dev/tty0), /dev/console is the same as /dev/tty0. In that
+   case everything will still work.
+
+6. Thanks
+
+   Thanks to Geert Uytterhoeven <Geert.Uytterhoeven@cs.kuleuven.ac.be>
+   for porting the patches from 2.1.4x to 2.1.6x for taking care of
+   the integration of these patches into m68k, ppc and alpha.
 
-Miquel van Smoorenburg <miquels@cistron.nl>, 21-Jun-1996
-Stephen C. Tweedie <sct@dcs.ed.ac.uk>, 23-Dec-1996
+Miquel van Smoorenburg <miquels@cistron.nl>, 03-Dec-1997
diff -ruN linux-2.1.70.orig/drivers/char/Config.in linux-2.1.70/drivers/char/Config.in
--- linux-2.1.70.orig/drivers/char/Config.in	Wed Dec  3 12:09:30 1997
+++ linux-2.1.70/drivers/char/Config.in	Wed Dec  3 12:10:11 1997
@@ -6,16 +6,18 @@
 
 bool 'Virtual terminal' CONFIG_VT
 if [ "$CONFIG_VT" = "y" ]; then
-  bool 'Console on virtual terminal' CONFIG_VT_CONSOLE
+  bool 'Support for console on virtual terminal' CONFIG_VT_CONSOLE
 fi
 tristate 'Standard/generic (dumb) serial support' CONFIG_SERIAL
+if [ "$CONFIG_SERIAL" = "y" ]; then
+   bool '   Support for console on serial port' CONFIG_SERIAL_CONSOLE
+fi
 bool 'Extended dumb serial driver options' CONFIG_SERIAL_EXTENDED
 if [ "$CONFIG_SERIAL_EXTENDED" = "y" ]; then
    bool '   Support more than 4 serial ports' CONFIG_SERIAL_MANY_PORTS
    bool '   Support for sharing serial interrupts' CONFIG_SERIAL_SHARE_IRQ
    bool '   Support special multiport boards' CONFIG_SERIAL_MULTIPORT
    bool '   Support the Bell Technologies HUB6 card' CONFIG_HUB6
-   bool '   Console on serial port' CONFIG_SERIAL_CONSOLE
 fi
 bool 'Non-standard serial port support' CONFIG_SERIAL_NONSTANDARD
 if [ "$CONFIG_SERIAL_NONSTANDARD" = "y" ]; then
diff -ruN linux-2.1.70.orig/drivers/char/console.c linux-2.1.70/drivers/char/console.c
--- linux-2.1.70.orig/drivers/char/console.c	Thu Jun 26 21:33:38 1997
+++ linux-2.1.70/drivers/char/console.c	Wed Dec  3 22:07:20 1997
@@ -158,6 +158,7 @@
 extern void compute_shiftstate(void);
 extern void reset_palette(int currcons);
 extern void set_palette(void);
+extern int con_is_present(void);
 extern unsigned long con_type_init(unsigned long, const char **);
 extern void con_type_init_finish(void);
 extern int set_get_cmap(unsigned char *, int);
@@ -1836,7 +1837,7 @@
 }
 
 #ifdef CONFIG_VT_CONSOLE
-void vt_console_print(const char * b, unsigned count)
+void vt_console_print(struct console *co, const char * b, unsigned count)
 {
 	int currcons = fg_console;
 	unsigned char c;
@@ -1885,16 +1886,25 @@
 	printing = 0;
 }
 
-static int vt_console_device(void)
+static kdev_t vt_console_device(struct console *c)
 {
-	return MKDEV(TTY_MAJOR, fg_console + 1);
+	return MKDEV(TTY_MAJOR, c->index ? c->index : fg_console + 1);
 }
 
-extern void keyboard_wait_for_keypress(void);
+extern int keyboard_wait_for_keypress(struct console *);
 
 struct console vt_console_driver = {
-	vt_console_print, do_unblank_screen,
-        keyboard_wait_for_keypress, vt_console_device
+	"tty",
+	vt_console_print,
+	NULL,
+	vt_console_device,
+	keyboard_wait_for_keypress,
+	do_unblank_screen,
+	NULL,
+	CON_PRINTBUFFER,
+	-1,
+	0,
+	NULL
 };
 #endif
 
@@ -1983,6 +1993,9 @@
  *
  * Reads the information preserved by setup.s to determine the current display
  * type and sets everything accordingly.
+ *
+ * FIXME: return early if we don't _have_ a video card installed.
+ *
  */
 __initfunc(unsigned long con_init(unsigned long kmem_start))
 {
@@ -2117,7 +2130,7 @@
 	 * within the bus probing code... :-(
 	 */
 #ifdef CONFIG_VT_CONSOLE
-	if (video_type != VIDEO_TYPE_TGAC)
+	if (video_type != VIDEO_TYPE_TGAC && con_is_present())
 		register_console(&vt_console_driver);
 #endif
 
diff -ruN linux-2.1.70.orig/drivers/char/keyboard.c linux-2.1.70/drivers/char/keyboard.c
--- linux-2.1.70.orig/drivers/char/keyboard.c	Thu Jul 31 22:09:17 1997
+++ linux-2.1.70/drivers/char/keyboard.c	Wed Dec  3 13:16:20 1997
@@ -63,10 +63,12 @@
 extern void scrollfront(int);
 
 struct wait_queue * keypress_wait = NULL;
+struct console;
 
-void keyboard_wait_for_keypress(void)
+int keyboard_wait_for_keypress(struct console *co)
 {
 	sleep_on(&keypress_wait);
+	return 0;
 }
 
 /*
diff -ruN linux-2.1.70.orig/drivers/char/n_tty.c linux-2.1.70/drivers/char/n_tty.c
--- linux-2.1.70.orig/drivers/char/n_tty.c	Sun Nov 30 19:59:02 1997
+++ linux-2.1.70/drivers/char/n_tty.c	Tue Dec  2 15:17:54 1997
@@ -40,6 +40,7 @@
 #include <asm/bitops.h>
 
 #define CONSOLE_DEV MKDEV(TTY_MAJOR,0)
+#define SYSCONS_DEV  MKDEV(TTYAUX_MAJOR,1)
 
 #ifndef MIN
 #define MIN(a,b)	((a) < (b) ? (a) : (b))
@@ -854,6 +855,7 @@
 	   check of the logic of this change. -- jlc */
 	/* don't stop on /dev/console */
 	if (file->f_dentry->d_inode->i_rdev != CONSOLE_DEV &&
+	    file->f_dentry->d_inode->i_rdev != SYSCONS_DEV &&
 	    current->tty == tty) {
 		if (tty->pgrp <= 0)
 			printk("read_chan: tty->pgrp <= 0!\n");
@@ -1012,7 +1014,8 @@
 	ssize_t retval = 0, num;
 
 	/* Job control check -- must be done at start (POSIX.1 7.1.1.4). */
-	if (L_TOSTOP(tty) && file->f_dentry->d_inode->i_rdev != CONSOLE_DEV) {
+	if (L_TOSTOP(tty) && file->f_dentry->d_inode->i_rdev != CONSOLE_DEV &&
+	    file->f_dentry->d_inode->i_rdev != SYSCONS_DEV) {
 		retval = tty_check_change(tty);
 		if (retval)
 			return retval;
diff -ruN linux-2.1.70.orig/drivers/char/serial.c linux-2.1.70/drivers/char/serial.c
--- linux-2.1.70.orig/drivers/char/serial.c	Mon Dec  1 18:45:43 1997
+++ linux-2.1.70/drivers/char/serial.c	Wed Dec  3 22:07:12 1997
@@ -49,6 +49,9 @@
 #include <linux/mm.h>
 #include <linux/malloc.h>
 #include <linux/init.h>
+#ifdef CONFIG_SERIAL_CONSOLE
+#include <linux/console.h>
+#endif
 
 #include <asm/system.h>
 #include <asm/io.h>
@@ -152,6 +155,9 @@
 static volatile int rs_irq_triggered;
 static volatile int rs_triggered;
 static int rs_wild_int_mask;
+#ifdef CONFIG_SERIAL_CONSOLE
+static struct console sercons;
+#endif
 
 static void autoconfig(struct serial_state * info);
 static void change_speed(struct async_struct *info);
@@ -2752,7 +2758,13 @@
 			*tty->termios = info->state->callout_termios;
 		change_speed(info);
 	}
-
+#ifdef CONFIG_SERIAL_CONSOLE
+	if (sercons.cflag && sercons.index == line) {
+		tty->termios->c_cflag = sercons.cflag;
+		sercons.cflag = 0;
+		change_speed(info);
+	}
+#endif
 	info->session = current->session;
 	info->pgrp = current->pgrp;
 
@@ -3195,7 +3207,18 @@
 		       sizeof(struct rs_multiport_struct));
 #endif
 	}
-	
+#ifdef CONFIG_SERIAL_CONSOLE
+	/*
+	 *	The interrupt of the serial console port
+	 *	can't be shared.
+	 */
+	if (sercons.flags & CON_FIRST) {
+		for(i = 0; i < NR_PORTS; i++)
+			if (i != sercons.index &&
+			    rs_table[i].irq == rs_table[sercons.index].irq)
+				rs_table[i].irq = 0;
+	}
+#endif
 	show_serial_version();
 
 	/* Initialize the tty_driver structure */
@@ -3398,53 +3421,51 @@
  */
 #ifdef CONFIG_SERIAL_CONSOLE
 
-#include <linux/console.h>
+#define BOTH_EMPTY (UART_LSR_TEMT | UART_LSR_THRE)
 
 /*
- * this defines the index into rs_table for the port to use
+ *	Wait for transmitter & holding register to empty
  */
-#ifndef CONFIG_SERIAL_CONSOLE_PORT
-#define CONFIG_SERIAL_CONSOLE_PORT	0
-#endif
-
-#define BOTH_EMPTY (UART_LSR_TEMT | UART_LSR_THRE)
-
-/* Wait for transmitter & holding register to empty */
 static inline void wait_for_xmitr(struct serial_state *ser)
 {
 	int lsr;
+	unsigned int tmout = 1000000;
+
 	do {
 		lsr = inb(ser->port + UART_LSR);
+		if (--tmout == 0) break;
 	} while ((lsr & BOTH_EMPTY) != BOTH_EMPTY);
 }
 
 /*
- * Print a string to the serial port trying not to disturb any possible
- * real use of the port...
+ *	Print a string to the serial port trying not to disturb
+ *	any possible real use of the port...
  */
-static void serial_console_write(const char *s, unsigned count)
+static void serial_console_write(struct console *co, const char *s,
+				unsigned count)
 {
 	struct serial_state *ser;
 	int ier;
 	unsigned i;
 
-	ser = rs_table + CONFIG_SERIAL_CONSOLE_PORT;
+	ser = rs_table + co->index;
 	/*
-	 * First save the IER then disable the interrupts
+	 *	First save the IER then disable the interrupts
 	 */
 	ier = inb(ser->port + UART_IER);
 	outb(0x00, ser->port + UART_IER);
 
 	/*
-	 * Now, do each character
+	 *	Now, do each character
 	 */
 	for (i = 0; i < count; i++, s++) {
 		wait_for_xmitr(ser);
 
-		/* Send the character out. */
+		/*
+		 *	Send the character out.
+		 *	If a LF, also do CR...
+		 */
 		outb(*s, ser->port + UART_TX);
-
-		/* if a LF, also do CR... */
 		if (*s == 10) {
 			wait_for_xmitr(ser);
 			outb(13, ser->port + UART_TX);
@@ -3452,29 +3473,29 @@
 	}
 
 	/*
-	 * Finally, Wait for transmitter & holding register to empty
-	 *  and restore the IER
+	 *	Finally, Wait for transmitter & holding register to empty
+	 * 	and restore the IER
 	 */
 	wait_for_xmitr(ser);
 	outb(ier, ser->port + UART_IER);
 }
 
 /*
- * Receive character from the serial port
+ *	Receive character from the serial port
  */
-static void serial_console_wait_key(void)
+static int serial_console_wait_key(struct console *co)
 {
 	struct serial_state *ser;
 	int ier;
 	int lsr;
 	int c;
 
-	ser = rs_table + CONFIG_SERIAL_CONSOLE_PORT;
+	ser = rs_table + co->index;
 
 	/*
-	 * First save the IER then disable the interrupts so
-	 * that the real driver for the port does not get the
-	 * character.
+	 *	First save the IER then disable the interrupts so
+	 *	that the real driver for the port does not get the
+	 *	character.
 	 */
 	ier = inb(ser->port + UART_IER);
 	outb(0x00, ser->port + UART_IER);
@@ -3484,39 +3505,142 @@
 	} while (!(lsr & UART_LSR_DR));
 	c = inb(ser->port + UART_RX);
 
-	/* Restore the interrupts */
+	/*
+	 *	Restore the interrupts
+	 */
 	outb(ier, ser->port + UART_IER);
+
+	return c;
 }
 
-static int serial_console_device(void)
+static kdev_t serial_console_device(struct console *c)
 {
-	return MKDEV(TTYAUX_MAJOR, 64 + CONFIG_SERIAL_CONSOLE_PORT);
+	return MKDEV(TTY_MAJOR, 64 + c->index);
 }
 
-long serial_console_init(long kmem_start, long kmem_end)
+/*
+ *	Setup initial baud/bits/parity. We do two things here:
+ *	- construct a cflag setting for the first rs_open()
+ *	- initialize the serial port
+ */
+__initfunc(static void serial_console_setup(struct console *co, char *options))
 {
-	static struct console console = {
-		serial_console_write, 0,
-                serial_console_wait_key, serial_console_device
-	};
 	struct serial_state *ser;
+	unsigned cval;
+	int	baud = 9600;
+	int	bits = 8;
+	int	parity = 'n';
+	int	cflag = CREAD | HUPCL | CLOCAL;
+	int	quot = 0;
+	char	*s;
+
+	if (options) {
+		baud = simple_strtoul(options, NULL, 10);
+		s = options;
+		while(*s >= '0' && *s <= '9')
+			s++;
+		if (*s) parity = *s++;
+		if (*s) bits   = *s - '0';
+	}
 
-	ser = rs_table + CONFIG_SERIAL_CONSOLE_PORT;
+	/*
+	 *	Now construct a cflag setting.
+	 */
+	switch(baud) {
+		case 1200:
+			cflag |= B1200;
+			break;
+		case 2400:
+			cflag |= B2400;
+			break;
+		case 4800:
+			cflag |= B4800;
+			break;
+		case 19200:
+			cflag |= B19200;
+			break;
+		case 38400:
+			cflag |= B38400;
+			break;
+		case 57600:
+			cflag |= B57600;
+			break;
+		case 115200:
+			cflag |= B115200;
+			break;
+		case 9600:
+		default:
+			cflag |= B9600;
+			break;
+	}
+	switch(bits) {
+		case 7:
+			cflag |= CS7;
+			break;
+		default:
+		case 8:
+			cflag |= CS8;
+			break;
+	}
+	switch(parity) {
+		case 'o': case 'O':
+			cflag |= PARODD;
+			break;
+		case 'e': case 'E':
+			cflag |= PARENB;
+			break;
+	}
+	co->cflag = cflag;
 
-	/* Disable all interrupts, it works in polled mode */
-	outb(0x00, ser->port + UART_IER); 
+	/*
+	 *	Divisor, bytesize and parity
+	 */
+	ser = rs_table + co->index;
+	quot = BASE_BAUD / baud;
+	cval = cflag & (CSIZE | CSTOPB);
+#if defined(__powerpc__) || defined(__alpha__)
+	cval >>= 8;
+#else /* !__powerpc__ && !__alpha__ */
+	cval >>= 4;
+#endif /* !__powerpc__ && !__alpha__ */
+	if (cflag & PARENB)
+		cval |= UART_LCR_PARITY;
+	if (!(cflag & PARODD))
+		cval |= UART_LCR_EPAR;
 
 	/*
-	 * now do hardwired init
+	 *	Disable UART interrupts, set DTR and RTS high
+	 *	and set speed.
 	 */
-	outb(0x03, ser->port + UART_LCR); /* No parity, 8 data bits, 1 stop */
-	outb(0x83, ser->port + UART_LCR); /* Access divisor latch */
-	outb(0x00, ser->port + UART_DLM); /* 9600 baud */
-	outb(0x0c, ser->port + UART_DLL);
-	outb(0x03, ser->port + UART_LCR); /* Done with divisor */
+	outb(0, ser->port + UART_IER);
+	outb(UART_MCR_DTR | UART_MCR_RTS, ser->port + UART_MCR);
+	outb(cval | UART_LCR_DLAB, ser->port + UART_LCR);	/* set DLAB */
+	outb(quot & 0xff, ser->port + UART_DLL);	/* LS of divisor */
+	outb(quot >> 8, ser->port + UART_DLM);		/* MS of divisor */
+	outb(cval, ser->port + UART_LCR);		/* reset DLAB */
 
-	register_console(&console);
-	return kmem_start;
 }
 
+static struct console sercons = {
+	"ttyS",
+	serial_console_write,
+	NULL,
+	serial_console_device,
+	serial_console_wait_key,
+	NULL,
+	serial_console_setup,
+	CON_PRINTBUFFER,
+	-1,
+	0,
+	NULL
+};
+
+/*
+ *	Register console.
+ */
+__initfunc (long serial_console_init(long kmem_start, long kmem_end))
+{
+	register_console(&sercons);
+	return kmem_start;
+}
 #endif
diff -ruN linux-2.1.70.orig/drivers/char/tga.c linux-2.1.70/drivers/char/tga.c
--- linux-2.1.70.orig/drivers/char/tga.c	Tue Jun 17 01:35:55 1997
+++ linux-2.1.70/drivers/char/tga.c	Tue Dec  2 15:17:55 1997
@@ -443,6 +443,19 @@
 {
 }
 
+
+/*
+ *	See if we have a TGA card.
+ */
+__initfunc(int con_is_present())
+{
+	int status;
+
+	status = pcibios_find_device (PCI_VENDOR_ID_DEC, PCI_DEVICE_ID_DEC_TGA,
+				      0, &pci_bus, &pci_devfn);
+	return (status == PCIBIOS_DEVICE_NOT_FOUND) ? 0 : 1;
+}
+
 /*
  * video init code, called from within the PCI bus probing code;
  * when TGA console is configured, at the end of the probing code,
diff -ruN linux-2.1.70.orig/drivers/char/tty_io.c linux-2.1.70/drivers/char/tty_io.c
--- linux-2.1.70.orig/drivers/char/tty_io.c	Wed Dec  3 12:09:31 1997
+++ linux-2.1.70/drivers/char/tty_io.c	Wed Dec  3 22:33:28 1997
@@ -90,6 +90,7 @@
 
 #define CONSOLE_DEV MKDEV(TTY_MAJOR,0)
 #define TTY_DEV MKDEV(TTYAUX_MAJOR,0)
+#define SYSCONS_DEV MKDEV(TTYAUX_MAJOR,1)
 
 #undef TTY_DEBUG_HANGUP
 
@@ -386,7 +387,8 @@
 			continue;
 		if (!filp->f_dentry->d_inode)
 			continue;
-		if (filp->f_dentry->d_inode->i_rdev == CONSOLE_DEV)
+		if (filp->f_dentry->d_inode->i_rdev == CONSOLE_DEV ||
+		    filp->f_dentry->d_inode->i_rdev == SYSCONS_DEV)
 			continue;
 		if (filp->f_op != &tty_fops)
 			continue;
@@ -517,9 +519,7 @@
 void wait_for_keypress(void)
 {
         struct console *c = console_drivers;
-        while(c && !c->wait_key)
-                c = c->next;
-        if (c) c->wait_key();
+        if (c) c->wait_key(c);
 }
 
 void stop_tty(struct tty_struct *tty)
@@ -647,8 +647,13 @@
 	if (ppos != &file->f_pos)
 		return -ESPIPE;
 
+	/*
+	 *      For now, we redirect writes from /dev/console as
+	 *      well as /dev/tty0.
+	 */
 	inode = file->f_dentry->d_inode;
-	is_console = (inode->i_rdev == CONSOLE_DEV);
+	is_console = (inode->i_rdev == SYSCONS_DEV ||
+		      inode->i_rdev == CONSOLE_DEV);
 
 	if (is_console && redirect)
 		tty = redirect;
@@ -1182,13 +1187,20 @@
 		filp->f_flags |= O_NONBLOCK; /* Don't let /dev/tty block */
 		/* noctty = 1; */
 	}
+#ifdef CONFIG_VT
 	if (device == CONSOLE_DEV) {
+		extern int fg_console;
+		device = MKDEV(TTY_MAJOR, fg_console + 1);
+		noctty = 1;
+	}
+#endif
+	if (device == SYSCONS_DEV) {
 		struct console *c = console_drivers;
 		while(c && !c->device)
 			c = c->next;
 		if (!c)
                         return -ENODEV;
-                device = c->device();
+                device = c->device(c);
 		noctty = 1;
 	}
 	minor = MINOR(device);
@@ -1369,7 +1381,8 @@
 
 static int tioccons(struct tty_struct *tty, struct tty_struct *real_tty)
 {
-	if (tty->driver.type == TTY_DRIVER_TYPE_CONSOLE) {
+	if (tty->driver.type == TTY_DRIVER_TYPE_CONSOLE ||
+	    tty->driver.type == TTY_DRIVER_TYPE_SYSCONS) {
 		if (!suser())
 			return -EPERM;
 		redirect = NULL;
@@ -1890,16 +1903,17 @@
 	 * set up the console device so that later boot sequences can 
 	 * inform about problems etc..
 	 */
-#ifdef CONFIG_SERIAL_CONSOLE
-	kmem_start = serial_console_init(kmem_start, kmem_end);
-#endif
 #ifdef CONFIG_VT
 	kmem_start = con_init(kmem_start);
 #endif
+#ifdef CONFIG_SERIAL_CONSOLE
+	kmem_start = serial_console_init(kmem_start, kmem_end);
+#endif
 	return kmem_start;
 }
 
-static struct tty_driver dev_tty_driver, dev_console_driver;
+static struct tty_driver dev_tty_driver, dev_console_driver,
+	dev_syscons_driver;
 
 /*
  * Ok, now we can initialize the rest of the tty devices and can count
@@ -1930,17 +1944,28 @@
 	if (tty_register_driver(&dev_tty_driver))
 		panic("Couldn't register /dev/tty driver\n");
 
+	dev_syscons_driver = dev_tty_driver;
+	dev_syscons_driver.driver_name = "/dev/console";
+	dev_syscons_driver.name = dev_syscons_driver.driver_name + 5;
+	dev_syscons_driver.major = TTYAUX_MAJOR;
+	dev_syscons_driver.minor_start = 1;
+	dev_syscons_driver.type = TTY_DRIVER_TYPE_SYSTEM;
+	dev_syscons_driver.subtype = SYSTEM_TYPE_SYSCONS;
+
+	if (tty_register_driver(&dev_syscons_driver))
+		panic("Couldn't register /dev/console driver\n");
+
+#ifdef CONFIG_VT
 	dev_console_driver = dev_tty_driver;
-	dev_console_driver.driver_name = "/dev/console";
+	dev_console_driver.driver_name = "/dev/tty0";
 	dev_console_driver.name = dev_console_driver.driver_name + 5;
 	dev_console_driver.major = TTY_MAJOR;
 	dev_console_driver.type = TTY_DRIVER_TYPE_SYSTEM;
 	dev_console_driver.subtype = SYSTEM_TYPE_CONSOLE;
 
 	if (tty_register_driver(&dev_console_driver))
-		panic("Couldn't register /dev/console driver\n");
+		panic("Couldn't register /dev/tty0 driver\n");
 
-#ifdef CONFIG_VT
 	kbd_init();
 #endif
 #ifdef CONFIG_ESPSERIAL  /* init ESP before rs, so rs doesn't see the port */
diff -ruN linux-2.1.70.orig/drivers/char/vga.c linux-2.1.70/drivers/char/vga.c
--- linux-2.1.70.orig/drivers/char/vga.c	Thu Jul 31 22:09:17 1997
+++ linux-2.1.70/drivers/char/vga.c	Tue Dec  2 15:17:55 1997
@@ -153,6 +153,32 @@
 		hide_cursor();
 }
 
+__initfunc(int con_is_present(void))
+{
+	unsigned short saved;
+	unsigned short *p;
+
+	/*
+	 *	Find out if there is a graphics card present.
+	 *	Are there smarter methods around?
+	 */
+	p = (unsigned short *)(((ORIG_VIDEO_MODE == 7) ? 0xb0000 : 0xb8000) +
+			       + VGA_OFFSET);
+	saved = scr_readw(p);
+	scr_writew(0xAA55, p);
+	if (scr_readw(p) != 0xAA55) {
+		scr_writew(saved, p);
+		return 0;
+	}
+	scr_writew(0x55AA, p);
+	if (scr_readw(p) != 0x55AA) {
+		scr_writew(saved, p);
+		return 0;
+	}
+	scr_writew(saved, p);
+	return 1;
+}
+
 __initfunc(unsigned long
 con_type_init(unsigned long kmem_start, const char **display_desc))
 {
diff -ruN linux-2.1.70.orig/fs/proc/proc_tty.c linux-2.1.70/fs/proc/proc_tty.c
--- linux-2.1.70.orig/fs/proc/proc_tty.c	Thu Mar 27 23:40:06 1997
+++ linux-2.1.70/fs/proc/proc_tty.c	Tue Dec  2 15:17:55 1997
@@ -49,8 +49,10 @@
 		case TTY_DRIVER_TYPE_SYSTEM:
 			if (p->subtype == SYSTEM_TYPE_TTY)
 				type = "system:/dev/tty";
-			else if (p->subtype == SYSTEM_TYPE_CONSOLE)
+			else if (p->subtype == SYSTEM_TYPE_SYSCONS)
 				type = "system:console";
+			else if (p->subtype == SYSTEM_TYPE_CONSOLE)
+				type = "system:vtmaster";
 			else
 				type = "system";
 			break;
diff -ruN linux-2.1.70.orig/include/linux/console.h linux-2.1.70/include/linux/console.h
--- linux-2.1.70.orig/include/linux/console.h	Wed May 14 07:41:18 1997
+++ linux-2.1.70/include/linux/console.h	Wed Dec  3 13:20:57 1997
@@ -62,36 +62,40 @@
 struct tty_struct;
 int tioclinux(struct tty_struct *tty, unsigned long arg);
 
-/* The interface for /dev/console(s) and printk output */
-
-struct console
+/*
+ *	Array of consoles built from command line options (console=)
+ */
+struct console_cmdline
 {
-	/*
-	 * This function should not return before the string is written.
-	 */
-	void (*write)(const char*, unsigned);
-
-        /* To unblank the console in case of panic */
-        void (*unblank)(void);
+	char	name[8];			/* Name of the driver	    */
+	int	index;				/* Minor dev. to use	    */
+	char	*options;			/* Options for the driver   */
+};
+#define MAX_CMDLINECONSOLES 8
+extern struct console_cmdline console_list[MAX_CMDLINECONSOLES];
 
-	/*
-         * Only the console that was registered last with wait_key !=
-	 * NULL will be used. This blocks until there is a character
-	 * to give back, it does not schedule.
-         */
-	void (*wait_key)(void);
+/*
+ *	The interface for a console, or any other device that
+ *	wants to capture console messages (printer driver?)
+ */
 
-	/*
-	 * Return the device to use when opening /dev/console. Only the
-	 * last registered console will do.
-	 */
-	int (*device)(void);
+#define CON_PRINTBUFFER	(1)
+#define CON_FIRST	(2)
+#define CON_ENABLED	(4)
 
-	/* 
-	 * For a linked list of consoles for multiple output. Any console
-         * not at the head of the list is used only for output.
-	 */
-	struct console *next;
+struct console
+{
+	char	name[8];
+	void	(*write)(struct console *, const char *, unsigned);
+	int	(*read)(struct console *, const char *, unsigned);
+	kdev_t	(*device)(struct console *);
+	int	(*wait_key)(struct console *);
+        void	(*unblank)(void);
+	void	(*setup)(struct console *, char *);
+	short	flags;
+	short	index;
+	int	cflag;
+	struct	 console *next;
 };
 
 extern void register_console(struct console *);
diff -ruN linux-2.1.70.orig/include/linux/tty_driver.h linux-2.1.70/include/linux/tty_driver.h
--- linux-2.1.70.orig/include/linux/tty_driver.h	Mon Dec  1 22:26:04 1997
+++ linux-2.1.70/include/linux/tty_driver.h	Wed Dec  3 22:44:55 1997
@@ -207,10 +207,12 @@
 #define TTY_DRIVER_TYPE_SERIAL		0x0003
 #define TTY_DRIVER_TYPE_PTY		0x0004
 #define TTY_DRIVER_TYPE_SCC		0x0005	/* scc driver */
+#define TTY_DRIVER_TYPE_SYSCONS		0x0006
 
 /* system subtypes (magic, used by tty_io.c) */
 #define SYSTEM_TYPE_TTY			0x0001
 #define SYSTEM_TYPE_CONSOLE		0x0002
+#define SYSTEM_TYPE_SYSCONS		0x0003
 
 /* pty subtypes (magic, used by tty_io.c) */
 #define PTY_TYPE_MASTER			0x0001
diff -ruN linux-2.1.70.orig/init/main.c linux-2.1.70/init/main.c
--- linux-2.1.70.orig/init/main.c	Wed Dec  3 12:09:33 1997
+++ linux-2.1.70/init/main.c	Wed Dec  3 12:10:15 1997
@@ -84,6 +84,7 @@
 extern void panic_setup(char *str, int *ints);
 extern void bmouse_setup(char *str, int *ints);
 extern void msmouse_setup(char *str, int *ints);
+extern void console_setup(char *str, int *ints);
 #ifdef CONFIG_PRINTER
 extern void lp_setup(char *str, int *ints);
 #endif
@@ -352,6 +353,7 @@
 	{ "swap=", swap_setup },
 	{ "buff=", buff_setup },
 	{ "panic=", panic_setup },
+	{ "console=", console_setup },
 #ifdef CONFIG_VT
 	{ "no-scroll", no_scroll },
 #endif
@@ -1022,7 +1024,7 @@
 
 	close(0);close(1);close(2);
 	setsid();
-	(void) open("/dev/tty1",O_RDWR,0);
+	(void) open("/dev/console",O_RDWR,0);
 	(void) dup(0);
 	(void) dup(0);
 	return execve(shell, argv, envp_init);
@@ -1106,8 +1108,8 @@
 
 	setup(1);
 	
-	if (open("/dev/console",O_RDWR,0) < 0)
-		printk("Unable to open an initial console.\n");
+	if (open("/dev/console", O_RDWR, 0) < 0)
+		printk("Warning: unable to open an initial console.\n");
 
 	(void) dup(0);
 	(void) dup(0);
diff -ruN linux-2.1.70.orig/kernel/printk.c linux-2.1.70/kernel/printk.c
--- linux-2.1.70.orig/kernel/printk.c	Sat Nov 29 19:41:10 1997
+++ linux-2.1.70/kernel/printk.c	Wed Dec  3 22:05:38 1997
@@ -53,6 +53,46 @@
 static char log_buf[LOG_BUF_LEN];
 static unsigned long log_start = 0;
 static unsigned long logged_chars = 0;
+struct console_cmdline console_cmdline[MAX_CMDLINECONSOLES];
+static int selected_console = 0;
+
+/*
+ *	Setup a list of consoles. Called from init/main.c
+ */
+__initfunc(void console_setup(char *str, int *ints))
+{
+	char *s;
+	int i;
+	struct console_cmdline *c;
+
+	for(i = 0; i < MAX_CMDLINECONSOLES && console_cmdline[i].name[0]; i++)
+		;
+	if (i == MAX_CMDLINECONSOLES)
+		return;
+	c = &console_cmdline[i];
+	selected_console = 1;
+
+	if (str[0] >= '0' && str[0] <= '9') {
+		strcpy(c->name, "ttyS");
+		strncpy(c->name + 4, str, sizeof(c->name) - 5);
+	} else
+		strncpy(c->name, str, sizeof(c->name) - 1);
+	if ((c->options = strchr(str, ',')) != NULL)
+		*(c->options++) = 0;
+#ifdef __sparc__
+	if (strcmp(str, "ttya"))
+		strcpy(c->name, "ttyS0");
+	if (strcmp(str, "ttyb"))
+		strcpy(c->name, "ttyS1");
+#endif
+
+	for(s = c->name; *s; s++)
+		if (*s >= '0' && *s <= '9')
+			break;
+	c->index = simple_strtoul(s, NULL, 10);
+	*s = 0;
+}
+
 
 /*
  * Commands to sys_syslog:
@@ -222,8 +262,8 @@
 		if (msg_level < console_loglevel && console_drivers) {
 			struct console *c = console_drivers;
 			while(c) {
-				if (c->write)
-					c->write(msg, p - msg + line_feed);
+				if ((c->flags & CON_ENABLED) && c->write)
+					c->write(c, msg, p - msg + line_feed);
 				c = c->next;
 			}
 		}
@@ -239,9 +279,10 @@
 {
 	struct console *c = console_drivers;
 	int len = strlen(s);
+
 	while(c) {
-		if (c->write)
-			c->write(s, len);
+		if ((c->flags & CON_ENABLED) && c->write)
+			c->write(c, s, len);
 		c = c->next;
 	}
 }
@@ -250,7 +291,7 @@
 {
 	struct console *c = console_drivers;
 	while(c) {
-		if (c->unblank)
+		if ((c->flags & CON_ENABLED) && c->unblank)
 			c->unblank();
 		c = c->next;
 	}
@@ -262,7 +303,7 @@
  * print any messages that were printed by the kernel before the
  * console driver was initialized.
  */
-__initfunc(void register_console(struct console * console))
+void register_console(struct console * console)
 {
 	int	i,j,len;
 	int	p = log_start;
@@ -270,9 +311,52 @@
 	signed char msg_level = -1;
 	char	*q;
 
-	console->next = console_drivers;
-	console_drivers = console;
+	/*
+	 *	See if we want to use this console driver. If we
+	 *	didn't select a console we take the first one
+	 *	that registers here.
+	 */
+	if (selected_console == 0) {
+		console->flags |= CON_ENABLED | CON_FIRST;
+		selected_console = 1;
+		if (console->setup)
+			console->setup(console, NULL);
+	}
+	/*
+	 *	See if this console matches one we selected on
+	 *	the command line.
+	 */
+	for(i = 0; i < MAX_CMDLINECONSOLES && console_cmdline[i].name[0]; i++) {
+		if (strcmp(console_cmdline[i].name, console->name) != 0)
+			continue;
+		if (console->index >= 0 &&
+		    console->index != console_cmdline[i].index)
+			continue;
+		console->flags |= CON_ENABLED;
+		console->index = console_cmdline[i].index;
+		if (i == 0)
+			console->flags |= CON_FIRST;
+		if (console->setup)
+			console->setup(console, console_cmdline[i].options);
+		break;
+	}
+
+	/*
+	 *	Put this console in the list - keep the
+	 *	preferred driver at the head of the list.
+	 */
+	if ((console->flags & CON_FIRST) || console_drivers == NULL) {
+		console->next = console_drivers;
+		console_drivers = console;
+	} else {
+		console->next = console_drivers->next;
+		console_drivers->next = console;
+	}
+	if ((console->flags & CON_PRINTBUFFER) == 0) return;
 
+	/*
+	 *	Print out buffered log messages.
+	 */
 	for (i=0,j=0; i < log_size; i++) {
 		buf[j++] = log_buf[p];
 		p++; p &= LOG_BUF_LEN-1;
@@ -287,7 +371,7 @@
 			len -= 3;
 		}
 		if (msg_level < console_loglevel)
-			console->write(q, len);
+			console->write(console, q, len);
 		if (buf[j-1] == '\n')
 			msg_level = -1;
 		j = 0;
-- 
 Miquel van Smoorenburg |  Studying to be a technomage   <*>
    miquels@cistron.nl  | "May you live in interesting times"
