//---------------------------------------------------------------------------
//
// Copyright (c) Microsoft Corporation 1993-1996
//
// File: serialui.c
//
// This files contains the DLL entry-points.
//
// Much of this file contains the code that builds the default property dialog
// for serial ports.
//
// History:
//   1-12-94 ScottH     Created
//   8-15-94 ScottH     Split from modemui.dll
//  11-06-95 ScottH     Ported to NT
//
//---------------------------------------------------------------------------


#include "proj.h"     // common headers

#define INITGUID
#include <objbase.h>
#include <initguid.h>
#include <devguid.h>

#pragma data_seg(DATASEG_READONLY)

LPGUID c_pguidModem     = (LPGUID)&GUID_DEVCLASS_MODEM;

// (scotth):  it looks like for the NT SUR release, that there
// will be no Port class key or GUID.  So we have to hack something
// up.
#ifdef DCB_IN_REGISTRY
LPGUID c_pguidPort      = (LPGUID)&GUID_DEVCLASS_PORT;
#else
LPGUID c_pguidPort      = (LPGUID)NULL;
#endif

#pragma data_seg()


#define MAX_PROP_PAGES  8          // Define a reasonable limit


#ifdef DEBUG

//-----------------------------------------------------------------------------------
//  Debug routines
//-----------------------------------------------------------------------------------

/*----------------------------------------------------------
Purpose: Dumps the DCB struct
Returns: --
Cond:    --
*/
void PRIVATE DumpDCB(
    LPWIN32DCB pdcb)
    {
    ASSERT(pdcb);

    if (IsFlagSet(g_dwDumpFlags, DF_DCB))
        {
        int i;
        LPDWORD pdw = (LPDWORD)pdcb;

        TRACE_MSG(TF_ALWAYS, "DCB  %08lx %08lx %08lx %08lx", pdw[0], pdw[1], pdw[2], pdw[3]);
        pdw += 4;
        for (i = 0; i < sizeof(WIN32DCB)/sizeof(DWORD); i += 4, pdw += 4)
            {
            TRACE_MSG(TF_ALWAYS, "     %08lx %08lx %08lx %08lx", pdw[0], pdw[1], pdw[2], pdw[3]);
            }
        }
    }

#endif //DEBUG


//-----------------------------------------------------------------------------------
//  
//-----------------------------------------------------------------------------------


/*----------------------------------------------------------
Purpose: Composes a string of the format "baud,parity,data,stopbit"

Returns: --
Cond:    --
*/
void PRIVATE ComposeModeComString(
    LPCOMMCONFIG pcc,
    LPTSTR pszBuffer)
    {
    WIN32DCB FAR * pdcb = &pcc->dcb;
    TCHAR chParity;
    LPCTSTR pszStop;
    TCHAR chFlow;

    const static TCHAR rgchParity[] = {'n', 'o', 'e', 'm', 's'};
    const static LPCTSTR rgpszStop[] = {TEXT("1"), TEXT("1.5"), TEXT("2")};
    
    // Parity
//    ASSERT(!pdcb->fParity && NOPARITY == pdcb->Parity || pdcb->fParity);
    ASSERT(0 <= pdcb->Parity && ARRAYSIZE(rgchParity) > pdcb->Parity);

    if (0 <= pdcb->Parity && ARRAYSIZE(rgchParity) > pdcb->Parity)
        {
        chParity = rgchParity[pdcb->Parity];
        }
    else
        {
        chParity = rgchParity[0];   // Safety net
        }

    // Stop bits
    ASSERT(0 <= pdcb->StopBits && ARRAYSIZE(rgpszStop) > pdcb->StopBits);

    if (0 <= pdcb->StopBits && ARRAYSIZE(rgpszStop) > pdcb->StopBits)
        {
        pszStop = rgpszStop[pdcb->StopBits];
        }
    else
        {
        pszStop = rgpszStop[0];   // Safety net
        }

    // Flow control
    if (FALSE != pdcb->fOutX && FALSE == pdcb->fOutxCtsFlow)
        {
        chFlow = 'x';       // XON/XOFF flow control
        }
    else if (FALSE == pdcb->fOutX && FALSE != pdcb->fOutxCtsFlow)
        {
        chFlow = 'p';       // Hardware flow control
        }
    else
        {
        chFlow = ' ';       // No flow control
        }

    wsprintf(pszBuffer, TEXT("%ld,%c,%d,%s,%c"), pdcb->BaudRate, chParity, pdcb->ByteSize,
        pszStop, chFlow);
    }


