/*****************************************************************************
 * Copyright (C) Cinterion Wireless Modules GmbH
 *****************************************************************************
 * Project: CE-MUX-Driver
 *****************************************************************************
 * Author : Siemens AG Berlin COM WM RD SW1
 *****************************************************************************
 * $Workfile: Y:/z_ms-d/src/working/Classic/MuxDrv/DrvEntryVCom.cpp $
 * $Revision: /main/ms-d/12 $
 * $Date: 2009-02-18 11:58:08 $
 * $Author: ctna0256 $
 *****************************************************************************
 * Target:
 *  Microsoft Windows CE 4.2 or 5.0
 *****************************************************************************
 * Description:
 *  Cinterion Multiplex Driver virtual COM port driver object entry points
 *****************************************************************************
 *
 * $Log: Y:/z_ms-d/src/working/Classic/MuxDrv/DrvEntryVCom.cpp$ 
 * 
 * /main/ms-d/12   2009-02-18 11:58:08   ctna0256 
 * Set copyright text 
 * 
 * /main/ms-d/11   2009-02-11 12:16:37   ctna0450 
 * Virtualized WaitCommEvent() functionality in buffered parent port
 *
 * /main/ms-d/10   2009-02-10 09:55:54   ctna0450
 * Added buffered parent port as workaround for serial drivers without integrated queue handling
 *
 * /main/ms-d/9   2009-01-13 14:22:07   ctna0450
 * Make mux port instancable to properly support handling of multiple WaitCommEvent() calls
 *
 * /main/ms-d/8   2008-10-17 12:03:59   ctna0450
 * Added debug zone for IOCTLs
 *
 * /main/ms-d/7   2008-08-20 11:38:24   ctna0450
 * Prepared for new disclaimer handling
 *
 * /main/ms-d/6   2006-05-18 13:33:00   wm36264
 * Fixed all useful lint warnings
 *
 * /main/ms-d/5   2006-05-15 14:22:27   wm36264
 * Avoid strange exception
 *
 * /main/ms-d/4   2005-11-21 11:44:51   wm36264
 * Fixed wrong return values
 *
 * /main/ms-d/3   2005-10-24 14:21:23   WM36264
 * Added file descriptions
 *
 * /main/ms-d/2   2005-10-20 13:11:47   WM36264
 * Added proper copyright header
 *
 * /main/ms-d/1   2005-10-07 16:29:49   wm36264
 * First version of Windows CE Muxer
 *
*/

//////////////////////////////////////////////////////////////////////////////
// Copyright (C) 2008 Cinterion Wireless Modules GmbH
// Copyright (C) 2005-2008 Siemens AG
//
// Version: 1.0
// Date:    dummy
//
//////////////////////////////////////////////////////////////////////////////

//////////////////////////////////////////////////////////////////////////////
//
// DrvEntryVCom.cpp
//
// This file contains the Windows CE device driver interface for the "VCA" and "BPP"
// virtual port device contexts.
//
//////////////////////////////////////////////////////////////////////////////

#include "precomp.h"

//////////////////////////////////////////////////////////////////////////////
//
// Virtual multiplex port
//
//////////////////////////////////////////////////////////////////////////////

//////////////////////////////////////////////////////////////////////////////
//
// This function is called by the Device Manager to initialize a device.
//
// Parameters:
// dwContext: Specifies a pointer to a string containing the registry path to
//            the active key for the stream interface driver.
//
// Return:
// Returns a handle to the device context created, or 0 if unsuccessful.
// This handle is passed to the VCA_Open, VCA_PowerDown, VCA_PowerUp, and
// VCA_Deinit functions.
//
//////////////////////////////////////////////////////////////////////////////
DWORD VCA_Init(DWORD dwContext) {
  return (DWORD)(new CMuxChannel((LPCTSTR)dwContext));
}

//////////////////////////////////////////////////////////////////////////////
//
// This function is called by the Device Manager to deinitialize a device.
//
// Parameters:
// hDeviceContext: Handle to the device context. The VCA_Init function
//                 creates and returns this identifier.
//
// Return:
// TRUE indicates success. FALSE indicates failure.
//
//////////////////////////////////////////////////////////////////////////////
BOOL VCA_Deinit(DWORD hDeviceContext) {
  if (!hDeviceContext) {
    return FALSE;
  }
  delete (CMuxChannel*)hDeviceContext;
  return TRUE;
}

