/*     
 **********************************************************************
 *     sblive_mi.c - MIDI UART input HAL 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        clean up
 * 
 ********************************************************************** 
 * 
 *     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 "cardmi.h"

/****************************************************************************/
/* int    sblive_mpuinInit(struct sblive_mpuin *card_mpuin, PICARD pICard ) */
/*                                                                          */
/* Function:   Initialize the parameters and reset the MPU port             */
/****************************************************************************/
int sblive_mpuinInit(struct sblive_mpuin *card_mpuin, struct sblive_hw *sb_hw)
{
	char name[128];

	DPF("sblive_mpuinInit\n");

	memset(card_mpuin, 0, sizeof(struct sblive_mpuin));

	card_mpuin->status = FLAGS_AVAILABLE;	// clear

	card_mpuin->caps.cbsize = sizeof(struct midi_caps);
	card_mpuin->caps.support = MIDICAPS_INPUT;
	card_mpuin->caps.technology = 0;
	card_mpuin->caps.product = MM_CREATIVE_MIDIIN;
	card_mpuin->caps.manufacturer = MM_CREATIVE;
	card_mpuin->caps.voices = 0;
	card_mpuin->caps.notes = 0;
	card_mpuin->caps.channelmask = 0;
	card_mpuin->caps.caps = CARDMIDI_IN;

	card_mpuin->dpc.is_active = FALSE;
	card_mpuin->dpc.refdata = (u32) card_mpuin;
	card_mpuin->dpc.DPCCallBackFn = sblive_mpuinDpcCallback;

	spin_lock_init(&card_mpuin->lock);

	/* Determines if MPU in device can be acquired. */
	if (sblive_irqmgrInstallIrqHandler
	    (sb_hw,IRQTYPE_MPUIN,
	     sblive_mpuinIrqCallback,
	     (u32)sb_hw) != CTSTATUS_SUCCESS) 
	{
		DPF("CARDMIDI: IRQ in use\n");
		return CTSTATUS_ALLOCATED;
	} else 
	{
		DPF("CARDMIDI: IRQ installed\n");
	}

	strcpy(name, IDS_EMU_MIDIIN_PNAME);

	/* Fill in card caps */
	sprintf(card_mpuin->caps.MIDIname, "%s [%lx]", name, sb_hw->hwaddr );

	/* Reset the MPU port */
	if (hwmpuReset(sb_hw) != CTSTATUS_SUCCESS) 
	{
		DPF("cardmi.c: MPU hardware reset failure\n");
		return CTSTATUS_NOTENABLED;
	}
	
	return CTSTATUS_SUCCESS;
}


/****************************************************************************/
/* int    sblive_mpuinExit(struct sblive_mpuin *card_mpuin)                 */
/*                                                                          */
/* Function:   Disable the IRQ TX and uninstall the IRQ handler             */
/****************************************************************************/
int sblive_mpuinExit(struct sblive_hw *sb_hw)
{
	unsigned long flags;
	struct sblive_mpuin *card_mpuin = sb_hw->card_mpuin;
	
	DPF("sblive_mpuinExit\n");

	/* Disable RX interrupt */
	spin_lock_irqsave(&card_mpuin->lock, flags);
	sblive_irqmgrDisableIrq(sb_hw, INTE_MIDIRXENABLE);
	spin_unlock_irqrestore(&card_mpuin->lock, flags);

	/* Uninstall IRQ handler */
	sblive_irqmgrUninstallIrqHandler(sb_hw, IRQTYPE_MPUIN);

	return CTSTATUS_SUCCESS;
}

/****************************************************************************/
/* int    sblive_mpuinGetCaps(struct sblive_mpuin *card_mpuin,              */
/*                              struct midi_caps *midi_caps)                */
/*                                                                          */
/* Function:   Returns the MPU IN capabilities                              */
/****************************************************************************/
int sblive_mpuinGetCaps(struct sblive_mpuin *card_mpuin, struct midi_caps *midi_caps)
{
	u32 cbsize;

	DPF("sblive_mpuinGetCaps\n");

	cbsize = min(midi_caps->cbsize, card_mpuin->caps.cbsize);
	memcpy(midi_caps, &card_mpuin->caps, cbsize);
	midi_caps->cbsize = cbsize;

	return CTSTATUS_SUCCESS;
}