/*----------------------------------------------------------
Purpose: Initialize the port info.

Returns: --
Cond:    --
*/
void PRIVATE InitializePortInfo(
    LPCTSTR pszFriendlyName,
    LPPORTINFO pportinfo,
    LPCOMMCONFIG pcc)
    {
    ASSERT(pportinfo);
    ASSERT(pcc);

    // Read-only fields
    pportinfo->pcc = pcc;

    CopyMemory(&pportinfo->dcb, &pcc->dcb, sizeof(pportinfo->dcb));

    lstrcpyn(pportinfo->szFriendlyName, pszFriendlyName, SIZECHARS(pportinfo->szFriendlyName));
    }



/*----------------------------------------------------------
Purpose: Gets a WIN32DCB from the registry.

Returns: One of the ERROR_ values
Cond:    --
*/
DWORD 
PRIVATE 
RegQueryDCB(
    IN  LPFINDDEV      pfd,
    OUT WIN32DCB FAR * pdcb)
    {
    DWORD dwRet = ERROR_BADKEY;

#ifdef DCB_IN_REGISTRY

    DWORD cbData;

    ASSERT(pdcb);

    // Does the DCB key exist in the driver key?
    if (ERROR_SUCCESS == RegQueryValueEx(pfd->hkeyDrv, c_szDCB, NULL, NULL, NULL, &cbData))
        {
        // Yes; is the size in the registry okay?  
        if (sizeof(*pdcb) < cbData)
            {
            // No; the registry has bogus data
            dwRet = ERROR_BADDB;
            }
        else
            {
            // Yes; get the DCB from the registry
            if (ERROR_SUCCESS == RegQueryValueEx(pfd->hkeyDrv, c_szDCB, NULL, NULL, (LPBYTE)pdcb, &cbData))
                {
                if (sizeof(*pdcb) == pdcb->DCBlength)
                    {
                    dwRet = NO_ERROR;
                    }
                else
                    {
                    dwRet = ERROR_BADDB;
                    }
                }
            else
                {
                dwRet = ERROR_BADKEY;
                }
            }
        }

#else

    static TCHAR const FAR c_szDefaultDCBString[] = TEXT("9600,n,8,1");
    
    TCHAR sz[MAX_BUF_MED];
    TCHAR szKey[MAX_BUF_SHORT];

    lstrcpy(szKey, pfd->szPort);
    lstrcat(szKey, TEXT(":"));

    GetProfileString(c_szPortClass, szKey, c_szDefaultDCBString, sz, SIZECHARS(sz));

    TRACE_MSG(TF_GENERAL, "DCB string is \"%s\"", sz);

    // Convert the DCB string to a DCB structure
    if ( !BuildCommDCB(sz, pdcb) )
        {
        dwRet = GetLastError();

        ASSERT(NO_ERROR != dwRet);
        }
    else
        {
        dwRet = NO_ERROR;
        }

#endif

    return dwRet;
    }


/*----------------------------------------------------------
Purpose: Save the DCB to the permanent storage

Returns: win32 error
Cond:    --
*/
DWORD
PRIVATE
RegSetDCB(
    IN  LPFINDDEV      pfd,
    IN  WIN32DCB FAR * pdcb)
    {
    DWORD dwRet;

#ifdef DCB_IN_REGISTRY

    DWORD cbData;

    // Write the DCB to the driver key
    cbData = sizeof(WIN32DCB);
    dwRet = RegSetValueEx(pfd->hkeyDrv, c_szDCB, 0, REG_BINARY, (LPBYTE)&pcc->dcb, cbData);

#else

    dwRet = NO_ERROR;

#endif

    return dwRet;
    }


/*----------------------------------------------------------
Purpose: Frees the portinfo struct

Returns: --
Cond:    --
*/
void PRIVATE FreePortInfo(
    LPPORTINFO pportinfo)
    {
    if (pportinfo)
        {
        if (pportinfo->pcc)
            LocalFree(LOCALOF(pportinfo->pcc));

        LocalFree(LOCALOF(pportinfo));
        }
    }