//////////////////////////////////////////////////////////////////////////////
//
// This function opens a device for reading and/or writing. An application
// indirectly invokes this function when it calls CreateFile to open special
// device file names.
//
// Parameters:
// hDeviceContext: Handle to the device context. The VCA_Init function
//                 creates and returns this handle.
// AccessCode    : Specifies the requested access code of the device. The
//                 access is a combination of read and write.
// ShareMode     : Specifies the requested file share mode of the PC Card
//                 device. The share mode is a combination of file read and
//                 write sharing.
//
// Return:
// This function returns a handle that identifies the open context of the
// device to the calling application. If your device can be opened multiple
// times, use this handle to identify each open context. This identifier is
// passed into the VCA_Read, VCA_Write, VCA_Seek, and VCA_IOControl functions.
// If the device cannot be opened, this function returns NULL.
//
//////////////////////////////////////////////////////////////////////////////
DWORD VCA_Open(DWORD hDeviceContext, DWORD AccessCode, DWORD ShareMode) {
CPortInstance* pInstance;

  if (!hDeviceContext) {
    return 0;
  }
  pInstance = new CPortInstance((CMuxChannel*)hDeviceContext);
  if (!pInstance) {
    return 0;
  }
  if (!pInstance->Open()) {
    delete pInstance;
    return 0;
  }
  return (DWORD)pInstance;
}

//////////////////////////////////////////////////////////////////////////////
//
// This function closes the device context identified by hOpenContext.
//
// Parameters:
// hOpenContext: Handle returned by the VCA_Open function, used to identify
//               the open context of the device.
//
// Return:
// TRUE indicates success. FALSE indicates failure.
//
//////////////////////////////////////////////////////////////////////////////
BOOL VCA_Close(DWORD hOpenContext) {
  if (!hOpenContext) {
    return FALSE;
  }
  if (!((CPortInstance*)hOpenContext)->Close()) {
    return FALSE;
  }
  delete (CPortInstance*)hOpenContext;
  return TRUE;
}

//////////////////////////////////////////////////////////////////////////////
//
// This function reads data from the device identified by the open context.
//
// Parameters:
// hOpenContext: Handle to the open context of the device. The VCA_Open
//               function creates and returns this identifier.
// pBuffer     : Pointer to the buffer which stores the data read from the
//               device. This buffer should be at least Count bytes long.
// Count       : Specifies the number of bytes to read from the device into
//               pBuffer.
//
// Return:
// Returns 0 for the end-of-file, –1 for an error, or the number of bytes read
// for success.
//
//////////////////////////////////////////////////////////////////////////////
DWORD VCA_Read(DWORD hOpenContext, LPVOID pBuffer, DWORD Count) {
  if (!hOpenContext) {
    return (DWORD)-1;
  }
  return ((CPortInstance*)hOpenContext)->ReadData((PBYTE)pBuffer, Count);
}

//////////////////////////////////////////////////////////////////////////////
//
// This function writes data to the device.
//
// Parameters:
// hOpenContext: Handle to the open context of the device. The call to the
//               VCA_Open function returns this identifier.
// pBuffer     : Pointer to the buffer that contains the data to write.
// Count       : Specifies the number of bytes to write from the pSourceBytes
//               buffer into the device.
//
// Return:
// The number of bytes written indicates success. A value of –1 indicates
// failure.
//
//////////////////////////////////////////////////////////////////////////////
DWORD VCA_Write(DWORD hOpenContext, LPCVOID pBuffer, DWORD Count) {
  if (!hOpenContext) {
    return (DWORD)-1;
  }
  return ((CPortInstance*)hOpenContext)->WriteData((PBYTE)pBuffer, Count);
}

//////////////////////////////////////////////////////////////////////////////
//
// This function moves the data pointer in the device.
//
// Parameters:
// hOpenContext: Handle to the open context of the device. The VCA_Open
//               function creates and returns this identifier.
// Amount      : Specifies the number of bytes to move the data pointer in the
//               device. A positive value moves the data pointer toward the
//               end of the file, and a negative value moves it toward the
//               beginning.
// Type        : Specifies the starting point for the data pointer
//              (FILE_BEGIN, FILE_CURRENT, FILE_END).
//
// Return:
// The device’s new data pointer indicates success. A value of –1 indicates
// failure.
//
//////////////////////////////////////////////////////////////////////////////
DWORD VCA_Seek(DWORD hOpenContext, long Amount, WORD Type) {
  return (DWORD)-1;
}

