/*
 **********************************************************************
 *     cardwi.c - PCM input HAL for emu10k1 driver
 *     Copyright 1999, 2000 Creative Labs, Inc.
 *
 **********************************************************************
 *
 *     Date                 Author          Summary of changes
 *     ----                 ------          ------------------
 *     October 20, 1999     Bertrand Lee    base code release
 *
 **********************************************************************
 *
 *     This program is free software; you can redistribute it and/or
 *     modify it under the terms of the GNU General Public License as
 *     published by the Free Software Foundation; either version 2 of
 *     the License, or (at your option) any later version.
 *
 *     This program is distributed in the hope that it will be useful,
 *     but WITHOUT ANY WARRANTY; without even the implied warranty of
 *     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *     GNU General Public License for more details.
 *
 *     You should have received a copy of the GNU General Public
 *     License along with this program; if not, write to the Free
 *     Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139,
 *     USA.
 *
 **********************************************************************
 */

#include "hwaccess.h"
#include "mycommon.h"
#include "mmwave.h"
#include "cardwi.h"

/****************************************************************************/
/* Function : sblive_waveinInit                                             */
/*                                                                          */
/* Input    : card_wavein - pointer to card wavein object                   */
/*            sb_hw - pointer to hardware object                            */
/*            mixer - pointer to card mixer                                 */
/*            carddesc - card description                                   */
/*                                                                          */
/* Return   : CTSTATUS_SUCCESS  -- successful                               */
/*            CTSTATUS_ERROR    -- failure                                  */
/*                                                                          */
/* About    : initialize card wave input device.                            */
/****************************************************************************/
int sblive_waveinInit(struct sblive_wavein *card_wavein, u8 *carddesc)
{
	/* Init Cardwave Caps */
	card_wavein->caps.product_id = MM_CREATIVE_WAVEIN_PID;
	card_wavein->caps.caps = CARDWAVE_IN;
	card_wavein->caps.controls = 0;
	card_wavein->caps.maxchannels = 2;
	card_wavein->caps.minrate = 8000;
	card_wavein->caps.maxrate = 48000;
	strcpy(card_wavein->caps.wavedesc, carddesc);

	card_wavein->numrecordinst = 0;
	card_wavein->wave_inlist = NULL;

	return CTSTATUS_SUCCESS;
}


/****************************************************************************/
/* Function : sblive_waveinExit                                             */
/*                                                                          */
/* Input    : card_wavein - pointer to card wavein object                   */
/*                                                                          */
/* Return   : CTSTATUS_SUCCESS  -- successful                               */
/*            CTSTATUS_ERROR    -- failure                                  */
/*                                                                          */
/* About    : exit card wave operation.                                     */
/****************************************************************************/
int sblive_waveinExit(struct sblive_wavein *card_wavein)
{

	return CTSTATUS_SUCCESS;
}

/****************************************************************************/
/* Function : sblive_waveinQueryFormat                                      */
/*                                                                          */
/* Input    : card_wavein - pointer to card wavein object                   */
/*            wave_fmt - pointer to wave format object                      */
/*            flags - flags that identifies the format to be queried.       */
/*                                                                          */
/* About    : query whether a specified wave format is supported by wave    */
/*            device.                                                       */
/****************************************************************************/
int sblive_waveinQueryFormat(struct sblive_wavein *card_wavein, struct wave_format *wave_fmt, u32 flags)
{
	if (flags & CARDWAVE_QF_CHANNEL) 
	{
		/* Only support one wave input instance */
		if (card_wavein->numrecordinst > 1)
		  return CTSTATUS_INUSE;
	}
	
	if (flags & CARDWAVE_QF_RATE) 
	{
		/* Recording
		 * Sampling rates supported:
		 * 48kHz, 44.1kHz, 32kHz, 24kHz, 22.05kHz, 16kHz, 11.025kHz, 8kHz.
		 */
		if ((wave_fmt->samplingrate != 0xBB80)
		    && (wave_fmt->samplingrate != 0xAC44)
		    && (wave_fmt->samplingrate != 0x7D00)
		    && (wave_fmt->samplingrate != 0x5DC0)
		    && (wave_fmt->samplingrate != 0x5622)
		    && (wave_fmt->samplingrate != 0x3E80)
		    && (wave_fmt->samplingrate != 0x2B11)
		    && (wave_fmt->samplingrate != 0x1F40))
		{
			DPF("Bad sample rate\n");
			return CTSTATUS_BADFORMAT_RATE;
		}
	}
	
	if (flags & CARDWAVE_QF_BITS) 
	{
		/* 16 bit and 8 bit recording are supported */
		if ((wave_fmt->bitspersample != 16) && (wave_fmt->bitspersample != 8))
		{
			DPF("Bad sample bits\n");
			return CTSTATUS_BADFORMAT_BITS;
		}
	}
	
	return CTSTATUS_SUCCESS;
}


