/*
 **********************************************************************
 *     audio.c -- /dev/dsp interface for emu10k1 driver
 *     Copyright 1999, 2000 Creative Labs, Inc.
 *
 **********************************************************************
 *
 *     Date                 Author          Summary of changes
 *     ----                 ------          ------------------
 *     October 20, 1999     Bertrand Lee    base code release
 *     November 2, 1999	    Alan Cox        cleaned up types/leaks
 *
 **********************************************************************
 *
 *     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.
 *
 **********************************************************************
 */

/*
 *	FIXME:
 *		There are some 32bit assumptions here
 *		Cast of pointers to 32bit values is unsafe
 *			- Wave callbacks and elsewhere
 */

#include "audio.h"


/* Audio file operations */
static int emu10k1_audio_llseek(struct inode *inode, struct file *file, off_t offset, int nOrigin)
{
	DPF("emu10k1_audio_lseek() called\n");
	return -ESPIPE;
}

static int emu10k1_audio_read(struct inode *inode, struct file *file, char *buffer, int count)
{
	struct sblive_wavedevice * wave_dev = (struct sblive_wavedevice *) file->private_data;
	struct wiinst *wiinst = wave_dev->wiinst;
	struct wave_in *wave_in = wiinst->wave_in;
	ssize_t ret = 0;
	u32 flags;

	spin_lock_irqsave(&wiinst->lock, flags);

	DPD("emu10k1_audio_read() called, buffer=%x, count=%d\n", (unsigned int) buffer, count);

	if (file->f_mode & FMODE_READ) 
	{
		if (!wave_in) 
		{
			calculate_ifrag(wiinst);
			
			if (sblive_waveinOpen(wave_dev->sb_hw, &wiinst->wave_fmt, waveInCallbackFn,
					      (u32)wave_dev, &wiinst->fragment_size,
					      wiinst->numfrags, &wiinst->wave_in) != CTSTATUS_SUCCESS) 
			{
				spin_unlock_irqrestore(&wiinst->lock, flags);
				
				DPF("sblive_waveinOpen failed!\n");
				return -EINVAL;
			}
			
			DPD("fragment_size size is -> %x\n", wiinst->fragment_size);
		}

		wave_in = wiinst->wave_in;
		
		if (!access_ok(VERIFY_WRITE, buffer, count))
		{
			spin_unlock_irqrestore(&wiinst->lock, flags);
			
			DPF("Cannot write to buffer!\n");
			
			return -EFAULT;
		}

		while (count > 0) 
		{
			u32 bytestocopy, pending;

			if (wave_dev->enablebits & PCM_ENABLE_INPUT && wave_in->state != CARDWAVE_STATE_STARTED) 
			{
				if (sblive_waveinStart(wave_dev->sb_hw, wave_in) != CTSTATUS_SUCCESS) 
				{
					spin_unlock_irqrestore(&wiinst->lock, flags);
					
					DPF("sblive_waveinStart() failed.\n");
					return -EFAULT;
				}
				
				DPF("sblive_waveinStart() succeeded.\n");
			}
			
			if (sblive_waveinGetXferSize(wave_dev->sb_hw->card_wavein, wave_in, &bytestocopy, &pending) != CTSTATUS_SUCCESS)
			{
				spin_unlock_irqrestore(&wiinst->lock, flags);
				
				DPF("sblive_waveinGetXferSize failed\n");
				return -EFAULT;
			}

			DPD("bytestocopy --> %x\n", bytestocopy);

			if (bytestocopy) {

				bytestocopy = min(bytestocopy, count);
			
				if (sblive_waveinXferData(wave_dev->sb_hw->card_wavein, wave_in, (u8 *)buffer, &bytestocopy) != CTSTATUS_SUCCESS)			
				{
					spin_unlock_irqrestore(&wiinst->lock, flags);
					DPF("sblive_waveinXferData failed.\n");
					return ret ? ret : -EFAULT;
				}
			
				count -= bytestocopy;
				buffer += bytestocopy;
				ret += bytestocopy;
			}
			
			if (count > 0) 
			{
				if ((file->f_flags & O_NONBLOCK)
				    || (!(wave_dev->enablebits & PCM_ENABLE_INPUT))) 
				{
					spin_unlock_irqrestore(&wiinst->lock, flags);
					return (ret ? ret : -EAGAIN);
				}
				
				spin_unlock_irqrestore(&wiinst->lock, flags);
				
				interruptible_sleep_on(&wave_in->wait_queue);
				
				if (signal_pending(current))
				  return (ret ? ret : -ERESTARTSYS);

				spin_lock_irqsave(&wiinst->lock, flags);
			}
		}
		DPD("bytes copied -> %x\n", ret);
	}
	
	spin_unlock_irqrestore(&wiinst->lock, flags);
	
	return ret;
}

