Provide support for the FTDI serial properties.
authorLeopold Gerlinger <leopold.gerlinger@siemens.com>
Wed, 3 Dec 2008 11:48:37 +0000 (12:48 +0100)
committerPat Thoyts <patthoyts@users.sourceforge.net>
Sun, 7 Dec 2008 18:23:02 +0000 (18:23 +0000)
This patch makes it possible to use -mode, -handshake and -xchar in
the fconfigure command similar to using direct serial
lines. Additionally I extended the open procedure to set the defaults
to 19200,n,8,1 no handshake and standard xon/xoff chars. If you know
about another way of doing these things please let me know.

The -xchar option is currently only supporting chars in the range from
1 .. 127 dec, maybe you know of a way how to extend this to at least
the full ASCII charset (I'm obviously no expert in encodings ;-).

Signed-off-by: Leopold Gerlinger <leopold.gerlinger@siemens.com>
Signed-off-by: Pat Thoyts <patthoyts@users.sourceforge.net>
tclftd2xx.c

index 1185d2b70c2f7c6b264f3525281de2f0915c96fa..f025f113d82b593243f0a564b334a6694a5314cd 100644 (file)
@@ -15,6 +15,8 @@
 #include <errno.h>
 #include "ftd2xx.h"
 
+#define PACKAGE_VERSION        "1.0.0"
+
 typedef FT_STATUS (WINAPI FT_CloseProc)(FT_HANDLE);
 typedef FT_STATUS (WINAPI FT_CreateDeviceInfoListProc)(LPDWORD);
 typedef FT_STATUS (WINAPI FT_GetDeviceInfoListProc)
@@ -31,6 +33,10 @@ typedef FT_STATUS (WINAPI FT_SetTimeoutsProc)(FT_HANDLE,ULONG,ULONG);
 typedef FT_STATUS (WINAPI FT_WriteProc)(FT_HANDLE,LPVOID,DWORD,LPDWORD);
 typedef FT_STATUS (WINAPI FT_GetLibraryVersionProc)(LPDWORD);
 typedef FT_STATUS (WINAPI FT_RescanProc)(VOID);
+typedef FT_STATUS (WINAPI FT_SetBaudRateProc)(FT_HANDLE,ULONG);
+typedef FT_STATUS (WINAPI FT_SetDataCharacteristicsProc)
+    (FT_HANDLE,UCHAR,UCHAR,UCHAR);
+typedef FT_STATUS (WINAPI FT_SetFlowControlProc)(FT_HANDLE,USHORT,UCHAR,UCHAR);
 
 typedef struct FTDIPROCS {
     FT_CloseProc *FT_Close;
@@ -48,6 +54,9 @@ typedef struct FTDIPROCS {
     FT_WriteProc *FT_Write;
     FT_GetLibraryVersionProc *FT_GetLibraryVersion;
     FT_RescanProc *FT_Rescan;
+    FT_SetBaudRateProc *FT_SetBaudRate;
+    FT_SetDataCharacteristicsProc *FT_SetDataCharacteristics;
+    FT_SetFlowControlProc *FT_SetFlowControl;
 } FTDIPROCS;
 
 static FTDIPROCS procs;
@@ -64,6 +73,13 @@ typedef struct Channel {
     int flags;
     int watchmask;
     int validmask;
+       int baudrate;
+       unsigned char parity;
+       unsigned short handshake;
+       char xonchar;
+       char xoffchar;
+       unsigned char databits;
+       unsigned char stopbits;
     unsigned long rxtimeout;
     unsigned long txtimeout;
     FT_HANDLE handle;
@@ -271,11 +287,92 @@ ChannelSetOption(ClientData instance, Tcl_Interp *interp,
            }
        }
     } else if (!strcmp("-latency", optionName)) {
-       int tmp = 1;
-       r = Tcl_GetInt(interp, newValue, &tmp);
-       if (r == TCL_OK) {
-           fts = procs.FT_SetLatencyTimer(instPtr->handle, (UCHAR)tmp);
-       }
+               int tmp = 1;
+               r = Tcl_GetInt(interp, newValue, &tmp);
+               if (r == TCL_OK) {
+                       fts = procs.FT_SetLatencyTimer(instPtr->handle, (UCHAR)tmp);
+               }
+    } else if (!strcmp("-mode", optionName)) {
+               int baudrate, databits, stopbits;
+               char parity;
+               int args;
+
+               if ((args = sscanf(newValue, "%d,%c,%d,%d", &baudrate, &parity, &databits, &stopbits)) != 4) {
+                       Tcl_AppendResult(interp, "error setting mode: ", NULL);
+                       return TCL_ERROR;
+               }
+
+               switch (databits) {
+                       case 8: databits = FT_BITS_8; break;
+                       case 7: databits = FT_BITS_7; break;
+                       case 6: databits = FT_BITS_6; break;
+                       case 5: databits = FT_BITS_5; break;
+                       default:
+                               Tcl_AppendResult(interp, "error setting mode: databit count not supported", NULL);
+                               return TCL_ERROR;
+               }
+
+               switch (stopbits) {
+                       case 1: stopbits = FT_STOP_BITS_1; break;
+                       case 2: stopbits = FT_STOP_BITS_2; break;
+                       default:
+                               Tcl_AppendResult(interp, "error setting mode: stopbit count not supported", NULL);
+                               return TCL_ERROR;
+               }
+
+               switch (parity) {
+                       case 'n': parity = FT_PARITY_NONE; break;
+                       case 'o': parity = FT_PARITY_ODD; break;
+                       case 'e': parity = FT_PARITY_EVEN; break;
+                       case 'm': parity = FT_PARITY_MARK; break;
+                       case 's': parity = FT_PARITY_SPACE; break;
+                       default:
+                               Tcl_AppendResult(interp, "error setting mode: parity not supported", NULL);
+                               return TCL_ERROR;
+               }
+
+               if ((fts = procs.FT_SetBaudRate(instPtr->handle, baudrate)) != FT_OK) {
+                       Tcl_AppendResult(interp, "failed set baudrate: \"",
+                                ConvertError(fts), NULL);
+                       return TCL_ERROR;
+               }
+
+               if ((fts = procs.FT_SetDataCharacteristics(instPtr->handle, databits, stopbits, parity)) != FT_OK) {
+                       Tcl_AppendResult(interp, "failed set data characteristics: \"",
+                                ConvertError(fts), NULL);
+                       return TCL_ERROR;
+               }
+
+               instPtr->baudrate = baudrate;
+               instPtr->databits = databits;
+               instPtr->stopbits = stopbits;
+               instPtr->parity = parity;
+    } else if (!strcmp("-handshake", optionName)) {
+               unsigned short handshake = 0xFFFF;
+               if (!strcmp(newValue,"none")) {
+                       handshake = FT_FLOW_NONE;
+               } else if (!strcmp(newValue,"rtscts")) {
+                       handshake = FT_FLOW_RTS_CTS;
+               } else if (!strcmp(newValue,"dtrdsr")) {
+                       handshake = FT_FLOW_DTR_DSR;
+               } else if (!strcmp(newValue,"xonxoff")) {
+                       handshake = FT_FLOW_XON_XOFF;
+               } else {
+                       Tcl_AppendResult(interp, "error setting handshake: handshake not supported", NULL);
+                       return TCL_ERROR;
+               }
+               if ((fts = procs.FT_SetFlowControl(instPtr->handle, handshake, instPtr->xonchar, instPtr->xoffchar)) == FT_OK)
+                   instPtr->handshake = handshake;
+    } else if (!strcmp("-xchar", optionName)) {
+               if (strlen(newValue) != 3) {
+                       Tcl_AppendResult(interp, "error setting xon/xoff characters: values < 1 and > 127 not supported", NULL);
+                       return TCL_ERROR;
+               }
+
+               if ((fts = procs.FT_SetFlowControl(instPtr->handle, FT_FLOW_NONE, newValue[0], newValue[2])) == FT_OK) {
+                       instPtr->xonchar = newValue[0];
+                       instPtr->xoffchar = newValue[2];
+               }
     }
 
     if (fts != FT_OK) {
@@ -284,7 +381,7 @@ ChannelSetOption(ClientData instance, Tcl_Interp *interp,
        r = TCL_ERROR;
     }
 
-    return TCL_OK;
+    return r;
 }
 
 /**
@@ -298,7 +395,7 @@ ChannelGetOption(ClientData instance, Tcl_Interp *interp,
                 const char *optionName, Tcl_DString *optionValue)
 {
     Channel *instPtr = instance;
-    const char *options[] = {"readtimeout", "writetimeout", "latency", NULL};
+    const char *options[] = {"readtimeout", "writetimeout", "latency", "mode", "handshake", "xchar", NULL};
     int r = TCL_OK;
 
     if (optionName == NULL) {
@@ -339,6 +436,35 @@ ChannelGetOption(ClientData instance, Tcl_Interp *interp,
                                 ConvertError(fts), NULL);
                r = TCL_ERROR;
            }
+       } else if (!strcmp("-mode", optionName)) {
+               char parity = 0;
+               switch (instPtr->parity) {
+                       case FT_PARITY_NONE: parity = 'n'; break;
+                       case FT_PARITY_ODD: parity = 'o'; break;
+                       case FT_PARITY_EVEN: parity = 'e'; break;
+                       case FT_PARITY_MARK: parity = 'm'; break;
+                       case FT_PARITY_SPACE: parity = 's'; break;
+                       default: parity = '?';
+               }
+           Tcl_DStringSetLength(&ds, 64);
+               sprintf(Tcl_DStringValue(&ds), "%d,%c,%d,%d",
+                       instPtr->baudrate, parity, instPtr->databits,
+                       instPtr->stopbits == FT_STOP_BITS_1 ? 1 : 2);
+       } else if (!strcmp("-handshake", optionName)) {
+               switch (instPtr->handshake) {
+                       case FT_FLOW_NONE: Tcl_DStringAppend(&ds, "none", 4); break;
+                       case FT_FLOW_RTS_CTS: Tcl_DStringAppend(&ds, "rtscts", 6); break;
+                       case FT_FLOW_DTR_DSR: Tcl_DStringAppend(&ds, "dtrdsr", 6); break;
+                       case FT_FLOW_XON_XOFF: Tcl_DStringAppend(&ds, "xonxoff", 7); break;
+                       default : Tcl_DStringAppend(&ds, "unknown", 7);
+               }
+       } else if (!strcmp("-xchar", optionName)) {
+               char cbuf[2];
+               cbuf[1] = 0;
+               cbuf[0] = instPtr->xonchar;
+               Tcl_DStringAppendElement(&ds, cbuf);
+               cbuf[0] = instPtr->xoffchar;
+               Tcl_DStringAppendElement(&ds, cbuf);
        } else {
            const char **p;
            for (p = options; *p != NULL; ++p) {
@@ -611,6 +737,25 @@ OpenCmd(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *const objv
                         devname, "\": ", ConvertError(fts), NULL);
         return TCL_ERROR;
     }
+
+    if ((fts = procs.FT_SetBaudRate(handle, 19200)) != FT_OK) {
+               Tcl_AppendResult(interp, "failed set baudrate: \"",
+                        ConvertError(fts), NULL);
+        return TCL_ERROR;
+    }
+
+    if ((fts = procs.FT_SetDataCharacteristics(handle, FT_BITS_8, FT_STOP_BITS_1, FT_PARITY_NONE)) != FT_OK) {
+               Tcl_AppendResult(interp, "failed set data characteristics: \"",
+                        ConvertError(fts), NULL);
+        return TCL_ERROR;
+    }
+
+    if ((fts = procs.FT_SetFlowControl(handle, FT_FLOW_NONE, 0x11, 0x13)) != FT_OK) {
+               Tcl_AppendResult(interp, "failed set flowcontrol: \"",
+                        ConvertError(fts), NULL);
+        return TCL_ERROR;
+    }
+
     if ((fts = procs.FT_SetTimeouts(handle, rxtimeout, txtimeout)) != FT_OK) {
        procs.FT_Close(handle);
         Tcl_AppendResult(interp, "failed initialize timeouts: ",
@@ -631,6 +776,13 @@ OpenCmd(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *const objv
     instPtr->flags = 0;
     instPtr->watchmask = 0;
     instPtr->validmask = TCL_READABLE | TCL_WRITABLE;
+       instPtr->baudrate = 19200;
+       instPtr->parity = FT_PARITY_NONE;
+       instPtr->databits = FT_BITS_8;
+       instPtr->stopbits = FT_STOP_BITS_1;
+       instPtr->handshake = FT_FLOW_NONE;
+       instPtr->xonchar = 0x11;
+       instPtr->xoffchar = 0x13;
     instPtr->rxtimeout = rxtimeout;
     instPtr->txtimeout = txtimeout;
     instPtr->handle = handle;
@@ -913,7 +1065,11 @@ Ftd2xx_Init(Tcl_Interp *interp)
        || LOADPROC(FT_SetTimeouts)
        || LOADPROC(FT_Write)
        || LOADPROC(FT_GetLibraryVersion)
-       || LOADPROC(FT_Rescan) )
+       || LOADPROC(FT_Rescan)
+       || LOADPROC(FT_SetBaudRate)
+       || LOADPROC(FT_SetDataCharacteristics)
+       || LOADPROC(FT_SetFlowControl)
+       )
     {
        Tcl_SetResult(interp, "invalid ftd2xx.dll library!", TCL_STATIC);
        Tcl_SetErrorCode(interp, "DLL_INVALID", szDllName, NULL);