Subversion Repositories f9daq

Rev

Rev 23 | Blame | Compare with Previous | Last modification | View Log | RSS feed

//-------------------------------------------------------------------------
// WINNT driver for PCICC32 interface from ARW Elektronik, Germany ---------
// the ioctl functions
//
// (c) 1999-2002 ARW Elektronik
//
// this source code is published under GPL (Open Source). You can use, redistrubute and
// modify it unless this header   is not modified or deleted. No warranty is given that
// this software will work like expected.
// This product is not authorized for use as critical component in life support systems
// wihout the express written approval of ARW Elektronik Germany.
//
// Please announce changes and hints to ARW Elektronik
//
// what                                            who          when
// started                                         AR           03.07.1999
// first release 1.0                                                       AR                   17.10.1999
// added access to PLX LC-Register                 AR           30.03.2001
// changed making procedure (only VCC > 6.0)       AR           30.05.2002
// multiple interrupt enable allowed               AR           01.06.2002
// added KeSynchronizeExecution for interrupt sync AR           16.06.2002
// extended ioctl_irq_status_kernel                AR           18.06.2002
//
 
//-------------------------------------------------------------------------
// INCLUDES
//
#include <ntddk.h>
#include <devioctl.h>
#include <pcicc32_drv.h>
#include <pcicc32.h>
#include <pcicc32_v.h>
#include <pcicc32_io.h>
#include <pcicc32_local.h>
#include <pcicc32_i.h>

//------------------------------------------------------------------------
// DEFINES
//

// buffers usage must match the corresponding ioctl code!
#define SET_BUFFERS_METHOD_OUT_DIRECT \
{\
        InputLength   = IrpStack->Parameters.DeviceIoControl.InputBufferLength;\
        OutputLength  = IrpStack->Parameters.DeviceIoControl.OutputBufferLength;\
        pInputBuffer  = ((void *)(Irp->AssociatedIrp.SystemBuffer));\
        pOutputBuffer = ((void *)(MmGetSystemAddressForMdl(Irp->MdlAddress)));\
}


#define SET_BUFFERS_METHOD_IN_DIRECT \
{\
        InputLength   = IrpStack->Parameters.DeviceIoControl.InputBufferLength;\
        OutputLength  = IrpStack->Parameters.DeviceIoControl.OutputBufferLength;\
        pInputBuffer  = ((void *)(MmGetSystemAddressForMdl(Irp->MdlAddress)));\
        pOutputBuffer = ((void *)(Irp->AssociatedIrp.SystemBuffer));\
}


#define SET_BUFFERS_METHOD_BUFFERED \
{\
        InputLength   = IrpStack->Parameters.DeviceIoControl.InputBufferLength;\
        OutputLength  = IrpStack->Parameters.DeviceIoControl.OutputBufferLength;\
        pInputBuffer  = pOutputBuffer = ((void *)(Irp->AssociatedIrp.SystemBuffer));\
}


#define COMPLETE_REQUEST \
{\
        if (Status != STATUS_PENDING)\
        {\
                Irp->IoStatus.Status      = Status; \
                Irp->IoStatus.Information = irp_info; \
                IoCompleteRequest(Irp,IO_NO_INCREMENT); \
        }\
}


// compatibilty issues to WIN95 driver calls
#ifndef WORD
#define WORD USHORT
#endif

#ifndef DWORD
#define DWORD ULONG
#endif

#ifndef BYTE
#define BYTE UCHAR
#endif

#ifndef BOOL
#define BOOL BOOLEAN
#endif


//-------------------------------------------------------------------------
// TYPEDEFS
//
typedef struct
{
        FILE_OBJ    *file_obj;
        PCIADA      *pciada;
        PCICC32_IRQ_RESPONSE *pIrqStatus;
        PIRP        *Irp;
        ULONG       *irp_info;
        NTSTATUS    *Status;
} IOCTL_IRQ_STATUS_CONTEXT;

//--------------------------------------------------------------------------
// LOCAL FUNCTIONS
//

