update for HEAD-2003021201
[reactos.git] / lib / kernel32 / misc / comm.c
index 4ffd707..51b4adc 100644 (file)
  * 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