//////////////////////////////////////////////////////////////////////////////
//
// This function restores power to a device.
//
// Parameters:
// hOpenContext: Handle to the open context of the device. The VCA_Open
//               function creates and returns this identifier.
//
// Return:
// None.
//
//////////////////////////////////////////////////////////////////////////////
void VCA_PowerUp(DWORD hOpenContext) {
}

//////////////////////////////////////////////////////////////////////////////
//
// This function suspends power to the device. It is useful only with devices
// that can be shut off under software control. Such devices are typically,
// but not exclusively, PC Card devices.
//
// Parameters:
// hOpenContext: Handle to the open context of the device. The VCA_Open
//               function creates and returns this identifier.
//
// Return:
// None.
//
//////////////////////////////////////////////////////////////////////////////
void VCA_PowerDown(DWORD hOpenContext) {
}

//////////////////////////////////////////////////////////////////////////////
//
// This function sends a command to a device.
//
// Parameters:
// hOpenContext: Handle to the open context of the device. The VCA_Open
//               function creates and returns this identifier.
// dwCode      : Specifies a value indicating the I/O control operation to
//               perform. These codes are device-specific and are usually
//               exposed to programmers by means of a header file.
// pBufIn      : Pointer to the buffer containing data to be transferred to
//               the device.
// dwLenIn     : Specifies the number of bytes of data in the buffer specified
//               for pBufIn.
// pBufOut     : Pointer to the buffer used to transfer the output data from
//               the device.
// dwLenOut    : Specifies the maximum number of bytes in the buffer specified
//               by pBufOut.
// pdwActualOut: Pointer to the DWORD buffer that this function uses to return
//               the actual number of bytes received from the device.
//
// Return:
// TRUE indicates success. FALSE indicates failure.
//
//////////////////////////////////////////////////////////////////////////////
BOOL VCA_IOControl(DWORD hOpenContext, DWORD dwCode, PBYTE pBufIn, DWORD dwLenIn, PBYTE pBufOut, DWORD dwLenOut, PDWORD pdwActualOut) {
CPortInstance*  pInstance = (CPortInstance*)hOpenContext;
BOOL            fRet = TRUE;

  if (!pdwActualOut) {
    SetLastError(ERROR_INVALID_PARAMETER);
    return FALSE;
  }

  *pdwActualOut = 0;

  if (!pInstance) return FALSE;

  MUXDBG(ZONE_IOCTL, (TEXT("MuxDrv: Received IOCTL %08x on VCA%d\r\n"), dwCode, pInstance->GetDlci()));

  switch(dwCode) {
    // Set Data Terminal Ready (DTR) signal to high
    case IOCTL_SERIAL_SET_DTR:
      fRet = pInstance->SetDtr();
      break;

    // Set the Data Terminal Ready (DTR) signal to low
    case IOCTL_SERIAL_CLR_DTR:
      fRet = pInstance->ClearDtr();
      break;

    // Set the Ready to Send (RTS) signal to high
    case IOCTL_SERIAL_SET_RTS:
      fRet = pInstance->SetRts();
      break;

    // Set the Request to Send (RTS) signal to low
    case IOCTL_SERIAL_CLR_RTS:
      fRet = pInstance->ClearRts();
      break;

    // Retrieve the value of the wait mask
    case IOCTL_SERIAL_GET_WAIT_MASK:
      if ((dwLenOut >= sizeof(DWORD)) && pBufOut) {
        fRet = pInstance->GetWaitMask((PDWORD)pBufOut);
        *pdwActualOut = sizeof(DWORD);
      } else {
        SetLastError(ERROR_INVALID_PARAMETER);
        fRet = FALSE;
      }
      break;

    // Set the value of the wait mask
    case IOCTL_SERIAL_SET_WAIT_MASK:
      if (pBufIn && (dwLenIn >= sizeof(DWORD))) {
        fRet = pInstance->SetWaitMask(*(PDWORD)pBufIn);
      } else {
        SetLastError(ERROR_INVALID_PARAMETER);
        fRet = FALSE;
      }
      break;

    // Wait for a communications event that matches the event specified in the event mask
    case IOCTL_SERIAL_WAIT_ON_MASK:
      if ((dwLenOut >= sizeof(DWORD)) && pBufOut) {
        fRet = pInstance->WaitOnMask((PDWORD)pBufOut);
        *pdwActualOut = sizeof(DWORD);
      } else {
        SetLastError(ERROR_INVALID_PARAMETER);
        fRet = FALSE;
      }
      break;

    // Clear any pending communications errors and returns the current communication status
    case IOCTL_SERIAL_GET_COMMSTATUS:
      if ((dwLenOut >= sizeof(SERIAL_DEV_STATUS)) && pBufOut) {
        fRet = pInstance->GetComStat(&(((PSERIAL_DEV_STATUS)pBufOut)->ComStat));
        *pdwActualOut = sizeof(SERIAL_DEV_STATUS);
      } else {
        SetLastError(ERROR_INVALID_PARAMETER);
        fRet = FALSE;
      }
      break;

    // Retrieve the current modem control-register values
    case IOCTL_SERIAL_GET_MODEMSTATUS:
      if ((dwLenOut >= sizeof(DWORD)) && pBufOut) {
        fRet = pInstance->GetModemState((PDWORD)pBufOut);
        *pdwActualOut = sizeof(DWORD);
      } else {
        SetLastError(ERROR_INVALID_PARAMETER);
        fRet = FALSE;
      }
      break;

    // Retrieve information about the communications properties
    case IOCTL_SERIAL_GET_PROPERTIES:
      if ((dwLenOut >= sizeof(COMMPROP)) && pBufOut) {
        fRet = pInstance->GetProperties((LPCOMMPROP)pBufOut);
        *pdwActualOut = sizeof(COMMPROP);
      } else {
        SetLastError(ERROR_INVALID_PARAMETER);
        fRet = FALSE;
      }
      break;

    // Set the time-out parameters for all read and write operations
    case IOCTL_SERIAL_SET_TIMEOUTS:
      if (pBufIn && (dwLenIn >= sizeof(COMMTIMEOUTS))) {
        fRet = pInstance->SetTimeouts((COMMTIMEOUTS*)pBufIn);
      } else {
        SetLastError(ERROR_INVALID_PARAMETER);
        fRet = FALSE;
      }
      break;

    // Get the time-out parameters for all read and write operations
    case IOCTL_SERIAL_GET_TIMEOUTS:
      if ((dwLenOut >= sizeof(COMMTIMEOUTS)) && pBufOut) {
        fRet = pInstance->GetTimeouts((COMMTIMEOUTS*)pBufOut);
        *pdwActualOut = sizeof(COMMTIMEOUTS);
      } else {
        SetLastError(ERROR_INVALID_PARAMETER);
        fRet = FALSE;
      }
      break;

    // Discard characters from the output or input buffer, it can also terminate pending read or write operations
    case IOCTL_SERIAL_PURGE:
      if (pBufIn && (dwLenIn >= sizeof(DWORD))) {
        DWORD dwFlags = *((PDWORD)pBufIn);
        if ((dwFlags & PURGE_RXCLEAR) || (dwFlags & PURGE_RXABORT)) {
          fRet = pInstance->ClearReceiveBuffer();
        }
        if ((dwFlags & PURGE_TXCLEAR) || (dwFlags & PURGE_TXABORT)) {
          fRet = pInstance->ClearSendBuffer();
        }
      } else {
        SetLastError(ERROR_INVALID_PARAMETER);
        fRet = FALSE;
      }
      break;

    // Get the device-control block
    case IOCTL_SERIAL_GET_DCB:
      if ((dwLenOut >= sizeof(DCB)) && pBufOut) {
        fRet = pInstance->GetDcb((DCB*)pBufOut);
        *pdwActualOut = sizeof(DCB);
      } else {
        SetLastError(ERROR_INVALID_PARAMETER);
        fRet = FALSE;
      }
      break;

    // Set the device-control block
    case IOCTL_SERIAL_SET_DCB:
      if (pBufIn && (dwLenIn >= sizeof(DCB))) {
        fRet = pInstance->SetDcb((DCB*)pBufIn);
      } else {
        SetLastError(ERROR_INVALID_PARAMETER);
        fRet = FALSE;
      }
      break;

    // Unsupported serial interface functions in mux mode
    case IOCTL_SERIAL_SET_BREAK_ON:
    case IOCTL_SERIAL_SET_BREAK_OFF:
    case IOCTL_SERIAL_SET_XOFF:
    case IOCTL_SERIAL_SET_XON:
    case IOCTL_SERIAL_SET_QUEUE_SIZE:
    case IOCTL_SERIAL_IMMEDIATE_CHAR:
    case IOCTL_SERIAL_ENABLE_IR:
    case IOCTL_SERIAL_DISABLE_IR:
      MUXDBG(ZONE_IOCTL, (TEXT("MuxDrv: Received IOCTL is not supported\r\n")));
      SetLastError(ERROR_NOT_SUPPORTED);
      fRet = FALSE;
      break;

    // Unknown serial interface functions
    default:
      MUXDBG(ZONE_IOCTL, (TEXT("MuxDrv: Received IOCTL is unknown\r\n")));
      SetLastError(ERROR_INVALID_PARAMETER);
      fRet = FALSE;
      break;
  }

  return fRet;
}