static int emu10k1_audio_write(struct inode *inode, struct file *file, const char *buffer, int count)
{
	struct sblive_wavedevice *wave_dev = (struct sblive_wavedevice *) file->private_data;
	struct woinst *woinst = wave_dev->woinst;
	struct wave_out *wave_out = woinst->wave_out;
	ssize_t ret;
	u32 flags;
	
	/* FIXME: Is the inode necessary?
	 Before the code was split into seperate kernel directories, the struct
	 wasn't there as ver>=2.1.0, but the code that used it was < 2.3.7 
	 */
#if LINUX_VERSION_CODE >= 0x020100	
	struct inode *inode =  file->f_dentry->d_inode; 
#endif	

	spin_lock_irqsave(&woinst->lock, flags);

	DPD("emu10k1_audio_write() called, buffer=%#x, count=%d\n", (unsigned int) buffer, count);

	if (woinst->mapped)
	{
		spin_unlock_irqrestore(&woinst->lock, flags);
		return -ENXIO;
	}

	if (!wave_out) 
	{
		calculate_ofrag(woinst);
		
		while (sblive_waveoutOpen(wave_dev->sb_hw, &woinst->wave_fmt, waveOutCallbackFn,
					  (u32)wave_dev, &woinst->fragment_size,
					  woinst->numfrags, &woinst->wave_out) != CTSTATUS_SUCCESS) 
		{
			if (file->f_flags & O_NONBLOCK)
			{
				spin_unlock_irqrestore(&woinst->lock, flags);
				return -EAGAIN;
			}
			
#if LINUX_VERSION_CODE < 0x020307
			up(&inode->i_sem);
#endif
			spin_unlock_irqrestore(&woinst->lock, flags);
			
			interruptible_sleep_on(&wave_dev->sb_hw->open_wait);
#if LINUX_VERSION_CODE < 0x020307
			down(&inode->i_sem);
#endif
			if (signal_pending(current))
			  return -ERESTARTSYS;
			
			spin_lock_irqsave(&woinst->lock, flags);
		}
		wave_out = woinst->wave_out;
	}

	if (!access_ok(VERIFY_READ, buffer, count))
	{
		spin_unlock_irqrestore(&woinst->lock, flags);
		return -EFAULT;
	}

	ret = 0;
	while (count > 0) 
	{
		u32 bytestocopy, pending;
		int status;

		status = sblive_waveoutGetXferSize(wave_dev->sb_hw->card_waveout,
						   wave_out, &bytestocopy, &pending);
		
		if (status != CTSTATUS_SUCCESS) 
		{
                        spin_unlock_irqrestore(&woinst->lock, flags);
                        DPF("sblive_waveoutGetXferSize failed\n");
                        return -EFAULT;
                }

		if (woinst->silence_filled > 0)
		{
			if (woinst->silence_filled == 1)
			{
				if (pending <= woinst->fragment_size)
				  woinst->silence_filled = 2;
				else
				  bytestocopy += woinst->fragment_size;
			}
			
                	if (woinst->silence_filled == 2)	
			  bytestocopy = woinst->fragment_size * woinst->numfrags;
		}

		DPD("bytestocopy --> %x\n", bytestocopy);

		if (bytestocopy) {

			bytestocopy = min(bytestocopy, count);

			if (woinst->silence_filled > 0)
			{
				u32 tmp;

				if (woinst->silence_filled == 1)
				  tmp = woinst->silence_start;
				else 
				{
	                        	sblive_waveoutGetControl(wave_dev->sb_hw->card_waveout,
	                                	                 wave_out, WAVECURPOS, &tmp);
					pending = 0;
				}

				if (pending + bytestocopy < woinst->fragment_size)
				{
					woinst->silence_filled = 1;
					woinst->silence_start = tmp + bytestocopy;
				
					if (woinst->silence_start > woinst->fragment_size * woinst->numfrags)
					  woinst->silence_start -= woinst->fragment_size * woinst->numfrags;
				}
				else
				  woinst->silence_filled = 0;
			
	                        sblive_waveoutSetControl(wave_dev->sb_hw, wave_out,
	                                                 WAVEWRITEPOINTER, &tmp);
	                }
		
			status = sblive_waveoutXferData(wave_dev->sb_hw->card_waveout,
							wave_out, (u8 *)buffer, &bytestocopy);

			if (woinst->silence_filled) 
			{
				u32 tmp = woinst->fragment_size;
				sblive_waveoutFillSilence(wave_dev->sb_hw->card_waveout, wave_out, &tmp);
			}

			if (status != CTSTATUS_SUCCESS) 
			{
				spin_unlock_irqrestore(&woinst->lock, flags);
				DPF("sblive_waveoutXferData failed.\n");
				return (ret ? ret : -EFAULT);
			}
		
			count -= bytestocopy;
			buffer += bytestocopy;
			ret += bytestocopy;
			woinst->total_copied += bytestocopy;
		
			if ((wave_dev->enablebits & PCM_ENABLE_OUTPUT)
			    && (wave_out->state != CARDWAVE_STATE_STARTED)
			    && (woinst->total_copied >= woinst->fragment_size)) 
			{
				if (sblive_waveoutStart(wave_dev->sb_hw, wave_out) != CTSTATUS_SUCCESS) 
				{
					spin_unlock_irqrestore(&woinst->lock, flags);
					DPF("sblive_waveoutStart failed.\n");
					return -EFAULT;
				}
			}
		}

		if (count > 0) 
		{
			if ((file->f_flags & O_NONBLOCK)
			    || (!(wave_dev->enablebits & PCM_ENABLE_OUTPUT))) 
			{
				spin_unlock_irqrestore(&woinst->lock, flags);
				return (ret ? ret : -EAGAIN);
			}
			
#if LINUX_VERSION_CODE < 0x020307
			up(&inode->i_sem);
#endif
			spin_unlock_irqrestore(&woinst->lock, flags);
			
			interruptible_sleep_on(&woinst->wait_queue);
#if LINUX_VERSION_CODE < 0x020307
			down(&inode->i_sem);
#endif
			if (signal_pending(current))
			  return (ret ? ret : -ERESTARTSYS);
			
			spin_lock_irqsave(&woinst->lock, flags);
		}
		
	}
	
	spin_unlock_irqrestore(&woinst->lock, flags);
	
	DPD("bytes copied -> %x\n", ret);
	
	return ret;
}

