/*++

Copyright (c) 1990-2000  Microsoft Corporation

Module Name:

    sync.c

Abstract:

    This file contains code for the video port synchronization routines.

Environment:

    kernel mode only

--*/

#include "videoprt.h"

#ifdef ALLOC_PRAGMA
#pragma alloc_text(PAGE, VideoPortCreateEvent)
#pragma alloc_text(PAGE, VideoPortCreateSpinLock)
#endif

VP_STATUS
VideoPortCreateSpinLock(
    IN PVOID HwDeviceExtension,
    OUT PSPIN_LOCK *SpinLock
    )

/*++

Routine Description:

    Creates a spin lock object

Arguments:

    HwDeviceExtension - pointer to the miniports device extension

    SpinLock - Location in which to store the pointer to the newly
        acquired spin lock.

Returns:

    NO_ERROR if the spin lock was created successfully, otherwise
    an appropriate error message is returned.

Notes:

    none

--*/

{
    PAGED_CODE();
    ASSERT(HwDeviceExtension != NULL);

    *SpinLock = ExAllocatePoolWithTag(NonPagedPool,
                                      sizeof(VIDEO_PORT_SPIN_LOCK),
                                      VP_TAG);

    if (*SpinLock) {
        KeInitializeSpinLock(&(*SpinLock)->Lock);
        return NO_ERROR;
    } else {
        return ERROR_NOT_ENOUGH_MEMORY;
    }
}

VP_STATUS
VideoPortDeleteSpinLock(
    IN PVOID HwDeviceExtension,
    IN PSPIN_LOCK SpinLock
    )

/*++

Routine Description:

    Deletes the given spin lock

Arguments:

    HwDeviceExtension - pointer to the miniports device extension

    SpinLock - A pointer to the spin lock being deleted.

Returns:

    NO_ERROR if the spin lock is deleted successfully.
Notes:

--*/

{
    ASSERT(HwDeviceExtension != NULL);
    ASSERT(SpinLock != NULL);

    ExFreePool(SpinLock);

    return NO_ERROR;
}

VOID
VideoPortAcquireSpinLock(
    IN PVOID HwDeviceExtension,
    IN PSPIN_LOCK SpinLock,
    OUT PUCHAR OldIrql
    )

/*++

Routine Description:

    Acquires the given spin lock

Arguments:

    HwDeviceExtension - pointer to the miniports device extension

    SpinLock - The spin lock being acquired

    OldIrql - location in which to store the old IRQL level

Returns:

    none

Notes:

--*/

{
    ASSERT(HwDeviceExtension != NULL);
    ASSERT(SpinLock != NULL);

    KeAcquireSpinLock(&SpinLock->Lock, OldIrql);
}

VOID
VideoPortAcquireSpinLockAtDpcLevel(
    IN PVOID HwDeviceExtension,
    IN PSPIN_LOCK SpinLock
    )

/*++

Routine Description:

    Acquires the given spin lock.

Arguments:

    HwDeviceExtension - pointer to the miniports device extension

    SpinLock - The spin lock being acquired

Returns:

    none

Notes:

    This routine can only be called inside a DPC.

--*/

{
    ASSERT(KeGetCurrentIrql() == DISPATCH_LEVEL);
    ASSERT(HwDeviceExtension != NULL);
    ASSERT(SpinLock != NULL);

    KeAcquireSpinLockAtDpcLevel(&SpinLock->Lock);
}

VOID
VideoPortReleaseSpinLock(
    IN PVOID HwDeviceExtension,
    IN PSPIN_LOCK SpinLock,
    IN UCHAR NewIrql
    )

/*++

Routine Description:

    Releases ownership of a given spin lock

Arguments:

    HwDeviceExtension - pointer to the miniports device extension

    SpinLock - the spin lock being released

    NewIrql - Irql level to restore to.

Returns:

    none

Notes:

--*/

{
    ASSERT(HwDeviceExtension != NULL);
    ASSERT(SpinLock != NULL);

    KeReleaseSpinLock(&SpinLock->Lock, NewIrql);
}

VOID
VideoPortReleaseSpinLockFromDpcLevel(
    IN PVOID HwDeviceExtension,
    IN PSPIN_LOCK SpinLock
    )

/*++

Routine Description:

    Releases ownership of a given spin lock

Arguments:

    HwDeviceExtension - pointer to the miniports device extension

    SpinLock - the spin lock being released

Returns:

    none

Notes:

    This routine can only be called inside a DPC.

--*/

