* UPDATE HISTORY:
* Created 01/11/98
* RDD (30/09/2002) implemented many function bodies to call serial driver.
+ * KJK (11/02/2003) implemented BuildCommDCB & BuildCommDCBAndTimeouts
*/
-#include <ddk/ntddk.h>
-#include <ddk/ntddser.h>
-#include <kernel32/proc.h>
-#include <kernel32/thread.h>
-#include <wchar.h>
-#include <string.h>
+#include <k32.h>
-//#define NDEBUG
-#define DBG
+#define NDEBUG
#include <kernel32/kernel32.h>
-#include <kernel32/error.h>
+/* BUILDCOMMDCB & BUILDCOMMDCBANDTIMEOUTS */
+
+/* TYPES */
+
+/* Pointer to a callback that handles a particular parameter */
+typedef BOOL (*COMMDCB_PARAM_CALLBACK)
+(
+ DCB *,
+ COMMTIMEOUTS *,
+ BOOL *,
+ LPWSTR *
+);
+
+/* Symbolic flag of any length */
+typedef struct _COMMDCB_PARAM_STRFLAG
+{
+ UNICODE_STRING String;
+ ULONG_PTR Value;
+} COMMDCB_PARAM_STRFLAG;
+
+/* One char long symbolic flag */
+typedef struct _COMMDCB_PARAM_CHARFLAG
+{
+ WCHAR Char;
+ ULONG_PTR Value;
+} COMMDCB_PARAM_CHARFLAG;
+
+/* MACROS */
+/* stupid Borland C++ requires this */
+#define _L(__S__) L ## __S__
+
+/* Declare a parameter handler */
+#define COMMDCB_PARAM_HANDLER(__P__) \
+ BOOL COMMDCB_ ## __P__ ## Param \
+ ( \
+ DCB * Dcb, \
+ COMMTIMEOUTS * Timeouts, \
+ BOOL * StopBitsSet, \
+ LPWSTR * StrTail \
+ )
+
+/* UTILITIES */
+/*
+ Lookup a string flag and return its numerical value. The flags array must be
+ sorted - a dichotomycal search is performed
+*/
+BOOL COMMDCB_LookupStrFlag
+(
+ UNICODE_STRING * Flag,
+ COMMDCB_PARAM_STRFLAG * Flags,
+ int FlagCount,
+ ULONG_PTR * Value
+)
+{
+ /* Lower and upper bound for dichotomycal search */
+ int nLowerBound = 0;
+ int nUpperBound = FlagCount - 1;
+
+ do
+ {
+ LONG nComparison;
+ /* pick the element in the middle of the area of interest as the pivot */
+ int nCurFlag = nLowerBound + (nUpperBound - nLowerBound) / 2;
+
+ /* compare the string with the pivot */
+ nComparison = RtlCompareUnicodeString
+ (
+ Flag,
+ &Flags[nCurFlag].String,
+ TRUE
+ );
+
+ /* string is equal */
+ if(nComparison == 0)
+ {
+ /* return the flag's value */
+ *Value = Flags[nCurFlag].Value;
+
+ /* success */
+ return TRUE;
+ }
+ /* string is less than */
+ else if(nComparison < 0)
+ {
+ /*
+ restrict the search to the first half of the current slice, minus the pivot
+ */
+ nUpperBound = nCurFlag - 1;
+
+ /* fallthrough */
+ }
+ /* string is greater than */
+ else
+ {
+ /*
+ restrict the search to the second half of the current slice, minus the pivot
+ */
+ nLowerBound = nCurFlag + 1;
+
+ /* fallthrough */
+ }
+ }
+ /* continue until the slice is empty */
+ while(nLowerBound <= nUpperBound);
+
+ /* string not found: failure */
+ return FALSE;
+}
+
+/* PARSERS */
+/*
+ Find the next character flag and return its numerical value. The flags array
+ must be sorted - a dichotomycal search is performed
+*/
+BOOL COMMDCB_ParseCharFlag
+(
+ LPWSTR * StrTail,
+ COMMDCB_PARAM_CHARFLAG * Flags,
+ int FlagCount,
+ ULONG_PTR * Value
+)
+{
+ /* Lower and upper bound for dichotomycal search */
+ int nLowerBound = 0;
+ int nUpperBound = FlagCount - 1;
+ /* get the first character as the flag */
+ WCHAR wcFlag = (*StrTail)[0];
+
+ /* premature end of string, or the character is whitespace */
+ if(!wcFlag || iswspace(wcFlag))
+ /* failure */
+ return FALSE;
+
+ /* uppercase the character for case-insensitive search */
+ wcFlag = towupper(wcFlag);
+
+ /* skip the character flag */
+ ++ (*StrTail);
+
+ /* see COMMDCB_LookupStrFlag for a description of the algorithm */
+ do
+ {
+ LONG nComparison;
+ int nCurFlag = nLowerBound + (nUpperBound - nLowerBound) / 2;
+
+ nComparison = wcFlag - towupper(Flags[nCurFlag].Char);
+
+ if(nComparison == 0)
+ {
+ *Value = Flags[nCurFlag].Value;
+
+ return TRUE;
+ }
+ else if(nComparison < 0)
+ {
+ nUpperBound = nCurFlag - 1;
+ }
+ else
+ {
+ nLowerBound = nCurFlag + 1;
+ }
+ }
+ while(nUpperBound >= nLowerBound);
+
+ /* flag not found: failure */
+ return FALSE;
+}
+
+/*
+ Find the next string flag and return its numerical value. The flags array must
+ be sorted - a dichotomycal search is performed
+*/
+BOOL COMMDCB_ParseStrFlag
+(
+ LPWSTR * StrTail,
+ COMMDCB_PARAM_STRFLAG * Flags,
+ int FlagCount,
+ ULONG_PTR * Value
+)
+{
+ LPWSTR pwcNewTail = *StrTail;
+ UNICODE_STRING wstrFlag;
+
+ /* scan the string until the first space character or the terminating null */
+ while(pwcNewTail[0] && !iswspace(pwcNewTail[0]))
+ ++ pwcNewTail;
+
+ /* string flag empty */
+ if(pwcNewTail == *StrTail)
+ /* failure */
+ return FALSE;
+
+ /* build the UNICODE_STRING description of the string flag */
+ wstrFlag.Buffer = *StrTail;
+ wstrFlag.Length = (pwcNewTail - *StrTail) * sizeof(WCHAR);
+ wstrFlag.MaximumLength = wstrFlag.Length;
+
+ /* skip the string flag */
+ *StrTail = pwcNewTail;
+
+ /* lookup the string flag's value and return it */
+ return COMMDCB_LookupStrFlag(&wstrFlag, Flags, FlagCount, Value);
+}
+
+/*
+ Parse a boolean value in the symbolic form on/off
+*/
+BOOL COMMDCB_ParseBool(LPWSTR * StrTail, BOOL * Value)
+{
+ BOOL bRetVal;
+ ULONG_PTR nValue;
+ static COMMDCB_PARAM_STRFLAG a_BoolFlags[] =
+ {
+ { UNICODE_STRING_INITIALIZER(L"off"), FALSE },
+ { UNICODE_STRING_INITIALIZER(L"on"), TRUE }
+ };
+
+ /* try to recognize the next flag as a boolean */
+ bRetVal = COMMDCB_ParseStrFlag
+ (
+ StrTail,
+ a_BoolFlags,
+ sizeof(a_BoolFlags) / sizeof(a_BoolFlags[0]),
+ &nValue
+ );
+
+ /* failure */
+ if(!bRetVal) return FALSE;
+
+ /* success */
+ *Value = nValue ? TRUE : FALSE;
+ return TRUE;
+}
+
+/*
+ Parse a decimal integer
+*/
+BOOL COMMDCB_ParseInt(LPWSTR * StrTail, DWORD * Value)
+{
+ LPWSTR pwcPrevTail = *StrTail;
+ DWORD nValue = wcstoul(*StrTail, StrTail, 10);
+
+ /* no character was consumed: failure */
+ if(pwcPrevTail == *StrTail) return FALSE;
+
+ /* success */
+ *Value = nValue;
+ return TRUE;
+}
+
+/* PARAMETER HANDLERS */
+/* baud= */
+COMMDCB_PARAM_HANDLER(baud)
+{
+ DWORD nValue;
+
+ (void)Timeouts;
+
+ /* parse the baudrate */
+ if(!COMMDCB_ParseInt(StrTail, &nValue))
+ /* failure */
+ return FALSE;
+
+ switch(nValue)
+ {
+ /* documented abbreviations */
+ case 11: Dcb->BaudRate = 110; break;
+ case 15: Dcb->BaudRate = 150; break;
+ case 30: Dcb->BaudRate = 300; break;
+ case 60: Dcb->BaudRate = 600; break;
+ case 12: Dcb->BaudRate = 1200; break;
+ case 24: Dcb->BaudRate = 2400; break;
+ case 48: Dcb->BaudRate = 4800; break;
+ case 96: Dcb->BaudRate = 9600; break;
+ case 19: Dcb->BaudRate = 19200; break;
+ /* literal value */
+ default: Dcb->BaudRate = nValue; break;
+ }
+
+ /* if the stop bits haven't been specified explicitely */
+ if(!(*StopBitsSet))
+ {
+ /* default the stop bits to 2 for 110 baud */
+ if(Dcb->BaudRate == 110) Dcb->StopBits = TWOSTOPBITS;
+ /* else, default the stop bits to 1 */
+ else Dcb->StopBits = ONESTOPBIT;
+ }
+
+ /* success */
+ return TRUE;
+}
+
+/* data= */
+COMMDCB_PARAM_HANDLER(data)
+{
+ DWORD nValue;
+
+ (void)Timeouts;
+ (void)StopBitsSet;
+
+ /* parse the data bits */
+ if(!COMMDCB_ParseInt(StrTail, &nValue))
+ /* failure */
+ return FALSE;
+
+ /* value out of range: failure */
+ if(nValue < 5 || nValue > 8) return FALSE;
+
+ /* success */
+ Dcb->ByteSize = nValue;
+ return TRUE;
+}
+
+/* dtr= */
+COMMDCB_PARAM_HANDLER(dtr)
+{
+ BOOL bRetVal;
+ ULONG_PTR nValue;
+ static COMMDCB_PARAM_STRFLAG a_DTRFlags[] =
+ {
+ { UNICODE_STRING_INITIALIZER(L"hs"), DTR_CONTROL_HANDSHAKE },
+ { UNICODE_STRING_INITIALIZER(L"off"), DTR_CONTROL_DISABLE },
+ { UNICODE_STRING_INITIALIZER(L"on"), DTR_CONTROL_ENABLE }
+ };
+
+ (void)Timeouts;
+ (void)StopBitsSet;
+
+ /* parse the flag */
+ bRetVal = COMMDCB_ParseStrFlag
+ (
+ StrTail,
+ a_DTRFlags,
+ sizeof(a_DTRFlags) / sizeof(a_DTRFlags[0]),
+ &nValue
+ );
+
+ /* failure */
+ if(!bRetVal) return FALSE;
+
+ /* success */
+ Dcb->fDtrControl = nValue;
+ return TRUE;
+}
+
+/* idsr= */
+COMMDCB_PARAM_HANDLER(idsr)
+{
+ BOOL bValue;
+
+ (void)Timeouts;
+ (void)StopBitsSet;
+
+ /* parse the flag */
+ if(!COMMDCB_ParseBool(StrTail, &bValue))
+ /* failure */
+ return FALSE;
+
+ /* success */
+ Dcb->fDsrSensitivity = bValue;
+ return TRUE;
+}
+
+/* octs= */
+COMMDCB_PARAM_HANDLER(octs)
+{
+ BOOL bValue;
+
+ (void)Timeouts;
+ (void)StopBitsSet;
+
+ /* parse the flag */
+ if(!COMMDCB_ParseBool(StrTail, &bValue))
+ /* failure */
+ return FALSE;
+
+ /* success */
+ Dcb->fOutxCtsFlow = bValue;
+ return TRUE;
+}
+
+/* odsr= */
+COMMDCB_PARAM_HANDLER(odsr)
+{
+ BOOL bValue;
+
+ (void)Timeouts;
+ (void)StopBitsSet;
+
+ /* parse the flag */
+ if(!COMMDCB_ParseBool(StrTail, &bValue))
+ /* failure */
+ return FALSE;
+
+ /* success */
+ Dcb->fOutxDsrFlow = bValue;
+ return TRUE;
+}
+
+/* parity= */
+COMMDCB_PARAM_HANDLER(parity)
+{
+ BOOL bRetVal;
+ ULONG_PTR nValue;
+ static COMMDCB_PARAM_CHARFLAG a_ParityFlags[] =
+ {
+ { L'e', EVENPARITY },
+ { L'm', MARKPARITY },
+ { L'n', NOPARITY },
+ { L'o', ODDPARITY },
+ { L's', SPACEPARITY }
+ };
+
+ (void)Timeouts;
+ (void)StopBitsSet;
+
+ /* parse the flag */
+ bRetVal = COMMDCB_ParseCharFlag
+ (
+ StrTail,
+ a_ParityFlags,
+ sizeof(a_ParityFlags) / sizeof(a_ParityFlags[0]),
+ &nValue
+ );
+
+ /* failure */
+ if(!bRetVal) return FALSE;
+
+ /* success */
+ Dcb->Parity = nValue;
+ return TRUE;
+}
+
+/* rts= */
+COMMDCB_PARAM_HANDLER(rts)
+{
+ DWORD nRetVal;
+ ULONG_PTR nValue;
+ static COMMDCB_PARAM_STRFLAG a_RTSFlags[] =
+ {
+ { UNICODE_STRING_INITIALIZER(L"hs"), RTS_CONTROL_HANDSHAKE },
+ { UNICODE_STRING_INITIALIZER(L"off"), RTS_CONTROL_DISABLE },
+ { UNICODE_STRING_INITIALIZER(L"on"), RTS_CONTROL_ENABLE },
+ { UNICODE_STRING_INITIALIZER(L"tg"), RTS_CONTROL_TOGGLE }
+ };
+
+ (void)Timeouts;
+ (void)StopBitsSet;
+
+ /* parse the flag */
+ nRetVal = COMMDCB_ParseStrFlag
+ (
+ StrTail,
+ a_RTSFlags,
+ sizeof(a_RTSFlags) / sizeof(a_RTSFlags[0]),
+ &nValue
+ );
+
+ /* failure */
+ if(!nRetVal) return FALSE;
+
+ /* success */
+ Dcb->fRtsControl = nValue;
+ return TRUE;
+}
+
+/* stop= */
+COMMDCB_PARAM_HANDLER(stop)
+{
+ BOOL bRetVal;
+ ULONG_PTR nValue;
+ static COMMDCB_PARAM_STRFLAG a_StopFlags[] =
+ {
+ { UNICODE_STRING_INITIALIZER(L"1"), ONESTOPBIT },
+ { UNICODE_STRING_INITIALIZER(L"1.5"), ONE5STOPBITS },
+ { UNICODE_STRING_INITIALIZER(L"2"), TWOSTOPBITS }
+ };
+
+ (void)Timeouts;
+
+ /* parse the flag */
+ bRetVal = COMMDCB_ParseStrFlag
+ (
+ StrTail,
+ a_StopFlags,
+ sizeof(a_StopFlags) / sizeof(a_StopFlags[0]),
+ &nValue
+ );
+
+ /* failure */
+ if(!bRetVal) return FALSE;
+
+ /* tell the baud= handler that the stop bits have been specified explicitely */
+ *StopBitsSet = TRUE;
+
+ /* success */
+ Dcb->StopBits = nValue;
+ return TRUE;
+}
+
+/* to= */
+COMMDCB_PARAM_HANDLER(to)
+{
+ BOOL bValue;
+
+ (void)Dcb;
+ (void)StopBitsSet;
+
+ /* parse the flag */
+ if(!COMMDCB_ParseBool(StrTail, &bValue))
+ /* failure */
+ return FALSE;
+
+ /* for BuildCommDCB(), Timeouts is NULL */
+ if(Timeouts)
+ {
+ /* why? no idea. All values taken from Windows 2000 with experimentation */
+ Timeouts->ReadIntervalTimeout = 0;
+ Timeouts->ReadTotalTimeoutMultiplier = 0;
+ Timeouts->ReadTotalTimeoutConstant = 0;
+ Timeouts->WriteTotalTimeoutMultiplier = 0;
+
+ /* timeout */
+ if(bValue) Timeouts->WriteTotalTimeoutConstant = 60000;
+ /* no timeout */
+ else Timeouts->WriteTotalTimeoutConstant = 0;
+ }
+
+ /* success */
+ return TRUE;
+}
+
+/* xon= */
+COMMDCB_PARAM_HANDLER(xon)
+{
+ BOOL bValue;
+
+ (void)Timeouts;
+ (void)StopBitsSet;
+
+ /* parse the flag */
+ if(!COMMDCB_ParseBool(StrTail, &bValue))
+ /* failure */
+ return FALSE;
+
+ /* XON/XOFF */
+ if(bValue) Dcb->fInX = Dcb->fOutX = TRUE;
+ /* no XON/XOFF */
+ else Dcb->fInX = Dcb->fOutX = FALSE;
+
+ /* success */
+ return TRUE;
+}
+
+/* FUNCTIONS */
+#define COMMDCB_PARAM(__P__) \
+ { \
+ UNICODE_STRING_INITIALIZER(_L(#__P__)), \
+ (ULONG_PTR)&COMMDCB_ ## __P__ ## Param \
+ }
WINBOOL
STDCALL
-BuildCommDCBA(LPCSTR lpDef, LPDCB lpDCB)
+BuildCommDCBAndTimeoutsW
+(
+ LPCWSTR lpDef,
+ LPDCB lpDCB,
+ LPCOMMTIMEOUTS lpCommTimeouts
+)
{
- if (lpDCB == NULL) {
- DPRINT("ERROR: BuildCommDCBA() - NULL DCB pointer\n");
- return FALSE;
- }
- SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
- return FALSE;
+ /* tell the baud= handler that the stop bits should be defaulted */
+ BOOL bStopBitsSet = FALSE;
+
+ /* parameter validation */
+ if(lpDCB->DCBlength != sizeof(DCB)) goto InvalidParam;
+
+ /* set defaults */
+ lpDCB->StopBits = ONESTOPBIT;
+
+ /*
+ The documentation for MODE says that data= defaults to 7, but BuildCommDCB
+ doesn't seem to set it
+ */
+ /* lpDCB->ByteSize = 7; */
+
+ /* skip COMx[n] */
+ if
+ (
+ lpDef[0] &&
+ towupper(lpDef[0]) == L'C' &&
+ lpDef[1] &&
+ towupper(lpDef[1]) == L'O' &&
+ lpDef[2] &&
+ towupper(lpDef[2]) == L'M'
+ )
+ {
+ DWORD nDummy;
+
+ /* skip "COM" */
+ lpDef += 3;
+
+ /* premature end of string */
+ if(!lpDef[0]) goto InvalidParam;
+
+ /* skip "x" */
+ if(!COMMDCB_ParseInt((LPWSTR *)&lpDef, &nDummy)) goto InvalidParam;
+
+ /* skip ":" */
+ if(lpDef[0] == L':') ++ lpDef;
+ }
+
+ /* skip leading whitespace */
+ while(lpDef[0] && iswspace(lpDef[0])) ++ lpDef;
+
+ /* repeat until the end of the string */
+ while(lpDef[0])
+ {
+ static COMMDCB_PARAM_STRFLAG a_Params[] =
+ {
+ COMMDCB_PARAM(baud),
+ COMMDCB_PARAM(data),
+ COMMDCB_PARAM(dtr),
+ COMMDCB_PARAM(idsr),
+ COMMDCB_PARAM(octs),
+ COMMDCB_PARAM(odsr),
+ COMMDCB_PARAM(parity),
+ COMMDCB_PARAM(rts),
+ COMMDCB_PARAM(stop),
+ COMMDCB_PARAM(to),
+ COMMDCB_PARAM(xon)
+ };
+ BOOL bRetVal;
+ COMMDCB_PARAM_CALLBACK pCallback;
+ UNICODE_STRING wstrParam;
+ LPWSTR pwcPrevTail = (LPWSTR)lpDef;
+
+ /* get the parameter */
+ while(lpDef[0] && lpDef[0] != L'=') ++ lpDef;
+
+ /* premature end of string */
+ if(!lpDef[0]) goto InvalidParam;
+
+ /* build the parameter's UNICODE_STRING */
+ wstrParam.Buffer = pwcPrevTail;
+ wstrParam.Length = (lpDef - pwcPrevTail) * sizeof(WCHAR);
+ wstrParam.MaximumLength = wstrParam.Length;
+
+ /* skip the "=" */
+ ++ lpDef;
+
+ /* lookup the callback for the parameter */
+ bRetVal = COMMDCB_LookupStrFlag
+ (
+ &wstrParam,
+ a_Params,
+ sizeof(a_Params) / sizeof(a_Params[0]),
+ (ULONG_PTR *)&pCallback
+ );
+
+ /* invalid parameter */
+ if(!bRetVal) goto InvalidParam;
+
+ /* call the callback to parse the parameter's argument */
+ if(!pCallback(lpDCB, lpCommTimeouts, &bStopBitsSet, (LPWSTR *)&lpDef))
+ /* failure */
+ goto InvalidParam;
+
+ /* skip trailing whitespace */
+ while(lpDef[0] && iswspace(lpDef[0])) ++ lpDef;
+ }
+
+ /* success */
+ return TRUE;
+
+InvalidParam:
+ /* failure */
+ SetLastError(ERROR_INVALID_PARAMETER);
+ return FALSE;
}
+
WINBOOL
STDCALL
-BuildCommDCBW(LPCWSTR lpDef, LPDCB lpDCB)
+BuildCommDCBAndTimeoutsA(LPCSTR lpDef, LPDCB lpDCB, LPCOMMTIMEOUTS lpCommTimeouts)
{
- if (lpDCB == NULL) {
- DPRINT("ERROR: BuildCommDCBW() - NULL DCB pointer\n");
- return FALSE;
- }
- SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
- return FALSE;
+ NTSTATUS nErrCode;
+ WINBOOL bRetVal;
+ ANSI_STRING strDef;
+ UNICODE_STRING wstrDef;
+
+ RtlInitAnsiString(&strDef, (LPSTR)lpDef);
+
+ nErrCode = RtlAnsiStringToUnicodeString(&wstrDef, &strDef, TRUE);
+
+ if(!NT_SUCCESS(nErrCode))
+ {
+ SetLastErrorByStatus(nErrCode);
+ return FALSE;
+ }
+
+ bRetVal = BuildCommDCBAndTimeoutsW(wstrDef.Buffer, lpDCB, lpCommTimeouts);
+
+ RtlFreeUnicodeString(&wstrDef);
+
+ return bRetVal;
}
WINBOOL
STDCALL
-BuildCommDCBAndTimeoutsA(LPCSTR lpDef, LPDCB lpDCB, LPCOMMTIMEOUTS lpCommTimeouts)
+BuildCommDCBA(LPCSTR lpDef, LPDCB lpDCB)
{
- if (lpDCB == NULL) {
- DPRINT("ERROR: BuildCommDCBAndTimeoutsA() - NULL DCB pointer\n");
- return FALSE;
- }
- SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
- return FALSE;
+ return BuildCommDCBAndTimeoutsA(lpDef, lpDCB, NULL);
}
WINBOOL
STDCALL
-BuildCommDCBAndTimeoutsW(LPCWSTR lpDef, LPDCB lpDCB, LPCOMMTIMEOUTS lpCommTimeouts)
+BuildCommDCBW(LPCWSTR lpDef, LPDCB lpDCB)
{
- if (lpDCB == NULL) {
- DPRINT("ERROR: BuildCommDCBAndTimeoutsW() - NULL DCB pointer\n");
- return FALSE;
- }
- SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
- return FALSE;
+ return BuildCommDCBAndTimeoutsW(lpDef, lpDCB, NULL);
}
WINBOOL