/****************************************************************************/
/* int    sblive_mpuinOpen(struct sblive_mpuin *card_mpuin,                 */
/*                           struct midi_openinfo *openinfo,                */
/*                           u32 * handle)                                  */
/*                                                                          */
/* Function:   Installs the IRQ handler for the MPU in port                 */
/*             and initialize parameters                                    */
/****************************************************************************/
int sblive_mpuinOpen(struct sblive_hw *sb_hw, struct midi_openinfo *openinfo, u32 *handle)
{
	struct sblive_mpuin *card_mpuin = sb_hw->card_mpuin;
	
	DPF("sblive_mpuinOpen\n");

	if (!(card_mpuin->status & FLAGS_AVAILABLE))
	  return CTSTATUS_INUSE;

	/* Copy open info and mark channel as in use */
	card_mpuin->openinfo = *openinfo;
	card_mpuin->status &= ~FLAGS_AVAILABLE;	/* clear */
	card_mpuin->status |= FLAGS_READY;	/* set */
	card_mpuin->status &= ~FLAGS_MIDM_STARTED; /* clear */
	card_mpuin->firstmidiq = NULL;
	card_mpuin->lastmidiq = NULL;
	card_mpuin->qhead = 0;
	card_mpuin->qtail = 0;

	*handle = (u32)card_mpuin;

	sblive_miStateInit(card_mpuin);

	hwmpuReset(sb_hw);
	hwmpuAcquire(sb_hw);

	return CTSTATUS_SUCCESS;
}

/****************************************************************************/
/* int    sblive_mpuinClose(struct sblive_mpuin *card_mpuin,                */
/*                            u32 handle)                                   */
/*                                                                          */
/* Function:   If Midi Buffers are present,                                 */
/*             return CTSTATUS_STILLPLAYING                                 */
/*             Otherwise disable and uninstall IRQ handler                  */
/****************************************************************************/
int sblive_mpuinClose(struct sblive_hw *sb_hw, u32 handle)
{
	struct sblive_mpuin *card_mpuin = sb_hw->card_mpuin;
	
	DPF("sblive_mpuinClose\n");

	/* Check if there are pending input SysEx buffers */
	if (card_mpuin->firstmidiq != NULL) 
	{
		DPF("Cannot close, MIDI buffers are present.\n");
		return CTSTATUS_STILLPLAYING;
	}
	
	/* Disable RX interrupt */
	sblive_irqmgrDisableIrq(sb_hw, INTE_MIDIRXENABLE);

	hwmpuRelease(sb_hw);

	card_mpuin->status |= FLAGS_AVAILABLE;	   /* set */
	card_mpuin->status &= ~FLAGS_MIDM_STARTED; /* clear */

	return CTSTATUS_SUCCESS;
}

/****************************************************************************/
/* int    sblive_mpuinAddBuffer(struct sblive_mpuin *card_mpuin,            */
/*                                u32 handle,                               */
/*                                struct midi_hdr *INFO midihdrInfo)        */
/*                                                                          */
/* Function:   Adds MIDI buffer to local queue list                         */
/****************************************************************************/
int sblive_mpuinAddBuffer(struct sblive_mpuin *card_mpuin, u32 handle, struct midi_hdr *midihdr)
{
	struct midi_queue * midiq;
	unsigned long flags;

	DPF("sblive_mpuinAddBuffer\n");

	/* Update MIDI buffer flags */
	midihdr->flags |= MIDIBUF_INQUEUE; /* set */
	midihdr->flags &= ~MIDIBUF_DONE;   /* clear */

	midiq = (struct midi_queue *) kmalloc(sizeof(struct midi_queue), GFP_ATOMIC);
	if (midiq == NULL) 
	{
		/* Message lost */
		return CTSTATUS_NOMEMORY;
	}
	
	midiq->next = NULL;
	midiq->qtype = 1;
	midiq->length = midihdr->bufferlength;
	midiq->sizeLeft = midihdr->bufferlength;
	midiq->midibyte = midihdr->lpData;
	midiq->refdata = (u32) midihdr;

	spin_lock_irqsave(&card_mpuin->lock, flags);

	if (card_mpuin->firstmidiq == NULL) 
	{
		card_mpuin->firstmidiq = midiq;
		card_mpuin->lastmidiq = midiq;
	} else 
	{
		(card_mpuin->lastmidiq)->next = midiq;
		card_mpuin->lastmidiq = midiq;
	}

	spin_unlock_irqrestore(&card_mpuin->lock, flags);

	return CTSTATUS_SUCCESS;
}


