/*++

Copyright (c) 2000 Microsoft Corporation

Module Name:

    processor.c

Abstract:

    Processor support

Author:

    Stephane Plante (splante)

Environment:

    NT Kernel Model Driver only

Revision History:

    Adapted for processors from buttons - JakeO (3-28-2000)

--*/

#include "pch.h"
#include "..\shared\acpictl.h"

#ifdef ALLOC_PRAGMA
#pragma alloc_text(PAGE, ACPIButtonStartDevice)
#endif

//
// Spinlock to protect the processor list
//
KSPIN_LOCK  AcpiProcessorLock;

//
// List entry to store the thermal requests on
//
LIST_ENTRY  AcpiProcessorList;


VOID
ACPIProcessorCancelRequest(
    IN  PDEVICE_OBJECT  DeviceObject,
    IN  PIRP            Irp
    )
/*++

Routine Description:

    This routine cancels an outstanding processor request

Arguments:

    DeviceObject    - the device which as a request being cancelled
    Irp             - the cancelling irp

Return Value:

    None

--*/
{
    KIRQL               oldIrql;
    PDEVICE_EXTENSION   deviceExtension = ACPIInternalGetDeviceExtension(DeviceObject);

    //
    // We no longer need the cancel lock
    //
    IoReleaseCancelSpinLock( Irp->CancelIrql );

    //
    // We do however need the processor queue lock
    //
    KeAcquireSpinLock( &AcpiProcessorLock, &oldIrql );

    //
    // Remove the irp from the list that it is on
    //
    RemoveEntryList( &(Irp->Tail.Overlay.ListEntry) );

    //
    // Complete the irp now
    //
    Irp->IoStatus.Status = STATUS_CANCELLED;
    IoCompleteRequest( Irp, IO_NO_INCREMENT );
}

BOOLEAN
ACPIProcessorCompletePendingIrps(
    IN  PDEVICE_OBJECT  DeviceObject,
    IN  ULONG           ProcessorEvent
    )
/*++

Routine Description:

    This routine completes any pending processor irp sent to the specified
    device object with the knowledge of which processor events have occured

    The respective's processor's spinlock is held during this call

Arguments:

    DeviceObject    - the target processor object
    ProcessorEvent     - the processor event that occured

Return Value:

    TRUE if we completed an irp, FALSE, otherwise

--*/
{
    BOOLEAN             handledRequest = FALSE;
    KIRQL               oldIrql;
    LIST_ENTRY          doneList;
    PDEVICE_OBJECT      targetObject;
    PIO_STACK_LOCATION  irpSp;
    PIRP                irp;
    PLIST_ENTRY         listEntry;
    PULONG              resultBuffer;

    //
    // Initialize the list that will hold the requests that we need to
    // complete
    //
    InitializeListHead( &doneList );

    //
    // Acquire the thermal lock so that we can pend these requests
    //
    KeAcquireSpinLock( &AcpiProcessorLock, &oldIrql );

    //
    // Walk the list of pending irps to see which ones match this extension
    //
    listEntry = AcpiProcessorList.Flink;
    while (listEntry != &AcpiProcessorList) {

        //
        // Grab the irp from the list entry and update the next list entry
        // that we will look at
        //
        irp = CONTAINING_RECORD( listEntry, IRP, Tail.Overlay.ListEntry );
        listEntry = listEntry->Flink;

        //
        // We need the current irp stack location
        //
        irpSp = IoGetCurrentIrpStackLocation( irp );

        //
        // what is the target object for this irp?
        //
        targetObject = irpSp->DeviceObject;

        //
        // Is this an irp that we care about? IE: does the does target mage
        // the ones specified in this function
        //
        if (targetObject != DeviceObject) {

            continue;

        }

        //
        // At this point, we need to set the cancel routine to NULL because
        // we are going to take care of this irp and we don't want it cancelled
        // underneath us
        //
        if (IoSetCancelRoutine(irp, NULL) == NULL) {

            //
            // Cancel routine is active. stop processing this irp and move on
            //
            continue;

        }

        //
        // set the data to return in the irp
        //
        resultBuffer  = (PULONG) irp->AssociatedIrp.SystemBuffer;
        *resultBuffer = ProcessorEvent;
        irp->IoStatus.Status = STATUS_SUCCESS;
        irp->IoStatus.Information = sizeof(ULONG);

        //
        // Remove the entry from the list
        //
        RemoveEntryList( &(irp->Tail.Overlay.ListEntry) );

        //
        // Insert the list onto the next queue, so that we know how to
        // complete it later on
        //
        InsertTailList( &doneList, &(irp->Tail.Overlay.ListEntry) );

    }

    //
    // At this point, droup our processor lock
    //
    KeReleaseSpinLock( &AcpiProcessorLock, oldIrql );

    //
    // Walk the list of irps to be completed
    //
    listEntry = doneList.Flink;
    while (listEntry != &doneList) {

        //
        // Grab the irp from the list entry, update the next list entry
        // that we will look at, and complete the request
        //
        irp = CONTAINING_RECORD( listEntry, IRP, Tail.Overlay.ListEntry );
        listEntry = listEntry->Flink;
        RemoveEntryList( &(irp->Tail.Overlay.ListEntry) );

        //
        // Complete the request and remember that we handled a request
        //
        IoCompleteRequest( irp, IO_NO_INCREMENT );
        handledRequest = TRUE;


    }

    //
    // Return wether or not we handled a request
    //
    return handledRequest;
}