static int emu10k1_audio_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg)
{
	struct sblive_wavedevice *wave_dev = (struct sblive_wavedevice *) file->private_data;
	int val = 0;
	struct woinst *woinst = NULL;
	struct wave_out *wave_out = NULL;
	struct wiinst *wiinst = NULL;
	struct wave_in *wave_in = NULL;
	u32 flags, pending, bytestocopy, dummy;

	DPF("emu10k1_audio_ioctl() called:\n");

	if (file->f_mode & FMODE_WRITE){
		woinst = wave_dev->woinst;
        	wave_out = woinst->wave_out;
	}
	
	if (file->f_mode & FMODE_READ){
		wiinst = wave_dev->wiinst;
        	wave_in = wiinst->wave_in;
	}

	switch (cmd) 
	{
	case OSS_GETVERSION:
		DPF("OSS_GETVERSION:\n");
		return put_user(SOUND_VERSION, (int *) arg);

	case SNDCTL_DSP_RESET:
		DPF("SNDCTL_DSP_RESET:\n");
		wave_dev->enablebits = PCM_ENABLE_OUTPUT | PCM_ENABLE_INPUT;
		
		if (file->f_mode & FMODE_WRITE) 
		{
			spin_lock_irqsave(&woinst->lock, flags);
			
			if (wave_out) 
			{
				sblive_waveoutStop(wave_dev->sb_hw, wave_out, &dummy);
				wave_out->wavexferbuf->xferpos = 0;
				wave_out->wavexferbuf->stopposition = 0;
			}
			
			woinst->total_copied = 0;
			woinst->total_played = 0;
			woinst->silence_filled = 0;
			woinst->silence_start = 0;
			woinst->getoptr_blocks = 0;
			woinst->wave_ptr = 0;
			
			spin_unlock_irqrestore(&woinst->lock, flags);
		}
		
		if (file->f_mode & FMODE_READ) 
		{
			spin_lock_irqsave(&wiinst->lock, flags);
			
			if (wave_in)
			  sblive_waveinStop(wave_dev->sb_hw, wave_in, &dummy);
			spin_unlock_irqrestore(&wiinst->lock, flags);
		}
		
		break;

	case SNDCTL_DSP_SYNC:
		DPF("SNDCTL_DSP_SYNC:\n");
		
		if (file->f_mode & FMODE_WRITE) 
		{
			spin_lock_irqsave(&woinst->lock, flags);
			
			if (wave_out && wave_out->state == CARDWAVE_STATE_STARTED)
			  while ((woinst->total_played < woinst->total_copied)
				 && !signal_pending(current))
			  {
				  spin_unlock_irqrestore(&woinst->lock, flags);				  
				  interruptible_sleep_on(&woinst->wait_queue);
				  spin_lock_irqsave(&woinst->lock, flags);
			  }
			
			spin_unlock_irqrestore(&woinst->lock, flags);
		}
		
		break;
		
	case SNDCTL_DSP_SETDUPLEX:
		DPF("SNDCTL_DSP_SETDUPLEX:\n");
		break;

	case SNDCTL_DSP_GETCAPS:
		DPF("SNDCTL_DSP_GETCAPS:\n");
		return put_user(DSP_CAP_DUPLEX | DSP_CAP_REALTIME | DSP_CAP_TRIGGER | DSP_CAP_MMAP, (int *)arg);

	case SNDCTL_DSP_SPEED:
		DPF("SNDCTL_DSP_SPEED:\n");

		get_user_ret(val, (int *) arg, -EFAULT);
		DPD("val is %d\n", val);
		
		if (val >= 0) 
		{
			if (file->f_mode & FMODE_WRITE) 
			{
				spin_lock_irqsave(&woinst->lock, flags);
				
				if (wave_out)
				  sblive_waveoutStop(wave_dev->sb_hw, wave_out, &dummy);

				woinst->wave_fmt.samplingrate = val;

				if (woinst->mapped) 
				{
					struct wave_format wave_fmt = woinst->wave_fmt;
					
					if (sblive_waveoutSetControl(wave_dev->sb_hw, wave_out, WAVEINSTANCEFORMAT, (u32 *)&wave_fmt)
					    != CTSTATUS_SUCCESS)
					{
						spin_unlock_irqrestore(&woinst->lock, flags);
						return -EINVAL;
					}
				}
				
				spin_unlock_irqrestore(&woinst->lock, flags);
				
				DPD("Set playback sampling rate -> %x\n", woinst->wave_fmt.samplingrate);
			}
			
			if (file->f_mode & FMODE_READ) 
			{
				struct wave_format *wave_fmt;

				spin_lock_irqsave(&wiinst->lock, flags);

				if (wave_in)
				  sblive_waveinStop(wave_dev->sb_hw, wave_in, &dummy);
				
				wave_fmt = &wiinst->wave_fmt;
				wave_fmt->samplingrate = val;
				
				if (sblive_waveinQueryFormat(wave_dev->sb_hw->card_wavein, wave_fmt, CARDWAVE_QF_ALL) != CTSTATUS_SUCCESS)
				  val = wave_fmt->samplingrate = 8000;	/* Default */
				
				DPD("Set recording sampling rate -> %d\n", wave_fmt->samplingrate);
				
				spin_unlock_irqrestore(&wiinst->lock, flags);
			}

			return put_user(val, (int *) arg);
		} else 
		{
			if (file->f_mode & FMODE_READ)
			  val = wiinst->wave_fmt.samplingrate;
			else if (file->f_mode & FMODE_WRITE)
			  val = woinst->wave_fmt.samplingrate;

			return put_user(val, (int *) arg);
		}
		break;

	case SNDCTL_DSP_STEREO:
		DPF("SNDCTL_DSP_STEREO:\n");

		get_user_ret(val, (int *) arg, -EFAULT);
		DPD(" val is %d\n", val);
		
		if (file->f_mode & FMODE_WRITE) 
		{
			spin_lock_irqsave(&woinst->lock, flags);
			
			if (wave_out)
			  sblive_waveoutStop(wave_dev->sb_hw, wave_out, &dummy);

			woinst->wave_fmt.channels = val ? 2 : 1;

			if (woinst->mapped) 
			{
				struct wave_format wave_fmt = woinst->wave_fmt;
				
				if (sblive_waveoutSetControl(wave_dev->sb_hw, wave_out, WAVEINSTANCEFORMAT, (u32 *)&wave_fmt) != CTSTATUS_SUCCESS)
				{
					spin_unlock_irqrestore(&woinst->lock, flags);
					DPF("waveoutSetControl() failed!\n");
					return -EINVAL;
				}
			}

			spin_unlock_irqrestore(&woinst->lock, flags);
			
			DPD("Set playback stereo -> %d\n", woinst->wave_fmt.channels);
		}
		
		if (file->f_mode & FMODE_READ) 
		{
			struct wave_format *wave_fmt;

			spin_lock_irqsave(&wiinst->lock, flags);
			
			if (wave_in)
			  sblive_waveinStop(wave_dev->sb_hw, wave_in, &dummy);
			
			wave_fmt = &wiinst->wave_fmt;
			wave_fmt->channels = val ? 2 : 1;
			
			if (sblive_waveinQueryFormat(wave_dev->sb_hw->card_wavein, wave_fmt, CARDWAVE_QF_ALL) != CTSTATUS_SUCCESS)
			{
				spin_unlock_irqrestore(&wiinst->lock, flags);
				DPF("waveinQueryFormat() failed!\n");
				return -EINVAL;
			}
			
			spin_unlock_irqrestore(&wiinst->lock, flags);
			DPD("Set recording stereo -> %d\n", wave_fmt->channels);
		}
		
		break;

	case SNDCTL_DSP_CHANNELS:
		DPF("SNDCTL_DSP_CHANNELS:\n");

		get_user_ret(val, (int *) arg, -EFAULT);
		DPD(" val is %d\n", val);
		
		if (val != 0) 
		{
			if (file->f_mode & FMODE_WRITE) 
			{
				spin_lock_irqsave(&woinst->lock, flags);
				
				if (wave_out)
				  sblive_waveoutStop(wave_dev->sb_hw, wave_out, &dummy);

				woinst->wave_fmt.channels = val;

				if (woinst->mapped) 
				{
					struct wave_format wave_fmt = woinst->wave_fmt;
					
					if (sblive_waveoutSetControl(wave_dev->sb_hw, wave_out, WAVEINSTANCEFORMAT, (u32 *)&wave_fmt) != CTSTATUS_SUCCESS)
					{
						spin_unlock_irqrestore(&woinst->lock, flags);
						DPF("waveoutSetControl() failed!\n");
						return -EINVAL;
					}
					
					DPD("Set playback number of channels -> %d\n", woinst->wave_fmt.channels);
				}
				
				spin_unlock_irqrestore(&woinst->lock, flags);
			}
			
			if (file->f_mode & FMODE_READ) 
			{
				struct wave_format *wave_fmt;

				spin_lock_irqsave(&wiinst->lock, flags);
				
				if (wave_in)
				  sblive_waveinStop(wave_dev->sb_hw, wave_in, &dummy);
				
				wave_fmt = &wiinst->wave_fmt;
				wave_fmt->channels = val;
				
				if (sblive_waveinQueryFormat(wave_dev->sb_hw->card_wavein, wave_fmt, CARDWAVE_QF_ALL) != CTSTATUS_SUCCESS)
				{
					spin_unlock_irqrestore(&wiinst->lock, flags);
					DPF("waveinQueryFormat() called!\n");
					return -EINVAL;
				}
								
				spin_unlock_irqrestore(&wiinst->lock, flags);
				
				DPD("Set recording number of channels -> %d\n", wave_fmt->channels);
			}

			return put_user(val, (int *) arg);
		} else 
		{
			if (file->f_mode & FMODE_READ)
			  val = wiinst->wave_fmt.channels;
			else if (file->f_mode & FMODE_WRITE)
			  val = woinst->wave_fmt.channels;

			return put_user(val, (int *) arg);
		}
		break;

	case SNDCTL_DSP_GETFMTS:
		DPF("SNDCTL_DSP_GETFMTS:\n");
		return put_user(AFMT_S16_LE | AFMT_U8, (int *) arg);

	case SNDCTL_DSP_SETFMT:	/* Same as SNDCTL_DSP_SAMPLESIZE */
		DPF("SNDCTL_DSP_SETFMT:\n");

		get_user_ret(val, (int *) arg, -EFAULT);
		DPD(" val is %d\n", val);

		if (val != AFMT_QUERY) 
		{
			if (file->f_mode & FMODE_WRITE) 
			{
				spin_lock_irqsave(&woinst->lock, flags);

				if (wave_out)
				  sblive_waveoutStop(wave_dev->sb_hw, wave_out, &dummy);

				woinst->wave_fmt.bitspersample = val;

				if (woinst->mapped) 
				{
					struct wave_format wave_fmt = woinst->wave_fmt;

					if (sblive_waveoutSetControl(wave_dev->sb_hw, wave_out, WAVEINSTANCEFORMAT, (u32 *) &wave_fmt) != CTSTATUS_SUCCESS)
					{
						spin_unlock_irqrestore(&woinst->lock, flags);
						DPF("waveoutSetControl() failed!\n");
						return -EINVAL;
					}
				}

				spin_unlock_irqrestore(&woinst->lock, flags);
				
				DPD("Set playback sample size -> %d\n", woinst->wave_fmt.bitspersample);
			}

			if (file->f_mode & FMODE_READ) 
			{
				struct wave_format *wave_fmt;

				spin_lock_irqsave(&wiinst->lock, flags);
					
				if (wave_in)
				  sblive_waveinStop(wave_dev->sb_hw, wave_in, &dummy);

				wave_fmt = &wiinst->wave_fmt;
				wave_fmt->bitspersample = val;

				if (sblive_waveinQueryFormat(wave_dev->sb_hw->card_wavein, wave_fmt, CARDWAVE_QF_ALL) != CTSTATUS_SUCCESS)
				{
					spin_unlock_irqrestore(&wiinst->lock, flags);
					DPF("waveinQueryFormat() failed!\n");
					return -EINVAL;
				}

				spin_unlock_irqrestore(&wiinst->lock, flags);
				
				DPD("set recording sample size -> %d\n", wave_fmt->bitspersample);
			}

			return put_user((val == 16) ? AFMT_S16_LE : AFMT_U8, (int *)arg);
		} else 
		{
			if (file->f_mode & FMODE_READ)
			  val = wiinst->wave_fmt.bitspersample;
			else if (file->f_mode & FMODE_WRITE)
			  val = woinst->wave_fmt.bitspersample;

			return put_user((val == 16) ? AFMT_S16_LE : AFMT_U8, (int *) arg);
		}
		break;

	case SOUND_PCM_READ_BITS:

		if (file->f_mode & FMODE_READ)
		  val = wiinst->wave_fmt.bitspersample;
		else if ( file->f_mode & FMODE_WRITE )
		  val = woinst->wave_fmt.bitspersample;

		return put_user((val == 16) ? AFMT_S16_LE : AFMT_U8, (int *)arg);
		
	case SOUND_PCM_READ_RATE:

		if (file->f_mode & FMODE_READ)
	 	  val = wiinst->wave_fmt.samplingrate;
		else if ( file->f_mode & FMODE_WRITE )
		  val = woinst->wave_fmt.samplingrate;

		return put_user(val, (int *)arg);
		 
	case SOUND_PCM_READ_CHANNELS:

		if (file->f_mode & FMODE_READ)
		  val = wiinst->wave_fmt.channels;
		else if ( file->f_mode & FMODE_WRITE )
		  val = woinst->wave_fmt.channels;

		return put_user(val, (int *)arg);
		
	case SOUND_PCM_WRITE_FILTER:
		DPF("SNDCTL_DSP_POST: not implemented\n");
		break;
		
	case SOUND_PCM_READ_FILTER:
		DPF("SNDCTL_DSP_POST: not implemented\n");
		break;
		
	case SNDCTL_DSP_SETSYNCRO:
		DPF("SNDCTL_DSP_POST: not implemented\n");
		break;

	case SNDCTL_DSP_GETTRIGGER:
		DPF("SNDCTL_DSP_GETTRIGGER:\n");

		if (file->f_mode & FMODE_WRITE && (wave_dev->enablebits & PCM_ENABLE_OUTPUT))
		  val |= PCM_ENABLE_OUTPUT;
		if (file->f_mode & FMODE_READ && (wave_dev->enablebits & PCM_ENABLE_INPUT))
		  val |= PCM_ENABLE_INPUT;

		return put_user(val, (int *) arg);

	case SNDCTL_DSP_SETTRIGGER:
		DPF("SNDCTL_DSP_SETTRIGGER:\n");

		get_user_ret(val, (int *) arg, -EFAULT);
			
		if (file->f_mode & FMODE_WRITE) 
		{
			spin_lock_irqsave(&woinst->lock, flags);

			if (wave_out) 
			{
				if (val & PCM_ENABLE_OUTPUT) 
				{
					sblive_waveoutStart(wave_dev->sb_hw, wave_out);
					wave_dev->enablebits |= PCM_ENABLE_OUTPUT;
				} else 
				{
					sblive_waveoutStop(wave_dev->sb_hw, wave_out, &dummy);
					wave_dev->enablebits &= ~PCM_ENABLE_OUTPUT;
				}
			}

			spin_unlock_irqrestore(&woinst->lock, flags);
		}
			
		if (file->f_mode & FMODE_READ) 
		{
			spin_lock_irqsave(&wiinst->lock, flags);

			if (wave_in) 
			{
				if (val & PCM_ENABLE_INPUT) 
				{
					sblive_waveinStart(wave_dev->sb_hw, wave_in);
					wave_dev->enablebits |= PCM_ENABLE_INPUT;
				} else 
				{
					sblive_waveinStop(wave_dev->sb_hw, wave_in, &dummy);
					wave_dev->enablebits &= ~PCM_ENABLE_INPUT;
				}
			}

			spin_unlock_irqrestore(&wiinst->lock, flags);
		}
		break;

	case SNDCTL_DSP_GETOSPACE:
		{
			audio_buf_info info;
			
			DPF("SNDCTL_DSP_GETOSPACE:\n");

			if (!(file->f_mode & FMODE_WRITE))
			  return -EINVAL;
			
			spin_lock_irqsave(&woinst->lock, flags);

			if (wave_out) 
			{
				if (sblive_waveoutGetXferSize(wave_dev->sb_hw->card_waveout, wave_out, &bytestocopy, &pending)
				    != CTSTATUS_SUCCESS) 
				{
					DPF("sblive_waveoutGetXferSize failed\n");

				        spin_unlock_irqrestore(&woinst->lock, flags);
					return -EFAULT;
				}
				info.bytes = bytestocopy;
			} else 
			{
				calculate_ofrag(woinst);
				info.bytes = woinst->numfrags * woinst->fragment_size;
			}

			if (woinst->silence_filled > 0)
			{
				if (woinst->silence_filled == 1)
				{
                       	        	if (pending <= woinst->fragment_size)
					  woinst->silence_filled = 2;
                               		else
					  info.bytes += woinst->fragment_size;
				}

				if (woinst->silence_filled == 2)
				  info.bytes = woinst->numfrags * woinst->fragment_size;
			}

			info.fragstotal = woinst->numfrags;
			info.fragments = info.bytes / woinst->fragment_size;
			info.fragsize = woinst->fragment_size;

			spin_unlock_irqrestore(&woinst->lock, flags);

			if (copy_to_user((int *) arg, &info, sizeof(info)))
			  return -EFAULT;
		}
		break;

	case SNDCTL_DSP_GETISPACE:
		{
			audio_buf_info info;
			
			DPF("SNDCTL_DSP_GETISPACE:\n");

			if (!(file->f_mode & FMODE_READ))
			  return -EINVAL;

			spin_lock_irqsave(&wiinst->lock, flags);

			if (wave_in) 
			{
				if (sblive_waveinGetXferSize(wave_dev->sb_hw->card_wavein, wave_in, &bytestocopy, &pending) 
				    != CTSTATUS_SUCCESS) 
				{
					DPF("sblive_waveinGetXferSize failed\n");

				        spin_unlock_irqrestore(&wiinst->lock, flags);
					return -EFAULT;
				}

				info.bytes = bytestocopy;

			} else 
			{
				calculate_ifrag(wiinst);
				info.bytes = wiinst->numfrags * wiinst->fragment_size;
			}

			info.fragstotal = wiinst->numfrags;
			info.fragments = info.bytes / wiinst->fragment_size;
			info.fragsize = wiinst->fragment_size;

			spin_unlock_irqrestore(&wiinst->lock, flags);

			if (copy_to_user((int *) arg, &info, sizeof(info)))
			  return -EFAULT;
		}
		break;

	case SNDCTL_DSP_NONBLOCK:
		DPF("SNDCTL_DSP_NONBLOCK:\n");
		
		file->f_flags |= O_NONBLOCK;
		break;

	case SNDCTL_DSP_GETODELAY:
		DPF("SNDCTL_DSP_GETODELAY:\n");

		if (!(file->f_mode & FMODE_WRITE))
			return -EINVAL;

		spin_lock_irqsave(&woinst->lock, flags);

		if (wave_out) 
		{
			if (sblive_waveoutGetXferSize(wave_dev->sb_hw->card_waveout, wave_out, &bytestocopy, &pending) 
			    != CTSTATUS_SUCCESS) 
			{
				DPF("sblive_waveoutGetXferSize failed\n");

				spin_unlock_irqrestore(&woinst->lock, flags);
				return -EFAULT;
			}

			if (woinst->silence_filled > 0)
			{
				if (woinst->silence_filled == 1)
				{
					if (pending <= woinst->fragment_size)
					  woinst->silence_filled = 2;
					else
					  pending -= woinst->fragment_size;
				}

				if (woinst->silence_filled == 2)
				  pending = 0;
                               }

			val = pending;
		}

		spin_unlock_irqrestore(&woinst->lock, flags);

		return put_user(val, (int *) arg);

	case SNDCTL_DSP_GETIPTR:
		DPF("SNDCTL_DSP_GETIPTR: not implemented\n");
		break;

	case SNDCTL_DSP_GETOPTR:
		{
			count_info cinfo;

			DPF("SNDCTL_DSP_GETOPTR:\n");
			
			if (!(file->f_mode & FMODE_WRITE))
			  return -EINVAL;

			spin_lock_irqsave(&woinst->lock, flags);
			
			if (wave_out) 
			{
				if (sblive_waveoutGetControl(wave_dev->sb_hw->card_waveout, wave_out, WAVECURPOS, (u32 *)&cinfo.ptr)
				    != CTSTATUS_SUCCESS) 
				{
					spin_unlock_irqrestore(&woinst->lock, flags);
					return -EINVAL;
				}
			} else
			  cinfo.ptr = 0;
			
			DPD("cinfo.ptr -> %d\n", cinfo.ptr);
			
			cinfo.bytes = cinfo.ptr + woinst->total_played - woinst->total_played % (woinst->fragment_size * woinst->numfrags);
			cinfo.blocks = cinfo.bytes / woinst->fragment_size - woinst->getoptr_blocks;

			woinst->getoptr_blocks = cinfo.bytes / woinst->fragment_size;

			spin_unlock_irqrestore(&woinst->lock, flags);
			
			if (copy_to_user((void *) arg, &cinfo, sizeof(cinfo)))
			  return -EFAULT;
		}
		break;

	case SNDCTL_DSP_GETBLKSIZE:
		DPF("SNDCTL_DSP_GETBLKSIZE:\n");
		
		if (file->f_mode & FMODE_WRITE) 
		{
			spin_lock_irqsave(&woinst->lock, flags);
			
			calculate_ofrag(woinst);
			
			spin_unlock_irqrestore(&woinst->lock, flags);
			
			return put_user(woinst->fragment_size, (int *) arg);
		}
		
		if (file->f_mode & FMODE_READ) 
		{
			spin_lock_irqsave(&wiinst->lock, flags);
			
			calculate_ifrag(wiinst);
			
			spin_unlock_irqrestore(&wiinst->lock, flags);
			
			return put_user(wiinst->fragment_size, (int *) arg);
		}
		break;
		
	case SNDCTL_DSP_POST:
		DPF("SNDCTL_DSP_POST: not implemented\n");
		break;

	case SNDCTL_DSP_SUBDIVIDE:
		DPF("SNDCTL_DSP_SUBDIVIDE: not implemented\n");
		break;

	case SNDCTL_DSP_SETFRAGMENT:
		DPF("SNDCTL_DSP_SETFRAGMENT:\n");

		get_user_ret(val, (int *) arg, -EFAULT);

		DPD("val is %x\n", val);

		if (val == 0)
		  return -EIO;

		if (file->f_mode & FMODE_WRITE) 
		{
			if (wave_out)
			  return -EINVAL;		/* too late to change */

			woinst->ossfragshift = val & 0xffff;
			woinst->numfrags = (val >> 16) & 0xffff;
		}

		if (file->f_mode & FMODE_READ) 
		{
			if (wave_in)
			  return -EINVAL;		/* too late to change */

			wiinst->ossfragshift = val & 0xffff;
			wiinst->numfrags = (val >> 16) & 0xffff;
		}

		break;
		
	default:		/* Default is unrecognized command */
		DPD("default: %x\n", cmd);
		return -EINVAL;
	}
	return 0;
}