//--------------------------------------------------------------------------
// fast read or write functions - portable -
static void readWord(void *to, void *from)
{
        *(PUSHORT)to = READ_REGISTER_USHORT((PUSHORT)from);
}

static void readLong(void *to, void *from)
{
        *(PULONG)to = READ_REGISTER_ULONG((PULONG)from);
}

static void writeWord(void *to, void *from)
{
        WRITE_REGISTER_USHORT((PUSHORT)to, *(PUSHORT)from);
}

static void writeLong(void *to, void *from)
{
        WRITE_REGISTER_ULONG((PULONG)to, *(PULONG)from);
}

//------------------------------------------------------------------------
// init the interface with build in and user supplied constants
static BOOLEAN InitInterface(PVOID pvLcr, PVOID pvIfr)
{
  UNREFERENCED_PARAMETER(pvLcr);
  UNREFERENCED_PARAMETER(pvIfr);
  return TRUE;
}

// deinit the interface with user supplied and build in constants
static BOOLEAN DeInitInterface(PVOID pvLcr, PVOID pvIfr)
{
  UNREFERENCED_PARAMETER(pvLcr);
  UNREFERENCED_PARAMETER(pvIfr);
  return TRUE;
}

//------------------------------------------------------------------------
// the default cancel routine for an queued Irp
//
void CancelRequest(PDEVICE_OBJECT device_Obj, PIRP Irp)
{
        FILE_OBJ    *file_obj = (FILE_OBJ *)Irp->Tail.Overlay.OriginalFileObject->FsContext;
        DEVICE_EXT      *pDevExt  = (DEVICE_EXT *)(device_Obj->DeviceExtension);
        PCIADA      *pciada   = pDevExt->cc32[file_obj->uwAssociatedCC32];

        if (pciada->pBlockingIrp == (PIRP *)NULL)
        {
                IoReleaseCancelSpinLock(Irp->CancelIrql);
                KdPrint(("Nothing to do: CancelRequest(0x%08x)\n", Irp));
                return;
        }
        else
        {
                // release control of interrupt
                if (pciada->pIrqControlFile == file_obj)
                {
                        pciada->pIrqControlFile = (FILE_OBJ *)NULL;
                        globalInterruptDisable(pciada);
                }

                // cancel any blocking Irp origin from this file
                if (file_obj->blockingIrp != (PIRP)NULL)
                        file_obj->blockingIrp = (PIRP)NULL;

                if (pciada->pBlockingIrp == &file_obj->blockingIrp)
                        pciada->pBlockingIrp = (PIRP *)NULL;

                IoReleaseCancelSpinLock(Irp->CancelIrql);

                KdPrint(("Done: CancelRequest(0x%08x)\n", Irp));

                Irp->IoStatus.Status = STATUS_CANCELLED;
                Irp->IoStatus.Information = 0;
 
                IoCompleteRequest(Irp, IO_NO_INCREMENT);
        }
}

