/** 
 * \file pio.c
 * \brief pio source file
 * \details File contains functions definitions for Parallel Input/Output Controller
 *
 * \author Juraj Golej
 * \version 1.0
 */ 

#include "pio.h"

static struct linearlist gList = { NULL };

/**
 * \brief Interrupt routine
 * 
 * \param[in] port Pointer on \c Pio* instance
 * 
 * \return void
 */
static inline void _PIO_irq_realize(Pio* port)
{
	/* Read ISR register (automatically clear it) */
	uint32_t isrReg = port->PIO_ISR;

	struct PIO_irq* module = linearlist_get_head(&gList);
	while(module) {
		if((module->port == port) && (module->port->PIO_IMR & (1 << module->pin)) && (isrReg & (1 << module->pin))) {
			module->callback(module);
		}
		module = linearlist_get_next(module);
	}
}

void PIOA_Handler()
{
	_PIO_irq_realize(PIOA);
}

void PIOB_Handler()
{
	_PIO_irq_realize(PIOB);
}

void PIOC_Handler()
{
	_PIO_irq_realize(PIOC);
}

void PIOD_Handler()
{
	_PIO_irq_realize(PIOD);
}

void PIOE_Handler()
{
	_PIO_irq_realize(PIOE);
}

enum retCode PIO_interrupt_init(struct PIO_irq* const module, const uint8_t pinCode, enum PIO_irq_type irqType, PIO_irq_cb callback)
{
	/* Check NULL pointers */
	Assert(module);

	/* Check, if callback isn't NULL */
	Try(!callback, EInvalidParameter);

	Pio* port = PIO_PORT(pinCode);
	uint8_t pin = PIO_PIN(pinCode);

	/* Set edge/level detection */
	switch(irqType) {
		case PIO_IRQ_BOTH_EDGE:
			port->PIO_AIMDR = (1 << pin);
			break;
		case PIO_IRQ_RISING_EDGE:
			port->PIO_REHLSR = (1 << pin);
			port->PIO_ESR = (1 << pin);
			break;
		case PIO_IRQ_FALLING_EDGE:
			port->PIO_FELLSR = (1 << pin);
			port->PIO_ESR = (1 << pin);
			break;
		case PIO_IRQ_LOW_LEVEL:
			port->PIO_FELLSR = (1 << pin);
			port->PIO_LSR = (1 << pin);
			break;
		case PIO_IRQ_HIGH_LEVEL:
			port->PIO_REHLSR = (1 << pin);
			port->PIO_LSR = (1 << pin);
			break;
		default:
			return EInvalidParameter;
	}
	if(irqType != PIO_IRQ_BOTH_EDGE) {
		port->PIO_AIMER = (1 << pin);
	}

	/* Register user defined interrupt function */
	module->pin = pin;
	module->port = port;
	module->callback = callback;
	linearlist_push(&gList, module);

	return ENone;
}

enum retCode PIO_interrupt_deinit(struct PIO_irq* const module)
{
	/* Check NULL pointers */
	Assert(module);

	/* Check, if this module is initialized */
	Try(!linearlist_is_member(&gList, module), EDenied);

	/* Disable interrupt */
	module->port->PIO_IDR = (1 << module->pin);

	/* Remove from linear list */
	linearlist_delete(&gList, module);

	return ENone;
}