/****************************************************************************/
/* int    sblive_mpuinStart(struct sblive_mpuin *card_mpuin,                */
/*                            u32 handle,                                   */
/*                                                                          */
/* Function:   First set the Time Stamp if MIDI IN has not started.         */
/*             Then enable RX Irq.                                          */
/****************************************************************************/
int sblive_mpuinStart(struct sblive_hw *sb_hw, u32 handle)
{
	struct sblive_mpuin *card_mpuin = sb_hw->card_mpuin;
	u8 dummy;

	DPF("sblive_mpuinStart\n");

	/* Set timestamp if not set */
	if (card_mpuin->status & FLAGS_MIDM_STARTED) 
	{
		DPF("Time Stamp not changed\n");
	} else 
	{
		while (hwmpuReadData(sb_hw, &dummy) == CTSTATUS_SUCCESS);

		card_mpuin->status |= FLAGS_MIDM_STARTED; /* set */

		/* Set new time stamp */
		card_mpuin->timestart = (jiffies * 1000)/HZ;
		DPD("New Time Stamp = %d\n", card_mpuin->timestart);

		card_mpuin->qhead = 0;
		card_mpuin->qtail = 0;
		card_mpuin->dpc.is_active = FALSE;

		sblive_irqmgrEnableIrq(sb_hw, INTE_MIDIRXENABLE);
	}

	return CTSTATUS_SUCCESS;
}


/****************************************************************************/
/* int    sblive_mpuinStop(struct sblive_mpuin *card_mpuin,                 */
/*                           u32 handle,                                    */
/*                                                                          */
/* Function:   Disable the RX Irq.  If a partial recorded buffer            */
/*             exist, send it up to IMIDI level.                            */
/****************************************************************************/
int sblive_mpuinStop(struct sblive_hw *sb_hw, u32 handle)
{
	struct sblive_mpuin *card_mpuin = sb_hw->card_mpuin;
	struct midi_queue *midiq;
	unsigned long flags;

	DPF("sblive_mpuinStop\n");

	sblive_irqmgrDisableIrq(sb_hw, INTE_MIDIRXENABLE);

	card_mpuin->status &= ~FLAGS_MIDM_STARTED; /* clear */

	if (card_mpuin->firstmidiq) 
	{
		spin_lock_irqsave(&card_mpuin->lock, flags);

		midiq = card_mpuin->firstmidiq;
		if (midiq) 
		{
			if (midiq->sizeLeft == midiq->length)
			  midiq = NULL;
			else 
			{
				card_mpuin->firstmidiq = midiq->next;
				if (card_mpuin->firstmidiq == NULL)
				  card_mpuin->lastmidiq = NULL;
			}
		}
		
		spin_unlock_irqrestore(&card_mpuin->lock, flags);

		if (midiq) 
		{
			sblive_mpuinMidiCallback (card_mpuin, ICARDMIDI_INLONGERROR, (u32)midiq, 0);
			kfree(midiq);
		}
	}
	
	return CTSTATUS_SUCCESS;
}


/****************************************************************************/
/* int    sblive_mpuinWriteShortData(struct sblive_mpuin *card_mpuin,       */
/*                                     u32 handle,                          */
/*                                     u32 midimsg)                         */
/*                                                                          */
/* Function:   Not Used                                                     */
/****************************************************************************/
int sblive_mpuinWriteShortData(struct sblive_mpuin *card_mpuin, u32 handle, u32 midimsg)
{
	DPF("sblive_mpuinWriteShortData(): not supported\n");
	return CTSTATUS_NOTSUPPORTED;
}


/****************************************************************************/
/* int    sblive_mpuinPause(struct sblive_mpuin *card_mpuin,                */
/*                            u32 handle,                                   */
/*                                                                          */
/* Function:   Not Used                                                     */
/****************************************************************************/
int sblive_mpuinPause(struct sblive_mpuin *card_mpuin, u32 handle)
{
	DPF("sblive_mpuinPause(): not supported\n");
	return CTSTATUS_NOTSUPPORTED;
}


/****************************************************************************/
/* int    sblive_mpuinStream(struct sblive_mpuin * card_mpuin,              */
/*                             u32 handle,                                  */
/*                             struct midi_hdr *INFO midihdrInfo)           */
/*                                                                          */
/* Function:   Not Used                                                     */
/****************************************************************************/
int sblive_mpuinStream(struct sblive_mpuin *card_mpuin, u32 handle, struct midi_hdr *midihdr)
{
	DPF("sblive_mpuinStream(): not supported\n");
	return CTSTATUS_NOTSUPPORTED;
}


/****************************************************************************/
/* int    sblive_mpuinRestart(struct sblive_mpuin *card_mpuin,              */
/*                              u32 handle,                                 */
/*                                                                          */
/* Function:   Not Used                                                     */
/****************************************************************************/
int sblive_mpuinRestart(struct sblive_mpuin *card_mpuin, u32 handle)
{
	DPF("sblive_mpuinRestart(): not supported\n");
	return CTSTATUS_NOTSUPPORTED;
}