//------------------------------------------------------------------------
// the custom deffered routine to finish blocking io on irq_block
void fMyDefferedRoutine(PKDPC Dpc, PVOID pvDevice_object, PVOID pvPciada, PVOID pdwIrqStatus)
{
        NTSTATUS Status = STATUS_SUCCESS;
        ULONG irp_info  = sizeof(PCICC32_IRQ_RESPONSE);
    PIRP  Irp = (PIRP)NULL;
        PCIADA          *pciada = (PCIADA *)pvPciada;
        KIRQL                   oldIrqlCancel;

        UNREFERENCED_PARAMETER(Dpc);
        UNREFERENCED_PARAMETER(pdwIrqStatus);
        UNREFERENCED_PARAMETER(pvDevice_object);
    KdPrint(("fMyDefferedRoutine(0x%08x)\n", pciada->dwIrqStatus));

        // beware off damage due to intercept at cancel of thread
    IoAcquireCancelSpinLock(&oldIrqlCancel);
 
        // get my associated packet
        if (pciada->pBlockingIrp != (PIRP *)NULL)
        {
                Irp = *pciada->pBlockingIrp;

                if (Irp != (PIRP)NULL) // then a blcoking Irp is waiting
                {
                        FILE_OBJ *file_obj               = (FILE_OBJ *)Irp->Tail.Overlay.OriginalFileObject->FsContext;
                        PCICC32_IRQ_RESPONSE *pIrqStatus = (PCICC32_IRQ_RESPONSE *)(Irp->AssociatedIrp.SystemBuffer);

                        // fill the response structure
                        pIrqStatus->dwInterruptFlags = pciada->dwIrqStatus;
                        pciada->dwIrqStatus = 0;          // to prevent a following direct return
                        pIrqStatus->dwInterface      = file_obj->uwAssociatedCC32;

                        // release the cancel routine from this Irp
                        IoSetCancelRoutine(Irp, NULL);

                        COMPLETE_REQUEST;

                        file_obj->blockingIrp = (PIRP)NULL;
                }

                pciada->pBlockingIrp  = (PIRP *)NULL;
        }

        // release the spin locks
        IoReleaseCancelSpinLock(oldIrqlCancel);
}

//------------------------------------------------------------------------
// if the interrupt is disabled for a blocking path, cancel the block
static void ReleaseBlockingIrp(PDEVICE_OBJECT device_Obj, PCIADA *pciada, PFILE_OBJ pFile_obj)
{
        NTSTATUS Status = STATUS_CANCELLED;
        ULONG irp_info  = sizeof(PCICC32_IRQ_RESPONSE);
    PIRP  Irp       = (PIRP)NULL;
        KIRQL oldIrqlCancel;
        UNREFERENCED_PARAMETER(device_Obj);
        UNREFERENCED_PARAMETER(pFile_obj);

        UNREFERENCED_PARAMETER(irp_info);
    KdPrint(("ReleaseBlockingIrp()\n"));

        // beware off damage due to intercept with cancel of thread
        IoAcquireCancelSpinLock(&oldIrqlCancel);

        if (pciada->pBlockingIrp != (PIRP *)NULL)
        {
                // get my associated packet
                Irp = *pciada->pBlockingIrp;

                if (Irp != (PIRP)NULL)
                {
                        FILE_OBJ *file_obj               = (FILE_OBJ *)Irp->Tail.Overlay.OriginalFileObject->FsContext;
                        PCICC32_IRQ_RESPONSE *pIrqStatus = (PCICC32_IRQ_RESPONSE *)(Irp->AssociatedIrp.SystemBuffer);
                        ULONG irp_info  = sizeof(PCICC32_IRQ_RESPONSE);

                        pIrqStatus->dwInterruptFlags = pciada->dwIrqStatus;
                        pIrqStatus->dwInterface      = file_obj->uwAssociatedCC32;

                        // release the cancel routine from this Irp
                        IoSetCancelRoutine(Irp, NULL);

                        COMPLETE_REQUEST;

                        file_obj->blockingIrp = (PIRP)NULL;
                }

                pciada->pBlockingIrp = (PIRP *)NULL; // mark the storage for blocking Irp free
        }

        // release the spin locks
        IoReleaseCancelSpinLock(oldIrqlCancel);
}

//------------------------------------------------------------------------
// all functions called from ioctl jump table
//

//------------------------------------------------------------------------
// a dummy entry because of compatibiltiy (near) WIN95 driver
static NTSTATUS ioctl_dummy(PDEVICE_OBJECT device_Obj, PIRP Irp, PIO_STACK_LOCATION IrpStack)
{
        NTSTATUS        Status    = STATUS_SUCCESS;
        ULONG           irp_info  = 0;
        PVOID           pInputBuffer,pOutputBuffer;
        ULONG           InputLength, OutputLength;
        char            *pCommand;

        FILE_OBJ *file_obj = (FILE_OBJ *)Irp->Tail.Overlay.OriginalFileObject->FsContext;
#ifndef _DEBUG
        UNREFERENCED_PARAMETER(file_obj);
#endif
        SET_BUFFERS_METHOD_BUFFERED;
        UNREFERENCED_PARAMETER(device_Obj);
        pCommand = (char *)pInputBuffer;

    KdPrint(("ioctl_dummy(%d)\n", file_obj->uwAssociatedCC32));

        // do what must be here in between -----------
       
        // do what must be here in between --- end ---

        COMPLETE_REQUEST;

    KdPrint(("ioctl_dummy(), Status = 0x%08x\n", Status));

        return Status;
}

