/*++

Copyright (c) 1990  Microsoft Corporation

Module Name:

    smbcsrv.c

Abstract:

    SMBus class driver service functions

Author:

    Ken Reneris

Environment:

Notes:


Revision History:

--*/

#include "smbc.h"


VOID
SmbCRetry (
    IN struct _KDPC *Dpc,
    IN PVOID DeferredContext,
    IN PVOID SystemArgument1,
    IN PVOID SystemArgument2
    )
/*++

Routine Description:

    Handles retry timer

--*/
{
    PSMBDATA    Smb;

    Smb = (PSMBDATA) DeferredContext;
    SmbClassLockDevice (&Smb->Class);

    //
    // State is waiting for retry, move it to send request
    //
    ASSERT (Smb->IoState == SMBC_WAITING_FOR_RETRY);
    Smb->IoState = SMBC_START_REQUEST;
    SmbClassStartIo (Smb);

    SmbClassUnlockDevice (&Smb->Class);
}

VOID
SmbClassStartIo (
    IN PSMBDATA         Smb
    )
/*++

Routine Description:

    Main class driver state loop

    N.B. device lock is held by caller.
    N.B. device lock may be released and re-acquired during call

--*/
{
    PLIST_ENTRY         Entry;
    PIRP                Irp;
    PIO_STACK_LOCATION  IrpSp;
    PSMB_REQUEST        SmbReq;
    LARGE_INTEGER       duetime;

    //
    // If already servicing the device, done
    //

    if (Smb->InService) {
        return ;
    }


    //
    // Service the device
    //

    Smb->InService = TRUE;
    while (Smb->InService) {
        ASSERT_DEVICE_LOCKED (Smb);

        switch (Smb->IoState) {
            case SMBC_IDLE:
                //
                // Check if there is a request to give to the miniport
                //

                ASSERT (!Smb->Class.CurrentIrp);
                if (IsListEmpty(&Smb->WorkQueue)) {
                    // nothing to do, stop servicing the device
                    Smb->InService = FALSE;
                    break;
                }

                //
                // Get the next IRP
                //

                Entry = RemoveHeadList(&Smb->WorkQueue);
                Irp = CONTAINING_RECORD (
                            Entry,
                            IRP,
                            Tail.Overlay.ListEntry
                            );

                //
                // Make it the current request
                //

                Smb->RetryCount = 0;
                Smb->Class.DeviceObject->CurrentIrp = Irp;

                Smb->IoState = SMBC_START_REQUEST;
                break;

            case SMBC_START_REQUEST:
                //
                // Tell miniport to start on this request
                //

                Irp = Smb->Class.DeviceObject->CurrentIrp;
                IrpSp = IoGetCurrentIrpStackLocation(Irp);
                Smb->Class.CurrentIrp = Irp;
                Smb->Class.CurrentSmb = IrpSp->Parameters.DeviceIoControl.Type3InputBuffer;

                Smb->IoState = SMBC_WAITING_FOR_REQUEST;
                
                #if DEBUG 
                if (SMBCDebug & SMB_TRANSACTION) {
                    PUCHAR protocols [SMB_MAXIMUM_PROTOCOL+1] = {
                        "SMB_WRITE_QUICK",
                        "SMB_READ_QUICK",
                        "SMB_SEND_BYTE",
                        "SMB_RECEIVE_BYTE",
                        "SMB_WRITE_BYTE",
                        "SMB_READ_BYTE",
                        "SMB_WRITE_WORD",
                        "SMB_READ_WORD",
                        "SMB_WRITE_BLOCK",
                        "SMB_READ_BLOCK",
                        "SMB_PROCESS_CALL",
                        "SMB_BLOCK_PROCESS_CALL"};
                    UCHAR i;
                    
                    SmbPrint (SMB_TRANSACTION, ("SmbClassStartIo: started %s (%02x) Add: %02x", 
                                                (Smb->Class.CurrentSmb->Protocol <= SMB_MAXIMUM_PROTOCOL) ?
                                                    protocols[Smb->Class.CurrentSmb->Protocol] : "BAD PROTOCOL",
                                                Smb->Class.CurrentSmb->Protocol, Smb->Class.CurrentSmb->Address));
                    switch (Smb->Class.CurrentSmb->Protocol) {
                    case SMB_WRITE_QUICK:
                    case SMB_READ_QUICK:
                    case SMB_RECEIVE_BYTE:
                        SmbPrint (SMB_TRANSACTION, ("\n"));
                        break;
                    case SMB_SEND_BYTE:
                        SmbPrint (SMB_TRANSACTION, (", Data: %02x\n", Smb->Class.CurrentSmb->Data[0]));
                        break;
                    case SMB_WRITE_BYTE:
                        SmbPrint (SMB_TRANSACTION, (", Com: %02x, Data: %02x\n", 
                                                    Smb->Class.CurrentSmb->Command, Smb->Class.CurrentSmb->Data[0]));
                        break;
                    case SMB_READ_BYTE:
                    case SMB_READ_WORD:
                    case SMB_READ_BLOCK:
                        SmbPrint (SMB_TRANSACTION, (", Com: %02x\n",
                                                    Smb->Class.CurrentSmb->Command));
                        break;
                    case SMB_WRITE_WORD:
                    case SMB_PROCESS_CALL:
                        SmbPrint (SMB_TRANSACTION, (", Com: %02x, Data: %04x\n", 
                                                    Smb->Class.CurrentSmb->Command, *((PUSHORT)Smb->Class.CurrentSmb->Data)));
                        break;
                    case SMB_WRITE_BLOCK:
                    case SMB_BLOCK_PROCESS_CALL:
                        SmbPrint (SMB_TRANSACTION, (", Com: %02x, Len: %02x, Data:", 
                                                    Smb->Class.CurrentSmb->Command, Smb->Class.CurrentSmb->BlockLength));
                        for (i=0; i < Smb->Class.CurrentSmb->BlockLength; i++) {
                            SmbPrint (SMB_TRANSACTION, (" %02x", Smb->Class.CurrentSmb->Data[i]));

                        }
                        SmbPrint (SMB_TRANSACTION, ("\n"));
                        break;
                    default:
                        SmbPrint (SMB_TRANSACTION, ("\n"));
                    }
                }
                #endif

                Smb->Class.StartIo (&Smb->Class, Smb->Class.Miniport);
                break;

            case SMBC_WAITING_FOR_REQUEST:
                //
                // Waiting for miniport, just keep waiting
                //

                Smb->InService = FALSE;
                break;

            case SMBC_COMPLETE_REQUEST:
                //
                // Miniport has returned the request
                //

                Irp = Smb->Class.DeviceObject->CurrentIrp;
                IrpSp = IoGetCurrentIrpStackLocation(Irp);
                SmbReq = IrpSp->Parameters.DeviceIoControl.Type3InputBuffer;

                #if DEBUG 
                if (SMBCDebug & SMB_TRANSACTION) {
                    PUCHAR protocols [SMB_MAXIMUM_PROTOCOL+1] = {
                        "SMB_WRITE_QUICK",
                        "SMB_READ_QUICK",
                        "SMB_SEND_BYTE",
                        "SMB_RECEIVE_BYTE",
                        "SMB_WRITE_BYTE",
                        "SMB_READ_BYTE",
                        "SMB_WRITE_WORD",
                        "SMB_READ_WORD",
                        "SMB_WRITE_BLOCK",
                        "SMB_READ_BLOCK",
                        "SMB_PROCESS_CALL",
                        "SMB_BLOCK_PROCESS_CALL"};
                    UCHAR i;
                    
                    SmbPrint (SMB_TRANSACTION, ("SmbClassStartIo: finished %s (%02x) Status: %02x, Add: %02x", 
                                                (SmbReq->Protocol <= SMB_MAXIMUM_PROTOCOL) ?
                                                    protocols[SmbReq->Protocol] : "BAD PROTOCOL",
                                                SmbReq->Protocol, SmbReq->Status, SmbReq->Address));
                    if (SmbReq->Status != SMB_STATUS_OK) {
                        SmbPrint (SMB_TRANSACTION, ("\n"));
                    } else {
                        switch (SmbReq->Protocol) {
                        case SMB_WRITE_QUICK:
                        case SMB_READ_QUICK:
                        case SMB_SEND_BYTE:
                            SmbPrint (SMB_TRANSACTION, ("\n"));
                            break;
                        case SMB_RECEIVE_BYTE:
                            SmbPrint (SMB_TRANSACTION, (", Data: %02x\n", SmbReq->Data[0]));
                            break;
                        case SMB_READ_BYTE:
                            SmbPrint (SMB_TRANSACTION, (", Com: %02x, Data: %02x\n", 
                                                        SmbReq->Command, SmbReq->Data[0]));
                            break;
                        case SMB_WRITE_BYTE:
                        case SMB_WRITE_WORD:
                        case SMB_WRITE_BLOCK:
                            SmbPrint (SMB_TRANSACTION, (", Com: %02x\n",
                                                        SmbReq->Command));
                            break;
                        case SMB_READ_WORD:
                        case SMB_PROCESS_CALL:
                            SmbPrint (SMB_TRANSACTION, (", Com: %02x, Data: %04x\n", 
                                                        SmbReq->Command, *((PUSHORT)SmbReq->Data)));
                            break;
                        case SMB_READ_BLOCK:
                        case SMB_BLOCK_PROCESS_CALL:
                            SmbPrint (SMB_TRANSACTION, (", Com: %02x, Len: %02x, Data:", 
                                                        SmbReq->Command, SmbReq->BlockLength));
                            for (i=0; i < SmbReq->BlockLength; i++) {
                                SmbPrint (SMB_TRANSACTION, (" %02x", SmbReq->Data[i]));

                            }
                            SmbPrint (SMB_TRANSACTION, ("\n"));
                            break;
                        default:
                            SmbPrint (SMB_TRANSACTION, ("\n"));
                        }
                    }
                }
                #endif


                if (SmbReq->Status != SMB_STATUS_OK) {

                    //
                    // SMB request had an error, check for a retry
                    //

                    SmbPrint (SMB_WARN, ("SmbCStartIo: smb request error %x\n", SmbReq->Status));
                    if (Smb->RetryCount < MAX_RETRIES) {
                        Smb->RetryCount += 1;
                        Smb->IoState = SMBC_WAITING_FOR_RETRY;

                        duetime.QuadPart = RETRY_TIME;
                        KeSetTimer (&Smb->RetryTimer, duetime, &Smb->RetryDpc);
                        break;
                    }

                }

                //
                // Complete the request
                //

                Smb->Class.DeviceObject->CurrentIrp = NULL;
                Smb->IoState = SMBC_COMPLETING_REQUEST;
                SmbClassUnlockDevice (&Smb->Class);
                IoCompleteRequest (Irp, IO_NO_INCREMENT);
                SmbClassLockDevice (&Smb->Class);

                //
                // Now idle
                //

                Smb->IoState = SMBC_IDLE;
                break;

            case SMBC_WAITING_FOR_RETRY:
                //
                // Waiting to retry, just keep waiting
                //

                Smb->InService = FALSE;
                break;

            default:
                SmbPrint(SMB_ERROR, ("SmbCStartIo: unknown state\n"));
                Smb->IoState = SMBC_IDLE;
                Smb->InService = FALSE;
                break;
        }
    }

    return ;
}

