/*
 * EthDev_LPC1769.c
 *
 *  Created on: 2011/12/07
 *      Author: nyatla
 */

#include "EthDev_LPC17xx.h"

/* If no buffers are available, then wait this long before looking again.... */
#define emacBUFFER_WAIT_DELAY		( 3 / portTICK_RATE_MS )
#define emacBUFFER_WAIT_EMPTY_DELAY	( 10 / portTICK_RATE_MS )
//--------------------------------------------------
// common function.
//--------------------------------------------------
/**
 * 割込み解除を通知するセマフォ。lEMACInitで設定する。
 */
static xSemaphoreHandle _xEMACSemaphore=NULL;

void setEMACSemaphoreHandle(xSemaphoreHandle i_sh)
{
	_xEMACSemaphore=i_sh;
}
void vEMAC_ISR( void )
{
	unsigned long ulStatus;
	long lHigherPriorityTaskWoken = pdFALSE;

	ulStatus = LPC_EMAC->IntStatus;
	/* Clear the interrupt. */
	LPC_EMAC->IntClear = ulStatus;

	if( ulStatus & INT_RX_DONE )
	{
		//受信系のセマフォブロックの解除
		xSemaphoreGiveFromISR(_xEMACSemaphore, &lHigherPriorityTaskWoken );
	}
	if( ulStatus & INT_TX_DONE )
	{
	}
	portEND_SWITCHING_ISR( lHigherPriorityTaskWoken );
}
/***********************************************************************
 * TXバッファ
 ***********************************************************************/

//TXバッファの先頭アドレス
#define ETH_TX_BUF_BASE (ETH_BUF_BASE+ETH_FRAG_SIZE*NUM_RX_FRAG)

#define NUM_OF_1536_BUF 3
#define NUM_OF_512_BUF  3
#define NUM_OF_256_BUF  4
#define NUM_OF_128_BUF 16

/**
 * TXメモリブロックの配置
 * 9312バイト
 */
struct TTxMemoryBlock
{
	struct{
		struct NyLPC_TTxBufferHeader h;
		NyLPC_TUInt8 b[1536];
	}buf1536[NUM_OF_1536_BUF];//(4+1536)*3=4620
	struct{
		struct NyLPC_TTxBufferHeader h;
		NyLPC_TUInt8 b[512];
	}buf512[NUM_OF_512_BUF];//(4+512)*3=1548
	struct{
		struct NyLPC_TTxBufferHeader h;
		NyLPC_TUInt8 b[256];
	}buf256[NUM_OF_256_BUF];//(4+256)*4=1560
	struct{
		struct NyLPC_TTxBufferHeader h;
		NyLPC_TUInt8 b[128];
	}buf128[NUM_OF_128_BUF];//(4+128)*16=1584
};

//メモリブロックの定義
static struct TTxMemoryBlock* _txbuf=(struct TTxMemoryBlock*)ETH_TX_BUF_BASE;

/**
 * デバック用。使用中のTxブロックの数を返す。
 */
int dbg_getNumofUsedTx(void)
{
	int x,r=0;
	for(x=0;x<NUM_OF_1536_BUF;x++){
		if(_txbuf->buf1536[x].h.is_lock || _txbuf->buf1536[x].h.ref>0){
			continue;
		}
		r++;
	}
	for(x=0;x<NUM_OF_512_BUF;x++){
		if(_txbuf->buf512[x].h.is_lock || _txbuf->buf512[x].h.ref>0){
			continue;
		}
		r++;
	}
	for(x=0;x<NUM_OF_256_BUF;x++){
		if(_txbuf->buf256[x].h.is_lock || _txbuf->buf256[x].h.ref>0){
			continue;
		}
		r++;
	}
	for(x=0;x<NUM_OF_128_BUF;x++){
		if(_txbuf->buf128[x].h.is_lock || _txbuf->buf128[x].h.ref>0){
			continue;
		}
		r++;
	}
	return r;
}

/**
 * 送信デスクリプタを準備します。
 */