//------------------------------------------------------------------------
// requests status
static NTSTATUS ioctl_get_status(PDEVICE_OBJECT device_Obj, PIRP Irp, PIO_STACK_LOCATION IrpStack)
{
        NTSTATUS        Status    = STATUS_SUCCESS;
        ULONG           irp_info  = sizeof(PCICC32_STATUS);
        PVOID           pInputBuffer,pOutputBuffer;
        ULONG           InputLength, OutputLength;
        PCIADA      *pciada;
        DEVICE_EXT      *pDevExt;
        PCICC32_STATUS *pStatus;
        FILE_OBJ    *file_obj = (FILE_OBJ *)Irp->Tail.Overlay.OriginalFileObject->FsContext;
        USHORT      wModuleNumber = file_obj->uwAssociatedCC32;

        // do what must be here in between -----------
        pDevExt = (DEVICE_EXT *)(device_Obj->DeviceExtension);

        SET_BUFFERS_METHOD_BUFFERED;

    KdPrint(("ioctl_get_status(%d)\n", wModuleNumber));

        // do what must be here in between -----------
        if (OutputLength >= sizeof(PCICC32_STATUS))
        {
                USHORT temp;

                pStatus = (PCICC32_STATUS *)pOutputBuffer;

                pciada = pDevExt->cc32[wModuleNumber];

                pStatus->dwInterface    = wModuleNumber;
 
                temp = READ_REGISTER_USHORT(pciada->pwIntCSR);
                pStatus->bTimeout                       = (temp & 0x0020) ? 1 : 0;
                pStatus->bInterrupt                     = (temp & 0x0004) ? 1 : 0;
        }
        else
                Status = STATUS_BUFFER_TOO_SMALL;
        // do what must be here in between --- end ---

        COMPLETE_REQUEST;

    KdPrint(("ioctl_get_status(), Status = 0x%08x\n", Status));

        return Status;
}

//------------------------------------------------------------------------
// clears status
static BOOLEAN ioctl_clear_status_kernel(PVOID pvContext)
{
        PCIADA *pciada = (PCIADA *)pvContext;
        USHORT wCntrl;

        // get current Cntrl - and clear interrupt
        wCntrl  = READ_REGISTER_USHORT(pciada->pwCntrl);
        wCntrl &= ~0x0100;  // disable
        WRITE_REGISTER_USHORT(pciada->pwCntrl, wCntrl);
        wCntrl |= 0x0100;   // enable again
        WRITE_REGISTER_USHORT(pciada->pwCntrl, wCntrl);

        return TRUE;
}

static NTSTATUS ioctl_clear_status(PDEVICE_OBJECT device_Obj, PIRP Irp, PIO_STACK_LOCATION IrpStack)
{
        NTSTATUS        Status    = STATUS_SUCCESS;
        ULONG           irp_info  = 0;
        PVOID           pInputBuffer,pOutputBuffer;
        ULONG           InputLength, OutputLength;
        PCIADA      *pciada;
        DEVICE_EXT      *pDevExt;
        FILE_OBJ    *file_obj = (FILE_OBJ *)Irp->Tail.Overlay.OriginalFileObject->FsContext;
        USHORT      wModuleNumber = file_obj->uwAssociatedCC32;

        // do what must be here in between -----------
        pDevExt = (DEVICE_EXT *)(device_Obj->DeviceExtension);

        SET_BUFFERS_METHOD_BUFFERED;

    KdPrint(("ioctl_clear_status(%d)\n", wModuleNumber));

        // do what must be here in between -----------
        pciada = pDevExt->cc32[wModuleNumber];

        KeSynchronizeExecution(pciada->InterruptObject, ioctl_clear_status_kernel, pciada);

        // do what must be here in between --- end ---

        COMPLETE_REQUEST;

    KdPrint(("ioctl_clear_status() OK\n"));

        return Status;
}

