/*
 * ----------------------------------------------------------------------------
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions, and the following disclaimer,
 *    without modification.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 * 3. The name of the author may not be used to endorse or promote products
 *    derived from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR
 * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY.
 *
 * ----------------------------------------------------------------------------
 *
 * Alternatively, this software may be distributed under the terms of the
 * terms of the GNU General Public License version 2 as published by the
 * Free Software Foundation.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
 * for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
 *
 * ----------------------------------------------------------------------------
 *
 * For history of changes, ChangeLog.txt
 *
 * This header file describes the register of the Quantis PCI card. The code has been adapted from
 * a microsoft provided sample file.
 *
 Module Name:

    Transfer.c - Do all the moves of data to/from the Quantis card.

Environment:

    Kernel mode
 */


#include "CommonWindows.h"

QUANTIS_EXTERN_C_START

#include "Quantis.h"
#include "QuantisOp.h"

/* Be careful!!!
   All the code in this file seems to be run at IRQL >=2. Be careful with synchronisation.
*/

#include "Transfer.tmh" //auto generated by WPP

/* ---------------------------------------------------------------------------------------------------------------
                                            Global functions
   ---------------------------------------------------------------------------------------------------------------*/   
BOOLEAN
QuantisEvtInterruptIsr(
    __in WDFINTERRUPT Interrupt,
    __in ULONG        MessageID
    )
/*++

Routine Description:

    This routine assumes that only a single I/O can be completed at a
    time on the hardware (i.e. at most one I/O completed per interrupt).

Arguments:

    Interupt - Address of the framework interrupt object
    MessageID -

Return Value:

    TRUE - Interrupt belongs to this device.

--*/
{
    PQUANTIS_DEVICE_EXTENSION   devExt = NULL;
    WDFDEVICE                hDevice;

    union {
        QuantisConfReg ulong;
        INTCSR_REG bits;
    } intcsr /*interrupt status */, mcsr /*Interrupt mask */;

    UNREFERENCED_PARAMETER( MessageID );

    hDevice = WdfInterruptGetDevice(Interrupt);
    devExt = QuantisGetDevExt(hDevice);

    //
    // Read interrupt control/status register and see if an interrupt is pending.
    // If not, return FALSE immediately.
    //
    intcsr.ulong = quantis_reg_get(devExt,&(devExt->Regs->IR_SR));

    if ((intcsr.ulong & IS_QUANTIS_INTERRUPT) == 0) {
        return FALSE;
    }

    //
    // Check if there is a current Request.  If not, then this interrupt cannot
    // do anything.  This driver design requires an I/O to be pending in order
    // to queue the DPC.  If there is no I/O current, then there is no need
    // to have a DPC queued.  This driver also assumes one I/O per interrupt.
    //
    // IMPORTANT: Before returning TRUE, the interrupt must have been cleared
    // on the device or the system will hang trying to service this level
    // sensitive interrupt.
    //
    if (!devExt->CurrentRequest) {
	    KdPrint(("Hardware generated interrupt with no request pending.\n"));
        DoTraceMessage(QUANTIS_WARNING,"Hardware generated interrupt with no request pending");
        return TRUE;
    }

    //
    // Request the DPC to complete the transfer.
    //
    WdfInterruptQueueDpcForIsr( Interrupt );

    //
    // Indicate that this adapter was interrupting.
    //
    return TRUE;
}


VOID
QuantisEvtInterruptDpc(
    __in WDFINTERRUPT WdfInterrupt,
    __in WDFOBJECT    WdfDevice
    )
/*++

Routine Description:

    DPC callback for ISR.

Arguments:

    WdfInterrupt - Handle to the framework interrupt object

    WdfDevice - Associated device object.

Return Value:

--*/
{
    PQUANTIS_DEVICE_EXTENSION   devExt;
    WDFREQUEST               request;
    NTSTATUS                 status=STATUS_SUCCESS;
    size_t                   transferred=0;
    BOOLEAN                  transactionComplete;

    UNREFERENCED_PARAMETER( WdfInterrupt );

    devExt = QuantisGetDevExt(WdfDevice);

    //
    // Retrieve request.
    //
    request  = devExt->CurrentRequest;

    //
    // Check to see if the request is cancelled by the system. While
    // we are DMAing a large buffer into multiple transaction,
    // there is good possibilty for the request to get cancelled because
    // the originator of the request exited or cancelled the I/O explicitly.
    //
    if(WdfRequestIsCanceled(request)) {
	    KdPrint(("Aborted random number generation transaction 0x%p\n",  request));
        DoTraceMessage(QUANTIS_WARNING,"Aborted random number generation transaction 0x%p",  request);
        devExt->CurrentRequest = NULL;
        WdfRequestComplete(request, STATUS_CANCELLED);
        return;
    }
    //
    // Clean-up for this request.
    //
    devExt->CurrentRequest = NULL;

    //
    // Complete this IO request.
    //
    WdfRequestCompleteWithInformation( request,
                                       status,
                                       (NT_SUCCESS(status)) ?
                                       transferred : 0 );
}