//////////////////////////////////////////////////////////////////////////////
//
// Buffered parent port
//
//////////////////////////////////////////////////////////////////////////////

//////////////////////////////////////////////////////////////////////////////
//
// This function is called by the Device Manager to initialize a device.
//
// Parameters:
// dwContext: Specifies a pointer to a string containing the registry path to
//            the active key for the stream interface driver.
//
// Return:
// Returns a handle to the device context created, or 0 if unsuccessful.
// This handle is passed to the VCA_Open, VCA_PowerDown, VCA_PowerUp, and
// VCA_Deinit functions.
//
//////////////////////////////////////////////////////////////////////////////
DWORD BPP_Init(DWORD dwContext) {
  return (DWORD)(new CBufferedParentPort((LPCTSTR)dwContext));
}

//////////////////////////////////////////////////////////////////////////////
//
// This function is called by the Device Manager to deinitialize a device.
//
// Parameters:
// hDeviceContext: Handle to the device context. The BPP_Init function
//                 creates and returns this identifier.
//
// Return:
// TRUE indicates success. FALSE indicates failure.
//
//////////////////////////////////////////////////////////////////////////////
BOOL BPP_Deinit(DWORD hDeviceContext) {
  if (!hDeviceContext) {
    return FALSE;
  }
  delete (CBufferedParentPort*)hDeviceContext;
  return TRUE;
}