//------------------------------------------------------------------------
// set parameter for this path for future access to CC32
static NTSTATUS ioctl_access_para(PDEVICE_OBJECT device_Obj, PIRP Irp, PIO_STACK_LOCATION IrpStack)
{
        NTSTATUS        Status    = STATUS_SUCCESS;
        ULONG           irp_info  = 0;
        PVOID           pInputBuffer,pOutputBuffer;
        ULONG           InputLength, OutputLength;
        PCICC32_ACCESS_COMMAND *pAccessPara;
        FILE_OBJ    *file_obj = (FILE_OBJ *)Irp->Tail.Overlay.OriginalFileObject->FsContext;
        USHORT      wModuleNumber = file_obj->uwAssociatedCC32;
        DEVICE_EXT      *pDevExt  = (DEVICE_EXT *)(device_Obj->DeviceExtension);
        PCIADA      *pciada   = pDevExt->cc32[wModuleNumber];
        UNREFERENCED_PARAMETER(pciada);
        SET_BUFFERS_METHOD_BUFFERED;

    KdPrint(("ioctl_access_para(%d)\n", wModuleNumber));
   
        pAccessPara = (PCICC32_ACCESS_COMMAND *)pInputBuffer;

        // do here in between what has to be done -----------------
        file_obj->wAccessType       = pAccessPara->wAccessType;
        file_obj->wBlockTransfer    = pAccessPara->wBlockTransfer;

        pAccessPara->dwInterface = wModuleNumber;

        switch (pAccessPara->wAccessType)
        {
                case WORD_ACCESS: file_obj->fRead  = readWord;
                                                  file_obj->fWrite = writeWord;
                                                  break;
                case LONG_ACCESS: file_obj->fRead  = readLong;
                                                  file_obj->fWrite = writeLong;
                                                  break;
                default: Status = STATUS_UNSUCCESSFUL;
                                                  break;
        }
        // do here in between what has to be done end -------------

        COMPLETE_REQUEST;

    KdPrint(("ioctl_access_para(), Status = 0x%08x\n", Status));

        return Status;
}

//------------------------------------------------------------------------
// allow or inhibit interrupt requests from either CC32 or thru local timeout
static NTSTATUS ioctl_control_interrupts(PDEVICE_OBJECT device_Obj, PIRP Irp, PIO_STACK_LOCATION IrpStack)
{
        NTSTATUS        Status    = STATUS_SUCCESS;
        ULONG           irp_info  = 0;
        PVOID           pInputBuffer,pOutputBuffer;
        ULONG           InputLength, OutputLength;
        PCICC32_IRQ_CONTROL *pIrqControlIn, *pIrqControlOut;
        FILE_OBJ    *file_obj = (FILE_OBJ *)Irp->Tail.Overlay.OriginalFileObject->FsContext;
        USHORT      wModuleNumber = file_obj->uwAssociatedCC32;
        DEVICE_EXT      *pDevExt  = (DEVICE_EXT *)(device_Obj->DeviceExtension);
        PCIADA      *pciada   = pDevExt->cc32[wModuleNumber];

        SET_BUFFERS_METHOD_BUFFERED;

    KdPrint(("ioctl_control_interrupts(%d)\n", wModuleNumber));
   
        pIrqControlIn  = (PCICC32_IRQ_CONTROL *)pInputBuffer;
        pIrqControlOut = (PCICC32_IRQ_CONTROL *)pOutputBuffer;

        // do here in between what has to be done -----------------
        if (pIrqControlIn->wEnable)
        {
                // reserve the controlling of interrupts for this path
                if ((pciada->pIrqControlFile == (FILE_OBJ *)NULL) ||
                        (pciada->pIrqControlFile == file_obj))
                {
                        pciada->pIrqControlFile = file_obj;
                        globalInterruptEnable(pciada);
                }
                else
                        Status = STATUS_DEVICE_BUSY;
        }
        else
        {
                // nobody else is allowed to disable interrupts
                if (pciada->pIrqControlFile == file_obj)
                {
                        pciada->pIrqControlFile = (FILE_OBJ *)NULL;
                        globalInterruptDisable(pciada);
                }
                else
                        Status = STATUS_DEVICE_BUSY;
        }

        // give back if the user grants space
        if (OutputLength >= sizeof(PCICC32_IRQ_CONTROL))
        {
                pIrqControlOut->dwInterface = wModuleNumber;
                pIrqControlOut->wEnable     = globalInterruptEnabledStatus(pciada);
        }      
        // do here in between what has to be done end -------------

        COMPLETE_REQUEST;

    KdPrint(("ioctl_control_interrupts(), Status = 0x%08x\n", Status));

        return Status;
}

