/*
 *  rt_printk.c, hacked from linux/kernel/printk.c
 *
 * Modified for RT support, David Schleef
 */

/* #ifdef MODULE
#define EXPORT_SYMTAB
#endif
*/

#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/string.h>
#include <linux/sched.h>
#include <asm/irq.h>
#include <asm/ptrace.h>
#include <linux/rtl.h>
#include <string.h>


#define BUF_LEN	(16384)

static char rt_printk_buf[BUF_LEN];
static char buf[1024];

static int buf_front,buf_back;

static int rt_printk_irq;

/* this is rather bogus, but it will catch most errors */
static volatile int rt_printk_lock;

static void rt_printk_interrupt(int irq,void *junk,struct pt_regs *regs);

int rt_printk(const char *fmt, ...)
{
	va_list args;
	int len,i;

	if(rtl_rt_system_is_idle()){
		va_start(args, fmt);
		len=printk(fmt,args);
		va_end(args);
		return len;
	}

	if(rt_printk_lock){
		/* force a panic */
		*(int *)0=0;
	}
	rt_printk_lock=1;

	va_start(args, fmt);
	len = vsprintf(buf, fmt, args); /* hopefully i < sizeof(buf)-1 */
	va_end(args);

	if(buf_front+len>=BUF_LEN){
		i=BUF_LEN-buf_front;
		memcpy(rt_printk_buf+buf_front,buf,i);
		memcpy(rt_printk_buf,buf+i,len-i);
		buf_front=len-i;
	}else{
		memcpy(rt_printk_buf+buf_front,buf,len);
		buf_front+=len;
	}

	rt_printk_lock=0;

	rtl_global_pend_irq(rt_printk_irq);

	return len;
}


void rt_printk_interrupt(int irq,void *junk,struct pt_regs *regs)
{
	int tmp;

	for(;;){
		tmp=buf_front;
		if(buf_back>tmp){
			printk("%.*s",BUF_LEN-buf_back,rt_printk_buf+buf_back);
			buf_back=0;
		}
		if(buf_back==tmp)break;
		printk("%.*s",tmp-buf_back,rt_printk_buf+buf_back);
		buf_back=tmp;
	}
}

#ifdef MODULE
#define INIT_MODULE init_module

void cleanup_module(void)
{
	free_irq(rt_printk_irq,NULL);
	rt_printk_irq=0;
}

#else
#define INIT_MODULE init_rt_printk
#endif

int INIT_MODULE(void)
{
	rt_printk_irq=rtl_get_soft_irq(rt_printk_interrupt,"rt_printk");
	if(rt_printk_irq<0){
		printk("rt_printk: can't allocate soft irq\n");
		return -EIO;
	}

	return 0;
}

/* exported functions */

/* EXPORT_SYMBOL(rt_printk); */