void EthDev_LPC17xx_prevTxDescriptor(void)
{
	long x;
	//デスクリプタの設定
	for( x = 0; x < NUM_TX_FRAG; x++ )
	{
		TX_DESC_PACKET( x ) = ( unsigned long ) NULL;
		TX_DESC_CTRL( x ) = 0;
		TX_STAT_INFO( x ) = 0;
	}
	//TXバッファを初期化
	for(x=0;x<NUM_OF_1536_BUF;x++){
		_txbuf->buf1536[x].h.is_lock=NyLPC_TUInt8_FALSE;
		_txbuf->buf1536[x].h.ref=0;
	}
	for(x=0;x<NUM_OF_512_BUF;x++){
		_txbuf->buf512[x].h.is_lock=NyLPC_TUInt8_FALSE;
		_txbuf->buf512[x].h.ref=0;
	}
	for(x=0;x<NUM_OF_256_BUF;x++){
		_txbuf->buf256[x].h.is_lock=NyLPC_TUInt8_FALSE;
		_txbuf->buf256[x].h.ref=0;
	}
	for(x=0;x<NUM_OF_128_BUF;x++){
		_txbuf->buf128[x].h.is_lock=NyLPC_TUInt8_FALSE;
		_txbuf->buf128[x].h.ref=0;
	}
	/* Set LPC_EMAC Transmit Descriptor Registers. */
	LPC_EMAC->TxDescriptor = TX_DESC_BASE;
	LPC_EMAC->TxStatus = TX_STAT_BASE;
	LPC_EMAC->TxDescriptorNumber = NUM_TX_FRAG - 1;

	/* Tx Descriptors Point to 0 */
	LPC_EMAC->TxProduceIndex = 0;

}

/**
 * 空のTxバッファのポインタを返します。
 */
struct NyLPC_TTxBufferHeader* EthDev_LPC17xx_allocTxBuf(NyLPC_TUInt16 i_hint,NyLPC_TUInt16* o_size)
{
	int buf_type;
	int i;
	//ヒントから、割り当てるメモリブロックを決定
	if(i_hint<=128){
		buf_type=0;
	}else if(i_hint<=256){
		buf_type=1;
	}else if(i_hint<=512){
		buf_type=2;
	}else{
		buf_type=3;
	}
	for(;;)
	{
		switch(buf_type){
		case 3:
			for(i=0;i<NUM_OF_1536_BUF;i++){
				//未参照かつ送信中でないもの。
				if(_txbuf->buf1536[i].h.ref>0 || _txbuf->buf1536[i].h.is_lock){
					continue;
				}
				_txbuf->buf1536[i].h.ref++;
				*o_size=1536;
				return &(_txbuf->buf1536[i].h);
			}
		case 2:
			for(i=0;i<NUM_OF_512_BUF;i++){
				//未参照かつ送信中でないもの。
				if(_txbuf->buf512[i].h.ref>0 || _txbuf->buf512[i].h.is_lock){
					continue;
				}
				*o_size=512;
				_txbuf->buf512[i].h.ref++;
				return &(_txbuf->buf512[i].h);
			}
		case 1:
			for(i=0;i<NUM_OF_256_BUF;i++){
				//未参照かつ送信中でないもの。
				if(_txbuf->buf256[i].h.ref>0 || (_txbuf->buf256[i].h.is_lock)){
					continue;
				}
				*o_size=256;
				_txbuf->buf256[i].h.ref++;
				return &(_txbuf->buf256[i].h);
			}
		default:
			for(i=0;i<NUM_OF_128_BUF;i++){
				//未参照かつ送信中でないもの。
				if(_txbuf->buf128[i].h.ref>0 || (_txbuf->buf128[i].h.is_lock)){
					continue;
				}
				*o_size=128;
				_txbuf->buf128[i].h.ref++;
				return &(_txbuf->buf128[i].h);
			}
		}
		vTaskDelay( emacBUFFER_WAIT_DELAY );
	}
}


void EthDev_LPC17xx_releaseTxBuf(struct NyLPC_TTxBufferHeader* i_buf)
{
	//参照カウンタを1減算
	NyLPC_Assert(i_buf->ref>0);
	i_buf->ref--;
	return;
}

/**
 * 送信中のイーサフレームを処理する機会を与えて、送信キューが空くまで待ちます。
 * LPC1769の場合は、非同期に更新したディスクリプタの内容から、送信メモリのフラグを更新します。
 * @return
 * 次に書き込むことが出来る送信キュー。
 */
static NyLPC_TUInt32 waitForTxEthFrameEmpty(void)
{
	NyLPC_TUInt32	IndexNext;
	struct NyLPC_TTxBufferHeader *b;
	void* p;
	NyLPC_TUInt32 i;

	//送信キューの決定
	IndexNext = (LPC_EMAC->TxProduceIndex + 1)%NUM_TX_FRAG;

	//送信キューフルが解除されるまで待ち
	while(IndexNext == LPC_EMAC->TxConsumeIndex)
	{
		//
		vTaskDelay(emacBUFFER_WAIT_EMPTY_DELAY);
	}

	//(TxProduceIndex+1)→TxConsumeIndexにあるデータのsentフラグを消去
	for(i=IndexNext;i!=LPC_EMAC->TxConsumeIndex;i=(i+1)%NUM_TX_FRAG)
	{
		p=(void*)TX_DESC_PACKET(i);
		if(p!=NULL){
			b=((struct NyLPC_TTxBufferHeader*)p)-1;
			b->is_lock=NyLPC_TUInt8_FALSE;
			TX_DESC_PACKET(i)=0;
		}
	}
	p=(void*)TX_DESC_PACKET(i);
	if(p!=NULL){
		b=((struct NyLPC_TTxBufferHeader*)p)-1;
		b->is_lock=NyLPC_TUInt8_FALSE;
		TX_DESC_PACKET(i)=0;
	}
	return IndexNext;
}