static int emu10k1_audio_mmap(struct inode *inode, struct file *file, struct vm_area_struct *vma)
{
	struct sblive_wavedevice *wave_dev = (struct sblive_wavedevice *) file->private_data;
	struct woinst *woinst = wave_dev->woinst;
	u32 size, flags;

	DPF("emu10k1_audio_mmap() called\n");

	if (!(vma->vm_flags & VM_WRITE))
	  return -EINVAL;

	if (vma->vm_offset != 0)
		return -ENXIO;

	spin_lock_irqsave(&woinst->lock, flags);
	
	if (!woinst->wave_out) 
	{
		calculate_ofrag(woinst);
		
		if (sblive_waveoutOpen(wave_dev->sb_hw, &woinst->wave_fmt, waveOutCallbackFn, (u32)wave_dev, &woinst->fragment_size, woinst->numfrags, &woinst->wave_out)
		    != CTSTATUS_SUCCESS) 
		{
			DPF("sblive_waveoutOpen failed!\n");
			
			spin_unlock_irqrestore(&woinst->lock, flags);
			return -EINVAL;
		}
		
		/* Now mark the pages as reserved, otherwise remap_page_range doesn't do what we want */
		if (woinst->wave_out->memhandle) 
		{
			u32 order = ((PLMEMHANDLE) woinst->wave_out->memhandle)->order;
			u32 map, mapend;

			mapend = MAP_NR(woinst->wave_out->wavexferbuf->xferbuffer + (PAGE_SIZE << order) - 1);
			for (map = MAP_NR(woinst->wave_out->wavexferbuf->xferbuffer); map <= mapend; map++)
			  set_bit(PG_reserved, &mem_map[map].flags);
		}
	}
	
	size = vma->vm_end - vma->vm_start;

	if (size > (PAGE_SIZE << ((PLMEMHANDLE) woinst->wave_out->memhandle)->order))
	  return -EINVAL;
	
	/* In Linux, get_free_pages returns a virtual address (it may also be */
	/* physical but nothing is guaranteed. We have to do virt_to_phys here. */
	if (remap_page_range(vma->vm_start, virt_to_phys(((PLMEMHANDLE) woinst->wave_out->memhandle)->virtaddx), size, vma->vm_page_prot))
	{
		spin_unlock_irqrestore(&woinst->lock, flags);
		return -EAGAIN;
	}

	woinst->mapped = TRUE;

	vma->vm_inode = inode;
	inode->i_count++;

	spin_unlock_irqrestore(&woinst->lock, flags);
	
	return 0;
}

