/*
    ReactOS Sound System
    Sound Blaster DSP support
    General I/O

    Author:
        Andrew Greenwood (silverblade@reactos.org)

    History:
        2 July 2008 - Created (split from sbdsp.c)

    Notes:
        Functions documented in sbdsp.h
*/

#include "precomp.h"

NTSTATUS
SbDspReset(
    IN  PUCHAR BasePort,
    IN  ULONG Timeout)
{
    ULONG Expiry;
    BOOLEAN DataAvailable = FALSE;

    /* Should be called from DriverEntry with this IRQL */
    ASSERT(KeGetCurrentIrql() == PASSIVE_LEVEL);

    WRITE_SB_DSP_RESET(BasePort, 0x01);
    SleepMs(50);   /* Should be enough */
    WRITE_SB_DSP_RESET(BasePort, 0x00);

    Expiry = QuerySystemTimeMs() + Timeout;

    /* Wait for data to be available */
    while ( (QuerySystemTimeMs() < Expiry) || ( Timeout == 0) )
    {
        if ( SB_DSP_DATA_AVAILABLE(BasePort) )
        {
            DataAvailable = TRUE;
            break;
        }
    }

    if ( ! DataAvailable )
    {
        return STATUS_TIMEOUT;
    }

    /* Data is available - wait for the "DSP ready" code */
    while ( (QuerySystemTimeMs() < Expiry) || ( Timeout == 0) )
    {
        if ( READ_SB_DSP_DATA(BasePort) == SB_DSP_READY )
        {
            return STATUS_SUCCESS;
        }
    }

    return STATUS_TIMEOUT;
}

NTSTATUS
SbDspWaitToWrite(
    IN  PUCHAR BasePort,
    IN  ULONG Timeout)
{
    ULONG Expiry = QuerySystemTimeMs() + Timeout;

    while ( (QuerySystemTimeMs() < Expiry) || (Timeout == 0) )
    {
        if ( SB_DSP_CLEAR_TO_SEND(BasePort) )
        {
            return STATUS_SUCCESS;
        }
    }

    return STATUS_TIMEOUT;
}

NTSTATUS
SbDspWaitToRead(
    IN  PUCHAR BasePort,
    IN  ULONG Timeout)
{
    ULONG Expiry = QuerySystemTimeMs() + Timeout;

    while ( (QuerySystemTimeMs() < Expiry) || (Timeout == 0) )
    {
        if ( SB_DSP_DATA_AVAILABLE(BasePort) )
        {
            return STATUS_SUCCESS;
        }
    }

    return STATUS_TIMEOUT;
}

NTSTATUS
SbDspWrite(
    IN  PUCHAR BasePort,
    IN  UCHAR DataByte,
    IN  ULONG Timeout)
{
    NTSTATUS Status;

    Status = SbDspWaitToWrite(BasePort, Timeout);

    if ( Status != STATUS_SUCCESS )
    {
        return Status;
    }

    DbgPrint("SBDSP - Writing %02x\n", DataByte);
    WRITE_SB_DSP_DATA(BasePort, DataByte);

    return STATUS_SUCCESS;
}

NTSTATUS
SbDspRead(
    IN  PUCHAR BasePort,
    OUT PUCHAR DataByte,
    IN  ULONG Timeout)
{
    NTSTATUS Status;

    if ( ! DataByte )
    {
        return STATUS_INVALID_PARAMETER_2;
    }

    Status = SbDspWaitToRead(BasePort, Timeout);

    if ( Status != STATUS_SUCCESS )
    {
        return Status;
    }

    *DataByte = READ_SB_DSP_DATA(BasePort);
    DbgPrint("SBDSP - Read %02x\n", *DataByte);

    return STATUS_SUCCESS;
}