/****************************************************************************/
/* Function : sblive_waveinOpen                                             */
/*                                                                          */
/* Input    : card_wavein - pointer to card wavein object                   */
/*            wave_fmt - pointer to wave format object                      */
/*            CallbackFn - IRQ call back function                           */
/*            refdata - reference data for call back function               */
/*            pcallback_size - pointer to the size of data to transfer      */
/*                              before CallbackFn is called.                */
/*            numfrags - number of buffer fragments                         */
/*                                                                          */
/* Output   : pcallback_size - pointer to the actual call back size.        */
/*            phandle - pointer to the open handle.                         */
/*                                                                          */
/* About    : Open card wave device.                                        */
/*            1. query whether a specified wave format is supported by      */
/*               device.                                                    */
/*            2. allocate emu channel for the specified channel object.     */
/*            3. attach this wave instance to the channel object list.      */
/*            4. install wave IRQ handler.                                  */
/*            5. get wave transfer buffer.                                  */
/*            6. get wave instance format.                                  */
/*            7. for recording, install IRQ handler.                        */
/****************************************************************************/
int sblive_waveinOpen(struct sblive_hw *sb_hw, struct wave_format *wave_fmt, CALLBACKFN CallbackFn, u32 refdata, u32 *callback_size, u32 numfrags, struct wave_in **handle)
{
	struct sblive_wavein *card_wavein = sb_hw->card_wavein;
	struct wave_in *wave_in;
	int status;
	u32 bufsiz;
	u8 *buffer;

	if ((status = sblive_waveinQueryFormat(card_wavein, wave_fmt, CARDWAVE_QF_ALL)) != CTSTATUS_SUCCESS)
	{
		DPF("sblive_waveinQueryFormat failed\n");
		return status;
	}
	
	if (!(wave_in = (struct wave_in *)kmalloc(sizeof(struct wave_in), GFP_KERNEL)))
	{
		DPF("Failed to allocate wave instance!\n");
		return CTSTATUS_NOMEMORY;
	}
	
#ifdef MEMTEST
	DPD("kmalloc: [%p]\n", wave_in);
#endif

	/* Init wave in instance */
	wave_in->next = NULL;
	wave_in->status &= ~FLAGS_AVAILABLE;
	wave_in->state = CARDWAVE_STATE_STOPPED;
	wave_in->synchstart = FALSE;
	wave_in->wave_fmt = *wave_fmt;
	wave_in->CallbackFn = CallbackFn;
	wave_in->emu_voice = NULL;
	wave_in->refdata = refdata;
	wave_in->callbacksize = 0;
	wave_in->process_id = 0;
	wave_in->setpos = FALSE;
	wave_in->position = 0;
	wave_in->timerhandle = 0;
	wave_in->timerinterval = 0;

	/* Recording */

	if (!(wave_in->rec_ptr = (struct record *) kmalloc(sizeof(struct record), GFP_KERNEL)))
	{
		DPF("Failed to allocate recording buffer!\n");
		kfree((void *) wave_in);
		return CTSTATUS_NOMEMORY;
	}
	
#ifdef MEMTEST
	DPD("kmalloc: [%p]\n", wave_in->rec_ptr);
#endif

	initRecordObject(sb_hw, wave_in);

	/* Attach this wave instance to the channel object list */
	osListAttach((struct sblive_list **) & card_wavein->wave_inlist, (struct sblive_list *) wave_in);

	if (callback_size != NULL)
	  wave_in->callbacksize = *callback_size;
	else
	  wave_in->callbacksize = 0;

	bufsiz = (wave_in->callbacksize ? wave_in->callbacksize * numfrags : 0xffff);
	
	if ((status = sblive_emuGetRecBuffer(card_wavein, wave_in, &bufsiz, &buffer)) != CTSTATUS_SUCCESS)
	{
		DPF("WaveOpen GetBuffer Fail");
		sblive_waveinClose(sb_hw, wave_in);
		return CTSTATUS_ERROR;
	}
		
	wave_in->callbacksize = bufsiz / numfrags;
	*callback_size = bufsiz / numfrags;
		
	/* This callback size returned is the size in the play buffer.
	 * For 8-bit samples, callbacksize of user buffer should be
	 * half of the callbacksize in play buffer. */
	if (wave_in->wave_fmt.bitspersample == 8)
	  *callback_size >>= 1;

	*handle = wave_in;

	init_waitqueue(&wave_in->wait_queue);
	wave_in->mapped = FALSE;
	wave_in->dsDpc.is_active = FALSE;
	wave_in->dsDpc.refdata = (u32) wave_in;
	wave_in->dsDpc.DPCCallBackFn = sblive_waveinDpcCallback;

	if (sblive_irqmgrInstallIrqHandler(sb_hw, IRQTYPE_RECORD, sblive_waveinIrqCallback, (u32)wave_in) != CTSTATUS_SUCCESS)
	{
		sblive_waveinClose(sb_hw, wave_in);
		DPF("Failed to install IRQ handler!\n");
		return CTSTATUS_ERROR;
	}
	
	return CTSTATUS_SUCCESS;
}