NTSTATUS
ACPIProcessorDeviceControl (
    IN  PDEVICE_OBJECT  DeviceObject,
    IN  PIRP            Irp
    )
/*++

Routine Description:

    Fixed processor device IOCTL handler

Arguments:

    DeviceObject    - fixed feature processor device object
    Irp             - the ioctl request

Return Value:

    Status

--*/
{
    KIRQL                   oldIrql;
    NTSTATUS                status;
    PDEVICE_EXTENSION       deviceExtension = ACPIInternalGetDeviceExtension(DeviceObject);
    PIO_STACK_LOCATION      irpSp           = IoGetCurrentIrpStackLocation(Irp);
    PULONG                  resultBuffer;
    OBJDATA                 data;

    //
    // Do not allow user mode IRPs in this routine
    //
    if (Irp->RequestorMode != KernelMode) {

        return ACPIDispatchIrpInvalid( DeviceObject, Irp );

    }

    resultBuffer = (PULONG) Irp->AssociatedIrp.SystemBuffer;

    switch (irpSp->Parameters.DeviceIoControl.IoControlCode) {
    case IOCTL_GET_PROCESSOR_OBJ_INFO:

        if (irpSp->Parameters.DeviceIoControl.OutputBufferLength < 
            sizeof(IOCTL_GET_PROCESSOR_OBJ_INFO)) {

            Irp->IoStatus.Status = status = STATUS_INFO_LENGTH_MISMATCH;
            Irp->IoStatus.Information = 0;
        
        } else {

            status = AMLIEvalNameSpaceObject(deviceExtension->AcpiObject,
                                             &data,
                                             0,
                                             NULL);

            if (NT_SUCCESS(status)) {
                
                ASSERT (data.dwDataType == OBJTYPE_PROCESSOR);
                ASSERT (data.pbDataBuff != NULL);

                (*(PPROCESSOR_OBJECT_INFO)resultBuffer).PhysicalID = 
                    ((PROCESSOROBJ *)data.pbDataBuff)->bApicID;

                (*(PPROCESSOR_OBJECT_INFO)resultBuffer).PBlkAddress = 
                    ((PROCESSOROBJ *)data.pbDataBuff)->dwPBlk;

                (*(PPROCESSOR_OBJECT_INFO)resultBuffer).PBlkLength = 
                    (UCHAR)((PROCESSOROBJ *)data.pbDataBuff)->dwPBlkLen;
                
                AMLIFreeDataBuffs(&data, 1);

                status = STATUS_SUCCESS;
                Irp->IoStatus.Information = sizeof(PROCESSOR_OBJECT_INFO);
            
            }
            
            Irp->IoStatus.Status = status;
        }

        IoCompleteRequest (Irp, IO_NO_INCREMENT);
        break;

    case IOCTL_ACPI_ASYNC_EVAL_METHOD:

        //
        // Handle this elsewhere
        //
        status = ACPIIoctlAsyncEvalControlMethod(
            DeviceObject,
            Irp,
            irpSp
            );
        break;

    case IOCTL_ACPI_EVAL_METHOD:

        //
        // Handle this elsewhere
        //
        status = ACPIIoctlEvalControlMethod(
            DeviceObject,
            Irp,
            irpSp
            );
        break;

    default:

        status = STATUS_NOT_SUPPORTED;
        Irp->IoStatus.Status = STATUS_NOT_SUPPORTED;
        IoCompleteRequest (Irp, IO_NO_INCREMENT);
        break;
    }
    
    return status;
}

NTSTATUS
ACPIProcessorStartDevice (
    IN  PDEVICE_OBJECT  DeviceObject,
    IN  PIRP            Irp
    )
/*++

Routine Description:

    Start device function for the fixed feature power and sleep device

Arguments:

    DeviceObject    - fixed feature processor device object
    Irp             - the start request

Return Value:

    Status

--*/
{
    NTSTATUS        Status;

    Status = ACPIInternalSetDeviceInterface (
        DeviceObject,
        (LPGUID) &GUID_DEVICE_PROCESSOR
        );

    Irp->IoStatus.Status = Status;
    IoCompleteRequest( Irp, IO_NO_INCREMENT );
    return Status;

}