/*****************************************************************************
 *
 *  Hel.c
 *
 *  Copyright (c) 1996 Microsoft Corporation.  All Rights Reserved.
 *
 *  Abstract:
 *
 *      Hardware emulation layer calls. Used to provide common functionality
 *      for built-in device types we support ( WDM, serial and parallel)
 *      While access to DCB could be built as internal COM object , it does not make
 *      much sense to invest in it, because DCBs are exclusively owned and not shared
 *      between application objects or different applications. We also want to minimize
 *      any overhead when talking to raw device interface.
 *
 *      Note1: We don't deal at this level with access control, lower level drivers are supposed
 *      to take care of this. Queuing of requests for non-reentrant devices is also not done here.
 *      This Hel is basically thin layer of imaging device primitives, used only to isolate
 *      command translator from actual hardware.
 *
 *      Note2: Hel is not made extensible . If command translator needs to talk to non-supported
 *      device, it will need to establish direct link to it. There is no requirement to use
 *      Hel , it is service we provide to conformant devices.
 *
 *  Contents:
 *
 *      Hel_DcbNew
 *      Hel_DcbDelete
 *
 *      Hel_WDMInitialize
 *      Hel_WDMRawReadData
 *      Hel_WDMRawWriteData
 *      Hel_WDMRawReadCOmmand
 *      Hel_WDMRawWriteCommand
 *      Hel_WDMGetLastError
 *
 *      Hel_ParallelRawReadData
 *      Hel_ParallelRawWriteData
 *      Hel_ParallelRawReadCOmmand
 *      Hel_ParallelRawWriteCommand
 *      Hel_ParallelGetLastError
 *
 *      Hel_SerialRawReadData
 *      Hel_SerialRawWriteData
 *      Hel_SerialRawReadCOmmand
 *      Hel_SerialRawWriteCommand
 *      Hel_SerialGetLastError
 *
 *
 *****************************************************************************/

#include "pch.h"

#define DbgFl DbgFlDevice



/*****************************************************************************
 *
 *  @doc    INTERNAL
 *
 *  @func  HRESULT | Hel_DcbNew |
 *
 *          Creates and initializes DCB for given device.
 *
 *****************************************************************************/
STDMETHODIMP
Hel_DcbNew(
    DWORD   dwDeviceType,
    LPCWSTR pwszPortName,
    DWORD   dwFlags,
    PSTIDCB *ppStiDCB
    )
{

    HRESULT hres;
    PSTIDCB pDcb;

    EnterProc(Hel_DcbNew,(_ "xpp", dwDeviceType,pwszPortName,ppStiDCB));

    // Validate device type
    #ifdef DEBUG

    switch (dwDeviceType) {
        case HEL_DEVICE_TYPE_WDM:
            // Validate string
            if (!pwszPortName || !*pwszPortName) {
                ValidateF(0,("Invalid device name passed to DcbNew"));
            }
            break;
        case HEL_DEVICE_TYPE_PARALLEL:
            break;
        case HEL_DEVICE_TYPE_SERIAL:
            break;
        default:
            ValidateF(0,("Invalid dwvice type passed to DcbNew"));
            return STIERR_INVALID_PARAM;
    }

    hres = hresFullValidPdwOut(ppStiDCB,3);

    #endif

    //
    // Allocate DCB from the heap
    //
    hres = AllocCbPpv(sizeof(STIDCB), ppStiDCB);

    if (SUCCEEDED(hres)) {

        //
        // Initialize common fields ( note that heap block is assumed to be zero inited)
        //

        pDcb = *ppStiDCB;

        pDcb->dwDeviceType = dwDeviceType;
        OSUtil_lstrcpyW(pDcb->wszPortName,pwszPortName);
        pDcb-> dwContext = 0L;
        pDcb->dwLastOperationError = NO_ERROR;
        pDcb->hDeviceHandle = INVALID_HANDLE_VALUE;
        pDcb->hDeviceControlHandle = INVALID_HANDLE_VALUE;


        //
        // Now call appropriate initialization routine
        //
        switch (dwDeviceType) {
            case HEL_DEVICE_TYPE_WDM:
                hres =  Hel_WDMInitialize(pDcb,dwFlags);
                break;

            default:
                ValidateF(0,("Invalid device type passed to DcbNew"));
                return STIERR_INVALID_PARAM;
        }

    }


    // If we failed by some reasons , free allocated memory and bail out
    if (!SUCCEEDED(hres)) {
        FreePpv(ppStiDCB);
    }

    ExitOleProc();

    return hres;

}