//////////////////////////////////////////////////////////////////////////////
//
// This function opens a device for reading and/or writing. An application
// indirectly invokes this function when it calls CreateFile to open special
// device file names.
//
// Parameters:
// hDeviceContext: Handle to the device context. The BPP_Init function
//                 creates and returns this handle.
// AccessCode    : Specifies the requested access code of the device. The
//                 access is a combination of read and write.
// ShareMode     : Specifies the requested file share mode of the PC Card
//                 device. The share mode is a combination of file read and
//                 write sharing.
//
// Return:
// This function returns a handle that identifies the open context of the
// device to the calling application. If your device can be opened multiple
// times, use this handle to identify each open context. This identifier is
// passed into the BPP_Read, BPP_Write, BPP_Seek, and BPP_IOControl functions.
// If the device cannot be opened, this function returns NULL.
//
//////////////////////////////////////////////////////////////////////////////
DWORD BPP_Open(DWORD hDeviceContext, DWORD AccessCode, DWORD ShareMode) {
CBufferedPortInstance* pInstance;

  if (!hDeviceContext) {
    return 0;
  }
  pInstance = new CBufferedPortInstance((CBufferedParentPort*)hDeviceContext);
  if (!pInstance) {
    return 0;
  }
  if (!pInstance->Open()) {
    delete pInstance;
    return 0;
  }
  return (DWORD)pInstance;
}

//////////////////////////////////////////////////////////////////////////////
//
// This function closes the device context identified by hOpenContext.
//
// Parameters:
// hOpenContext: Handle returned by the BPP_Open function, used to identify
//               the open context of the device.
//
// Return:
// TRUE indicates success. FALSE indicates failure.
//
//////////////////////////////////////////////////////////////////////////////
BOOL BPP_Close(DWORD hOpenContext) {
  if (!hOpenContext) {
    return FALSE;
  }
  if (!((CBufferedPortInstance*)hOpenContext)->Close()) {
    return FALSE;
  }
  delete (CBufferedPortInstance*)hOpenContext;
  return TRUE;
}