static int emu10k1_audio_open(struct inode *inode, struct file *file)
{
	int minor = MINOR(inode->i_rdev);
	struct sblive_hw * sb_hw = sblive_devs;
	struct sblive_wavedevice * wave_dev;

	DPF("emu10k1_audio_open() called\n");

	/* Check for correct device to open */
	while (sb_hw && ((sb_hw->audio_num ^ minor) & ~0xf))
	  sb_hw = sb_hw->next;
	if (!sb_hw)
	  return -ENODEV;

	/* Wait for device to become free */
	down(&sb_hw->open_sem);
	
	while ((file->f_mode & FMODE_READ) && (sb_hw->open_mode & FMODE_READ)) 
	{
		if (file->f_flags & O_NONBLOCK) 
		{
			up(&sb_hw->open_sem);
			return -EAGAIN;
		}
		
		up(&sb_hw->open_sem);
		
		DPF("open sleep....\n");
		interruptible_sleep_on(&sb_hw->open_wait);
		
		if (signal_pending(current))
		  return -ERESTARTSYS;
		
		down(&sb_hw->open_sem);
	}

	wave_dev = (struct sblive_wavedevice *)kmalloc(sizeof(struct sblive_wavedevice), GFP_KERNEL);       
	if (!wave_dev) 
	{
		DPF("struct sblive_wavedevice alloc fail.\n");
		up(&sb_hw->open_sem);
		return -EINVAL;
	}
	
#ifdef MEMTEST
	DPD("kmalloc: [%p]\n", wave_dev);
#endif
	
	wave_dev->sb_hw = sb_hw;
	wave_dev->wiinst = NULL;
	wave_dev->woinst = NULL;
	wave_dev->enablebits = PCM_ENABLE_OUTPUT | PCM_ENABLE_INPUT;	/* Default */

	if (file->f_mode & FMODE_WRITE) 
	{
		struct woinst *woinst;

		woinst = (struct woinst *) kmalloc(sizeof(struct woinst), GFP_KERNEL);
		if (!woinst) 
		{
			DPF("struct woinst alloc failed.\n");
			up(&sb_hw->open_sem);
			return -ENODEV;
		}
		
#ifdef MEMTEST
		DPD("kmalloc: [%p]\n", woinst);
#endif
		
		/* Set default format to : mono 8-bit 8kHz */
		woinst->wave_fmt.flags = 0;
		woinst->wave_fmt.samplingrate = 8000;
		woinst->wave_fmt.bitspersample = 8;
		woinst->wave_fmt.channels = 1;
		woinst->wave_fmt.isinput = (file->f_mode == FMODE_READ) ? TRUE : FALSE;
		woinst->ossfragshift = 0;
		woinst->fragment_size = 0;
		woinst->numfrags = 0;
		woinst->wave_out = NULL;	// wave_out instance

		init_waitqueue(&woinst->wait_queue);

		woinst->mapped = FALSE;
		woinst->total_copied = 0;
		woinst->total_played = 0;
		woinst->silence_filled = 0;
		woinst->silence_start = 0;
		woinst->getoptr_blocks = 0;
		woinst->wave_ptr = 0;
		woinst->lock = SPIN_LOCK_UNLOCKED;
		wave_dev->woinst = woinst;
	}

	if (file->f_mode & FMODE_READ) 
	{
		/* Recording */
		struct wiinst *wiinst;

		/* Only support one wave input instance */
		if (sb_hw->card_wavein->numrecordinst >= 1) 
		{
			DPF("exceed one wave input instance\n");
			up(&sb_hw->open_sem);
			return -ENODEV;
		}
		
		wiinst = (struct wiinst *) kmalloc(sizeof(struct wiinst), GFP_KERNEL);
		if (!wiinst) 
		{
			DPF("struct wiinst alloc failed\n");
			up(&sb_hw->open_sem);
			return -ENODEV;
		}
		
#ifdef MEMTEST
		DPD("kmalloc: [%p]\n", wiinst);
#endif
		
		/* Set default format to : mono 8-bit 8kHz */
		wiinst->wave_fmt.flags = 0;
		wiinst->wave_fmt.samplingrate = 8000;
		wiinst->wave_fmt.bitspersample = 8;
		wiinst->wave_fmt.channels = 1;
		wiinst->wave_fmt.isinput = (file->f_mode == FMODE_READ) ? TRUE : FALSE;
		wiinst->ossfragshift = 0;
		wiinst->fragment_size = 0;
		wiinst->numfrags = 0;
		wiinst->wave_in = NULL;	/* sblive_wavein instance */
		wiinst->lock = SPIN_LOCK_UNLOCKED;
		wave_dev->wiinst = wiinst;
		wave_dev->sb_hw->card_wavein->numrecordinst++;
		
#ifdef EMU10K1_DEBUG
		if (wave_dev->woinst != NULL)
		  DPF("audio_open opened both WAVEOUT and WAVEIN!\n");
#endif
		
	}
	
	file->private_data = (void *) wave_dev;

	sb_hw->open_mode |= file->f_mode & FMODE_READ;
	up(&sb_hw->open_sem);

	MOD_INC_USE_COUNT;
	return 0; /* Success? */
}