/****************************************************************************/
/* Function : sblive_waveinClose                                            */
/*                                                                          */
/* Input    : card_wavein - pointer to card wavein object                   */
/*            wave_in  - open handle which is the pointer to the            */
/*                          specified channel object                        */
/*                                                                          */
/* About    : Close wave device.                                            */
/*            1. deallocate transfer buffer, for playback,                  */
/*               free emu addx space.                                       */
/*            2. free voice channels.                                       */
/*            3. uninstall wave IRQ handler.                                */
/*            4. remove wave instance from channel object list.             */
/****************************************************************************/
int sblive_waveinClose(struct sblive_hw *sb_hw, struct wave_in *wave_in)
{
	struct sblive_wavein *card_wavein= sb_hw->card_wavein;
	struct wave_in *tmp;
	u32 dummy;
	
	/* FIXME: Do we need spinlocks in here? */

	if (wave_in->state != CARDWAVE_STATE_STOPPED)
	  sblive_waveinStop(sb_hw, wave_in, &dummy);

	tmp = card_wavein->wave_inlist;
	while (tmp) 
	{
		if (tmp == wave_in) 
		{
			if (sblive_emuDeallocRecBuffer(wave_in) != CTSTATUS_SUCCESS)
			{
				DPF("Failed to deallocate recbuffer\n");
			}
			
			wave_in->status |= FLAGS_AVAILABLE;
			wave_in->state = CARDWAVE_STATE_STOPPED;
			wave_in->synchstart = FALSE;

			sblive_irqmgrUninstallIrqHandler(sb_hw, IRQTYPE_RECORD);
			kfree((void *) wave_in->rec_ptr);
			
			osListRemove((struct sblive_list **) & card_wavein->wave_inlist, (struct sblive_list *) wave_in);

			/* Free channel object which is allocated in sblive_waveOpen(). */
			kfree((void *) wave_in);
			
			break;
		}
		
		tmp = (struct wave_in *) tmp->next;
	}

	return CTSTATUS_SUCCESS;
}