void EthDev_LPC17xx_processTx(void)
{
	waitForTxEthFrameEmpty();
}

/**
 * Ethernetパケットを送信します。
 * allocTxBufで得たバッファか、NyLPC_TTxBufferHeaderのペイロード部分を指定すること。
 * <p>関数仕様</p>
 * この関数は、i_bufが
 * </div>
 */
void EthDev_LPC17xx_sendTxEthFrame(struct NyLPC_TTxBufferHeader* i_buf,unsigned short i_size)
{
	NyLPC_TUInt32	IndexNext,Index;

	//サイズ0なら送信の必要なし
	if(i_size == 0)
	{
		return;
	}
	//送信デスクリプタの反映
	IndexNext =waitForTxEthFrameEmpty();

	//送信対象のメモリブロックを送信中に設定。
//	b=(i_buf+1);
	//送信中のメモリブロックなら無視
	if(i_buf->is_lock){
		return;
	}
	//送信中にセット
	i_buf->is_lock=NyLPC_TUInt8_TRUE;

	//送信データのセット
	Index = LPC_EMAC->TxProduceIndex;
	if (i_size > ETH_FRAG_SIZE){
		i_size = ETH_FRAG_SIZE;
	}
	//送信処理
	TX_DESC_PACKET( Index ) = ( unsigned long )(i_buf+1);
	TX_DESC_CTRL( Index ) = ( i_size | TCTRL_LAST | TCTRL_INT );
	LPC_EMAC->TxProduceIndex = IndexNext;
	return;
}

/***********************************************************************
 * RXバッファ
 ***********************************************************************/

void EthDev_LPC17xx_prevRxDescriptor(void)
{
	int x;
	//デスクリプタの設定
	for( x = 0; x < NUM_RX_FRAG; x++ )
	{
		/* Allocate the next Ethernet buffer to this descriptor. */
		RX_DESC_PACKET(x) = ETH_BUF(x);
		RX_DESC_CTRL(x) = RCTRL_INT | ( ETH_FRAG_SIZE - 1 );
		RX_STAT_INFO(x) = 0;
		RX_STAT_HASHCRC(x) = 0;
	}

	/* Set LPC_EMAC Receive Descriptor Registers. */
	LPC_EMAC->RxDescriptor = RX_DESC_BASE;
	LPC_EMAC->RxStatus = RX_STAT_BASE;
	LPC_EMAC->RxDescriptorNumber = NUM_RX_FRAG - 1;

	/* Rx Descriptors Point to 0 */
	LPC_EMAC->RxConsumeIndex = 0;
}


/**
 * 受信キューの先頭にあるRXフレームのポインタを返します。
 * 関数は、受信キューのポインタを操作しません。続けて読み出したとしても、同じポインターを返します。
 * 制限として、返却したポインタの内容は、一時的に書き換え可としてください。（この制限は将来削除します。）
 * @return
 * 成功した場合、受信データを格納したバッファポインターです。
 * 次回nextRxEthFrameを呼び出すまで有効です。
 */
void* EthDev_LPC17xx_getRxEthFrame(unsigned short* o_len_of_data)
{

	if( LPC_EMAC->RxProduceIndex != LPC_EMAC->RxConsumeIndex )
	{
		//受信データを返却する。
		*o_len_of_data = (unsigned short)(( RX_STAT_INFO( LPC_EMAC->RxConsumeIndex ) & RINFO_SIZE ) - 3);
		return ( unsigned char * ) RX_DESC_PACKET( LPC_EMAC->RxConsumeIndex );
	}
	return NULL;
}


/**
 * 受信キューを進行します。
 */
void EthDev_LPC17xx_nextRxEthFrame(void)
{
	long lIndex;
	if( LPC_EMAC->RxProduceIndex != LPC_EMAC->RxConsumeIndex )
	{
		//キューすすめる。
		lIndex = LPC_EMAC->RxConsumeIndex;
		lIndex++;
		if( lIndex >= NUM_RX_FRAG )
		{
			lIndex = 0;
		}
		LPC_EMAC->RxConsumeIndex = lIndex;
	}
}