static void emu10k1_audio_release(struct inode *inode, struct file *file)
{
	struct sblive_wavedevice *wave_dev = (struct sblive_wavedevice *) file->private_data;
	struct sblive_hw *sb_hw = wave_dev->sb_hw;
	
	/* FIXME: Do we need a spinlock here? */
	
	DPF("emu10k1_audio_release() called\n");

	if (file->f_mode & FMODE_WRITE) 
	{
		struct woinst *woinst = wave_dev->woinst;
		struct wave_out *wave_out = woinst->wave_out;

		if (wave_out) 
		{
			if ((wave_out->state == CARDWAVE_STATE_STARTED) && !(file->f_flags & O_NONBLOCK)) 
			{
				while (!signal_pending(current)) 
				{
					if (woinst->total_played < woinst->total_copied) 
					{
						DPF("Buffer hasn't been totally played, sleep....\n");
						interruptible_sleep_on(&woinst->wait_queue);
					} else
					  break;
				}
			}
			
			if (woinst->mapped && wave_out->memhandle) 
			{
				u32 order = ((PLMEMHANDLE) wave_out->memhandle)->order;
				u32 map, mapend;

				/* Undo marking the pages as reserved */
				mapend = MAP_NR(wave_out->wavexferbuf->xferbuffer + (PAGE_SIZE << order) - 1);
				for (map = MAP_NR(wave_out->wavexferbuf->xferbuffer); map <= mapend; map++)
				  clear_bit(PG_reserved, &mem_map[map].flags);
			}
			
			woinst->mapped = FALSE;
			sblive_waveoutClose(wave_dev->sb_hw, wave_out);
		}
		kfree((void *) wave_dev->woinst);
	}
	
	if (file->f_mode & FMODE_READ) 
	{
		struct wave_in *wave_in = wave_dev->wiinst->wave_in;

		if (wave_in)
		  sblive_waveinClose(wave_dev->sb_hw, wave_in);
		
		kfree((void *) wave_dev->wiinst);
		
		wave_dev->sb_hw->card_wavein->numrecordinst--;
	}
	
	kfree((void *) wave_dev);
	

	down(&sb_hw->open_sem);
	sb_hw->open_mode &= ~(file->f_mode & FMODE_READ);
	up(&sb_hw->open_sem);
	wake_up(&sb_hw->open_wait);
	MOD_DEC_USE_COUNT;
}