//------------------------------------------------------------------------
// implements a blocking io-call to get irq status.
static BOOLEAN ioctl_irq_status_kernel(PVOID pvContext)
{
        IOCTL_IRQ_STATUS_CONTEXT *context = (IOCTL_IRQ_STATUS_CONTEXT *)pvContext;
    KIRQL       oldIrql;

        if (context->pciada->dwIrqStatus)
        {
                // there is a pending interrupt - return immediately
                KdPrint(("ioctl_irq_status(), direct return (0x%08x)\n", context->pciada->dwIrqStatus));

                context->pIrqStatus->dwInterruptFlags = context->pciada->dwIrqStatus;
                context->pciada->dwIrqStatus          = 0;  // release pending status

                *context->irp_info = sizeof(PCICC32_IRQ_RESPONSE);
        }
        else
        {
                // make the request blocking
                IoAcquireCancelSpinLock(&oldIrql);

                if ((*context->Irp)->Cancel)    // cancel while doing
                {
                        KdPrint(("ioctl_irq_status(), canceled return\n"));
                        *context->Status = STATUS_CANCELLED;
                }
                else
                {
                        KdPrint(("ioctl_irq_status(), blocking\n"));

                        if (context->pciada->pBlockingIrp != (PIRP *)NULL)
                        {
                                // a Irp is still waiting
                                *context->Status = STATUS_DEVICE_BUSY;
                        }
                        else
                        {
                                context->file_obj->blockingIrp = *context->Irp;
                                context->pciada->pBlockingIrp  = &context->file_obj->blockingIrp;

                                *context->Status = STATUS_PENDING;

                                // mark irp as pending and return
                                IoMarkIrpPending(*context->Irp);
                                IoSetCancelRoutine(*context->Irp, CancelRequest);
                        }
                } // if (Irp->Cancel) ...
        }

        return TRUE;
}

static NTSTATUS ioctl_irq_status(PDEVICE_OBJECT device_Obj, PIRP Irp, PIO_STACK_LOCATION IrpStack)
{
        NTSTATUS        Status    = STATUS_SUCCESS;
        ULONG           irp_info  = 0;
        PVOID           pInputBuffer,pOutputBuffer;
        ULONG           InputLength, OutputLength;
        DEVICE_EXT      *pDevExt  = (DEVICE_EXT *)(device_Obj->DeviceExtension);
        USHORT      wModuleNumber;
        IOCTL_IRQ_STATUS_CONTEXT context;

        SET_BUFFERS_METHOD_BUFFERED;

        context.file_obj   = (FILE_OBJ *)Irp->Tail.Overlay.OriginalFileObject->FsContext;
        wModuleNumber      = context.file_obj->uwAssociatedCC32;
        context.pciada     = pDevExt->cc32[wModuleNumber];
        context.pIrqStatus = (PCICC32_IRQ_RESPONSE *)pOutputBuffer;
        context.Status     = &Status;
        context.irp_info   = &irp_info;
        context.Irp        = &Irp;


    KdPrint(("ioctl_irq_status(%d)\n", wModuleNumber));
   
        // do here in between what has to be done -----------------
        if (OutputLength < sizeof(PCICC32_IRQ_RESPONSE))
                Status = STATUS_BUFFER_TOO_SMALL;
        else
        {
                context.pIrqStatus->dwInterface = wModuleNumber;

            KeSynchronizeExecution(context.pciada->InterruptObject, ioctl_irq_status_kernel, &context);
        }
        // do here in between what has to be done end -------------

        COMPLETE_REQUEST;

    KdPrint(("ioctl_irq_status(), Status = 0x%08x\n", Status));

        return Status;
}


