/***************************************************************************//**
 * @file  irq.cpp
 * @brief Implementation of Interrupt No-OS platform driver interfaces
********************************************************************************
 * Copyright (c) 2020 Analog Devices, Inc.
 *
 * All rights reserved.
 *
 * This software is proprietary to Analog Devices, Inc. and its licensors.
 * By using this software you agree to the terms of the associated
 * Analog Devices Software License Agreement.
*******************************************************************************/

/******************************************************************************/
/***************************** Include Files **********************************/
/******************************************************************************/

#include <stdio.h>
#include <mbed.h>

#include "platform_drivers.h"
#include "irq_extra.h"
#include "uart_extra.h"

/******************************************************************************/
/*************************** Types Declarations *******************************/
/******************************************************************************/

/******************************************************************************/
/************************ Functions Declarations ******************************/
/******************************************************************************/

/******************************************************************************/
/************************ Functions Definitions *******************************/
/******************************************************************************/

/**
 * @brief	Initialized the controller for the peripheral interrupts
 * @param	desc[in, out] - Pointer where the configured instance is stored
 * @param	param[in] - Configuration information for the instance
 * @return	SUCCESS in case of success, FAILURE otherwise.
 */
int32_t irq_ctrl_init(struct irq_ctrl_desc **desc,
		      const struct irq_init_param *param)
{
	if (!desc || !param) {
		return FAILURE;
	}

	irq_ctrl_desc *new_desc = (irq_ctrl_desc *)malloc(sizeof(irq_ctrl_desc));
	if (!new_desc) {
		return FAILURE;
	}

	mbed_irq_desc *new_mbed_desc = (mbed_irq_desc *)malloc(sizeof(mbed_irq_desc));
	if (!new_mbed_desc) {
		free(new_desc);
		return FAILURE;
	}

	new_desc->irq_ctrl_id = param->irq_ctrl_id;

	new_mbed_desc->int_mode = ((mbed_irq_init_param *)param->extra)->int_mode;
	new_mbed_desc->int_obj_type = ((mbed_irq_init_param *)
				       param->extra)->int_obj_type;

	new_desc->extra = (irq_ctrl_desc *)new_mbed_desc;

	*desc = new_desc;

	return SUCCESS;
}


/**
 * @brief	Free the resources allocated by irq_ctrl_init()
 * @param	desc[in, out] - Interrupt controller descriptor.
 * @return	SUCCESS in case of success, FAILURE otherwise.
 */
int32_t irq_ctrl_remove(struct irq_ctrl_desc *desc)
{
	if (!desc) {
		return FAILURE;
	}

	if (((mbed_irq_desc *)desc->extra)->int_obj) {
		free(((mbed_irq_desc *)desc->extra)->int_obj);
	}

	if ((irq_ctrl_desc *)desc->extra) {
		free((irq_ctrl_desc *)desc->extra);
	}

	free(desc);

	return SUCCESS;
}


/**
 * @brief	Registers a IRQ callback function to irq controller.
 * @param	desc[in] - The IRQ controller descriptor.
 * @param	irq_id[in] - Interrupt identifier.
 * @param	callback_desc - Descriptor of the callback. If it is NULL, the
 *			callback will be unregistered
 * @return	SUCCESS in case of success, FAILURE otherwise.
 */
int32_t irq_register_callback(struct irq_ctrl_desc *desc,
			      uint32_t irq_id,
			      struct callback_desc *callback_desc)
{
	InterruptIn *ext_interrupt;
	mbed::UnbufferedSerial *uart_rx_port;
	PinName *ext_int_pin;

	if (!desc || !callback_desc) {
		return FAILURE;
	}

	switch (irq_id) {
	case EXTERNAL_INT_ID:
		/* Attach external interrupt to input pin */
		ext_int_pin = (PinName *)((mbed_irq_desc *)(desc->extra))->int_obj_type;
		if (!ext_int_pin) {
			return FAILURE;
		}

		ext_interrupt = new InterruptIn(*ext_int_pin);
		if (!ext_interrupt) {
			return FAILURE;
		}

		/* Register a callback function to get external interrupt input */
		if (((mbed_irq_desc *)(desc->extra))->int_mode == EXT_IRQ_FALL) {
			ext_interrupt->fall(callback_desc->callback);
		} else if (((mbed_irq_desc *)(desc->extra))->int_mode == EXT_IRQ_RISE) {
			ext_interrupt->rise(callback_desc->callback);
		} else {
			return FAILURE;
		}

		/* Store the external interrupt object to be freed from irq_ctrl_remove() */
		((mbed_irq_desc *)(desc->extra))->int_obj = ext_interrupt;

		ext_interrupt->enable_irq();

		break;

	case UART_RX_INT_ID:
		uart_rx_port = (mbed::UnbufferedSerial *)(((mbed_irq_desc *)(
					desc->extra))->int_obj_type);
		if (!uart_rx_port) {
			return FAILURE;
		}

		/* Register a callback function to receive UnbufferedSerial port event */
		uart_rx_port->attach(callback_desc->callback, UnbufferedSerial::RxIrq);

		break;

	default:
		return FAILURE;
	}

	return SUCCESS;
}