VOID
QuantisEvtIoDeviceControl(
    __in WDFQUEUE      Queue,
    __in WDFREQUEST    Request,
    __in size_t         OutputBufferLength,
    __in size_t         InputBufferLength,
    __in ULONG         IoControlCode
    )
/*++
Routine Description:

    This event is called when the framework receives IRP_MJ_DEVICE_CONTROL
    requests from the system.

Arguments:

    Queue - Handle to the framework queue object that is associated
            with the I/O request.
    Request - Handle to a framework request object.

    OutputBufferLength - length of the request's output buffer,
                        if an output buffer is available.
    InputBufferLength - length of the request's input buffer,
                        if an input buffer is available.

    IoControlCode - the driver-defined or system-defined I/O control code
                    (IOCTL) that is associated with the request.
Return Value:

    VOID

--*/
{
    PQUANTIS_DEVICE_EXTENSION   devExt;
    size_t                      length = 0;
    NTSTATUS                    status = STATUS_SUCCESS;
    PVOID                       buffer = NULL;
    WDFDRIVER                   Driver;
    PQUANTIS_DRIVER_EXTENSION   drvExt;
    unsigned int                NbCards=0;

    UNREFERENCED_PARAMETER( InputBufferLength  );
    UNREFERENCED_PARAMETER( OutputBufferLength  );

    //
    // Get the device extension.
    //
    devExt = QuantisGetDevExt(WdfIoQueueGetDevice( Queue ));

	KdPrint(("Ioctl code %lu\n",IoControlCode));
	
    //
    // Handle this request's specific code.
    //
    switch (IoControlCode) {

    //IOCTL to get info
    case QUANTIS_IOCTL_GET_CARD_COUNT:  // code == 0x800
    case QUANTIS_IOCTL_GET_MODULES_MASK:   // code == 0x801
    case QUANTIS_IOCTL_GET_BOARD_VERSION:  // code == 0x802
    case QUANTIS_IOCTL_GET_MODULES_STATUS: // code == 0x807
    case QUANTIS_IOCTL_GET_DRIVER_VERSION: // code == 0x808
    {

        KdPrint(("Before requesting Buffer.\n"));
        status = WdfRequestRetrieveOutputBuffer(Request, sizeof(QuantisConfReg), &buffer, &length);
        if( !NT_SUCCESS(status)) {
		    KdPrint(("WdfRequestRetrieveOutputBuffer failed %!STATUS!\n", status));
            DoTraceMessage(QUANTIS_ERROR,"WdfRequestRetrieveOutputBuffer failed %!STATUS!\n", status);
            break;
        }

        switch (IoControlCode) {
             case QUANTIS_IOCTL_GET_CARD_COUNT:  // code == 0x800
                  if ((Driver=WdfGetDriver()) != NULL){
                    drvExt=QuantisGetDrvExt(Driver);
                    /* The reading of the number of cards should be protected by a spin
                       lock since this code executes at IRQL 2 */
                    WdfSpinLockAcquire(drvExt->NbCardsLock);
                    NbCards=(QuantisConfReg)(drvExt->NumberOfCards);
                    WdfSpinLockRelease(drvExt->NbCardsLock);
                  }
                  else{
                    KdPrint(("Cannot get the driver handle\n"));
                    DoTraceMessage(QUANTIS_ERROR,"Can not get the Wdf Driver handle.\n");
                  }
                  * (PQuantisConfReg)buffer= (QuantisConfReg)NbCards;
				  KdPrint(("There is %d Quantis cards.\n",NbCards));
                  break;
             case QUANTIS_IOCTL_GET_MODULES_MASK:   // code == 0x801
			      * (PQuantisConfReg)buffer = quantis_rng_modules_mask(devExt);
				  break;
             case QUANTIS_IOCTL_GET_MODULES_STATUS: // code == 0x807
                  * (PQuantisConfReg)buffer = quantis_rng_modules_status(devExt);
                  break;
             case QUANTIS_IOCTL_GET_BOARD_VERSION:  // code == 0x802
                  * (PQuantisConfReg)buffer = quantis_rng_version(devExt);
                   break;
             case QUANTIS_IOCTL_GET_DRIVER_VERSION: // code == 0x808
                  * (PQuantisConfReg)buffer = (QuantisConfReg)QUANTIS_PCI_DRIVER_VERSION;
                  break;
             default:
                  // should normally never happen!
                  * (PQuantisConfReg)buffer=(QuantisConfReg)0;
                  break;
        }
        status = STATUS_SUCCESS;
        length = sizeof(QuantisConfReg);
        break;
    }

    //Ioctl to set info
    case QUANTIS_IOCTL_ENABLE_MODULE: // code == 0x804
    case QUANTIS_IOCTL_DISABLE_MODULE: // code == 0x805
    //case QUANTIS_IOCTL_SET_DEBUG_LEVEL: // code == 0x806
    {
        status = WdfRequestRetrieveInputBuffer(Request, sizeof(QuantisConfReg), &buffer, &length);
        if( !NT_SUCCESS(status)) {
		    KdPrint(("WdfRequestRetrieveInputBuffer failed %!STATUS!\n", status));
            DoTraceMessage(QUANTIS_ERROR,"WdfRequestRetrieveInputBuffer failed %!STATUS!\n", status);
            break;
        }
      
        switch (IoControlCode) {
             case QUANTIS_IOCTL_ENABLE_MODULE: // code == 0x804
                  devExt->ModulesStatusMask=(ModuleMask_t)(*(PQuantisConfReg) buffer);
                  quantis_rng_enable_modules(devExt,devExt->ModulesStatusMask);
                  break;
             case QUANTIS_IOCTL_DISABLE_MODULE: // code == 0x805
                  devExt->ModulesStatusMask=(ModuleMask_t)(*(PQuantisConfReg) buffer);
                  quantis_rng_disable_modules(devExt,devExt->ModulesStatusMask);
                  break;
/* No more used
            case QUANTIS_IOCTL_SET_DEBUG_LEVEL: // code == 0x806
                     Not used on Windows since all messages (Debug, Info, Warning, Error)
				     are always emitted. The windows system itself manages the flow of
					 messages in order to keep only the message for which a consumer exists.
					 When defining a consumer, one can specify the level of the messages to be kept.
                  break;
*/
             default:
                  // should normally never happen!
                  break;
        }
        status = STATUS_SUCCESS;
        length = sizeof(QuantisConfReg);
        break;
    }

    //Ioctl to trigger action (without using any data)
    case QUANTIS_IOCTL_RESET_BOARD:               // code == 0x803
        quantis_rng_reset(devExt);
        break;
    default:
        status = STATUS_INVALID_DEVICE_REQUEST;
        break;
    }

    if (status != STATUS_PENDING) {

        WdfRequestCompleteWithInformation(Request, status, length);
    }
}