/****************************************************************************/
/* Function : sblive_emuGetRecBuffer                                        */
/*                                                                          */
/* Input    : card_wavein - pointer to card wavein object                   */
/*            wave_in - pointer to the specified wavein instance            */
/*            size - pointer to the size requested                          */
/*                                                                          */
/* Output   : size - pointer to the size allocated                          */
/*            buffer - pointer to the buffer pointer allocated              */
/*                                                                          */
/* About    : alloc record buffer.                                          */
/****************************************************************************/
int sblive_emuGetRecBuffer(struct sblive_wavein *card_wavein, struct wave_in *wave_in, u32 *size, u8 **buffer)
{
	DPF("CARDWAVE : GetBuffer");

	if (!size || !buffer)
	  return CTSTATUS_INVALIDPARAM;

	if (*size < wave_in->callbacksize) 
	{
		*size = wave_in->callbacksize;
		return CTSTATUS_INVALIDVALUE;
	}
	
	/* Allocate buffer here */
	if (sblive_emuAllocRecBuffer(wave_in, size, buffer) != CTSTATUS_SUCCESS) 
	{
		DPF("CARDWAVE: allocate buffer fail.");
		return CTSTATUS_ERROR;
	}
	
	/* recbufsize contains actual record buffer size */
	*size = wave_in->rec_ptr->recbufsize;

	wave_in->rec_ptr->recbuffer = *buffer;
	wave_in->rec_ptr->recpos = 0;

	return CTSTATUS_SUCCESS;
}


/****************************************************************************/
/* Function : sblive_emuAllocRecBuffer                                      */
/*                                                                          */
/* Input    : card_wavein - pointer to card wavein object                   */
/*            size - pointer to the size requested                          */
/*                                                                          */
/* Output   : size - pointer to the size returned                           */
/*            buffer - pointer to the buffer pointer allocated              */
/*                                                                          */
/* About    : allocate buffer for wave transfer.                            */
/*            recording: a) allocate page-aligned, continous PC memory      */
/*                          for recording buffer.                           */
/*                       b) determine start, end, startloop and             */
/*                          endloop.                                        */
/****************************************************************************/
int sblive_emuAllocRecBuffer(struct wave_in *wave_in, u32 *bufsize, u8 **buffer)
{
	int status;
	u32 numpages, reqsize;
	void *virtaddr;
	unsigned long physaddx;
	int i, j;
	u32 size[4];

	/* NOTE: record buffer size only can be certain sizes.  If the requested
	 * size is not a nice size, use the smaller nearest size. The minimum size is 1k. */
	if (!wave_in->rec_ptr->is16bit)
	  *bufsize <<= 1;

	if (*bufsize >= 0x10000) 
	{
		*bufsize = reqsize = 0x10000;
		wave_in->rec_ptr->bufsizereg = 31;
	} else 
	{
		reqsize = 0;
		size[0] = 384;
		size[1] = 448;
		size[2] = 512;
		size[3] = 640;

		for (i = 0; i < 8; i++)
		  for (j = 0; j < 4; j++)
		    if (*bufsize >= size[j]) 
		    {
			    reqsize = size[j];
			    size[j] = size[j] * 2;
			    wave_in->rec_ptr->bufsizereg = i * 4 + j + 1;
		    } else
		  goto exitloop;
		exitloop:
		if (reqsize == 0) 
		{
			reqsize = 384;
			wave_in->rec_ptr->bufsizereg = 1;
		}
		
		*bufsize = reqsize;
	}
	
	DPD("bufsizereg: %x", wave_in->rec_ptr->bufsizereg);

	if (wave_in->rec_ptr->is_stereo) 
	{
		numpages = (reqsize / (PAGE_SIZE * 2)) * 2;
		if (reqsize % (PAGE_SIZE * 2))
		  numpages += 2;
	} else 
	{
		numpages = reqsize / PAGE_SIZE;
		if (reqsize % PAGE_SIZE)
		  numpages += 1;
	}

	reqsize = numpages * PAGE_SIZE;

	/* Recording buffer must be continuous and page-aligned */
	status = osAllocMemPhysical(reqsize, &wave_in->memhandle, &virtaddr, &physaddx);

	if (status != CTSTATUS_SUCCESS)
	  return CTSTATUS_NOMEMORY;

	wave_in->rec_ptr->recbuffer = (u8 *) virtaddr;
	wave_in->rec_ptr->physaddx = physaddx;
	wave_in->rec_ptr->recbufsize = *bufsize;
	
	DPD("recbufsize: %x", wave_in->rec_ptr->recbufsize);

	*buffer = (u8 *) virtaddr;

	return CTSTATUS_SUCCESS;
}


