/*****************************************************************************
Copyright (c) 2006 Analog Devices.  All Rights Reserved.

This software is proprietary and confidential.  By using this software you agree
to the terms of the associated Analog Devices License Agreement.  
******************************************************************************

$File: fifo.c $
$Revision: 1.1 $
$Date: 2008/03/17 14:14:34 $

Project:    Audio Kit
Title:      FIFO
Author(s):  dwu
Revised by:

Description:
		This module implements a FIFO.

*********************************************************************************			
	
Modification History:
====================
$Log: fifo.c,v $
Revision 1.1  2008/03/17 14:14:34  gstephan
Updates for SDK 3.00

Revision 1.1  2007/05/22 18:20:09  gstephan
Creating VDSP 5.00 release folder.  Created from the head copy of
VDSP 4.50 release folder.

Revision 1.2  2006/11/30 05:57:08  randreol
 Modified to support multiple codecs

Revision 1.1  2006/11/10 04:55:26  randreol
Initial rev

Revision 1.1.1.1  2006/05/09 04:01:29  dwu
USB based audio demonstration


	
*********************************************************************************/
#include <stdio.h>

#include <string.h>
#include <fifo.h>

// Read and write bit masks
#define RD_BIT 0x01
#define WR_BIT 0x02 


// Resets the FIFO object
void FIFO_Reset(FIFO_STRUCT *fifo_obj)
{
	int i;
	
	fifo_obj->buffer_count = 0;
	fifo_obj->write_buffer = 0;
	fifo_obj->read_buffer = 0;
	fifo_obj->free_buffers = 0;
	
	for(i=0;i<MAX_NUM_BUFFERS;i++)
	{
		fifo_obj->buffer_list[i].buf_start = 0;
		fifo_obj->buffer_list[i].rd_ptr = 0;
		fifo_obj->buffer_list[i].wr_ptr = 0;
		fifo_obj->buffer_list[i].buf_size = 0;
		fifo_obj->buffer_list[i].byte_count = 0;
		fifo_obj->buffer_list[i].flags = WR_BIT;
	}
}


// Configure the FIFO object
int FIFO_Configure(FIFO_STRUCT *fifo_obj,CONFIG_ENUM config_item,int param1,int param2)
{
	switch(config_item)
	{
	case FIFO_CONFIG_SETBUFFER:
		if (fifo_obj->buffer_count >= MAX_NUM_BUFFERS)
			return -1;
		
		fifo_obj->buffer_list[fifo_obj->buffer_count].buf_start = (char*)param1;
		fifo_obj->buffer_list[fifo_obj->buffer_count].rd_ptr = (char*)param1;
		fifo_obj->buffer_list[fifo_obj->buffer_count].wr_ptr = (char*)param1;
		fifo_obj->buffer_list[fifo_obj->buffer_count++].buf_size = (long)param2;
		fifo_obj->free_buffers++;
		
		break;
		
	default:
		return -1;
	}
	
	return 0;
}


// Get a buffer block for writing. Function returns the pointer to this block. If no
// free block can be allocated, the function returns 0. Note that for this to work, the
// requested block size has to be less or equal to the size of each FIFO buffer since
// FIFO only allocates contiguous blocks for writing.
char* FIFO_GetFreeWriteBlock(FIFO_STRUCT *fifo_obj,unsigned long block_size)
{
	unsigned long free_bytes;
	unsigned long write_buf_index;
	
	// get current write buffer index
	write_buf_index = fifo_obj->write_buffer;
	
	if ((fifo_obj->buffer_list[write_buf_index].flags & WR_BIT) == 0)
		return 0;
	
	// calculate free space available in current write buffer
	free_bytes = fifo_obj->buffer_list[write_buf_index].buf_size - fifo_obj->buffer_list[write_buf_index].byte_count;
	
	// no space
	if (free_bytes < block_size) 
	{
		// Toggle the flag bits of current buffer to allow read and prohibit write
		fifo_obj->buffer_list[write_buf_index].flags = RD_BIT;
		fifo_obj->buffer_list[write_buf_index].wr_ptr = fifo_obj->buffer_list[write_buf_index].buf_start;
		
		// go to next write buffer in the array
		if (++write_buf_index >= fifo_obj->buffer_count)
			write_buf_index = 0;		

		fifo_obj->write_buffer = write_buf_index;
			
		// check to see if next buffer is available for write, if not, return 0
		if ((fifo_obj->buffer_list[write_buf_index].flags & WR_BIT) == 0)
			return 0;
			
		fifo_obj->free_buffers--;
	}
	
	return (fifo_obj->buffer_list[write_buf_index].wr_ptr);
}