/*----------------------------------------------------------
Purpose: Release the data associated with the Port Settings page
Returns: --
Cond:    --
*/
UINT CALLBACK PortSettingsCallback(
    HWND hwnd,
    UINT uMsg,
    LPPROPSHEETPAGE ppsp)
    {
    DBG_ENTER("PortSettingsCallback");

    if (PSPCB_RELEASE == uMsg)
        {
        LPPORTINFO pportinfo = (LPPORTINFO)ppsp->lParam;
        LPCOMMCONFIG pcc;

        ASSERT(pportinfo);

        pcc = pportinfo->pcc;

        if (IDOK == pportinfo->idRet)
            {
            // Save the changes back to the commconfig struct
            TRACE_MSG(TF_GENERAL, "Saving DCB");

            CopyMemory(&pcc->dcb, &pportinfo->dcb, sizeof(pcc->dcb));

            DEBUG_CODE( DumpDCB(&pcc->dcb); )

            // Are we releasing from the Device Mgr?
            if (IsFlagSet(pportinfo->uFlags, SIF_FROM_DEVMGR))
                {
                // Yes; save the commconfig now as well
                drvSetDefaultCommConfig(pportinfo->szFriendlyName, pcc, pcc->dwSize);

                // Free the portinfo struct only when called from the Device Mgr
                FreePortInfo(pportinfo);
                }
            }

        TRACE_MSG(TF_GENERAL, "Releasing the Port Settings page");
        }

    DBG_EXIT("PortSettingsCallback");
    return TRUE;
    }


/*----------------------------------------------------------
Purpose: Add the port settings page.  

Returns: ERROR_ value

Cond:    --
*/
DWORD PRIVATE AddPortSettingsPage(
    LPPORTINFO pportinfo,
    LPFNADDPROPSHEETPAGE pfnAdd, 
    LPARAM lParam)
    {
    DWORD dwRet = ERROR_NOT_ENOUGH_MEMORY;
    PROPSHEETPAGE   psp;
    HPROPSHEETPAGE  hpage;

    ASSERT(pportinfo);
    ASSERT(pfnAdd);

    // Add the Port Settings property page
    //
    psp.dwSize = sizeof(PROPSHEETPAGE);
    psp.dwFlags = PSP_USECALLBACK;
    psp.hInstance = g_hinst;
    psp.pszTemplate = MAKEINTRESOURCE(IDD_PORTSETTINGS);
    psp.pfnDlgProc = Port_WrapperProc;
    psp.lParam = (LPARAM)pportinfo;
    psp.pfnCallback = PortSettingsCallback;
    
    hpage = CreatePropertySheetPage(&psp);
    if (hpage)
        {
        if (!pfnAdd(hpage, lParam))
            DestroyPropertySheetPage(hpage);
        else
            dwRet = NO_ERROR;
        }
    
    return dwRet;
    }


/*----------------------------------------------------------
Purpose: Function that is called by EnumPropPages entry-point to
         add property pages.

Returns: TRUE on success
         FALSE on failure

Cond:    --
*/
BOOL CALLBACK AddInstallerPropPage(
    HPROPSHEETPAGE hPage, 
    LPARAM lParam)
    {
    PROPSHEETHEADER FAR * ppsh = (PROPSHEETHEADER FAR *)lParam;
 
    if (ppsh->nPages < MAX_PROP_PAGES)
        {
        ppsh->phpage[ppsh->nPages] = hPage;
        ++ppsh->nPages;
        return(TRUE);
        }
    return(FALSE);
    }