/****************************************************************************/
/* Function : sblive_emuDeallocRecBuffer                                    */
/*                                                                          */
/* Input    : card_wavein - pointer to card wavein object                   */
/*                                                                          */
/* About    : deallocate transfer buffer                                    */
/*            1. for playback, free emu address space.                      */
/*            2. free PC memory allocated for transfer buffer.              */
/*            3. clear VioceParam.                                          */
/****************************************************************************/
int sblive_emuDeallocRecBuffer(struct wave_in *wave_in)
{
	if (!wave_in->memhandle)
	  return CTSTATUS_ERROR;

	osFreeMemPhysical(wave_in->memhandle);

	if (wave_in->emu_voice != NULL) 
	{
		struct voice_param *left = &wave_in->emu_voice->voice_params;
		struct voice_param *right = &wave_in->emu_voice->linked_voice->voice_params;
		
		/* Clear VoiceParam */
		left->start = 0;
		left->startloop = 0;
		left->end = 0;
		left->endloop = 0;
		
		if (wave_in->wave_fmt.channels == 2) 
		{
			right->start = 0;
			right->startloop = 0;
			right->end = 0;
			right->endloop = 0;
		}
	}
	
	return CTSTATUS_SUCCESS;
}


/****************************************************************************/
/* Function : initRecordObject                                              */
/*                                                                          */
/* Input    : card_wavein - pointer to card wavein object                   */
/*                                                                          */
/* About    : init recording object                                         */
/****************************************************************************/
void initRecordObject(struct sblive_hw *sb_hw, struct wave_in *wave_in)
{
	wave_in->rec_ptr->reserved = NULL;
	wave_in->rec_ptr->sb_hw = sb_hw;
	wave_in->rec_ptr->recpos = 0;
	wave_in->rec_ptr->recbufsize = 0;
	wave_in->rec_ptr->recbuffer = NULL;
	wave_in->rec_ptr->physaddx = 0;
	wave_in->rec_ptr->samplingrate = wave_in->wave_fmt.samplingrate;
	wave_in->rec_ptr->is_stereo = (wave_in->wave_fmt.channels == 2) ? 1 : 0;
	wave_in->rec_ptr->is16bit = (wave_in->wave_fmt.bitspersample == 16) ? 1 : 0;
	wave_in->rec_ptr->fSetRecSrc = FALSE;
	wave_in->rec_ptr->prevadcidx = 0;
	wave_in->rec_ptr->pong = FALSE;
}


/****************************************************************************/
/* Function : sblive_waveinStart                                            */
/*                                                                          */
/* Input    : card_wavein - pointer to card wavein object                   */
/*            wave_in - pointer to the specified wavein instance            */
/*                                                                          */
/* About    : start wave in transfer.                                       */
/*            recording: a) setup voices.                                   */
/*                       b) set recording source.(CCCA_CC, ADCCR)           */
/*                       c) enable IRQ.                                     */
/*                       d) start recording.                                */
/*                       e) start CA (for get position).                    */
/****************************************************************************/
int sblive_waveinStart(struct sblive_hw *sb_hw, struct wave_in *wave_in)
{
	/* If already started, return success */
	if (wave_in->state == CARDWAVE_STATE_STARTED)
	  return CTSTATUS_SUCCESS;

	/* Recording */
	if (recmgrInit(wave_in->rec_ptr) != CTSTATUS_SUCCESS)
	  return CTSTATUS_ERROR;

	/* If recording source is not set yet, use default: AC97 ADC */
	if (!wave_in->rec_ptr->fSetRecSrc) 
	{
		if (recmgrSetControl(wave_in->rec_ptr, WAVERECORD_AC97, 0) != CTSTATUS_SUCCESS)
		  return CTSTATUS_SUCCESS;
		wave_in->rec_ptr->fSetRecSrc = TRUE;
	}
	
	if (sblive_irqmgrEnableIrq(sb_hw, INTE_ADCBUFENABLE) != CTSTATUS_SUCCESS)
	  return CTSTATUS_ERROR;

	/* Actual start */
	if (!wave_in->synchstart) 
	{
		/* Recording */
		if (recmgrStartRecord(wave_in->rec_ptr) != CTSTATUS_SUCCESS)
		  return CTSTATUS_ERROR;
		wave_in->rec_ptr->prevadcidx = recmgrGetRecIdx(wave_in->rec_ptr->sb_hw);
	}
	
	wave_in->state = CARDWAVE_STATE_STARTED;
	wave_in->setpos = FALSE;
	
	DPF("sblive_wave Started.\n");

	return CTSTATUS_SUCCESS;
}