/****************************************************************************/
/* int    sblive_mpuinReset(struct sblive_mpuin *card_mpuin,                */
/*                            u32 handle,                                   */
/*                                                                          */
/* Function:   Disable the RX Irq.  If any buffer                           */
/*             exist, send it up to IMIDI level.                            */
/****************************************************************************/
int sblive_mpuinReset(struct sblive_hw *sb_hw, u32 handle)
{
	struct sblive_mpuin *card_mpuin = sb_hw->card_mpuin;
	struct midi_queue *midiq;

	DPF("sblive_mpuinReset: start\n");

	sblive_irqmgrDisableIrq(sb_hw, INTE_MIDIRXENABLE);

	while (card_mpuin->firstmidiq) 
	{
		midiq = card_mpuin->firstmidiq;
		card_mpuin->firstmidiq = midiq->next;

		if (midiq->sizeLeft == midiq->length) 
		  sblive_mpuinMidiCallback(card_mpuin, ICARDMIDI_INLONGDATA, (u32) midiq, 0);
		else
		  sblive_mpuinMidiCallback(card_mpuin, ICARDMIDI_INLONGERROR, (u32) midiq, 0);
		
		kfree(midiq);
	}
	
	card_mpuin->lastmidiq = NULL;
	card_mpuin->status &= ~FLAGS_MIDM_STARTED;

	return CTSTATUS_SUCCESS;
}


/****************************************************************************/
/* int    sblive_mpuinCache(struct sblive_mpuin *card_mpuin,                */
/*                            u32 handle,                                   */
/*                            struct midi_cache * midicache)                */
/*                                                                          */
/* Function:   Not Used                                                     */
/****************************************************************************/
int sblive_mpuinCache(struct sblive_mpuin *card_mpuin, u32 handle, struct midi_cache * midicache)
{
	DPF("sblive_mpuinCache(): not supported\n");
	return CTSTATUS_NOTSUPPORTED;
}


/****************************************************************************/
/* int    sblive_mpuinGetPosition(struct sblive_mpuin *card_mpuin,          */
/*                                  u32 handle,                             */
/*                                  u32 *position)                          */
/*                                                                          */
/* Function:   Not Used                                                     */
/****************************************************************************/
int sblive_mpuinGetPosition(struct sblive_mpuin *card_mpuin, u32 handle, u32 *position)
{
	DPF("sblive_mpuinGetPosition(): not supported\n");
	return CTSTATUS_NOTSUPPORTED;
}


/****************************************************************************/
/* int    sblive_mpuinGetControl(struct sblive_mpuin *card_mpuin,           */
/*                                 u32 handle,                              */
/*                                 u32 controlid,                           */
/*                                 u32 *value)                              */
/*                                                                          */
/* Function:   Not Used                                                     */
/****************************************************************************/
int sblive_mpuinGetControl(struct sblive_mpuin *card_mpuin, u32 handle, u32 controlid, u32 *value)
{
	DPF("sblive_mpuinGetControl\n");

	switch (controlid) 
	{
	case MIDIQUERYACTIVEINST:
		DPF("controlid = MIDIQUERYACTIVE\n");
		if (card_mpuin == NULL)
		  return CTSTATUS_ERROR;
		if (!(card_mpuin->status & FLAGS_AVAILABLE))
		  return CTSTATUS_SUCCESS;
		else
		  return CTSTATUS_ERROR;
	default:
		break;
	}
	
	return CTSTATUS_NOTSUPPORTED;
}


/****************************************************************************/
/* int    sblive_mpuinSetControl(struct sblive_mpuin *card_mpuin,           */
/*                                 u32 handle,                              */
/*                                 u32 controlid,                           */
/*                                 u32 *value)                              */
/*                                                                          */
/* Function:   Not Used                                                     */
/****************************************************************************/
int sblive_mpuinSetControl(struct sblive_mpuin *card_mpuin, u32 handle, u32 controlid, u32 *value)
{
	DPF("sblive_mpuinSetControl(): not supported\n");
	return CTSTATUS_NOTSUPPORTED;
}