static int emu10k1_audio_poll(struct inode *inode, struct file *file, int sel_type, select_table * wait)
{
	struct sblive_wavedevice *wave_dev = (struct sblive_wavedevice *) file->private_data;
	u32 flags, bytestocopy, pending;

	DPF("emu10k1_audio_poll() called\n");
	
	if (sel_type == SEL_OUT && file->f_mode & FMODE_WRITE) 
	{
		spin_lock_irqsave(&wave_dev->woinst->lock, flags);
		
		struct wave_out *wave_out = wave_dev->woinst->wave_out;

		if (wave_out) 
		{
			if (sblive_waveoutGetXferSize(wave_dev->sb_hw->card_waveout, (u32) wave_out, &bytestocopy, &pending) == CTSTATUS_SUCCESS) 
			{
				if (bytestocopy > 0) 
				{
					spin_unlock_irqrestore(&wave_dev->woinst->lock, flags);
					DPF("bytestocopy > 0, returning from poll()\n");
					return 1; /* There is space in the playback buffer */
				}
				
				DPD("bytestocopy --> %d\n", bytestocopy);
				
				select_wait(&wave_dev->woinst->wait_queue, wait);
			}
		}
		
		spin_unlock_irqrestore(&wave_dev->woinst->lock, flags);
	}
	
	if (sel_type == SEL_IN && file->f_mode & FMODE_READ) 
	{
		spin_lock_irqsave(&wave_dev->wiinst->lock, flags);
		struct wave_in *wave_in = wave_dev->wiinst->wave_in;

		if (sblive_waveinGetXferSize(wave_dev->sb_hw->card_wavein, wave_in, &bytestocopy, &pending) == CTSTATUS_SUCCESS) 
		{
			if (bytestocopy > 0) 
			{
				spin_unlock_irqrestore(&wave_dev->wiinst->lock, flags);
				DPF("bytestocopy > 0, returning from poll()\n");
				return 1;
			}
			
			DPD("bytestocopy --> %d\n", bytestocopy);
			
			select_wait(&wave_in->wait_queue, wait);
		}
		
		spin_unlock_irqrestore(&wave_dev->wiinst->lock, flags);
	}
	
	return 0;
}


static int waveOutCallbackFn(unsigned long message, unsigned long refdata, unsigned long param)
{
	struct sblive_wavedevice *wave_dev = (struct sblive_wavedevice *) refdata;
	struct woinst *woinst = wave_dev->woinst;
	struct wave_out *wave_out = woinst->wave_out;
	unsigned long flags;

	spin_lock_irqsave(&woinst->lock, flags);
	
	switch (message) 
	{

	case CARDWAVE_EVENT_TIMER:
		{
			u32 bytestocopy, pending, waveptr;

			if (sblive_waveoutGetControl(wave_dev->sb_hw->card_waveout, wave_out, WAVECURPOS, (u32 *)&waveptr) == CTSTATUS_SUCCESS) 
			{
				woinst->total_played += waveptr - woinst->wave_ptr;

                                if (waveptr < woinst->wave_ptr)
				  woinst->total_played += woinst->fragment_size * woinst->numfrags;

                                 woinst->wave_ptr = waveptr;
			}

			if (woinst->mapped)
			  break;

			if (sblive_waveoutGetXferSize(wave_dev->sb_hw->card_waveout, wave_out, &bytestocopy, &pending) == CTSTATUS_SUCCESS) 
			{
				if (bytestocopy >= woinst->fragment_size || woinst->silence_filled > 0) 
				{
					if (pending <= woinst->fragment_size && bytestocopy >= woinst->fragment_size) 
					{
						if (woinst->silence_filled == 0)
						  sblive_waveoutGetControl(wave_dev->sb_hw->card_waveout, wave_out, WAVEWRITEPOINTER, &woinst->silence_start);

						bytestocopy = woinst->fragment_size;
						
						sblive_waveoutFillSilence(wave_dev->sb_hw->card_waveout, wave_out, &bytestocopy);
						
						if (woinst->silence_filled < 2)
						  woinst->silence_filled++;
					}
					
					wake_up(&woinst->wait_queue);
				} else 
				  DPD("Not enough transfer size -> %x\n", bytestocopy);
			}
		}
		break;

	default:
		break;

	}
	
	spin_unlock_irqrestore(&woinst->lock, flags);
	
	return CTSTATUS_SUCCESS;
}