/*****************************************************************************
 *
 *  @doc    INTERNAL
 *
 *  @func  void | Hel_DcbDelete |
 *
 *          Destroys DCB. It is responsibility of a caller to protect itself from incorrectly
 *          destroying this block
 *
 *****************************************************************************/
STDMETHODIMP
Hel_DcbDelete(
    PSTIDCB pDcb
    )
{

    HRESULT hres = STI_OK;

    // Validate parameters
    if (!pDcb) {
        return STIERR_INVALID_PARAM;
    }

    // Close device handle
    if (pDcb->hDeviceHandle) {
        CloseHandle(pDcb->hDeviceHandle);
    }

    if (pDcb->hDeviceControlHandle ) {
        CloseHandle(pDcb->hDeviceControlHandle );
    }

    // Free DCB and nullify pointer
    FreePv(pDcb);

    return hres;

}

/*****************************************************************************
 *
 *  WDM device specific implementation
 *
 *****************************************************************************/

STDMETHODIMP
Hel_WDMInitialize(
    PSTIDCB     pDcb,
    DWORD       dwFlags
    )
{

    HRESULT hres;
    WCHAR   wszDeviceSymbolicName[MAX_PATH] = {L'\0'};
    //LPSTR   pszAnsiDeviceName;

    //
    // Create symbolic name for the device we are trying to talk to
    // Try to open device data and control handles.
    //

    if (dwFlags & STI_HEL_OPEN_DATA) {

        OSUtil_lstrcatW(wszDeviceSymbolicName,pDcb->wszPortName);

        //
        // For devices with separate channels could open them specially. Kernel mode
        // driver will need to understand convention
        // OSUtil_lstrcatW(wszDeviceSymbolicName,L"\\Data");
        //

        pDcb->hDeviceHandle = OSUtil_CreateFileW(wszDeviceSymbolicName,
                                          GENERIC_READ | GENERIC_WRITE, // Access mask
                                          0,                            // Share mode
                                          NULL,                         // SA
                                          OPEN_EXISTING,                // Create disposition
                                          FILE_ATTRIBUTE_SYSTEM,        // Attributes
                                          NULL                                                          // Template
                                          );
        pDcb->dwLastOperationError = GetLastError();

        if (pDcb->hDeviceHandle != INVALID_HANDLE_VALUE) {

            // Fill function pointers
            pDcb->pfnRawReadData =        Hel_WDMRawReadData;
            pDcb->pfnRawWriteData=        Hel_WDMRawWriteData;
            pDcb->pfnGetLastError=        Hel_WDMGetLastError;

            hres = S_OK;

        }
        else {
            hres = MAKE_HRESULT(SEVERITY_ERROR,FACILITY_WIN32,pDcb->dwLastOperationError);
        }

    }

    //
    // If needed open control handle for the device
    //
    if (SUCCEEDED(hres) && (dwFlags & STI_HEL_OPEN_CONTROL)) {

        OSUtil_lstrcpyW(wszDeviceSymbolicName,REGSTR_PATH_STIDEVICES_W);
        OSUtil_lstrcatW(wszDeviceSymbolicName,L"\\");
        OSUtil_lstrcatW(wszDeviceSymbolicName,pDcb->wszPortName);

        // BUGBUG for devices with separate channels open them specially. Kernel mode
        // driver will need to understand convention
        // OSUtil_lstrcatW(wszDeviceSymbolicName,L"\\Control");

        pDcb->hDeviceControlHandle = OSUtil_CreateFileW(wszDeviceSymbolicName,
                                                 GENERIC_READ | GENERIC_WRITE,  // Access mask
                                                 0,                             // Share mode
                                                 NULL,                          // SA
                                                 OPEN_EXISTING,                 // Create disposition
                                                 FILE_ATTRIBUTE_SYSTEM,         // Attributes
                                                 NULL                                                   // Template
                                                 );
        pDcb->dwLastOperationError = GetLastError();

        if (pDcb->hDeviceControlHandle != INVALID_HANDLE_VALUE) {

            // Fill function pointers
            pDcb->pfnRawReadCommand=      Hel_WDMRawReadCommand;
            pDcb->pfnRawWriteCommand=     Hel_WDMRawWriteCommand;

            hres = S_OK;

        }
        else {
            hres = MAKE_HRESULT(SEVERITY_ERROR,FACILITY_WIN32,pDcb->dwLastOperationError);
        }

    }

    return hres;

}