/****************************************************************************/
/* Function : sblive_waveinStop                                             */
/*                                                                          */
/* Input    : card_wavein - pointer to card wavein object                   */
/*            wave_in - pointer to the specified wavein instance            */
/*            pending - for later use                                       */
/*                                                                          */
/* About    : stop wave transfer, disable IRQ.                              */
/****************************************************************************/
int sblive_waveinStop(struct sblive_hw *sb_hw, struct wave_in *wave_in, u32 *pending)
{
	struct sblive_wavein *card_wavein=sb_hw->card_wavein;
	u32 dummy;

	sblive_waveinGetXferSize(card_wavein, wave_in, &dummy, pending);

	if (wave_in->emu_voice != NULL) 
	  if (sblive_voiceStop(wave_in->emu_voice) != CTSTATUS_SUCCESS)
	    return CTSTATUS_ERROR;
	
	/* Recording */
	if (recmgrStopRecord(wave_in->rec_ptr) != CTSTATUS_SUCCESS)
	  return CTSTATUS_ERROR;
	if (sblive_irqmgrDisableIrq(sb_hw, INTE_ADCBUFENABLE) != CTSTATUS_SUCCESS)
	  return CTSTATUS_ERROR;

	if (wave_in->emu_voice != NULL) 
	{
		struct voice_param *left = &wave_in->emu_voice->voice_params;
		
		left->startloop = left->start;
		
		if (wave_in->rec_ptr->is_stereo) 
		{
			struct voice_param *right = &wave_in->emu_voice->linked_voice->voice_params;

			right->startloop = right->start;
		}
	}
	
	wave_in->rec_ptr->fSetRecSrc = FALSE;
	wave_in->rec_ptr->recpos = 0;
	wave_in->rec_ptr->pong = FALSE;
	wave_in->state = CARDWAVE_STATE_STOPPED;
	wave_in->process_id = 0;

	return CTSTATUS_SUCCESS;
}


/****************************************************************************/
/* Function : sblive_waveinGetXferSize                                      */
/*                                                                          */
/* Input    : card_wavein - pointer to card wavein object                   */
/*            wave_in - pointer to the specified wavein instance            */
/*                                                                          */
/* Output   : size - pointer to the transfer size                           */
/*            pending - pointer to the size to be transfered                */
/*                                                                          */
/* About    : get the size of data in bytes that the specified wave device  */
/*            can process at the time of this function is called.           */
/*                                                                          */
/* Note     : this transfer size returned is referring to user buffer.      */
/****************************************************************************/
int sblive_waveinGetXferSize(struct sblive_wavein *card_wavein, struct wave_in *wave_in, u32 *size, u32 *pending)
{
	struct record *rec_ptr = wave_in->rec_ptr;
	u32 curpos;
	
	/* FIXME: Do we need a spinlock in here?  Some callers lock around this fn, others don't.... */

	/* Get position of current address, this is in no. of bytes in play buffer */
	if (wave_in->emu_voice != NULL) 
	{
		if (sblive_waveinGetControl(card_wavein, wave_in, WAVECURPOS, &curpos) != CTSTATUS_SUCCESS) 
		{
			DPF("sblive_waveinGetControl() failed!\n");
			return CTSTATUS_ERROR;
		}
		
		curpos *= (rec_ptr->is_stereo + 1);
	} else 
	{
		/* No voices used for recording, get position from wall clock */
		if (wave_in->state == CARDWAVE_STATE_STOPPED)
		  curpos = 0;
		else 
		{
			if (recmgrGetPos(wave_in->rec_ptr, &curpos) != CTSTATUS_SUCCESS) 
			{
				DPF("recmgrGetPos() failed!\n");
				return CTSTATUS_ERROR;
			}
		}
	}

	/* Recpos is the actual position in user buffer and play buffer */
	if (curpos >= rec_ptr->recpos)
	  *size = curpos - rec_ptr->recpos;
	else
	  *size = rec_ptr->recbufsize - (rec_ptr->recpos - curpos);

	/* FIXME: Handle this at runtime */
#if defined(EMUA0)
	/* Only emu8010 A0 silicon needs this workaround,
	 * A1 silicon, take out this part.
	 */
	if (*size > 32)
	  *size -= 32;
#endif

	if (!rec_ptr->is16bit)
	  *size >>= 1;

	*pending = 0;

	return CTSTATUS_SUCCESS;
}