VOID
SmbClassCompleteRequest (
    IN PSMB_CLASS   SmbClass
    )
/*++

Routine Description:

    Called by the miniport to complete the request it was given

    N.B. device lock is held by caller.
    N.B. device lock may be released and re-acquired during call

--*/
{
    PSMBDATA        Smb;

    //
    // Device must be locked, and waiting for a request to compelte
    //

    Smb = CONTAINING_RECORD (SmbClass, SMBDATA, Class);
    ASSERT_DEVICE_LOCKED (Smb);
    ASSERT (Smb->IoState == SMBC_WAITING_FOR_REQUEST);

    //
    // No irp at miniport
    //

    SmbClass->CurrentIrp = NULL;
    SmbClass->CurrentSmb = NULL;

    //
    // Update state to complete it and handle it
    //

    Smb->IoState = SMBC_COMPLETE_REQUEST;
    SmbClassStartIo (Smb);
}

VOID
SmbClassLockDevice (
    IN PSMB_CLASS   SmbClass
    )
/*++

Routine Description:

    Called to acquire the device lock

--*/
{
    PSMBDATA        Smb;

    Smb = CONTAINING_RECORD (SmbClass, SMBDATA, Class);
    KeAcquireSpinLock (&Smb->SpinLock, &Smb->SpinLockIrql);
#if DEBUG
    ASSERT (!Smb->SpinLockAcquired);
    Smb->SpinLockAcquired = TRUE;
#endif
}


VOID
SmbClassUnlockDevice (
    IN PSMB_CLASS   SmbClass
    )
/*++

Routine Description:

    Called to release the device lock

--*/
{
    PSMBDATA        Smb;

    Smb = CONTAINING_RECORD (SmbClass, SMBDATA, Class);
#if DEBUG
    ASSERT_DEVICE_LOCKED (Smb);
    Smb->SpinLockAcquired = FALSE;
#endif
    KeReleaseSpinLock (&Smb->SpinLock, Smb->SpinLockIrql);
}