/****************************************************************************/
/* int    sblive_mpuinMidiCallback(struct sblive_mpuin *card_mpuin,         */
/*                                   u32 msg,                               */
/*                                   u32 data)                              */
/*                                                                          */
/* Function:   Passes the message with the data back to the client          */
/*             via IRQ & DPC callbacks to Ring 3                            */
/****************************************************************************/
int sblive_mpuinMidiCallback(struct sblive_mpuin *card_mpuin, u32 msg, u32 data, u32 bytesvalid)
{
	u32 timein;
	struct midi_queue *midiq;
	u32 callback_msg[3];
	struct midi_hdr *midihdr;

	/* Called during ISR. The data & code touched are:
	 * 1. card_mpuin
	 * 2. The function to be called
	 */

	timein = card_mpuin->timein;
	if (card_mpuin->timestart <= timein)
	  callback_msg[0] = timein - card_mpuin->timestart;
	else
	  callback_msg[0] = (0xFFFFFFFF - card_mpuin->timestart) + timein;

	if (msg == ICARDMIDI_INDATA || msg == ICARDMIDI_INDATAERROR) 
	{
		callback_msg[1] = data;
		callback_msg[2] = bytesvalid;
		DPD("sblive_mpuinMidiCallback: midimsg = %x\n", data);
	} else 
	{
		midiq = (struct midi_queue *) data;
		midihdr = (struct midi_hdr *) midiq->refdata;

		callback_msg[1] = midiq->length - midiq->sizeLeft;
		callback_msg[2] = midiq->refdata;
		midihdr->flags &= ~MIDIBUF_INQUEUE;
		midihdr->flags |= MIDIBUF_DONE;

		midihdr->bytesrecorded = midiq->length - midiq->sizeLeft;
	}

	/* Notify client that Sysex buffer has been sent */
	card_mpuin->openinfo.CallbackFn (msg, card_mpuin->openinfo.refdata, (u32)callback_msg);

	return CTSTATUS_SUCCESS;
}


/****************************************************************************/
/* int sblive_mpuinDpcCallback ()                                           */
/****************************************************************************/
int sblive_mpuinDpcCallback(unsigned long refdata, unsigned long param1, unsigned long param2)
{
	u8 data;
	unsigned idx;
	struct sblive_mpuin *card_mpuin = (struct sblive_mpuin *) refdata;
	unsigned long flags;
	extern spinlock_t sblive_spinlock;
	
	card_mpuin->dpc.is_active = FALSE;

	while (card_mpuin->qhead != card_mpuin->qtail) 
	{
		spin_lock_irqsave(&sblive_spinlock, flags);
		idx = card_mpuin->qhead;
		data = card_mpuin->midiq[idx].data;
		card_mpuin->timein = card_mpuin->midiq[idx].timein;
		idx = (idx + 1) % MIDIIN_MAX_BUFFER_SIZE;
		card_mpuin->qhead = idx;
		spin_unlock_irqrestore(&sblive_spinlock, flags);
		sblive_miStateEntry(card_mpuin, data);
	}

	return CTSTATUS_SUCCESS;
}


/****************************************************************************/
/* int    sblive_mpuinIrqCallback(u32 event,                                */
/*                                  u32 refdata,                            */
/*                                  u32 param)                              */
/*                                                                          */
/* Function:   IRQ callback handler routine for the MPU in port             */
/****************************************************************************/
int sblive_mpuinIrqCallback(unsigned long event, unsigned long refdata, unsigned long param)
{
	unsigned idx;
	unsigned count;
	u8 MPUIvalue;
	struct sblive_hw *sb_hw = (struct sblive_hw *) refdata; 
	struct sblive_mpuin *card_mpuin = sb_hw->card_mpuin; 


	/* IRQ service routine. The data and code touched are:
	 * 1. card_mpuin
	 */

	count = 0;
	idx = card_mpuin->qtail;
	
	while (1) 
	{
		if (hwmpuReadData(sb_hw, &MPUIvalue) == CTSTATUS_SUCCESS) 
		{
			++count;
			card_mpuin->midiq[idx].data = MPUIvalue;
			card_mpuin->midiq[idx].timein = (jiffies * 1000)/HZ;
			idx = (idx + 1) % MIDIIN_MAX_BUFFER_SIZE;
		} else 
		{
			break;
		}
	}

	if (count) 
	{
		card_mpuin->qtail = idx;
		
		if (!card_mpuin->dpc.is_active) 
		{
			card_mpuin->dpc.is_active = TRUE;
			osScheduleDPC(&card_mpuin->dpc);
		}
	}
	
	return CTSTATUS_SUCCESS;
}


/*****************************************************************************/
/*   Supporting functions for Midi-In Interpretation State Machine           */
/*****************************************************************************/

/* FIXME: This should be a macro */
int sblive_miStateInit(struct sblive_mpuin *card_mpuin)
{
	card_mpuin->status = 0;	/* For MIDI running status */
	card_mpuin->fstatus = 0; /* For 0xFn status only */
	card_mpuin->curstate = STIN_PARSE;
	card_mpuin->laststate = STIN_PARSE;
	card_mpuin->data = 0;
	card_mpuin->timestart = 0;
	card_mpuin->timein = 0;

	return CTSTATUS_SUCCESS;
}