/****************************************************************************/
/* Function : sblive_waveinXferData                                         */
/*                                                                          */
/* Input    : card_wavein - pointer to card wavein object                   */
/*            wave_in - pointer to the specified wavein instance            */
/*            Data - pointer to the data to be transfered                   */
/*            size - data size to be transfered(size in user buffer,        */
/*                      for 8-bit sample, this is different from play       */
/*                      buffer.                                             */
/*                                                                          */
/* Output   : size - data size transfered                                   */
/*                                                                          */
/* About    : transfer the data to/from the wave device.                    */
/****************************************************************************/
int sblive_waveinXferData(struct sblive_wavein *card_wavein, struct wave_in *wave_in, u8 *data, u32 *size)
{
	struct record *rec_ptr = wave_in->rec_ptr;
	u32 sizeToCopy, sizeToCopyNow, sizeCopied;

	if (!data || !size)
	  return CTSTATUS_INVALIDPARAM;
	
	DPD("Size request to xfer = %x\n", *size);

	sizeToCopy = min(rec_ptr->recbufsize * (rec_ptr->is16bit + 1) / 2, *size);
	sizeCopied = 0;

	if (!sizeToCopy) 
	{
		*size = 0;
		return CTSTATUS_SUCCESS;
	}
	
	while (sizeCopied < sizeToCopy) 
	{
		sizeToCopyNow = sizeToCopy - sizeCopied;
		sizeToCopyNow = min(sizeToCopyNow,(rec_ptr->recbufsize - rec_ptr->recpos) * (rec_ptr->is16bit + 1) / 2);
		
		if (rec_ptr->is16bit)
		  copy_to_user(&data[sizeCopied], &rec_ptr->recbuffer[rec_ptr->recpos], sizeToCopyNow);
		else 
		{
			u8 *DstBuf = &data[sizeCopied];
			u16 *SrcBuf = (u16 *) & rec_ptr->recbuffer[rec_ptr->recpos];
			u32 count = sizeToCopyNow;

			while (count--) 
			{
				long lSample;
				u8 bSample;

				lSample = (short) *SrcBuf++;
#ifdef RECTEST
				DPD(" lSample -> %x", lSample);
#endif
				lSample += 128;
				if (lSample > 32767)
				  lSample = 32767;
				bSample = (u8)((lSample >> 8) ^ 0x80);
				copy_to_user(DstBuf, &bSample, 1);
				DstBuf++;
			}
		}

		rec_ptr->recpos += sizeToCopyNow * 2 / (rec_ptr->is16bit + 1);
		rec_ptr->recpos %= rec_ptr->recbufsize;
		sizeCopied += sizeToCopyNow;
	}
	
	*size = sizeToCopy;
	
	DPD("Size copied = %x\n", *size);

	return CTSTATUS_SUCCESS;
}

