/********************************************************************/
/**               Copyright(c) 1989 Microsoft Corporation.	   **/
/********************************************************************/

//***
//
// Filename:	ioctl.c
//
// Description: This module contains wrappers around the actual ioctl
//		call to the kernel mode FSD.
//
// History:
//	May 11,1992.	NarenG		Created original version.
//	
//	For Enums the FSD should use zero-based indexing.
//

#include "afpsvcp.h"

//**
//
// Call:	AfpServerIOCtrl
//
// Returns:	NO_ERROR	- success
//		non-zero returns from AfpFSDIOControl
//		ERROR_INVALID_PARAMETER 	
//
// Description: This procedure is a wrapper around the I/O control to the
//	 	AFP kernel mode FSD. It unmarshals the information in the
//		AFP_REQUEST_PACKET calls the driver and then marshalls the
//		returned information back into the AFP_REQUEST_PACKET and
//		returns.
//		
//		NOTE: This should never be called directly for Enum and
//		      GetInfo type requests. AfpServerIOCtrlGetInfo should
//		      be called. It will take care of buffer manipulation.
//
DWORD
AfpServerIOCtrl(
	IN PAFP_REQUEST_PACKET pAfpSrp
)
{
DWORD		cbBytesReturned;
PVOID		pInputBuffer       = NULL;
PVOID		pOutputBuffer      = NULL;
DWORD		cbInputBufferSize  = 0;
DWORD		cbOutputBufferSize = 0;
DWORD		dwRetCode;

    // Set up the input and output buffers depending on the type of operation
    //
    switch( pAfpSrp->dwApiType ) {
	
    // No input or output buffers requred for this type of API
    //
    case AFP_API_TYPE_COMMAND:
	break;

    // Input buffer contains information to be set.
    // No output buffer required.
    //
    case AFP_API_TYPE_SETINFO:

	pInputBuffer      = pAfpSrp->Type.SetInfo.pInputBuf;
	cbInputBufferSize = pAfpSrp->Type.SetInfo.cbInputBufSize;

        ((PSETINFOREQPKT)pInputBuffer)->sirqp_parmnum =
					pAfpSrp->Type.SetInfo.dwParmNum;
	break;

    case AFP_API_TYPE_ADD:

	pInputBuffer      = pAfpSrp->Type.Add.pInputBuf;
	cbInputBufferSize = pAfpSrp->Type.Add.cbInputBufSize;

	break;

    case AFP_API_TYPE_DELETE:

	pInputBuffer      = pAfpSrp->Type.Delete.pInputBuf;
	cbInputBufferSize = pAfpSrp->Type.Delete.cbInputBufSize;

	break;

    // Input buffer contains resume handle
    // Output buffer needed to hold returned data
    //
    case AFP_API_TYPE_ENUM:

        pInputBuffer       = (PVOID)&( pAfpSrp->Type.Enum.EnumRequestPkt );
	cbInputBufferSize  = sizeof( pAfpSrp->Type.Enum.EnumRequestPkt );

	pOutputBuffer      = pAfpSrp->Type.Enum.pOutputBuf;
	cbOutputBufferSize = pAfpSrp->Type.Enum.cbOutputBufSize;

	break;

    // Input buffer contains information regarding the entity for
    // which information is requested.
    // Output buffer will contain information regarding that entity.
    //
    case AFP_API_TYPE_GETINFO:

        pInputBuffer       = pAfpSrp->Type.GetInfo.pInputBuf;
	cbInputBufferSize  = pAfpSrp->Type.GetInfo.cbInputBufSize;

	pOutputBuffer      = pAfpSrp->Type.GetInfo.pOutputBuf;
	cbOutputBufferSize = pAfpSrp->Type.GetInfo.cbOutputBufSize;

	break;

    default:
	return( ERROR_INVALID_PARAMETER );

    }
	
    dwRetCode = AfpFSDIOControl( AfpGlobals.hFSD,
				 pAfpSrp->dwRequestCode,
			   	 pInputBuffer,
			         cbInputBufferSize,
			         pOutputBuffer,
			         cbOutputBufferSize,
				 &cbBytesReturned
				);

    if ( (dwRetCode != ERROR_MORE_DATA) && (dwRetCode != NO_ERROR) )
	return( dwRetCode );

    // If API was of Enum type store the Total number of entries read,
    // total number of available entries and resumable handle into the
    // Srp
    //
    if ( pAfpSrp->dwApiType == AFP_API_TYPE_ENUM ) {

	pAfpSrp->Type.Enum.dwEntriesRead =
				((PENUMRESPPKT)pOutputBuffer)->ersp_cInBuf;
	pAfpSrp->Type.Enum.dwTotalAvail =
				((PENUMRESPPKT)pOutputBuffer)->ersp_cTotEnts;
	pAfpSrp->Type.Enum.EnumRequestPkt.erqp_Index =
				((PENUMRESPPKT)pOutputBuffer)->ersp_hResume;

	// Shift the data to the start of the buffer, over-writing the
	// enum reponse information.
	//
	CopyMemory( pOutputBuffer,
		    (PVOID)((ULONG_PTR)pOutputBuffer+sizeof(ENUMRESPPKT)),
		    cbBytesReturned - sizeof(ENUMRESPPKT) );
    }

    // If API type was GetInfo, store the Total number of bytes available
    // and the total number of bytes read.
    //
    if ( pAfpSrp->dwApiType == AFP_API_TYPE_GETINFO )
	pAfpSrp->Type.GetInfo.cbTotalBytesAvail = cbBytesReturned;

    return( dwRetCode );
}