/*----------------------------------------------------------
Purpose: Bring up property sheet for a serial port

Returns: ERROR_ value
Cond:    --
*/
DWORD PRIVATE DoProperties(
    LPCTSTR pszFriendlyName,
    HWND hwndParent,
    LPCOMMCONFIG pcc)
    {
    DWORD dwRet;
    PROPSHEETHEADER psh;
    HPROPSHEETPAGE hpsPages[MAX_PROP_PAGES];
    LPPORTINFO pportinfo;

    // Initialize the PropertySheet Header
    psh.dwSize = sizeof(psh);
    psh.dwFlags = PSH_PROPTITLE;
    psh.hwndParent = hwndParent;
    psh.hInstance = g_hinst;
    psh.nPages = 0;
    psh.nStartPage = 0;
    psh.phpage = (HPROPSHEETPAGE FAR *)hpsPages;

    // Allocate the working buffer
    //
    pportinfo = (LPPORTINFO)LocalAlloc(LPTR, sizeof(*pportinfo));
    if (pportinfo)
        {
        InitializePortInfo(pszFriendlyName, pportinfo, pcc);
        psh.pszCaption = pportinfo->szFriendlyName;

        DEBUG_CODE( DumpDCB(&pcc->dcb); )

        dwRet = AddPortSettingsPage(pportinfo, AddInstallerPropPage, (LPARAM)&psh);

        if (NO_ERROR == dwRet)
            {
            // Show the property sheet
            PropertySheet(&psh);

            dwRet = (IDOK == pportinfo->idRet) ? NO_ERROR : ERROR_CANCELLED;
            }

        // Clear the pcc field so FreePortInfo does not prematurely free it,
        // since we did not allocate it.
        pportinfo->pcc = NULL;
        FreePortInfo(pportinfo);
        }
    else
        {
        dwRet = ERROR_NOT_ENOUGH_MEMORY;
        }
    
    return dwRet;
    }


#ifdef WIN95

// The Device Manager allows DLLs to add pages to the properties
// of a device.  EnumPropPages is the entry-point that it would
// call to add pages.  
//
// This is not implemented in NT.


/*----------------------------------------------------------
Purpose: Derives a PORTINFO struct from a device info.

Returns: TRUE on success

Cond:    --
*/
BOOL PRIVATE DeviceInfoToPortInfo(
    LPDEVICE_INFO pdi,
    LPPORTINFO pportinfo)
    {
    BOOL bRet = FALSE;
    LPFINDDEV pfd;
    COMMCONFIG ccDummy;
    LPCOMMCONFIG pcommconfig;
    DWORD cbSize;
    DWORD cbData;
    TCHAR szFriendly[MAXFRIENDLYNAME];

    // Find the device by looking for the device description.  (Note the
    // device description is not always the same as the friendly name.)

    if (FindDev_Create(&pfd, c_pguidPort, c_szDeviceDesc, pdi->szDescription))
        {
        cbData = sizeof(szFriendly);
        if (ERROR_SUCCESS == RegQueryValueEx(pfd->hkeyDev, c_szFriendlyName, NULL, NULL, 
                                             (LPBYTE)szFriendly, &cbData))
            {
            ccDummy.dwProviderSubType = PST_RS232;
            cbSize = sizeof(COMMCONFIG);
            drvGetDefaultCommConfig(szFriendly, &ccDummy, &cbSize);

            pcommconfig = (LPCOMMCONFIG)LocalAlloc(LPTR, (UINT)cbSize);
            if (pcommconfig)
                {
                // Get the commconfig from the registry
                pcommconfig->dwProviderSubType = PST_RS232;
                if (NO_ERROR == drvGetDefaultCommConfig(szFriendly, pcommconfig, 
                    &cbSize))
                    {
                    // Initialize the modem info from the commconfig
                    InitializePortInfo(szFriendly, pportinfo, pcommconfig);

                    SetFlag(pportinfo->uFlags, SIF_FROM_DEVMGR);
                    bRet = TRUE;
                    }
                else
                    {
                    // Failure
                    LocalFree(LOCALOF(pcommconfig));
                    }

                // pcommconfig is freed in ReleasePortSettingsPage
                }
            }
        FindDev_Destroy(pfd);
        }

    return bRet;
    }


/*----------------------------------------------------------
Purpose: EnumDevicePropPages entry-point.  This entry-point
         gets called only when the Device Manager asks for 
         additional property pages.  

Returns: TRUE on success
         FALSE if pages could not be added
Cond:    --
*/
BOOL WINAPI EnumPropPages(
    LPDEVICE_INFO pdi, 
    LPFNADDPROPSHEETPAGE pfnAdd, 
    LPARAM lParam)              // Don't touch the lParam value, just pass it on!
    {
    BOOL bRet = FALSE;
    LPPORTINFO pportinfo;

    DBG_ENTER("EnumPropPages");

    ASSERT(pdi);
    ASSERT(pfnAdd);

    pportinfo = (LPPORTINFO)LocalAlloc(LPTR, sizeof(*pportinfo));
    if (pportinfo)
        {
        // Convert the device info struct to a portinfo.
        bRet = DeviceInfoToPortInfo(pdi, pportinfo);
        if (bRet)
            {
            AddPortSettingsPage(pportinfo, pfnAdd, lParam);
            }
        else
            {
            // Failed
            FreePortInfo(pportinfo);
            }
        // pportinfo is freed in ReleasePortSettingsPage
        }

    DBG_EXIT_BOOL("EnumPropPages", bRet);

    return bRet;
    }