//------------------------------------------------------------------------
// for test and debug purposes: direkt access to PLX LCR space
static NTSTATUS ioctl_access_lcr(PDEVICE_OBJECT device_Obj, PIRP Irp, PIO_STACK_LOCATION IrpStack)
{
        NTSTATUS        Status    = STATUS_SUCCESS;
        ULONG           irp_info  = 0;
        PVOID           pInputBuffer,pOutputBuffer;
        ULONG           InputLength, OutputLength;
        FILE_OBJ    *file_obj = (FILE_OBJ *)Irp->Tail.Overlay.OriginalFileObject->FsContext;
        USHORT      wModuleNumber = file_obj->uwAssociatedCC32;
        DEVICE_EXT      *pDevExt  = (DEVICE_EXT *)(device_Obj->DeviceExtension);
        PCIADA      *pciada   = pDevExt->cc32[wModuleNumber];
        PCICC32_LCR_ACCESS *pAccessOut;
        PCICC32_LCR_ACCESS *pAccessIn;

        SET_BUFFERS_METHOD_BUFFERED;

    KdPrint(("ioctl_access_lcr(%d)\n", wModuleNumber));
   
        pAccessOut = (PCICC32_LCR_ACCESS *)pOutputBuffer;
        pAccessIn  = (PCICC32_LCR_ACCESS *)pInputBuffer;

        // do here in between what has to be done -----------------
        if (OutputLength < sizeof(PCICC32_LCR_ACCESS))
                Status = STATUS_BUFFER_TOO_SMALL;
        else
        {
                *pAccessOut = *pAccessIn;
                pAccessOut->dwInterface = wModuleNumber;

                if (pAccessIn->wRegisterAddress <= 0x52)
                {
                        // 1st part: long word accesses
                        if (pAccessIn->bBytesLane == LONG_ACCESS)
                        {
                                if (pAccessIn->wRegisterAddress & 0x0003)
                                        Status = STATUS_INSTRUCTION_MISALIGNMENT;
                                else
                                {
                                        ULONG       *pdwVirtAddress;
                                        ULONG           dwDummy;

                                        pdwVirtAddress = (ULONG *)((ULONG)pciada->pvVirtLcr + pAccessIn->wRegisterAddress);

                                        switch (pAccessIn->bAccessMode)
                                        {
                                                case LCR_WRITE:
                                                        WRITE_REGISTER_ULONG(pdwVirtAddress, pAccessIn->dwContent);
                                                        pAccessOut->dwContent = READ_REGISTER_ULONG(pdwVirtAddress);
                                                        break;
                                                case LCR_OR:
                                                        dwDummy  = READ_REGISTER_ULONG(pdwVirtAddress);
                                                        dwDummy |= pAccessIn->dwContent;
                                                        WRITE_REGISTER_ULONG(pdwVirtAddress, dwDummy);
                                                        pAccessOut->dwContent = READ_REGISTER_ULONG(pdwVirtAddress);
                                                        break;
                                                case LCR_AND:
                                                        dwDummy  = READ_REGISTER_ULONG(pdwVirtAddress);
                                                        dwDummy &= pAccessIn->dwContent;
                                                        WRITE_REGISTER_ULONG(pdwVirtAddress, dwDummy);
                                                        pAccessOut->dwContent = READ_REGISTER_ULONG(pdwVirtAddress);
                                                        break;
                                                case LCR_WRITE_ONLY:
                                                        WRITE_REGISTER_ULONG(pdwVirtAddress, pAccessIn->dwContent);
                                                        break;
                                                case LCR_READ:
                                                        pAccessOut->dwContent = READ_REGISTER_ULONG(pdwVirtAddress);
                                                        break;

                                                default: Status = STATUS_ILLEGAL_INSTRUCTION;
                                        }
                                }
                        }

                        // 2nd part: short word accesses
                        if (pAccessIn->bBytesLane == WORD_ACCESS)
                        {
                                if (pAccessIn->wRegisterAddress & 0x0001)
                                        Status = STATUS_INSTRUCTION_MISALIGNMENT;
                                else
                                {
                                        USHORT      *pwVirtAddress;
                                        USHORT          wDummy;

                                        pwVirtAddress = (USHORT *)((ULONG)pciada->pvVirtLcr + pAccessIn->wRegisterAddress);

                                        switch (pAccessIn->bAccessMode)
                                        {
                                                case LCR_WRITE:
                                                        WRITE_REGISTER_USHORT(pwVirtAddress, (USHORT)pAccessIn->dwContent);
                                                        pAccessOut->dwContent = READ_REGISTER_USHORT(pwVirtAddress);
                                                        break;
                                                case LCR_OR:
                                                        wDummy  = READ_REGISTER_USHORT(pwVirtAddress);
                                                        wDummy |= (USHORT)pAccessIn->dwContent;
                                                        WRITE_REGISTER_USHORT(pwVirtAddress, wDummy);
                                                        pAccessOut->dwContent = READ_REGISTER_USHORT(pwVirtAddress);
                                                        break;
                                                case LCR_AND:
                                                        wDummy  = READ_REGISTER_USHORT(pwVirtAddress);
                                                        wDummy &= (USHORT)pAccessIn->dwContent;
                                                        WRITE_REGISTER_USHORT(pwVirtAddress, wDummy);
                                                        pAccessOut->dwContent = READ_REGISTER_USHORT(pwVirtAddress);
                                                        break;
                                                case LCR_WRITE_ONLY:
                                                        WRITE_REGISTER_USHORT(pwVirtAddress, (USHORT)pAccessIn->dwContent);
                                                        break;
                                                case LCR_READ:
                                                        pAccessOut->dwContent = READ_REGISTER_USHORT(pwVirtAddress);
                                                        break;

                                                default: Status = STATUS_ILLEGAL_INSTRUCTION;
                                                        break;
                                        }
                                }
                        }

                        // 3rd part: check illegal byte lanes
                        if (!((pAccessIn->bBytesLane == LONG_ACCESS) || (pAccessIn->bBytesLane == WORD_ACCESS)))
                                Status = STATUS_ILLEGAL_INSTRUCTION;
                }
                else
                        Status = STATUS_ILLEGAL_INSTRUCTION;
        }      
        // do here in between what has to be done end -------------

        if (Status == STATUS_SUCCESS)          
                irp_info = sizeof(PCICC32_LCR_ACCESS);

        COMPLETE_REQUEST;

    KdPrint(("ioctl_access_lcr(), Status = 0x%08x\n", Status));

        return Status;
}


//------------------------------------------------------------------------
// the ultimate jumptable for ioctl
//
NTSTATUS (*ioctl[])(PDEVICE_OBJECT device_Obj, PIRP Irp, PIO_STACK_LOCATION IrpStack) =  
{
        ioctl_dummy,                                    // 0
        ioctl_dummy,                                    // 4
        ioctl_get_status,                               // 8
        ioctl_clear_status,                             // 0x0c
        ioctl_access_para,                              // 0x10
        ioctl_control_interrupts,       // 0x14
        ioctl_dummy,                    // 0x18
        ioctl_irq_status,               // 0x1c
        ioctl_access_lcr                // 0x20
};