diff -u -r --new-file -x *.a -x *.o -x *.s -x .* linux-2.5.34/CREDITS linux-2.5.34-hc/CREDITS
--- linux-2.5.34/CREDITS	Wed Sep 11 23:25:49 2002
+++ linux-2.5.34-hc/CREDITS	Tue Aug 27 14:33:42 2002
@@ -578,6 +578,8 @@
 N: Hamish Coleman
-E: hamish@zot.apana.org.au
+E: hamish@zot.org
 D: SEEQ8005 network driver
+D: Circular Log File
+D: Random patches
 S: 98 Paxton Street
 S: East Malvern, Victoria, 3145
 S: Australia
diff -u -r --new-file -x *.a -x *.o -x *.s -x .* linux-2.5.34/drivers/char/Config.help linux-2.5.34-hc/drivers/char/Config.help
--- linux-2.5.34/drivers/char/Config.help	Wed Sep 11 23:26:53 2002
+++ linux-2.5.34-hc/drivers/char/Config.help	Wed Sep 11 17:21:49 2002
@@ -1123,3 +1123,9 @@
   Once bound, I/O against /dev/raw/rawN uses efficient zero-copy I/O. 
   See the raw(8) manpage for more details.
 
+CONFIG_CLOG
+  Implements a circular buffer log file.  Useful for sending log
+  messages to, so that the most recent messages can be viewed, without
+  requiring a writable storage medium.  Adds approximately 1.5 K to
+  the kernel size
+
diff -u -r --new-file -x *.a -x *.o -x *.s -x .* linux-2.5.34/drivers/char/Config.in linux-2.5.34-hc/drivers/char/Config.in
--- linux-2.5.34/drivers/char/Config.in	Wed Sep 11 23:26:18 2002
+++ linux-2.5.34-hc/drivers/char/Config.in	Wed Sep 11 15:56:54 2002
@@ -205,5 +205,6 @@
 fi
 
 tristate '  RAW driver (/dev/raw/rawN)' CONFIG_RAW_DRIVER
+tristate '  Circular buffer Log file' CONFIG_CLOG
 
 endmenu
diff -u -r --new-file -x *.a -x *.o -x *.s -x .* linux-2.5.34/drivers/char/Makefile linux-2.5.34-hc/drivers/char/Makefile
--- linux-2.5.34/drivers/char/Makefile	Wed Sep 11 23:27:14 2002
+++ linux-2.5.34-hc/drivers/char/Makefile	Wed Sep 11 20:36:39 2002
@@ -48,6 +48,7 @@
 obj-$(CONFIG_SERIAL_TX3912) += generic_serial.o serial_tx3912.o
 obj-$(CONFIG_HVC_CONSOLE) += hvc_console.o
 obj-$(CONFIG_RAW_DRIVER) += raw.o
+obj-$(CONFIG_CLOG) += clog.o
 
 obj-$(CONFIG_PRINTER) += lp.o
 