#endif


/*----------------------------------------------------------
Purpose: Invokes the serial port configuration dialog.  

Returns: One of the ERROR_ values
Cond:    --
*/
DWORD 
PRIVATE 
MyCommConfigDialog(
    IN     LPFINDDEV    pfd,
    IN     LPCTSTR      pszFriendlyName,
    IN     HWND         hwndOwner,
    IN OUT LPCOMMCONFIG pcc)
    {
    DWORD dwRet;
    
    ASSERT(pfd);
    // (Wrapper should have checked these first)
    ASSERT(pszFriendlyName);
    ASSERT(pcc);
    ASSERT(sizeof(*pcc) <= pcc->dwSize);

    dwRet = DoProperties(pszFriendlyName, hwndOwner, pcc);

    return dwRet;
    }


/*----------------------------------------------------------
Purpose: Gets the default COMMCONFIG for the specified device.
         This API doesn't require a handle.

         If the caller passed in a null device name or a null
         commconfig pointer, this function will set *pdwSize to
         the minimum COMMCONFIG size.  Calling this function
         a second time (after setting the dwSize and dwProviderSubType
         fields) will verify if the size is correct.

         So generally, when getting a commconfig for serial ports,
         the process is:

         COMMCONFIG ccDummy;
         LPCOMMCONFIG pcc;
         DWORD dwSize = sizeof(*pcc);

         // Determine real size of COMMCONFIG for RS-232 subtype
         ccDummy.dwProviderSubType = PST_RS232;
         GetDefaultCommConfig(pszFriendlyName, &ccDummy, &dwSize);

         // Allocate real commconfig struct and initialize
         pcc = LocalAlloc(LPTR, dwSize);
         if (pcc)
            {
            pcc->dwProviderSubType = PST_RS232;
            GetDefaultCommConfig(pszFriendlyName, pcc, &dwSize);
            ....
            }

Returns: One of the ERROR_ values in winerror.h

Cond:    --
*/
DWORD 
PRIVATE 
MyGetDefaultCommConfig(
    IN  LPFINDDEV   pfd,
    IN  LPCTSTR     pszFriendlyName,
    OUT LPCOMMCONFIG pcc,
    OUT LPDWORD     pdwSize)
    {
    DWORD dwRet;
    
    ASSERT(pfd);
    // (Wrapper should have checked these first)
    ASSERT(pszFriendlyName);
    ASSERT(pcc);
    ASSERT(pdwSize);
    ASSERT(sizeof(*pcc) <= *pdwSize);

    *pdwSize = sizeof(*pcc);

    // Initialize the commconfig structure
    pcc->dwSize = *pdwSize;
    pcc->wVersion = COMMCONFIG_VERSION_1;
    pcc->dwProviderSubType = PST_RS232;
    pcc->dwProviderOffset = 0;
    pcc->dwProviderSize = 0;

    dwRet = RegQueryDCB(pfd, &pcc->dcb);

    DEBUG_CODE( DumpDCB(&pcc->dcb); )

    return dwRet;
    }