STDMETHODIMP
Hel_WDMRawReadData(
    PSTIDCB     pDcb,
    LPVOID      lpBuffer,
    LPDWORD     lpdwNumberOfBytes,
    LPOVERLAPPED lpOverlapped
    )
{

    HRESULT hres;

    DWORD   dwBytesReturned=0;
    BOOL    fRet;

    EnterProc(Hel_WDMRawReadData, (_ "pppp",pDcb,lpBuffer,lpdwNumberOfBytes,lpOverlapped));

    // Validate parameters here
    if (SUCCEEDED(hres = hresFullValidReadPvCb(lpdwNumberOfBytes, 4, 3)) &&
        SUCCEEDED(hres = hresFullValidReadPvCb(lpBuffer,*lpdwNumberOfBytes, 2)) &&
        (!lpOverlapped || SUCCEEDED(hres = hresFullValidReadPx(lpOverlapped, OVERLAPPED, 4))) ){

        // Call appropriate entry point
        fRet = ReadFile(pDcb->hDeviceHandle,
                         lpBuffer,
                         *lpdwNumberOfBytes,
                         lpdwNumberOfBytes,
                         lpOverlapped
                         );
        pDcb->dwLastOperationError = GetLastError();

        hres = S_OK;

        if (!fRet) {
            hres =  HRESULT_FROM_WIN32(pDcb->dwLastOperationError);
        }


    } else {
        hres = STIERR_INVALID_PARAM;
    }

    ExitOleProc();
    return hres;

}

STDMETHODIMP
Hel_WDMRawWriteData(
    PSTIDCB     pDcb,
    LPVOID      lpBuffer,
    DWORD       dwNumberOfBytes,
    LPOVERLAPPED lpOverlapped
    )
{
    HRESULT hres;
    BOOL    fRet;
    DWORD   dwReturnedBytes;

    EnterProc(Hel_WDMRawWriteData, (_ "ppup",pDcb,lpBuffer,dwNumberOfBytes,lpOverlapped));

    // Validate parameters here

    hres = STIERR_INVALID_PARAM;

    if (SUCCEEDED(hres = hresFullValidReadPvCb(lpBuffer,dwNumberOfBytes, 2)) ) {
        if (!lpOverlapped || SUCCEEDED(hres = hresFullValidReadPx(lpOverlapped, OVERLAPPED, 4)) ){

            // Call appropriate entry point
            fRet = WriteFile(pDcb->hDeviceHandle,
                             lpBuffer,
                             dwNumberOfBytes,
                             &dwReturnedBytes,
                             lpOverlapped
                             );
            pDcb->dwLastOperationError = GetLastError();

            hres = S_OK;

            if (!fRet) {
                hres =  HRESULT_FROM_WIN32(pDcb->dwLastOperationError);
            }
        }
    }

    ExitOleProc();

    return hres;
}

STDMETHODIMP
Hel_WDMRawReadCommand(
    PSTIDCB     pDcb,
    LPVOID      lpBuffer,
    LPDWORD     lpdwNumberOfBytes,
    LPOVERLAPPED lpOverlapped
    )
{
    HRESULT hres;

    BOOL    fRet;

    EnterProc(Hel_WDMRawReadCommand, (_ "pppp",pDcb,lpBuffer,lpdwNumberOfBytes,lpOverlapped));

    // Validate parameters here
    if (SUCCEEDED(hres = hresFullValidReadPvCb(lpdwNumberOfBytes, 4, 3)) &&
        SUCCEEDED(hres = hresFullValidReadPvCb(lpBuffer,*lpdwNumberOfBytes, 2)) &&
        SUCCEEDED(hres = hresFullValidReadPx(lpOverlapped, OVERLAPPED, 4)) ){

        // Call appropriate entry point
        fRet = ReadFile(pDcb->hDeviceControlHandle,
                         lpBuffer,
                         *lpdwNumberOfBytes,
                         lpdwNumberOfBytes,
                         lpOverlapped
                         );
        pDcb->dwLastOperationError = GetLastError();

        hres = S_OK;

        if (!fRet) {
            hres =  HRESULT_FROM_WIN32(pDcb->dwLastOperationError);
        }

    } else {
        hres = STIERR_INVALID_PARAM;
    }

    ExitOleProc();

    return hres;
}