//////////////////////////////////////////////////////////////////////////////
//
// This function reads data from the device identified by the open context.
//
// Parameters:
// hOpenContext: Handle to the open context of the device. The BPP_Open
//               function creates and returns this identifier.
// pBuffer     : Pointer to the buffer which stores the data read from the
//               device. This buffer should be at least Count bytes long.
// Count       : Specifies the number of bytes to read from the device into
//               pBuffer.
//
// Return:
// Returns 0 for the end-of-file, –1 for an error, or the number of bytes read
// for success.
//
//////////////////////////////////////////////////////////////////////////////
DWORD BPP_Read(DWORD hOpenContext, LPVOID pBuffer, DWORD Count) {
  if (!hOpenContext) {
    return (DWORD)-1;
  }
  return ((CBufferedPortInstance*)hOpenContext)->ReadData((PBYTE)pBuffer, Count);
}

//////////////////////////////////////////////////////////////////////////////
//
// This function writes data to the device.
//
// Parameters:
// hOpenContext: Handle to the open context of the device. The call to the
//               BPP_Open function returns this identifier.
// pBuffer     : Pointer to the buffer that contains the data to write.
// Count       : Specifies the number of bytes to write from the pSourceBytes
//               buffer into the device.
//
// Return:
// The number of bytes written indicates success. A value of –1 indicates
// failure.
//
//////////////////////////////////////////////////////////////////////////////
DWORD BPP_Write(DWORD hOpenContext, LPCVOID pBuffer, DWORD Count) {
  if (!hOpenContext) {
    return (DWORD)-1;
  }
  return ((CBufferedPortInstance*)hOpenContext)->WriteData((PBYTE)pBuffer, Count);
}

//////////////////////////////////////////////////////////////////////////////
//
// This function moves the data pointer in the device.
//
// Parameters:
// hOpenContext: Handle to the open context of the device. The BPP_Open
//               function creates and returns this identifier.
// Amount      : Specifies the number of bytes to move the data pointer in the
//               device. A positive value moves the data pointer toward the
//               end of the file, and a negative value moves it toward the
//               beginning.
// Type        : Specifies the starting point for the data pointer
//              (FILE_BEGIN, FILE_CURRENT, FILE_END).
//
// Return:
// The device’s new data pointer indicates success. A value of –1 indicates
// failure.
//
//////////////////////////////////////////////////////////////////////////////
DWORD BPP_Seek(DWORD hOpenContext, long Amount, WORD Type) {
  return (DWORD)-1;
}

//////////////////////////////////////////////////////////////////////////////
//
// This function restores power to a device.
//
// Parameters:
// hOpenContext: Handle to the open context of the device. The BPP_Open
//               function creates and returns this identifier.
//
// Return:
// None.
//
//////////////////////////////////////////////////////////////////////////////
void BPP_PowerUp(DWORD hOpenContext) {
}

//////////////////////////////////////////////////////////////////////////////
//
// This function suspends power to the device. It is useful only with devices
// that can be shut off under software control. Such devices are typically,
// but not exclusively, PC Card devices.
//
// Parameters:
// hOpenContext: Handle to the open context of the device. The BPP_Open
//               function creates and returns this identifier.
//
// Return:
// None.
//
//////////////////////////////////////////////////////////////////////////////
void BPP_PowerDown(DWORD hOpenContext) {
}

//////////////////////////////////////////////////////////////////////////////
//
// This function sends a command to a device.
//
// Parameters:
// hOpenContext: Handle to the open context of the device. The BPP_Open
//               function creates and returns this identifier.
// dwCode      : Specifies a value indicating the I/O control operation to
//               perform. These codes are device-specific and are usually
//               exposed to programmers by means of a header file.
// pBufIn      : Pointer to the buffer containing data to be transferred to
//               the device.
// dwLenIn     : Specifies the number of bytes of data in the buffer specified
//               for pBufIn.
// pBufOut     : Pointer to the buffer used to transfer the output data from
//               the device.
// dwLenOut    : Specifies the maximum number of bytes in the buffer specified
//               by pBufOut.
// pdwActualOut: Pointer to the DWORD buffer that this function uses to return
//               the actual number of bytes received from the device.
//
// Return:
// TRUE indicates success. FALSE indicates failure.
//
//////////////////////////////////////////////////////////////////////////////
BOOL BPP_IOControl(DWORD hOpenContext, DWORD dwCode, PBYTE pBufIn, DWORD dwLenIn, PBYTE pBufOut, DWORD dwLenOut, PDWORD pdwActualOut) {
  if (!hOpenContext) {
    return FALSE;
  }
  return ((CBufferedPortInstance*)hOpenContext)->IoControl(dwCode, pBufIn, dwLenIn, pBufOut, dwLenOut, pdwActualOut);
}

