//! \file uart.c
//! \author tkl
//! \date Jul 15, 2012
//! \brief Source file of the architecture independent uart implementation.

#include <stddef.h>
#include <stdbool.h>

#include "ringbuffer.h"
#include "irq.h"
#include "stack.h"
#include "queue.h"
#include "thread.h"
#include "schedule.h"
#include "uart.h"

extern volatile struct thread_context *current_thread;
static const void *uart_it_callback(const void *param);

int uart_open(const struct uart *this)
{
	if(NULL == this)
		return (-1);

	int ret = this->fp->set_cb(this->arch_dep_device, uart_it_callback, this);
	ret |= this->fp->open(this->arch_dep_device);
	return (ret);
}

int uart_close(const struct uart *this)
{
	if(NULL == this)
		return (-1);

	blocking_read_wakeup((const void *)this);
	return (this->fp->close(this->arch_dep_device));
}

int uart_read(const struct uart *this, char *buffer, int len)
{
	int ret = -1;
	unsigned int irq;
	if(NULL == this)
		return (-1);
	irq = disable_irq();
	if(ringbuffer_is_empty(this->buffer)) {
		current_thread->status = THREAD_STATUS_BLOCKING;
		current_thread->wakeup_blocking_source = (void*) this;
		restore_irq(irq);
		schedule();
	}
	ret = ringbuffer_read(this->buffer, buffer, len);
	restore_irq(irq);
	return ret;
}

int uart_write(const struct uart *this, const char *buffer, int len)
{
	if(NULL == this)
		return (-1);

	return (this->fp->write(this->arch_dep_device, buffer, len));
}

static const void *uart_it_callback(const void *param)
{
	if(NULL == param)
		return (NULL);

	char c = 0;
	struct uart *uart = (struct uart *)param;
	uart->fp->read((const void *)uart->arch_dep_device, &c, 1);
	if(!ringbuffer_is_full(uart->buffer))
		ringbuffer_write(uart->buffer, &c, 1);
	blocking_read_wakeup(param);

	return (param);
}