STDMETHODIMP
Hel_WDMRawWriteCommand(
    PSTIDCB     pDcb,
    LPVOID      lpBuffer,
    DWORD       dwNumberOfBytes,
    LPOVERLAPPED lpOverlapped
    )
{
    HRESULT hres;

    BOOL    fRet;
    DWORD   dwReturnedBytes;

    EnterProc(Hel_WDMRawWriteCommand, (_ "ppup",pDcb,lpBuffer,dwNumberOfBytes,lpOverlapped));

    // Validate parameters here
    if (SUCCEEDED(hres = hresFullValidReadPvCb(lpBuffer,dwNumberOfBytes, 2)) &&
        SUCCEEDED(hres = hresFullValidReadPx(lpOverlapped, OVERLAPPED, 4)) ){

        // Call appropriate entry point
        fRet = WriteFile(pDcb->hDeviceControlHandle,
                         lpBuffer,
                         dwNumberOfBytes,
                         &dwReturnedBytes,
                         lpOverlapped
                         );
        pDcb->dwLastOperationError = GetLastError();

        hres = S_OK;

        if (!fRet) {
            hres =  HRESULT_FROM_WIN32(pDcb->dwLastOperationError);
        }

    } else {
        hres = STIERR_INVALID_PARAM;
    }

    ExitOleProc();
    return hres;
}

STDMETHODIMP
Hel_WDMGetLastError(
    PSTIDCB     pDcb,
    LPDWORD     lpdwLastError
    )
{

    if (!lpdwLastError) {
        return HRESULT_FROM_WIN32(ERROR_INVALID_PARAMETER);
    }

    *lpdwLastError = pDcb->dwLastOperationError                 ;

    return S_OK;
}


/*****************************************************************************
 *
 *  Parallel port  device specific implementation
 *
 *****************************************************************************/

STDMETHODIMP
Hel_ParallelRawReadData(
    PSTIDCB     pDcb,
    LPVOID      lpBuffer,
    LPDWORD     lpdwNumberOfBytes,
    LPOVERLAPPED lpOverlapped
    )
{
    HRESULT     hres = E_NOTIMPL;
    return hres;

}

STDMETHODIMP
Hel_ParallelRawWriteData(
    PSTIDCB     pDcb,
    LPVOID      lpBuffer,
    DWORD       dwNumberOfBytes,
    LPOVERLAPPED lpOverlapped
    )
{
    HRESULT     hres = E_NOTIMPL;
    return hres;

}

STDMETHODIMP
Hel_ParallelRawReadCommand(
    PSTIDCB     pDcb,
    LPVOID      lpBuffer,
    LPDWORD     lpdwNumberOfBytes,
    LPOVERLAPPED lpOverlapped
    )
{
    HRESULT     hres = E_NOTIMPL;
    return hres;

}

STDMETHODIMP
Hel_ParallelRawWriteCommand(
    PSTIDCB     pDcb,
    LPVOID      lpBuffer,
    DWORD       dwNumberOfBytes,
    LPOVERLAPPED lpOverlapped
    )
{
    HRESULT     hres = E_NOTIMPL;
    return hres;

}

STDMETHODIMP
Hel_ParallelGetLastError(
    PSTIDCB     pDcb,
    LPDWORD     lpdwLastError
    )
{
    HRESULT     hres = E_NOTIMPL;
    return hres;

}

/*****************************************************************************
 *
 *  Serial device specific implementation
 *
 *****************************************************************************/

STDMETHODIMP
Hel_SerialRawReadData(
    PSTIDCB     pDcb,
    LPVOID      lpBuffer,
    LPDWORD     lpdwNumberOfBytes,
    LPOVERLAPPED lpOverlapped
    )
{
    HRESULT     hres = E_NOTIMPL;
    return hres;

}

STDMETHODIMP
Hel_SerialRawWriteData(
    PSTIDCB     pDcb,
    LPVOID      lpBuffer,
    DWORD       dwNumberOfBytes,
    LPOVERLAPPED lpOverlapped
    )
{
    HRESULT     hres = E_NOTIMPL;
    return hres;

}

STDMETHODIMP
Hel_SerialRawReadCommand(
    PSTIDCB     pDcb,
    LPVOID      lpBuffer,
    LPDWORD     lpdwNumberOfBytes,
    LPOVERLAPPED lpOverlapped
    )
{
    HRESULT     hres = E_NOTIMPL;
    return hres;

}

STDMETHODIMP
Hel_SerialRawWriteCommand(
    PSTIDCB     pDcb,
    LPVOID      lpBuffer,
    DWORD       dwNumberOfBytes,
    LPOVERLAPPED lpOverlapped
    )
{
    HRESULT     hres = E_NOTIMPL;
    return hres;
}

STDMETHODIMP
Hel_SerialGetLastError(
    PSTIDCB     pDcb,
    LPDWORD     lpdwLastError
    )
{
    HRESULT     hres = E_NOTIMPL;
    return hres;
}