diff -u -r --new-file -x *.a -x *.o -x *.s -x .* linux-2.5.34/drivers/char/clog.c linux-2.5.34-hc/drivers/char/clog.c
--- linux-2.5.34/drivers/char/clog.c	Thu Jan  1 10:00:00 1970
+++ linux-2.5.34-hc/drivers/char/clog.c	Sat Aug 31 00:45:56 2002
@@ -0,0 +1,363 @@
+/*
+ *	Circular log file buffer for linux
+ *
+ *	Copyright (C) 2002 Hamish Coleman
+ *
+ *	This simplistic driver was written to provide a destination
+ *	for syslog on a system with read-only disks.
+ *
+ *	All writes are appended to the buffer.
+ *	All reads start reading from the oldest remaining data
+ *
+ *	The buffer is currently fixed in size, but I mean to fix that
+ *	eventually.
+ *
+ *	This device is also one possible solution for the early bootup
+ *	syslog destination issue that recently was discussed.  After
+ *	thinking about it, I believe that the user-space printk
+ *	solution is a better and more general answer, but that this
+ *	style of circular buffer is a good catch-all destination for
+ *	syslogd once it is actually started.
+ *
+ * See ../../COPYING for copyright details
+ *
+ *	0.90	This version
+ *	0.94	Changes to make it like a real file.
+ *		Working happily
+ *
+ * TODO
+ *	- define propper IOCTLs
+ *	- allow resizing of buffer
+ *	- allow multiple buffers
+ *	- test poll() code
+ *	- async io (overkill?)
+ *	- Fix open to not overwrite permissions (do it once in init)
+ */
+
+#define CLOG_VERSION	"0.94"
+static char clog_version[] = CLOG_VERSION;
+
+#include <linux/miscdevice.h>
+#include <linux/module.h>
+#include <linux/poll.h>
+#include <linux/init.h>
+
+/*#include <linux/kernel.h>*/
+/*#include <linux/string.h>*/
+/*#include <linux/fcntl.h>*/
+/*#include <linux/fs.h>*/
+/*#include <linux/tqueue.h>*/
+
+#if 0
+#define DPRINTK(fmt, arg...)	printk(KERN_INFO fmt, ##arg)
+#else
+#define DPRINTK(fmt, arg...)	{}
+#endif
+
+/*
+ * Lots of code stolen from printk.c
+ */
+
+#define	LOG_BUF_LEN	(1<<20)
+#define LOG_BUF_MASK	(LOG_BUF_LEN-1)
+
+/*
+ * Guard against a system that does not fully support 64-bit file access
+ * by clamping the inode size at approx 2^31 -2
+ * This stops fstat from resulting in -EOVERFLOW
+ */
+#define INODE_SIZE_MAX	( 2147483645 ) 
+
+/*
+ * Only needed if I implement polling features
+ */
+DECLARE_WAIT_QUEUE_HEAD(clog_wait);
+
+/* 
+ * protects log_end
+ */
+static spinlock_t clog_lock = SPIN_LOCK_UNLOCKED;
+
+static char clog_buf[LOG_BUF_LEN];
+#define LOG_BUF(idx) (clog_buf[(idx) & LOG_BUF_MASK])
+
+/*
+ * the indices into log_buf are not constrained - they must be masked before use
+ */
+static unsigned long clog_end;		/* most recently written char + 1 */
+static unsigned long clog_start;		/* "next char to read" - initialize value for ppos on read */
+static unsigned long clogged_chars;	/* number of valid chars in buffer */
+
+
+static ssize_t clog_read(struct file *file, char *data, size_t len, loff_t *ppos) {
+	unsigned long i;
+	unsigned long p = *ppos;
+	char c;
+	int error = 0;
+
+	DPRINTK("clog: read len=%li p=%lu clog_start=%u clog_end=%u clogged_chars=%u\n",
+		len,p,clog_start,clog_end,clogged_chars);
+
+	error = -EINVAL;
+	if (!data || len < 0)
+		goto out;
+	error = 0;
+	if (!len)
+		goto out;
+	error = verify_area(VERIFY_WRITE,data,len);
+	if (error)
+		goto out;
+
+#if 0
+	/*
+	 * Dont need this kind of code unless we wish to wait for new data
+	 *
+	 * I have decided that it makes more sense for the file to work exactly
+	 * like a real log file - cating it will give you the entire current
+	 * file, tail -f ing it will show new data as it arrives.  This means
+	 * that a non-blocking interface is not wanted
+	 *
+	 */
+	if (!(clog_end - p)) {
+		if (file->f_flags & O_NONBLOCK) {
+			error=-EAGAIN;
+			goto out;
+		}
+		
+		error = wait_event_interruptible(clog_wait, (clog_end - p));
+		if (error)
+			goto out;
+	}
+#endif
+
+	/*
+	 * I am not sure about the spin locking here.  Its mostly identical
+	 * to whats in the printk read function, but I am not entirely sure
+	 * what I am protecting against with my other changes.  I think I
+	 * want to compare log_end and get the buffer char atomically.  I also
+	 * think that I should drop the lock while doing the put_user.
+	 * Its all a little too much theory...
+	 */
+
+	i = 0;
+	spin_lock_irq(&clog_lock);
+	while ((p != clog_end) && i < len) {
+		if (p < clog_start)
+			p = clog_start;
+		c = LOG_BUF(p);
+		p++;
+		spin_unlock_irq(&clog_lock);
+		__put_user(c,data);
+		spin_lock_irq(&clog_lock);
+		data++;
+		i++;
+	}
+	spin_unlock_irq(&clog_lock);
+	*ppos = p;
+
+	file->f_dentry->d_inode->i_size = (clog_end > INODE_SIZE_MAX) ? INODE_SIZE_MAX : clog_end;
+	UPDATE_ATIME(file->f_dentry->d_inode);
+
+	error=i;
+out:
+	DPRINTK("clog: read2 len=%li p=%lu clog_start=%u clog_end=%u clogged_chars=%u\n",
+		len,p,clog_start,clog_end,clogged_chars);
+
+	return error;
+}
+
+static ssize_t clog_write(struct file *file, const char *data, size_t len, loff_t *ppos) {
+	unsigned long written;
+	char c;
+	int error = 0;
+
+	DPRINTK("clog: write len=%li ppos=%li clog_start=%u clog_end=%u clogged_chars=%u\n",
+		len,*ppos,clog_start,clog_end,clogged_chars);
+
+	error = -EINVAL;
+	if (!data || len < 0)
+		goto out;
+	error = 0;
+	if (!len)
+		goto out;
+	error = verify_area(VERIFY_READ,data,len);
+	if (error)
+		goto out;
+
+	written = 0;
+	while (written < len) {
+		__get_user(c,data);
+
+		/*
+		 * Once again, I'm in the dark about my locking.  I'm just protecting
+		 * updates to my contended variable
+		 */
+		spin_lock_irq(&clog_lock);
+		LOG_BUF(clog_end) = c;
+		clog_end++; 
+
+		/* 
+		 * if you wish to be paranoid, the following test should include
+		 * a || (log_start > log_end), but that test probably wouldnt be
+		 * needed until the system has been up for over a Million years.
+		 * We are safe until the number of characters logged overflows
+		 * the long int used to store log_end.
+		 *
+		 * printk would need the same test if you _are_ worried.
+		 */
+		if (clog_end - clog_start > LOG_BUF_LEN)
+			clog_start = clog_end - LOG_BUF_LEN;
+		if (clogged_chars < LOG_BUF_LEN)
+			clogged_chars++;
+		spin_unlock_irq(&clog_lock);
+
+		data++;
+		written++;
+	}
+
+	*ppos=clog_end;
+
+	file->f_dentry->d_inode->i_size = (clog_end > INODE_SIZE_MAX) ? INODE_SIZE_MAX : clog_end;
+	file->f_dentry->d_inode->i_mtime = CURRENT_TIME;
+	mark_inode_dirty(file->f_dentry->d_inode);
+
+	wake_up_interruptible(&clog_wait);
+
+	error=written;
+
+out:
+	DPRINTK("clog: write2 len=%li ppos=%li clog_start=%u clog_end=%u clogged_chars=%u\n",
+		len,*ppos,clog_start,clog_end,clogged_chars);
+
+	return error;
+}
+
+static int clog_ioctl(struct inode *inode, struct file *file,
+	unsigned int cmd, unsigned long arg)
+{
+	switch(cmd) {
+		case 1:
+			if (copy_to_user((char *)arg, &clog_version, sizeof(clog_version)))
+				return -EFAULT;
+			return 0;
+		case 2:
+			return clogged_chars;
+		case 3:
+			return clog_start;
+		case 4:
+			return clog_end;
+		case 5: /* clear */
+			if (!capable(CAP_SYS_ADMIN))
+				return -EPERM;
+			spin_lock_irq(&clog_lock);
+			clog_end = 0;
+			clog_start = 0;
+			clogged_chars = 0;
+			spin_unlock_irq(&clog_lock);
+			return 0;
+		case 6: /* resize */
+		default:
+			return -ENOTTY;
+	}
+}
+
+static unsigned int clog_poll(struct file *file, poll_table *wait) {
+	poll_wait(file, &clog_wait, wait);
+	if ((clog_end - file->f_pos) >0)
+		return POLLIN | POLLRDNORM;
+	return 0;
+}
+
+static int clog_open(struct inode *inode, struct file *file) {
+	file->f_dentry->d_inode->i_mode = S_IFREG | S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH;
+	file->f_dentry->d_inode->i_size = (clog_end > INODE_SIZE_MAX) ? INODE_SIZE_MAX : clog_end;
+	file->f_dentry->d_inode->i_blksize = 1024;
+	file->f_dentry->d_inode->i_blocks = (LOG_BUF_LEN / 1024);
+	return 0;
+}
+
+/*
+ * this is actually not required for proper operation.
+ * Most of the time, we dont really care what the file pointer says
+ */
+#if 0
+static long long clog_llseek(struct file *file, long long offset, int orig) {
+	ssize_t ret;
+
+	switch(orig) {
+		case 0:
+			if (offset < 0) {
+				ret = -EINVAL;
+				goto out;
+			}
+			if (offset > clog_end) {
+				ret = -EINVAL;
+				goto out;
+			}
+			file->f_pos = offset;
+			ret = file->f_pos;
+			break;
+		case 1:
+			if ((file->f_pos + offset) > clog_end) {
+				ret = -EINVAL;
+				goto out;
+			}
+			if ((file->f_pos + offset) < clog_start) {
+				ret = -EINVAL;
+				goto out;
+			}
+			file->f_pos += offset;
+			ret = file->f_pos;
+			break;
+		case 2:
+			if (offset > 0) {
+				ret = -EINVAL;
+				goto out;
+			}
+			if ((clog_end + offset) < clog_start) {
+				ret = -EINVAL;
+				goto out;
+			}
+			file->f_pos = clog_end + offset;
+			ret = file->f_pos;
+			break;
+		default:
+			ret = -EINVAL;
+	}
+
+out:
+	return ret;
+}
+#endif
+
+static struct file_operations clog_fops = {
+	.owner		= THIS_MODULE,
+	.open		= clog_open,
+	.write		= clog_write,
+	.read		= clog_read,
+	.ioctl		= clog_ioctl,
+/*	.poll		= clog_poll,*/
+};
+
+static struct miscdevice clog_miscdev = {
+	MISC_DYNAMIC_MINOR,
+	"clog",
+	&clog_fops
+};
+
+static int __init clog_init(void) {
+	DPRINTK("clog: init\n");
+	return misc_register(&clog_miscdev);
+}
+
+static void __exit clog_exit(void) {
+	misc_deregister(&clog_miscdev);
+}
+
+MODULE_AUTHOR("Hamish Coleman <hamish@zot.org>");
+MODULE_DESCRIPTION("Circular buffer log file");
+MODULE_LICENSE("GPL");
+
+module_init(clog_init);
+module_exit(clog_exit);
+