/****************************************************************************/
/* Function : sblive_waveinGetControl                                       */
/*                                                                          */
/* Input    : card_wavein - pointer to card wavein object                   */
/*            wave_in - pointer to the specified wavein instance            */
/*            ctrlid - control type                                         */
/*                                                                          */
/* Output   : value - pointer to the control value                          */
/*                                                                          */
/* About    : get the specified control value of the wave device.           */
/****************************************************************************/
int sblive_waveinGetControl(struct sblive_wavein *card_wavein, struct wave_in *wave_in, u32 ctrlid, u32 *value)
{
	u32 curpos;

	switch (ctrlid) 
	{
	case WAVEOBJVOLUME:
	case WAVEOBJREVERB:
	case WAVEOBJCHORUS:
		return CTSTATUS_NOTSUPPORTED;

	case WAVEQUERYACTIVEINST:
		if (card_wavein->wave_inlist != NULL)
		  return CTSTATUS_SUCCESS;
		else
		  return CTSTATUS_ERROR;

	default:
		break;
	}

	if (!wave_in)
	  return CTSTATUS_ERROR;

	switch (ctrlid) 
	{
	case WAVEINSTANCEVOLUME:
	case WAVEINSTANCEREVERB:
	case WAVEINSTANCECHORUS:
		return CTSTATUS_NOTSUPPORTED;

	case WAVECURPOS:
		if (sblive_voiceGetControl(wave_in->emu_voice, CCCA_CURRADDR, value) != CTSTATUS_SUCCESS)
		  return CTSTATUS_ERROR;

		curpos = *value;

		/* There is no actual start yet */
		if (wave_in->state == CARDWAVE_STATE_STOPPED)
		  *value = 0;
		else 
		{
			*value = curpos - wave_in->emu_voice->voice_params.start;
			*value <<= wave_in->rec_ptr->is16bit;
		}
		
		break;

	case WAVESTARTLOOP:
		if (sblive_voiceGetControl(wave_in->emu_voice, PSST_LOOPSTARTADDR, value) != CTSTATUS_SUCCESS)
		  return CTSTATUS_ERROR;

		*value -= wave_in->emu_voice->voice_params.start;
		*value <<= wave_in->rec_ptr->is16bit;
		break;

	case WAVEENDLOOP:
		if (sblive_voiceGetControl(wave_in->emu_voice, DSL_LOOPENDADDR, value) != CTSTATUS_SUCCESS)
		  return CTSTATUS_ERROR;
		*value -= wave_in->emu_voice->voice_params.start;
		*value <<= wave_in->rec_ptr->is16bit;
		break;

	case WAVEWRITEPOINTER:
		/* Get write pointer for a particular wave instance */
		*value = wave_in->rec_ptr->recpos;
		break;

	default:
		DPF("BUG: Default case\n");
		return CTSTATUS_ERROR;
	}

	return CTSTATUS_SUCCESS;
}


/****************************************************************************/
/* Function : sblive_waveinIrqCallback                                      */
/*                                                                          */
/* Input    : event - event that cause the callback                         */
/*            refdata - reference data for this callback                    */
/*            param - parameter used for this callback                      */
/*                                                                          */
/* About    : wave IRQ callback function.                                   */
/****************************************************************************/
int sblive_waveinIrqCallback(unsigned long event, unsigned long refdata, unsigned long param)
{
	struct wave_in *wave_in = (struct wave_in *) refdata;

	if (wave_in->state == CARDWAVE_STATE_STOPPED)
	  return CTSTATUS_SUCCESS;

	if (event == VOICECALLBACK_EVENT_CALLBACKSIZEREACHED) 
	{
		wave_in->rec_ptr->prevadcidx = recmgrGetRecIdx(wave_in->rec_ptr->sb_hw);
		wave_in->rec_ptr->pong ^= 1;

		if (wave_in->CallbackFn != NULL) 
		{
			if (!wave_in->dsDpc.is_active) 
			{
				wave_in->dsDpc.is_active = TRUE;
				osScheduleDPC(&wave_in->dsDpc);
			}
		}
		
		return CTSTATUS_SUCCESS;
	}
	
	return CTSTATUS_INVALIDPARAM;
}


int sblive_waveinDpcCallback(unsigned long refdata, unsigned long param1, unsigned long param2)
{
	struct wave_in *wave_in = (struct wave_in *) refdata;

	wave_in->dsDpc.is_active = FALSE;

	if (wave_in->CallbackFn != NULL)
	  wave_in->CallbackFn(VOICECALLBACK_EVENT_CALLBACKSIZEREACHED, wave_in->refdata, 0);

	return CTSTATUS_SUCCESS;
}