/*----------------------------------------------------------
Purpose: Sets the default COMMCONFIG for the specified device.
         This API doesn't require a handle.  This function
         strictly modifies the registry.  Use SetCommConfig
         to set the COMMCONFIG of an open device.

         If the dwSize parameter or the dwSize field are invalid 
         sizes (given the dwProviderSubType field in COMMCONFIG), 
         then this function fails.

Returns: One of the ERROR_ return values

Cond:    --
*/
DWORD 
PRIVATE 
MySetDefaultCommConfig(
    IN LPFINDDEV    pfd,
    IN LPCTSTR      pszFriendlyName,
    IN LPCOMMCONFIG pcc)
    {
    DWORD dwRet;
    TCHAR szValue[MAX_BUF_SHORT];
    TCHAR szKey[MAX_BUF_SHORT];

    ASSERT(pfd);
    // (Wrapper should have checked these first)
    ASSERT(pszFriendlyName);
    ASSERT(pcc);
    ASSERT(sizeof(*pcc) <= pcc->dwSize);

    ASSERT(0 == pcc->dwProviderSize);
    ASSERT(0 == pcc->dwProviderOffset);

    dwRet = RegSetDCB(pfd, &pcc->dcb);

    if (NO_ERROR == dwRet)
        {
        // For Win 3.1 compatibility, write some info to win.ini
        lstrcpy(szKey, pfd->szPort);
        lstrcat(szKey, TEXT(":"));

        // Delete the old win.ini entry first
        WriteProfileString(c_szPortClass, szKey, NULL);

        ComposeModeComString(pcc, szValue);
        WriteProfileString(c_szPortClass, szKey, szValue);

#ifdef WIN95
            {
            DWORD dwRecipients;

            // Send a broadcast proclaiming that the win.ini has changed
            // (Use the internal BroadcastSystemMessage to avoid deadlocks.
            // SendMessageTimeout would be more appropriate, but that is
            // not exported for 16-bit dlls.  PostMessage is not good because
            // lParam is a pointer.)

            dwRecipients = BSM_APPLICATIONS;
            BroadcastSystemMessage(BSF_NOHANG, &dwRecipients, WM_WININICHANGE, 
                NULL, (LPARAM)c_szPortClass);
            }
#else
            {
            SendNotifyMessage(HWND_BROADCAST, WM_WININICHANGE, 0, (LPARAM)c_szPortClass);
            }
#endif
        }


    DEBUG_CODE( DumpDCB(&pcc->dcb); )
        
    return dwRet;
    }


//-----------------------------------------------------------------------------------
//  Entry-points provided for KERNEL32 APIs
//-----------------------------------------------------------------------------------


DWORD 
APIENTRY 
#ifdef UNICODE
drvCommConfigDialogA(
    IN     LPCSTR       pszFriendlyName,
    IN     HWND         hwndOwner,
    IN OUT LPCOMMCONFIG pcc)
#else
drvCommConfigDialogW(
    IN     LPCWSTR      pszFriendlyName,
    IN     HWND         hwndOwner,
    IN OUT LPCOMMCONFIG pcc)
#endif
    {
    return ERROR_CALL_NOT_IMPLEMENTED;
    }


/*----------------------------------------------------------
Purpose: Entry point for CommConfigDialog

Returns: standard error value in winerror.h
Cond:    --
*/
DWORD 
APIENTRY 
drvCommConfigDialog(
    IN     LPCTSTR      pszFriendlyName,
    IN     HWND         hwndOwner,
    IN OUT LPCOMMCONFIG pcc)
    {
    DWORD dwRet;
    LPFINDDEV pfd;

    DEBUG_CODE( TRACE_MSG(TF_FUNC, "drvCommConfigDialog(%s, ...) entered",
                Dbg_SafeStr(pszFriendlyName)); )

    // We support friendly names (eg, "Communications Port (COM1)") or 
    // portname values (eg, "COM1").

    if (NULL == pszFriendlyName || 
        NULL == pcc)
        {
        dwRet = ERROR_INVALID_PARAMETER;
        }
    // Is the size sufficient?
    else if (sizeof(*pcc) > pcc->dwSize)
        {
        // No
        dwRet = ERROR_INSUFFICIENT_BUFFER;
        }
    else if (FindDev_Create(&pfd, c_pguidPort, c_szFriendlyName, pszFriendlyName) ||
        FindDev_Create(&pfd, c_pguidPort, c_szPortName, pszFriendlyName) ||
        FindDev_Create(&pfd, c_pguidModem, c_szPortName, pszFriendlyName))
        {
        dwRet = MyCommConfigDialog(pfd, pszFriendlyName, hwndOwner, pcc);

        FindDev_Destroy(pfd);
        }
    else
        {
        dwRet = ERROR_BADKEY;
        }

    DBG_EXIT_DWORD("drvCommConfigDialog", dwRet);

    return dwRet;
    }


DWORD 
APIENTRY 
#ifdef UNICODE
drvGetDefaultCommConfigA(
    IN     LPCSTR       pszFriendlyName,
    IN     LPCOMMCONFIG pcc,
    IN OUT LPDWORD      pdwSize)
