/*
 * MFRC530.c
 *
 *  Created on: Nov 11, 2011
 *      Author: RomanM
 */

#include "hal_Mfrc530.h"
#include "Mfrc530.h"
#include "Mfrc530_Internal.h"
#include "MFRC530_Registers.h"
#include "MFRC530_Commands.h"

uint8_t timeout = 0;

/*
 * Initialize MFRC530
 */
void Mfrc530_Init(void) {
	hal_Mfrc530_Init();		// Initialize low layer
	Mfrc530_Reset();		// Reset module
}

/*
 * Interrupt handler
 */
void Mfrc530_InterruptHandler(void) {
	timeout = 1;
}

/*
 * Reset MFRC530
 */
void Mfrc530_Reset(void) {
	hal_MFRC530_RSTPD_High();	// Set RSTPD high
	hal_Mfrc530_Wait100us();	// Minimum pulse width is 100us
	hal_Mfrc530_RSTPD_Low();	// Set RSTPD low to start initialization phase (automatically set command to 0x3f)

	while (Mfrc530_ReadReg(Command));	// After initialization phase MFRC530 set idle command (0x00)

	Mfrc530_SetRegMask(Page, UsePageSelect);
	Mfrc530_SetDefaultValues();
}

/*
 * Set register default values
 */
void Mfrc530_SetDefaultValues(void) {
	// Calibration
	Mfrc530_WriteReg(ClockQControl, 0x00);
	Mfrc530_WriteReg(ClockQControl, ClkQCalib);
	hal_Mfrc530_Wait100us();
	Mfrc530_WriteReg(ClockQControl, 0x00);

	Mfrc530_WriteReg(RxControl2, DecoderSource_Internal);	// Internal decoder source
	Mfrc530_WriteReg(InterruptEn, SetIEn | TimerIEn);		// Enable timer interrupt
}

/*
 *	Set antenna driver on
 */
void Mfrc530_DriverOn(void) {
	Mfrc530_SetRegMask(TxControl, TX1RFEn | TX2RFEn);
	hal_Mfrc530_Wait5ms();
}

/*
 *	Set antenna driver off
 */
void Mfrc530_DriverOff(void) {
	Mfrc530_ClearRegMask(TxControl, TX1RFEn | TX2RFEn);
}

/*
 *	Hard power-down
 *
 *		Turn-off all internal current sinks including oscillator. All digital
 *		input buffers are separated from input pads.
 */
void Mfrc530_HardPowerDown(void) {
	hal_MFRC530_RSTPD_High();
}

/*
 *	Soft power-down
 *
 *		Turn-off all internal current sinks including oscillator. All digital
 *		input buffers are NOT separated from input pads. Output pins do NOT
 *		change their state.
 */
void Mfrc530_SoftPowerDown_On(void) {
	Mfrc530_SetRegMask(Control, PowerDown);
}

/*
 *	Return from soft power-down
 */
void Mfrc530_SoftPowerDown_Off(void) {
	Mfrc530_ClearRegMask(Control, PowerDown);
	while (Mfrc530_ReadReg(Control) & PowerDown);
}

/*
 *	Stand-by
 *
 *		All internal current sinks, including the internal digital clock buffer
 *		are switched off. Oscillator buffer is NOT switched off.
 */
void Mfrc530_StandBy_On(void) {
	Mfrc530_SetRegMask(Control, StandBy);
}

/*
 *	Return from stand-by mode
 */
void Mfrc530_StandBy_Off(void) {
	Mfrc530_SetRegMask(Control, StandBy);
}


/*******************************************************************************
 * Mifare commands
 ******************************************************************************/

/*
 * Wakeup command
 */
uint8_t Mifare_WakeUp(void) {
	uint8_t cmd = 0x52;
	uint16_t in;

	Mfrc530_SetTimeout(16);
	Mfrc530_WriteReg(BitFraming, 0x07);			// Wake-up is 7 bit command
	Mfrc530_Transceive(&cmd, (uint8_t*)(&in), sizeof(cmd));
	Mfrc530_WriteReg(BitFraming, 0x00);			// Set back to 8 bit

	if (in == 0x0004) return 1;					// Return value of Mifare Classic
	else return 0;
}

/*
 * Request command
 */
uint8_t Mifare_Request(void) {
	uint8_t cmd = 0x26;
	uint16_t in;

	Mfrc530_SetTimeout(16);
	Mfrc530_WriteReg(ChannelRedundancy, ParityEn | ParityOdd);	// Enable parity
	Mfrc530_ClearRegMask(Control, Crypto1On);	// Disable Crypto 1
	Mfrc530_WriteReg(BitFraming, 0x07);			// Wake-up is 7 bit command
	Mfrc530_Transceive(&cmd, (uint8_t*)(&in), sizeof(cmd));
	Mfrc530_WriteReg(BitFraming, 0x00);			// Set back to 8 bit

	if (in == 0x0004) return 1;					// Return value of Mifare Classic
	else return 0;
}

/*
 * Anticolission command
 */
uint8_t Mifare_AntiCollision(uint8_t *cardNumber) {
	uint8_t cmd[] = {0x93, 0x20};

	Mfrc530_SetTimeout(106);							// Set timeout
	Mfrc530_Transceive(cmd, cardNumber, sizeof(cmd));	// Send command and read ID of the card

	if (Mfrc530_ReadReg(ErrorFlag) & (CollErr | ParityErr | FramingErr | CRCErr)) return 0;	// Test errors in transmission
	else return 1;
}