{
    ASSERT(KeGetCurrentIrql() == DISPATCH_LEVEL);
    ASSERT(HwDeviceExtension != NULL);
    ASSERT(SpinLock != NULL);

    KeReleaseSpinLockFromDpcLevel(&SpinLock->Lock);
}

VP_STATUS
VideoPortCreateEvent(
    IN PVOID HwDeviceExtension,
    IN ULONG EventFlag,
    PVOID  Unused,
    OUT PEVENT *ppEvent
    )
{
    ULONG size;
    PEVENT p;
    EVENT_TYPE EventType;


    size = sizeof(VIDEO_PORT_EVENT);

    //
    //  Align size to next higher multiple of 8.
    //

    size = (size + 7) & ~7;

    p = (PEVENT) ExAllocatePoolWithTag( NonPagedPool,
                                        size + sizeof(KEVENT),
                                        VP_TAG );
    if ( p ) {

        p->fFlags = 0;
        p->pKEvent = (PUCHAR) p + size;

        if( (EventFlag & EVENT_TYPE_MASK) == NOTIFICATION_EVENT ) {

            EventType = NotificationEvent;

        } else {

            EventType = SynchronizationEvent;
        }

        KeInitializeEvent( p->pKEvent,
                           EventType,
                           (BOOLEAN) (EventFlag & INITIAL_EVENT_STATE_MASK ) );

        *ppEvent = p;

        return NO_ERROR;

    } else {

        return ERROR_NOT_ENOUGH_MEMORY;
    }
}

VP_STATUS
VideoPortDeleteEvent(
    IN PVOID HwDeviceExtension,
    IN PEVENT pEvent
    )
{
    if ( pEvent == NULL ) {

        pVideoDebugPrint((Error, "VideoPortDeleteEvent: Can't delete NULL event\n"));
        ASSERT(FALSE);
        return ERROR_INVALID_PARAMETER;
    }

    if ( pEvent->fFlags & ENG_EVENT_FLAG_IS_MAPPED_USER ) {

        pVideoDebugPrint((Error, "VideoPortDeleteEvent: Can't delete mapped user event\n"));
        ASSERT(FALSE);
        return ERROR_INVALID_PARAMETER;
    }

    if( pEvent->pKEvent == NULL ) {

        pVideoDebugPrint((Error, "VideoPortDeleteEvent: pKEvent is NULL\n"));
        ASSERT(FALSE);
        return ERROR_INVALID_PARAMETER;
    }

    ExFreePool( (PVOID) pEvent );

    return NO_ERROR;
}

LONG
VideoPortSetEvent(
    IN PVOID HwDeviceExtension,
    IN PEVENT pEvent
    )
{
    return( KeSetEvent(pEvent->pKEvent, 0, FALSE) );
}

VOID
VideoPortClearEvent(
    IN PVOID HwDeviceExtension,
    IN PEVENT pEvent
    )
{
    KeClearEvent(pEvent->pKEvent);
}

LONG
VideoPortReadStateEvent(
    IN PVOID HwDeviceExtension,
    IN PEVENT pEvent
    )
{
    return ( KeReadStateEvent(pEvent->pKEvent) );
}


VP_STATUS
VideoPortWaitForSingleObject(
    IN PVOID HwDeviceExtension,
    IN PVOID pEvent,
    IN PLARGE_INTEGER Timeout
    )
{
    NTSTATUS status;

    if ( pEvent == NULL ) {

        return ERROR_INVALID_PARAMETER;
    }

    if( ((PEVENT) pEvent)->pKEvent == NULL) {

        return ERROR_INVALID_PARAMETER;
    }

    if (( (PEVENT) pEvent)->fFlags & ENG_EVENT_FLAG_IS_MAPPED_USER ) {

        pVideoDebugPrint((Error, "VideoPortVideoPortWaitForSingleObject: No wait ing on mapped user event\n")) ;
        ASSERT(FALSE);
        return ERROR_INVALID_PARAMETER;
    }

    status = KeWaitForSingleObject( ((PEVENT) pEvent)->pKEvent,
                                    Executive,
                                    KernelMode,
                                    FALSE,
                                    Timeout );

    if (status == STATUS_TIMEOUT) {

        return WAIT_TIMEOUT;

    } else if (NT_SUCCESS(status)) {

        return NO_ERROR;

    } else {

        return ERROR_INVALID_PARAMETER;
    }
}