/**
 * 	\file 	tcDspUpp.h
 * 
 * 	\brief 	DSP uPP driver header file
 *
 *     o  0
 *     | /       Copyright (c) 2005-2011
 *    (CL)---o   Critical Link, LLC
 *      \
 *       O
 */

//TODO:
//		-Think about interleave mode...

#ifndef _DSPUPP_H_
#define _DSPUPP_H_

#include <stdint.h>
#include <std.h>
#include <mbx.h>
#include <sem.h>
#include <tsk.h>

#include "regs_upp.h"

namespace MityDSP
{

class tcDspUpp
{
public:

	// Enumerations for uPP channels.
	enum teUppChan
	{
		eeChanA,
		eeChanB
	};

	// Channel data transfer directionality.
	enum teChanDir
	{
		eeTransmit,
		eeReceive,
		eeDisabled
	};

	// Channel data bit width. 
	enum teChanBitWidth
	{
		ee8Bit = 0,
		ee9Bit = 1,
		ee10Bit = 2,
		ee11Bit	= 3,
		ee12Bit = 4,
		ee13Bit = 5,
		ee14Bit = 6,
		ee15Bit = 7,
		ee16Bit = 8
	};

	// Read/Transmit threshold for UPTCR register bits
	enum teThreshold
	{
		ee64Bytes = 0,
		ee128Bytes = 1,
		ee256Bytes = 3
	};

	// uPP Transmit clock selection options
	enum teTxClockSel
	{
		eeUPP_2xTXCLK = 0, //!< External Tx clock source
		eePLL0_SYSCLK2 = 1, //!< Same as uPP Module clock. Runs at
			//!< 1/2 CPU speed
		eePLL1_SYSCLK2 = 2 //!< PLL1_SYSCLK2
	};

	// Structure for configuring the uPP device.
	struct tsDspUppConfig
	{
		uint32_t nHWInterruptLevel; //!< Interrupt level. Must be 
			//!< between 4 and 15. 	
		uint32_t nTskPriorityChanA; //!< Priority for thread handling
			//!< DMA programming for Channel A.
		uint32_t nTskPriorityChanB; //!< Priority for thread handling
			//!< DMA programming for Channel B.
		teChanDir eChanADir; //!< Directionality of Channel A.
		teChanDir eChanBDir; //!< Directionality of Channel B.
		teChanBitWidth eChanBitWidthA; //!< Chan A data bit width.
		teChanBitWidth eChanBitWidthB; //!< Chan B data bit width.
		bool bChanAUseXData; //!< Use XData[7:0] for ChanA[15:8]
			//!< even if ChanB is disabled. See Table 3 in uPP 
			//!< User's Guide for details. 
		uint32_t nMbxLenA; //!< Size of MBX for Channel A.
		uint32_t nMbxLenB; //!< Size of MBX for Channel B. 
		uint8_t nChanAClkDiv; //!< Clock divider for Channel A
			//!< (only in transmit mode). See Section 2.1.1 in uPP 
			//!< User's Guide for details. Value must be between 0 
			//!< and 15.
		uint8_t nChanBClkDiv; //!< Clock divider for Channel B
			//!< (only in transmit mode). See Section 2.1.1 in uPP 
			//!< User's Guide for details. Value must be between 0 
			//!< and 15.
		teThreshold eThresholdTxA; //!< Chan A Transmit Thresh.
		teThreshold eThresholdRxA; //!< Chan A Receive Thresh.
		teThreshold eThresholdTxB; //!< Chan B Transmit Thresh.
		teThreshold eThresholdRxB; //!< Chan B Receive Thresh.
		bool bChanAUseStart; //!< Use start signal for Chan A.
		bool bChanBUseStart; //!< Use start signal for Chan B.
		teTxClockSel eTxClockSel; //!< Select transmit clock source.
			//!< Only used if Chan A or Chan B is set to transmit.
		uint8_t nDmaMasterPriority; //!< Set the DMA master priority
			//!< for uPP.  Valid number, range 0-7.  0 is highest 
			//!< priority.
		// Chan A single/double data rate?
		// Chan B single/double data rate?
		// Multiplexing/interleaving mode options?
		// Chan A data packing format?
		// Chan B data packing format?
	};

	// MBX message structure.
	struct tsMbxMsg
	{
		uint8_t* pBufPtr; //!< Pointer to filled/emptied buffer.
		uint16_t nByteCnt; //!< # bytes per line DMAed to/from buf.
		uint16_t nLineCnt; //!< DMA line count. 
		uint16_t nLineOffset; //!< DMA offset between lines.
		void* pOptArg; //!< Pointer to optional additional arguments.
	};

	// Typdef for error callback function
	typedef int (*tfErrorCallback)(uint32_t anErrNum);

	/**
	 * Get instance of tcDspUpp.
	 *
	 * \return Poiner to the tcDspUpp object or NULL on failure.
	 */
	static tcDspUpp* getInstance();

	/**
	 * Perform software reset of the uPP.
	 *
	 * @return None.
	 */
	void reset();

	/**
	 * Intiailize the uPP device.
	 *
	 * \param apDspUppConfig Specifies configuration options.
	 *
	 * \return 0 on success, negative on failure. 
	 */
	int initialize(tsDspUppConfig const* apDspUppConfig);