/*
 * Halt command
 */
void Mifare_Halt(void) {
	uint8_t cmd[] = {0x50, 0x00};

	Mfrc530_TimerDisable();
	Mfrc530_Transmit(cmd, sizeof(cmd));
}

/*******************************************************************************
 * Functions for working with registers
 ******************************************************************************/

/*
 * Set register page
 */
void Mfrc530_SetPage(uint8_t address) {
	hal_Mfrc530_NSS_Low();
	hal_Mfrc530_RW(Page);					// Send address of page register
	hal_Mfrc530_RW((address >> 3) | UsePageSelect);	// Send page
	hal_Mfrc530_NSS_High();
}

/*
 * Read MFRC530 register
 */
uint8_t Mfrc530_ReadReg(uint8_t address) {
	uint8_t retValue;

	Mfrc530_SetPage(address);

	hal_Mfrc530_NSS_Low();
	hal_Mfrc530_RW((address << 1) | 0x80);	// Send address (read)
	retValue = hal_Mfrc530_RW(0x00);		// Send dummy byte and read data
	hal_Mfrc530_NSS_High();

	return retValue;
}

/*
 * Write MFRC register
 */
void Mfrc530_WriteReg(uint8_t address, uint8_t data) {
	Mfrc530_SetPage(address);

	hal_Mfrc530_NSS_Low();
	hal_Mfrc530_RW(address << 1);	// Send address (write)
	hal_Mfrc530_RW(data);			// Send data
	hal_Mfrc530_NSS_High();
}

/*
 * Set register bits
 */
void Mfrc530_SetRegMask(uint8_t address, uint8_t mask) {
	Mfrc530_WriteReg(address, Mfrc530_ReadReg(address) | mask);
}

/*
 * Clear register bits
 */
void Mfrc530_ClearRegMask(uint8_t address, uint8_t mask) {
	Mfrc530_WriteReg(address, Mfrc530_ReadReg(address) & (~mask));
}

/*
 * Read from FIFO buffer
 */
void Mfrc530_ReadFIFO(uint8_t *data, uint8_t n) {
	while (n--) *(data++) = Mfrc530_ReadReg(FIFOData);
}

/*
 * Write to FIFO buffer
 */
void Mfrc530_WriteFIFO(uint8_t *data, uint8_t n) {
	while (n--) Mfrc530_WriteReg(FIFOData, *(data++));
}

/*******************************************************************************
 * MFRC530 Commands
 ******************************************************************************/

/*
 * Transmit data
 */
void Mfrc530_Transmit(uint8_t *data, uint8_t n) {
	while (Mfrc530_IsBusy());
	Mfrc530_FlushFIFO();					// Clear FIFO buffer
	Mfrc530_WriteFIFO(data, n);				// Write data to be send to FIFO
	Mfrc530_Command(MFRC530_Cmd_Transmit);	// Transmit data
}

/*
 * Transmit data and then receive data
 */
uint8_t Mfrc530_Transceive(uint8_t *dataOut, uint8_t *dataIn, uint8_t nOut) {
	uint8_t nIn = 0;

	while (Mfrc530_IsBusy());
	Mfrc530_FlushFIFO();						// Clear FIFO buffer
	Mfrc530_WriteFIFO(dataOut, nOut);			// Write data to FIFO
	Mfrc530_Command(MFRC530_Cmd_Transceive);	// Transcieve command
	while (Mfrc530_IsBusy() && !timeout);		// Wait on end of transmission (of read timeout)
	if (timeout) {
		Mfrc530_Command(MFRC530_Cmd_Idle);		// Stom last command
		Mfrc530_FlushFIFO();					// Clear FIFO
		Mfrc530_WriteReg(InterruptRq, 0x20);	// Cleat interrupt flag
	}
	else {
		nIn = Mfrc530_ReadReg(FIFOLength);		// Read length of received data
		Mfrc530_ReadFIFO(dataIn, nIn);			// Read data from FIFO
	}
	return nIn;									// Return length of data
}

/*
 * Read MFRC530 EEPROM
 */
void Mfrc530_ReadE2(uint16_t address, uint8_t *data, uint8_t n) {
	while (Mfrc530_IsBusy());
	Mfrc530_FlushFIFO();										// Clear FIFO buffer
	Mfrc530_WriteFIFO((uint8_t*)(&address), sizeof(address));	// Write EEPROM address to FIFO
	Mfrc530_WriteFIFO(&n, sizeof(n));							// Write data length to read
	Mfrc530_Command(MFRC530_Cmd_ReadE2);						// Run EEPROM read command
	Mfrc530_ReadFIFO(data, n);									// Read EEPROM data from FIFO
}

/*
 * Set timeout
 */
void Mfrc530_SetTimeout(uint32_t tmoLength) {
	unsigned char prescale = 7;
	unsigned long reload = tmoLength;

	while (reload > 255)
	{
	   prescale++;
	   reload = reload >> 1; // division by 2
	}
	// if one of the higher bits are set, the prescaler is set
	// to the largest value
	if (prescale > 0x15)
	{
	   prescale = 0x15;
	   reload   = 0xFF;
	}
	// fcnt = 13560000 / (2^prescale)
	// T = (reload - counter) / fcnt
	Mfrc530_TimerEnable();
	Mfrc530_WriteReg(TimerClock, prescale);	// TAutoRestart=0,TPrescale=prescale
	Mfrc530_WriteReg(TimerReload, reload);	// TReloadVal = reload

	timeout = 0;
}