//**
//
// Call:	AfpServerIOCtrlGetInfo
//
// Returns:	NO_ERROR		- success
//		non-zero returns from DeviceIOCtrl
//		Non-zero returns from CreateEvent
//		ERROR_NOT_ENOUGH_MEMORY
//		ERROR_INVALID_PARAMETER 	
//
// Description: This is a wrapper around the AfpServerIOCtrl call for GetInfo
//		and Enum type calls that can return variable amounts of data.
//
//		For Enum calls, if AfpSrp.Enum.dwOutputBufSize == -1 it will
//		allocate and return ALL information that is available.
//		Otherwise it will allocate and return as much data that can
//		be contained in the AfpSrp.Enum.dwOutputBufSize parameter. The
//		caller of this procedure will set the value in
//		AfpSrp.Enum.dwOutputBufSize to be equal to the value of
//		MaxPreferredLength which was set by the caller of the Enum or
//		GetInfo API.
//
//		For GetInfo type calls AfpSrp.Enum.dwOutputBufSize == -1 always,
//		so this routine will always try to get ALL the available
//		information.
//
DWORD
AfpServerIOCtrlGetInfo(
	IN OUT PAFP_REQUEST_PACKET pAfpSrp
)
{
DWORD		dwRetCode;
BOOL		fGetEverything = FALSE;
PVOID		pOutputBuf;

    // Set up the output buffers
    //
    switch( pAfpSrp->dwApiType ) {
	
    case AFP_API_TYPE_ENUM:

	// Find out how much data the client wants.
  	//
 	if ( pAfpSrp->Type.Enum.cbOutputBufSize == -1 ) {

	    // Client wants everything, so allocate a default size buffer
	    //
	    pAfpSrp->Type.Enum.cbOutputBufSize = AFP_INITIAL_BUFFER_SIZE +
					         sizeof(ENUMRESPPKT);
	    fGetEverything = TRUE;

	}
	else {
	
	    // Otherwise just allocate enough for what the client wants
	    //
	    pAfpSrp->Type.Enum.cbOutputBufSize += sizeof(ENUMRESPPKT);
	}

	pOutputBuf = MIDL_user_allocate( pAfpSrp->Type.Enum.cbOutputBufSize );

	if ( pOutputBuf == NULL )
	    return( ERROR_NOT_ENOUGH_MEMORY );
	
	pAfpSrp->Type.Enum.pOutputBuf = pOutputBuf;

	break;

    case AFP_API_TYPE_GETINFO:

	// Client will ALWAYS want everything
	//
	pAfpSrp->Type.GetInfo.cbOutputBufSize = AFP_INITIAL_BUFFER_SIZE;

	pOutputBuf = MIDL_user_allocate(pAfpSrp->Type.GetInfo.cbOutputBufSize);

	if ( pOutputBuf == NULL )
	    return( ERROR_NOT_ENOUGH_MEMORY );

	pAfpSrp->Type.GetInfo.pOutputBuf = pOutputBuf;

	fGetEverything = TRUE;

    	break;

    default:
	return( ERROR_INVALID_PARAMETER );

    }

    // Make the IOCTL to the FSD
    //
    dwRetCode = AfpServerIOCtrl( pAfpSrp );

    if ( (dwRetCode != NO_ERROR) && (dwRetCode != ERROR_MORE_DATA) ) {
     	MIDL_user_free( pOutputBuf );
        return( dwRetCode );
    }
	
    // If we have obtained all requested data then we are done
    //
    if ( !(( dwRetCode == ERROR_MORE_DATA ) && fGetEverything ))
        return( dwRetCode );

    // Otherwise the client wants more data and there is more to be obtained
    //
    if ( pAfpSrp->dwApiType == AFP_API_TYPE_ENUM ) {

	// Increase the buffer size using a heuristic.
	//
	MIDL_user_free( pOutputBuf );
	pAfpSrp->Type.Enum.cbOutputBufSize = pAfpSrp->Type.Enum.dwTotalAvail
					   * AFP_AVG_STRUCT_SIZE
			  		   + AFP_INITIAL_BUFFER_SIZE
					   + sizeof(ENUMRESPPKT);

	pOutputBuf = MIDL_user_allocate( pAfpSrp->Type.Enum.cbOutputBufSize );

	if ( pOutputBuf == NULL )
	    return( ERROR_NOT_ENOUGH_MEMORY );
		
	pAfpSrp->Type.Enum.pOutputBuf = pOutputBuf;

	// If we are trying to get all the information then we reset the
	// resume handle to 0
	//
	if ( fGetEverything )
	    pAfpSrp->Type.Enum.EnumRequestPkt.erqp_Index = 0;
    }


    if ( pAfpSrp->dwApiType == AFP_API_TYPE_GETINFO ) {

    	// Increase the buffer size using the total available number
	// of bytes + Fudge Factor.
	//
	MIDL_user_free( pOutputBuf );

	pAfpSrp->Type.GetInfo.cbOutputBufSize =
			     pAfpSrp->Type.GetInfo.cbTotalBytesAvail +
			     AFP_INITIAL_BUFFER_SIZE;

	pOutputBuf=MIDL_user_allocate( pAfpSrp->Type.GetInfo.cbOutputBufSize );

	if ( pOutputBuf == NULL )
	    return( ERROR_NOT_ENOUGH_MEMORY );
		
	pAfpSrp->Type.GetInfo.pOutputBuf = pOutputBuf;
    }

	
    // Make the IOCTL to the FSD, if we dont get all the data this time
    // we give up and return to the caller.
    //
    dwRetCode = AfpServerIOCtrl( pAfpSrp );
	
    if ( (dwRetCode != NO_ERROR) && (dwRetCode != ERROR_MORE_DATA) )
        MIDL_user_free( pOutputBuf );

    return( dwRetCode );
		
}