VOID
QuantisEvtIoRead (
    WDFQUEUE      Queue,
    WDFREQUEST    Request,
    size_t        Length
    )
/*++

Routine Description:

    Performs read from the Quantis device. This event is called when the
    framework receives IRP_MJ_READ requests.

Arguments:

    Queue -  Handle to the framework queue object that is associated with the
             I/O request.
    Request - Handle to a framework request object.

    Length - Length of the data buffer associated with the request.
                 By default, the queue does not dispatch
                 zero length read & write requests to the driver and instead to
                 complete such requests with status success. So we will never get
                 a zero length request.

Return Value:

  None.

--*/
{
    NTSTATUS    status;
    PVOID RequestBuffer;  //Will contain the address of the destination buffer.
    PQUANTIS_DEVICE_EXTENSION devExt;
    int bytesCopied=0;
	int i=0;
    size_t SizeAcquired=0;
    unsigned char * Buffer;

    KdPrint(("QuantisEvtIoRead: Request: 0x%p, Queue: 0x%p, Length of data:%d\n",
                   Request, Queue,Length));
    DoTraceMessage(QUANTIS_DEBUG,"QuantisEvtIoRead: Request: 0x%p, Queue: 0x%p, Length of data:%d\n",
                   Request, Queue,Length);

    //
    // Get the device extension.
    //
    devExt = QuantisGetDevExt(WdfIoQueueGetDevice( Queue ));

    
    //
    // Get the request memory and perform read operation here
    //
    status = WdfRequestRetrieveOutputBuffer(Request, (size_t)Length, &RequestBuffer, &SizeAcquired);
    if(NT_SUCCESS(status) ) {
        bytesCopied=quantis_rng_read(devExt,(unsigned char*)RequestBuffer,Length);
        if (bytesCopied<0){
           status=STATUS_TIMEOUT;
           bytesCopied=0;
        }
		else{
		    KdPrint(("The read random numbers read are (%d):\n",bytesCopied));
            DoTraceMessage(QUANTIS_DEBUG,"The random numbers read are:\n");
			i=0;
            Buffer=(unsigned char *)RequestBuffer;
			while (i<bytesCopied){
              KdPrint(("Number %d = %x\n",i,(unsigned char)Buffer[i]));
              DoTraceMessage(QUANTIS_DEBUG,"Number %d = %x\n",i,(unsigned char)Buffer[i]);
              i++;
			}
		}
    }

    WdfRequestCompleteWithInformation(Request, status, bytesCopied);
}
QUANTIS_EXTERN_C_END