	/**
	 * Get handle to mailbox for associated channel where info on
	 * processed buffers are stored.
	 *
	 * \param aeChan The uPP channel.
	 *
	 * \return Handle to the channel's mailbox or NULL on failure. 
	 */
	MBX_Handle getMBX(teUppChan aeChan);

	/**
	 * Queue transmit of given data buffer. Use getMBX() to get 
	 * corresponding mailbox where pointer info will be posted once data 
	 * has been tramsmitted.
	 *
	 * \param aeChan The uPP channel to transmit on. Note: If the given 
	 * 	channel is not configured for transmit, function call 
	 * 	will fail. 
	 * \param apXmitData Pointer to transmit data. (note: Address must be
	 * 	be 64-bit aligned).
	 * \param anByteCnte Number of bytes in a line to DMA. (must be even).
	 * \param anLineCnt (Optional) Set the number of lines in the DMA 
	 * 	channel window. If set > 1 and anLineOffset = 0, 
	 * 	apXmitData will tranmist anLineCnt times.
	 * \param anLineOffset (Optional) Set the offset address between lines 
	 * 	within DMA channel. (must be 64-bit aligned).
	 *
	 * \return 0 on succes, negative on failure. 
	 */
	int transmit(teUppChan aeChan, const uint8_t* apXmitData, 
		uint16_t anByteCnt, uint16_t anLineCnt = 1,
		uint16_t anLineOffset = 0);

	/**
	 * Add buffer to receive queue. Use getMBX() to get corresponding
	 * mailbox where pointer info will be posted once data has been 
	 * received. 
	 *
	 * \param aeChan The uPP channel to receive on. Note: If the given 
	 * 	channel is not configured for receive, function call 
	 * 	will fail.
	 * \param apRcvData Pointer to location where to store receive data.
	 * 	(note: Address must be 64-bit aligned).
	 * \param anByteCnte Number of bytes in a line to DMA. (must be even).
	 * \param anLineCnt (Optional) Set the number of lines in the DMA 
	 * 	channel window. 
	 * \param anLineOffset (Optional) Set the offset address between lines 
	 * 	within DMA channel. (must be 64-bit aligned).
	 *
	 * \return 0 on succes, negative on failure.
	 */
	int receive(teUppChan aeChan, uint8_t* apRcvData, uint16_t anByteCnt, 
		uint16_t anLineCnt = 1, uint16_t anLineOffset = 0);

	/**
	 * Register an error callback function.
	 *
	 * \param afErrorCallback The error callback function. 
	 *
	 * return None.
	 */
	void registerErrorCallback(tfErrorCallback afErrorCallback);

private:

	/**
	 * Thread for programming the DMA for the specified channel.
	 *
	 * \param apDspUpp Pointer to singleton.
	 * \param aeChan The specified channel.
	 *
	 * \return None. 
	 */
	static void programDMA(tcDspUpp* apDspUpp, teUppChan aeChan);

	/**
	 * Handle any uPP related interrupts that might occur.
	 *
	 * \param apDspUpp Pointer to singleton. 
	 *
	 * \return 0 on success, negative on failure. 
	 */
	static int isr(tcDspUpp* acDspUpp); 

	/**
	 * Private constructor.
	 */
	tcDspUpp();

	/**
	 * Private destructor.
	 */
	~tcDspUpp();

	/**
	 * Private Copy Constructor to enforce use of getInstance(). 
	 */
	tcDspUpp(tcDspUpp const&); // Note implemented.
	/**
	 * Private assignment operator to enforce use of getInstance(). 
	 */
	tcDspUpp& operator=(tcDspUpp const&); // Not implemented


	tfErrorCallback mpErrorCallback; //!< Callback function for errors. 

	volatile tsUppRegs* const mpUppRegs; //!< Constant pointer to uPP Control Regs. 
	
	static tcDspUpp* mpDspUpp; //!< Singleton object.

	static SEM_Handle mhGetInstSem; //!< Static semaphore to be used in 
		//!< getInstance method so that we do not accidentally 
		//!< instantiate too many objects. 

	bool mbFirstInit; //!< True if we have not been initialized before. 

	teChanDir meChanADir; //!< Directionality of Channel A.
	teChanDir meChanBDir; //!< Directionality of Channel B.

        TSK_Handle mhDmaTskA; //!< Task for programming Channel A DMA.
        TSK_Handle mhDmaTskB; //!< Task for programming Channel B DMA.

	MBX_Handle mhMbxDoneA; //!< MBX for eeChanA or eeInterleave to store
		//!< pointers than have been filled or transmitted. 
	MBX_Handle mhMbxDoneB; //!< MBX for eeChanB to store pointers that
		//!< have been filled or transmitted.

	MBX_Handle mhMbxIntA; //!< Intermdiate MBX holding info on pointers
		//!< that have been sent for DMAing on Chan A.
	MBX_Handle mhMbxIntB; //!< Intermdiate MBX holding info on pointers
		//!< that have been sent for DMAing on Chan B.

	MBX_Handle mhMbxQueueA; //!< Queue of pointers to be transmitted or 
		//!< filled by eeChanA or eeInterleave.
	MBX_Handle mhMbxQueueB; //!< Queue of pointers to be transmitted or
		//!< filled by eeChanB. 
};

}

#endif