static int waveInCallbackFn(unsigned long message, unsigned long refdata, unsigned long param)
{
	struct sblive_wavedevice *wave_dev = (struct sblive_wavedevice *) refdata;
	struct wiinst *wiinst = wave_dev->wiinst;
	struct wave_in *wave_in = wiinst->wave_in;
	unsigned long flags;

        /* Are these locks necessary around the wake_up ? kabi@i.am */
	spin_lock_irqsave(&wiinst->lock, flags);
	
	switch (message) 
	{
		
	case CARDWAVE_EVENT_CALLBACKSIZEREACHED:
		{
			u32 bytestocopy, pending;

			if (sblive_waveinGetXferSize(wave_dev->sb_hw->card_wavein, wave_in, &bytestocopy, &pending) == CTSTATUS_SUCCESS) 
			{
				if (bytestocopy >= wiinst->fragment_size) 
				{
					DPF("Wake UP!\n");
					wake_up(&wave_in->wait_queue);
				} else 
				  DPD("Not enough transfer size, %d\n", bytestocopy);
			}
		}
		break;

	default:
		break;
	}
	
	spin_unlock_irqrestore(&wiinst->lock, flags);
	
	return CTSTATUS_SUCCESS;
}

static void calculate_ofrag(struct woinst *woinst)
{
	u32 fragsize = 0, bytespersec;
	
	if (woinst->fragment_size) 
	{
		DPF("fragment_size already calculated!\n");
		return;
	}
	
	bytespersec = woinst->wave_fmt.channels * (woinst->wave_fmt.bitspersample >> 3) * woinst->wave_fmt.samplingrate;

	woinst->fragment_size = 1;

	if (woinst->ossfragshift) 
	  woinst->fragment_size <<= (woinst->ossfragshift < MINFRAGSHIFT ? MINFRAGSHIFT : woinst->ossfragshift);
	else 
	  fragsize = (bytespersec * WAVEOUT_DEFAULTFRAGLEN) / 1000 - 1;

	while (fragsize) 
	{
		fragsize >>= 1;
		woinst->fragment_size <<= 1;
	}

	if (!woinst->numfrags)
	{
		u32 numfrags;

		numfrags = (bytespersec * WAVEOUT_DEFAULTBUFLEN)  / (woinst->fragment_size * 1000) - 1;

		woinst->numfrags = 1;
		
		while (numfrags) 
		{
                	numfrags >>= 1;
                	woinst->numfrags <<= 1;
		}
	}

	if (woinst->numfrags < MINFRAGS) 
	  woinst->numfrags = MINFRAGS;

	if (woinst->numfrags * woinst->fragment_size > WAVEOUT_MAXBUFSIZE) 
	{
		woinst->numfrags = WAVEOUT_MAXBUFSIZE / woinst->fragment_size;
		
		if (woinst->numfrags < MINFRAGS) 
		{
			woinst->numfrags = MINFRAGS;
			woinst->fragment_size = WAVEOUT_MAXBUFSIZE / MINFRAGS;
		}
		
	} else 
	  if (woinst->numfrags * woinst->fragment_size < WAVEOUT_MINBUFSIZE)
	    woinst->numfrags = WAVEOUT_MINBUFSIZE / woinst->fragment_size;
	
	DPD(" calculated playback fragment_size -> %d\n", woinst->fragment_size);
	DPD(" calculated playback numfrags -> %d\n", woinst->numfrags);
}

static void calculate_ifrag(struct wiinst *wiinst)
{
	u32 fragsize, bytespersec;
	
	if (wiinst->fragment_size) 
	{
		DPF("fragment_size already calculated!\n");
		return;
	}
	
	bytespersec = wiinst->wave_fmt.channels * (wiinst->wave_fmt.bitspersample >> 3) * wiinst->wave_fmt.samplingrate;

	wiinst->fragment_size = 1;

	if (wiinst->ossfragshift)
	  wiinst->fragment_size <<= (wiinst->ossfragshift < MINFRAGSHIFT ? MINFRAGSHIFT : wiinst->ossfragshift);
	else 
	{
		fragsize = (bytespersec * WAVEIN_DEFAULTFRAGLEN) / 1000 - 1;
		
		while (fragsize) 
		{
			fragsize >>= 1;
			wiinst->fragment_size <<= 1;
		}
	}

	if (!wiinst->numfrags)
	  wiinst->numfrags = (WAVEIN_DEFAULTBUFLEN * bytespersec) / (wiinst->fragment_size * 1000);

	if (wiinst->numfrags < MINFRAGS) 
	  wiinst->numfrags = MINFRAGS;

	if (wiinst->numfrags * wiinst->fragment_size > WAVEIN_MAXBUFSIZE) {
		wiinst->numfrags = WAVEIN_MAXBUFSIZE / wiinst->fragment_size;
		if (wiinst->numfrags < MINFRAGS) {
			wiinst->numfrags = MINFRAGS;
			wiinst->fragment_size = WAVEIN_MAXBUFSIZE / MINFRAGS;
		}
	}

	DPD(" calculated recording fragment_size -> %d\n", wiinst->fragment_size);
	DPD(" calculated recording numfrags -> %d\n", wiinst->numfrags);
}

int audio_init(struct sblive_hw * sb_hw, u8 * pname)
{
	/* Initialize CardwaveOut struct */
	sb_hw->card_waveout = kmalloc(sizeof(struct sblive_waveout), GFP_KERNEL);
	if (!sb_hw->card_waveout) 
	{
		printk(KERN_WARNING "sblive: Unable to allocate struct sblive_waveout: out of memory\n");
		return CTSTATUS_ERROR;
	}
	
#ifdef MEMTEST
	DPD("kmalloc: [%p]\n", sb_hw->card_waveout);
#endif
	
	memset(sb_hw->card_waveout, 0, sizeof(struct sblive_waveout));

	sblive_waveoutInit(sb_hw->card_waveout, pname);

	/* Initialize CardwaveIn struct */
	sb_hw->card_wavein = kmalloc(sizeof(struct sblive_wavein), GFP_KERNEL);
	if (!sb_hw->card_wavein) 
	{
		kfree(sb_hw->card_waveout);
		sb_hw->card_waveout = NULL;
		printk(KERN_WARNING "sblive: Unable to allocate struct sblive_wavein: out of memory\n");
		return CTSTATUS_ERROR;
	}
	
#ifdef MEMTEST
	DPD("kmalloc: [%p]\n", sb_hw->card_wavein);
#endif
	
	memset(sb_hw->card_wavein, 0, sizeof(struct sblive_wavein));

	sblive_waveinInit(sb_hw->card_wavein, pname);

	return CTSTATUS_SUCCESS;
}

int audio_exit(struct sblive_hw *sb_hw)
{
	sblive_waveoutExit(sb_hw->card_waveout);
	sblive_waveinExit(sb_hw->card_wavein);
	
	kfree(sb_hw->card_waveout);
	kfree(sb_hw->card_wavein);
	
#ifdef MEMTEST
	DPD("kfree: [%p]\n", sb_hw->card_waveout);
	DPD("kfree: [%p]\n", sb_hw->card_wavein);
#endif
	
	sb_hw->card_waveout = NULL;
	sb_hw->card_wavein = NULL;

	return CTSTATUS_SUCCESS;
}


struct file_operations emu10k1_audio_fops =
{
	&emu10k1_audio_llseek,
	&emu10k1_audio_read,
	&emu10k1_audio_write,
	NULL,
	&emu10k1_audio_poll,
	&emu10k1_audio_ioctl,
	&emu10k1_audio_mmap,
	&emu10k1_audio_open,
	&emu10k1_audio_release,
	NULL,
	NULL,
	NULL
};