#else
drvGetDefaultCommConfigW(
    IN     LPCWSTR      pszFriendlyName,
    IN     LPCOMMCONFIG pcc,
    IN OUT LPDWORD      pdwSize)
#endif
    {
    return ERROR_CALL_NOT_IMPLEMENTED;
    }


/*----------------------------------------------------------
Purpose: Entry point for GetDefaultCommConfig

Returns: standard error value in winerror.h
Cond:    --
*/
DWORD 
APIENTRY 
drvGetDefaultCommConfig(
    IN     LPCTSTR      pszFriendlyName,
    IN     LPCOMMCONFIG pcc,
    IN OUT LPDWORD      pdwSize)
    {
    DWORD dwRet;
    LPFINDDEV pfd;

    DEBUG_CODE( TRACE_MSG(TF_FUNC, "drvGetDefaultCommConfig(%s, ...) entered",
                Dbg_SafeStr(pszFriendlyName)); )

    // We support friendly names (eg, "Communications Port (COM1)") or 
    // portname values (eg, "COM1").

    if (NULL == pszFriendlyName || 
        NULL == pcc || 
        NULL == pdwSize)
        {
        dwRet = ERROR_INVALID_PARAMETER;
        }
    // Is the size sufficient?
    else if (sizeof(*pcc) > *pdwSize)
        {
        // No; return correct value
        dwRet = ERROR_INSUFFICIENT_BUFFER;
        *pdwSize = sizeof(*pcc);
        }
    else if (FindDev_Create(&pfd, c_pguidPort, c_szFriendlyName, pszFriendlyName) ||
        FindDev_Create(&pfd, c_pguidPort, c_szPortName, pszFriendlyName) ||
        FindDev_Create(&pfd, c_pguidModem, c_szPortName, pszFriendlyName))
        {
        dwRet = MyGetDefaultCommConfig(pfd, pszFriendlyName, pcc, pdwSize);

        FindDev_Destroy(pfd);
        }
    else
        {
        dwRet = ERROR_BADKEY;
        }

    DBG_EXIT_DWORD("drvGetDefaultCommConfig", dwRet);

    return dwRet;
    }


DWORD 
APIENTRY 
#ifdef UNICODE
drvSetDefaultCommConfigA(
    IN LPSTR        pszFriendlyName,
    IN LPCOMMCONFIG pcc,
    IN DWORD        dwSize)           
#else
drvSetDefaultCommConfigW(
    IN LPWSTR       pszFriendlyName,
    IN LPCOMMCONFIG pcc,
    IN DWORD        dwSize)           
#endif
    {
    return ERROR_CALL_NOT_IMPLEMENTED;
    }


/*----------------------------------------------------------
Purpose: Entry point for SetDefaultCommConfig

Returns: standard error value in winerror.h
Cond:    --
*/
DWORD 
APIENTRY 
drvSetDefaultCommConfig(
    IN LPTSTR       pszFriendlyName,
    IN LPCOMMCONFIG pcc,
    IN DWORD        dwSize)
    {
    DWORD dwRet;
    LPFINDDEV pfd;


    DEBUG_CODE( TRACE_MSG(TF_FUNC, "drvSetDefaultCommConfig(%s, ...) entered",
                Dbg_SafeStr(pszFriendlyName)); )

    // We support friendly names (eg, "Communications Port (COM1)") or 
    // portname values (eg, "COM1").

    if (NULL == pszFriendlyName || 
        NULL == pcc)
        {
        dwRet = ERROR_INVALID_PARAMETER;
        }
    // Is the size sufficient?
    else if ((sizeof(*pcc) > pcc->dwSize) || (sizeof(*pcc) > dwSize))
        {
        // No
        dwRet = ERROR_INSUFFICIENT_BUFFER;
        }
    else if (FindDev_Create(&pfd, c_pguidPort, c_szFriendlyName, pszFriendlyName) ||
        FindDev_Create(&pfd, c_pguidPort, c_szPortName, pszFriendlyName) ||
        FindDev_Create(&pfd, c_pguidModem, c_szPortName, pszFriendlyName))
        {
        dwRet = MySetDefaultCommConfig(pfd, pszFriendlyName, pcc);

        FindDev_Destroy(pfd);
        }
    else
        {
        dwRet = ERROR_BADKEY;
        }

    DBG_EXIT_DWORD("drvSetDefaultCommConfig", dwRet);

    return dwRet;
    }