/* FIXME: This should be a macro */
int sblive_miStateEntry(struct sblive_mpuin *card_mpuin, u8 data)
{
	return midistatefn[card_mpuin->curstate].Fn(card_mpuin, data);
}


int sblive_miStateParse(struct sblive_mpuin *card_mpuin, u8 data)
{
	switch (data & 0xf0) 
	{
	case 0x80:
	case 0x90:
	case 0xA0:
	case 0xB0:
	case 0xE0:
		card_mpuin->curstate = STIN_3BYTE;
		break;

	case 0xC0:
	case 0xD0:
		card_mpuin->curstate = STIN_2BYTE;
		break;

	case 0xF0:
		/* System messages do not affect the previous running status! */
		switch (data & 0x0f) 
		{
		case 0x0:
			card_mpuin->laststate = card_mpuin->curstate;
			card_mpuin->curstate = STIN_SYS_EX_NORM;
			
			if (card_mpuin->firstmidiq) 
			{
				struct midi_queue *midiq;

				midiq = card_mpuin->firstmidiq;
				*midiq->midibyte = data;
				--midiq->sizeLeft;
				++midiq->midibyte;
			}
			
			return CTSTATUS_NEXT_BYTE;

		case 0x7:
			sblive_mpuinMidiCallback(card_mpuin, ICARDMIDI_INDATAERROR, 0xF7, 0);
			return CTSTATUS_ERROR;

		case 0x2:
			card_mpuin->laststate = card_mpuin->curstate;
			card_mpuin->curstate = STIN_SYS_COMMON_3;
			break;

		case 0x1:
		case 0x3:
			card_mpuin->laststate = card_mpuin->curstate;
			card_mpuin->curstate = STIN_SYS_COMMON_2;
			break;

		default:
			/* includes 0xF4 - 0xF6, 0xF8 - 0xFF */
			return midistatefn[STIN_SYS_REAL].Fn(card_mpuin, data);
		}
		
		break;

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

	return midistatefn[card_mpuin->curstate].Fn(card_mpuin, data);
}


int sblive_miState3Byte(struct sblive_mpuin *card_mpuin, u8 data)
{
	u8 bTemp = data & 0xF0;

	if (bTemp < 0x80) 
	{
		return midistatefn[STIN_3BYTE_KEY].Fn(card_mpuin, data);
	} else 
	  if (bTemp <= 0xE0 && bTemp != 0xC0 && bTemp != 0xD0) 
	  {
		  card_mpuin->status = data;
		  card_mpuin->curstate = STIN_3BYTE_KEY;

		  return CTSTATUS_NEXT_BYTE;
	  }
	
	return midistatefn[STIN_PARSE].Fn(card_mpuin, data);
}


int sblive_miState3ByteKey(struct sblive_mpuin *card_mpuin, u8 data)
/* byte 1 */
{
	u32 tmp;

	if (data > 0x7F) 
	{
		/* Real-time messages check */
		if (data > 0xF7)
		  return midistatefn[STIN_SYS_REAL].Fn(card_mpuin, data);

		/* Invalid data! */
		DPF("Invalid data!\n");
		
		card_mpuin->curstate = STIN_PARSE;
		tmp = ((u32) data) << 8;
		tmp |= card_mpuin->status;

		sblive_mpuinMidiCallback(card_mpuin, ICARDMIDI_INDATAERROR, tmp, 0);

		return CTSTATUS_ERROR;
	}
	
	card_mpuin->data = data;
	card_mpuin->curstate = STIN_3BYTE_VEL;

	return CTSTATUS_NEXT_BYTE;
}


int sblive_miState3ByteVel(struct sblive_mpuin *card_mpuin, u8 data)
/* byte 2 */
{
	u32 tmp;

	if (data > 0x7F) 
	{
		/* Real-time messages check */
		if (data > 0xF7)
		  return midistatefn[STIN_SYS_REAL].Fn(card_mpuin, data);

		/* Invalid data! */
		DPF("Invalid data!\n");
		
		card_mpuin->curstate = STIN_PARSE;
		tmp = ((u32) data) << 8;
		tmp |= card_mpuin->data;
		tmp = tmp << 8;
		tmp |= card_mpuin->status;

		sblive_mpuinMidiCallback(card_mpuin, ICARDMIDI_INDATAERROR, tmp, 0);

		return CTSTATUS_ERROR;
	}
	
	card_mpuin->curstate = STIN_3BYTE;
	tmp = (u32) data;
	tmp = tmp << 8;
	tmp |= card_mpuin->data;
	tmp = tmp << 8;
	tmp |= card_mpuin->status;

	sblive_mpuinMidiCallback(card_mpuin, ICARDMIDI_INDATA, tmp, 3);

	return CTSTATUS_SUCCESS;
}


int sblive_miState2Byte(struct sblive_mpuin *card_mpuin, u8 data)
{
	u8 bTemp = data & 0xF0;

	if ((bTemp == 0xC0) || (bTemp == 0xD0)) 
	{
		card_mpuin->status = data;
		card_mpuin->curstate = STIN_2BYTE_KEY;

		return CTSTATUS_NEXT_BYTE;
	}
	
	if (bTemp < 0x80) 
	  return midistatefn[STIN_2BYTE_KEY].Fn(card_mpuin, data);
	
	return midistatefn[STIN_PARSE].Fn(card_mpuin, data);
}


int sblive_miState2ByteKey(struct sblive_mpuin *card_mpuin, u8 data)
/* byte 1 */
{
	u32 tmp;

	if (data > 0x7F) 
	{
		/* Real-time messages check */
		if (data > 0xF7)
		  return midistatefn[STIN_SYS_REAL].Fn(card_mpuin, data);

		/* Invalid data! */
		DPF("Invalid data!\n");
		
		card_mpuin->curstate = STIN_PARSE;
		tmp = (u32) data;
		tmp = tmp << 8;
		tmp |= card_mpuin->status;

		sblive_mpuinMidiCallback(card_mpuin, ICARDMIDI_INDATAERROR, tmp, 0);

		return CTSTATUS_ERROR;
	}
	
	card_mpuin->curstate = STIN_2BYTE;
	tmp = (u32) data;
	tmp = tmp << 8;
	tmp |= card_mpuin->status;

	sblive_mpuinMidiCallback(card_mpuin, ICARDMIDI_INDATA, tmp, 2);

	return CTSTATUS_SUCCESS;
}


int sblive_miStateSysCommon2(struct sblive_mpuin *card_mpuin, u8 data)
{
	card_mpuin->fstatus = data;
	card_mpuin->curstate = STIN_SYS_COMMON_2_KEY;

	return CTSTATUS_NEXT_BYTE;
}


int sblive_miStateSysCommon2Key(struct sblive_mpuin *card_mpuin, u8 data)
/* byte 1 */
{
	u32 tmp;

	if (data > 0x7F) 
	{
		/* Real-time messages check */
		if (data > 0xF7)
		  return midistatefn[STIN_SYS_REAL].Fn(card_mpuin, data);

		/* Invalid data! */
		DPF("Invalid data!\n");
		
		card_mpuin->curstate = card_mpuin->laststate;
		tmp = (u32) data;
		tmp = tmp << 8;
		tmp |= card_mpuin->fstatus;

		sblive_mpuinMidiCallback(card_mpuin, ICARDMIDI_INDATAERROR, tmp, 0);

		return CTSTATUS_ERROR;
	}
	
	card_mpuin->curstate = card_mpuin->laststate;
	tmp = (u32) data;
	tmp = tmp << 8;
	tmp |= card_mpuin->fstatus;

	sblive_mpuinMidiCallback(card_mpuin, ICARDMIDI_INDATA, tmp, 2);

	return CTSTATUS_SUCCESS;
}


int sblive_miStateSysCommon3(struct sblive_mpuin *card_mpuin, u8 data)
{
	card_mpuin->fstatus = data;
	card_mpuin->curstate = STIN_SYS_COMMON_3_KEY;

	return CTSTATUS_NEXT_BYTE;
}


int sblive_miStateSysCommon3Key(struct sblive_mpuin *card_mpuin, u8 data)
/* byte 1 */
{
	u32 tmp;

	if (data > 0x7F) 
	{
		/* Real-time messages check */
		if (data > 0xF7)
		  return midistatefn[STIN_SYS_REAL].Fn(card_mpuin, data);

		/* Invalid data! */
		DPF("Invalid data!\n");
		
		card_mpuin->curstate = card_mpuin->laststate;
		tmp = (u32) data;
		tmp = tmp << 8;
		tmp |= card_mpuin->fstatus;

		sblive_mpuinMidiCallback(card_mpuin, ICARDMIDI_INDATAERROR, tmp, 0);

		return CTSTATUS_ERROR;
	}
	
	card_mpuin->data = data;
	card_mpuin->curstate = STIN_SYS_COMMON_3_VEL;

	return CTSTATUS_NEXT_BYTE;
}


int sblive_miStateSysCommon3Vel(struct sblive_mpuin *card_mpuin, u8 data)
/* byte 2 */
{
	u32 tmp;

	if (data > 0x7F) 
	{
		/* Real-time messages check */
		if (data > 0xF7)
		  return midistatefn[STIN_SYS_REAL].Fn(card_mpuin, data);

		/* Invalid data! */
		DPF("Invalid data!\n");
		
		card_mpuin->curstate = card_mpuin->laststate;
		tmp = (u32) data;
		tmp = tmp << 8;
		tmp |= card_mpuin->data;
		tmp = tmp << 8;
		tmp |= card_mpuin->fstatus;

		sblive_mpuinMidiCallback(card_mpuin, ICARDMIDI_INDATAERROR, tmp, 0);

		return CTSTATUS_ERROR;
	}
	
	card_mpuin->curstate = card_mpuin->laststate;
	tmp = (u32) data;
	tmp = tmp << 8;
	tmp |= card_mpuin->data;
	tmp = tmp << 8;
	tmp |= card_mpuin->fstatus;

	sblive_mpuinMidiCallback(card_mpuin, ICARDMIDI_INDATA, tmp, 3);

	return CTSTATUS_SUCCESS;
}


int sblive_miStateSysExNorm(struct sblive_mpuin *card_mpuin, u8 data)
{
	unsigned long flags;
	
	if ((data > 0x7F) && (data != 0xF7)) 
	{
		/* Real-time messages check */
		if (data > 0xF7)
		  return midistatefn[STIN_SYS_REAL].Fn(card_mpuin, data);

		/* Invalid Data! */
		DPF("Invalid data!\n");
		
		card_mpuin->curstate = card_mpuin->laststate;

		if (card_mpuin->firstmidiq) 
		{
			struct midi_queue * midiq;

			midiq = card_mpuin->firstmidiq;
			*midiq->midibyte = data;
			--midiq->sizeLeft;
			++midiq->midibyte;

			spin_lock_irqsave(&card_mpuin->lock, flags);
			
			card_mpuin->firstmidiq = midiq->next;
			if (card_mpuin->firstmidiq == NULL)
			  card_mpuin->lastmidiq = NULL;
			
			spin_unlock_irqrestore(&card_mpuin->lock, flags);

			sblive_mpuinMidiCallback(card_mpuin, ICARDMIDI_INLONGERROR, (u32) midiq, 0);

			kfree(midiq);
		}
		
		return CTSTATUS_ERROR;
	}
	
	if (card_mpuin->firstmidiq) 
	{
		struct midi_queue *midiq;

		midiq = card_mpuin->firstmidiq;
		*midiq->midibyte = data;
		--midiq->sizeLeft;
		++midiq->midibyte;
	}
	
	if (data == 0xF7) 
	{
		/* End of Sysex buffer */
		/* Send down the buffer */
		 
		card_mpuin->curstate = card_mpuin->laststate;
		
		if (card_mpuin->firstmidiq) 
		{
			struct midi_queue * midiq;

			midiq = card_mpuin->firstmidiq;

			spin_lock_irqsave(&card_mpuin->lock, flags);
			
			card_mpuin->firstmidiq = midiq->next;
			if (card_mpuin->firstmidiq == NULL)
				card_mpuin->lastmidiq = NULL;
			
			spin_unlock_irqrestore(&card_mpuin->lock, flags);

			sblive_mpuinMidiCallback(card_mpuin, ICARDMIDI_INLONGDATA, (u32) midiq, 0);

			kfree(midiq);
		}
		
		return CTSTATUS_SUCCESS;
	}
	
	if (card_mpuin->firstmidiq) 
	{
		struct midi_queue *midiq;

		midiq = card_mpuin->firstmidiq;

		if (midiq->sizeLeft == 0) 
		{
			/* Special case */ 

			spin_lock_irqsave(&card_mpuin->lock, flags);
			
			card_mpuin->firstmidiq = midiq->next;
			if (card_mpuin->firstmidiq == NULL)
			  card_mpuin->lastmidiq = NULL;
			
			spin_unlock_irqrestore(&card_mpuin->lock, flags);

			sblive_mpuinMidiCallback(card_mpuin, ICARDMIDI_INLONGDATA, (u32) midiq, 0);

			kfree(midiq);

			return CTSTATUS_NEXT_BYTE;
		}
	}
	
	return CTSTATUS_NEXT_BYTE;
}


int sblive_miStateSysReal(struct sblive_mpuin *card_mpuin, u8 data)
{
	sblive_mpuinMidiCallback(card_mpuin, ICARDMIDI_INDATA, data, 1);

	return CTSTATUS_NEXT_BYTE;
}
