/*
 * stm32f4_uart.c
 *
 *  Created on: Jul 24, 2016
 *      Author: tkl
 */

#include <stddef.h>
#include <stdint.h>

#include "stm32f4xx.h"
#include "stm32f4xx_isr.h"
#include "stm32f4_uart.h"

struct stm32f4_uart_obj {
	const void *callback;	//!< Interrupt callback.
	const void *parameter;	//!< argument for the callback.
};

static volatile struct stm32f4_uart_obj uart1_obj;

int stm32f4_uart_open(const void *this)
{
	struct stm32f4_uart *uart = (struct stm32f4_uart *)this;
	uint8_t gpio_af = 0;
	uint32_t rcc_apb_uart = 0, rcc_apb_gpio = 0;

	if(uart->usart_port == USART1) {
		gpio_af = GPIO_AF_USART1;
		rcc_apb_uart = RCC_APB2Periph_USART1;
	}
	if(uart->gpio_port == GPIOA) {
		rcc_apb_gpio = RCC_AHB1Periph_GPIOA;
	}
	else if(uart->gpio_port == GPIOB) {
		rcc_apb_gpio = RCC_AHB1Periph_GPIOB;
	}

	RCC_APB2PeriphClockCmd(rcc_apb_uart, ENABLE);
	RCC_AHB1PeriphClockCmd(rcc_apb_gpio, ENABLE);

	GPIO_Init(uart->gpio_port, (GPIO_InitTypeDef *)uart->gpio_init);

	GPIO_PinAFConfig(uart->gpio_port, uart->pin_src_rx, gpio_af);
    GPIO_PinAFConfig(uart->gpio_port, uart->pin_src_tx, gpio_af);

    USART_Init(uart->usart_port, (USART_InitTypeDef *)uart->usart_init);
    USART_ITConfig(uart->usart_port, uart->usart_it_select, ENABLE);
    NVIC_Init((NVIC_InitTypeDef *)uart->nvic_init);
    USART_Cmd(uart->usart_port, ENABLE);
	return (0);
}

int stm32f4_uart_close(const void *this)
{
	struct stm32f4_uart *uart = (struct stm32f4_uart *)this;
    USART_Cmd(uart->usart_port, DISABLE);
	return (0);
}

int stm32f4_uart_read(const void *this, char *buffer, int len)
{
	struct stm32f4_uart *uart = (struct stm32f4_uart *)this;
	*buffer = uart->usart_port->DR;
	return (1);
}

int stm32f4_uart_write(const void *this, const char *buffer, int len)
{
	struct stm32f4_uart *uart = (struct stm32f4_uart *)this;
	int i;
	for(i = 0; i < len; i++) {
		// wait until data register is empty
		while(!(uart->usart_port->SR & 0x00000040));
		USART_SendData(uart->usart_port, buffer[i]);
	}
	return (i);
}

int stm32f4_uart_set_cb(const void *this, const void *callback, const void *param)
{
	struct stm32f4_uart *uart = (struct stm32f4_uart *)this;
	if(uart->usart_port == USART1) {
		uart1_obj.callback = callback;
		uart1_obj.parameter = param;
	}
	return (0);
}

// this is the interrupt request handler (IRQ) for ALL USART1 interrupts
void USART1_IRQHandler(void)
{
	enter_isr();
	// check if the USART1 receive interrupt flag was set
	if(USART_GetITStatus(USART1, USART_IT_RXNE)) {
		if(uart1_obj.callback) {
			void (*cb)(const void *) = uart1_obj.callback;
			cb(uart1_obj.parameter);
		}
	}
	exit_isr();
}