// Set the data count for the current write.
void FIFO_SetDataCount(FIFO_STRUCT *fifo_obj,unsigned long data_count)
{
	BUFFER_INFO_STRUCT *bufinfo;
	
	bufinfo = &(fifo_obj->buffer_list[fifo_obj->write_buffer]);
	
	bufinfo->wr_ptr += data_count;
	bufinfo->byte_count += data_count;
}
	

// Read a block of data from the FIFO into dst. The function returns the number of data bytes 
// actually read. If the number of data bytes read is less than the requested bytes, the remaining
// blanks are filled with 0's.
unsigned long FIFO_ReadDataBlock(FIFO_STRUCT *fifo_obj,unsigned long bytes_to_read,char *dst)
{
	int bytes_remain, total_bytes = 0, i;
	unsigned long read_buf_index;
	
	// get current write buffer index
	read_buf_index = fifo_obj->read_buffer;
	
	// check that RD bit is set
	if ((fifo_obj->buffer_list[read_buf_index].flags & RD_BIT) == 0)
	{
#if defined (CODEC_AD1938)
		memset(dst,0,bytes_to_read*8);// multichannel
#else
		memset(dst,0,bytes_to_read);
#endif		
		return 0;
	}

	bytes_remain = (int)bytes_to_read;
	
	while (bytes_remain)
	{
		if (fifo_obj->buffer_list[read_buf_index].byte_count >= bytes_remain)
			bytes_to_read = bytes_remain;
		else
			bytes_to_read = fifo_obj->buffer_list[read_buf_index].byte_count;
#if defined (CODEC_AD1938)
		CopyFifoToDACbuf(fifo_obj->buffer_list[read_buf_index].rd_ptr,dst,bytes_to_read);
		dst = dst+(bytes_to_read*8);

#else			
		//copy from FIFO to DAC-buffer
		memcpy(dst,fifo_obj->buffer_list[read_buf_index].rd_ptr,bytes_to_read);
		dst += bytes_to_read;
#endif
		total_bytes += bytes_to_read;
		fifo_obj->buffer_list[read_buf_index].rd_ptr += bytes_to_read;
		bytes_remain -= bytes_to_read;
		fifo_obj->buffer_list[read_buf_index].byte_count -= bytes_to_read;

		if (fifo_obj->buffer_list[read_buf_index].byte_count <= 0)
		{
			fifo_obj->buffer_list[read_buf_index].rd_ptr = fifo_obj->buffer_list[read_buf_index].buf_start;
			fifo_obj->buffer_list[read_buf_index].flags = WR_BIT;
			fifo_obj->free_buffers++;
			if (++read_buf_index >= fifo_obj->buffer_count)
				read_buf_index = 0;
			if ( (fifo_obj->buffer_list[read_buf_index].byte_count == 0) ||
				 ((fifo_obj->buffer_list[read_buf_index].flags & RD_BIT) == 0) )
			{
#if defined (CODEC_AD1938)
				memset(dst,0,bytes_remain*8);// multichannel
#else
				memset(dst,0,bytes_remain);
#endif		
				bytes_remain = 0;
			}
		}
	}
	
	fifo_obj->read_buffer = read_buf_index;
		
	return total_bytes;
}


// Set the RD bit flag for the current buffer. This is useful if there're no more inputs
// but there're data in the current buffer that ought be read. 
// Note that after this function is called, the FIFO object should be reset before inputting
// new data.
void FIFO_SetRDFlag(FIFO_STRUCT *fifo_obj)
{
	if (fifo_obj->buffer_list[fifo_obj->write_buffer].byte_count > 0)
	{
		fifo_obj->buffer_list[fifo_obj->write_buffer].flags = RD_BIT;
		fifo_obj->free_buffers--;
	}
}

