📄 tclwinserial.c
字号:
* *---------------------------------------------------------------------- */static DWORD WINAPISerialWriterThread(LPVOID arg){ SerialInfo *infoPtr = (SerialInfo *)arg; HANDLE *handle = infoPtr->handle; DWORD bytesWritten, toWrite; char *buf; OVERLAPPED myWrite; /* have an own OVERLAPPED in this thread */ for (;;) { /* * Wait for the main thread to signal before attempting to write. */ WaitForSingleObject(infoPtr->evStartWriter, INFINITE); buf = infoPtr->writeBuf; toWrite = infoPtr->toWrite; myWrite.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL); /* * Loop until all of the bytes are written or an error occurs. */ while (toWrite > 0) { /* * Check for pending writeError * Ignore all write operations until the user has been notified */ if (infoPtr->writeError) { break; } if (blockingWrite(infoPtr, (LPVOID) buf, (DWORD) toWrite, &bytesWritten, &myWrite) == FALSE) { infoPtr->writeError = GetLastError(); break; } if (bytesWritten != toWrite) { /* Write timeout */ infoPtr->writeError = ERROR_WRITE_FAULT; break; } toWrite -= bytesWritten; buf += bytesWritten; } CloseHandle(myWrite.hEvent); /* * Signal the main thread by signalling the evWritable event and * then waking up the notifier thread. */ SetEvent(infoPtr->evWritable); /* * Alert the foreground thread. Note that we need to treat this like * a critical section so the foreground thread does not terminate * this thread while we are holding a mutex in the notifier code. */ Tcl_MutexLock(&serialMutex); Tcl_ThreadAlert(infoPtr->threadId); Tcl_MutexUnlock(&serialMutex); } return 0; /* NOT REACHED */}/* *---------------------------------------------------------------------- * * TclWinSerialReopen -- * * Reopens the serial port with the OVERLAPPED FLAG set * * Results: * Returns the new handle, or INVALID_HANDLE_VALUE * Normally there shouldn't be any error, * because the same channel has previously been succeesfully opened. * * Side effects: * May close the original handle * *---------------------------------------------------------------------- */HANDLETclWinSerialReopen(handle, name, access) HANDLE handle; CONST TCHAR *name; DWORD access;{ ThreadSpecificData *tsdPtr; tsdPtr = SerialInit(); /* * Multithreaded I/O needs the overlapped flag set * otherwise ClearCommError blocks under Windows NT/2000 until serial * output is finished */ if (CloseHandle(handle) == FALSE) { return INVALID_HANDLE_VALUE; } handle = (*tclWinProcs->createFileProc)(name, access, 0, 0, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, 0); return handle;}/* *---------------------------------------------------------------------- * * TclWinOpenSerialChannel -- * * Constructs a Serial port channel for the specified standard OS handle. * This is a helper function to break up the construction of * channels into File, Console, or Serial. * * Results: * Returns the new channel, or NULL. * * Side effects: * May open the channel * *---------------------------------------------------------------------- */Tcl_ChannelTclWinOpenSerialChannel(handle, channelName, permissions) HANDLE handle; char *channelName; int permissions;{ SerialInfo *infoPtr; ThreadSpecificData *tsdPtr; DWORD id; tsdPtr = SerialInit(); infoPtr = (SerialInfo *) ckalloc((unsigned) sizeof(SerialInfo)); memset(infoPtr, 0, sizeof(SerialInfo)); infoPtr->validMask = permissions; infoPtr->handle = handle; /* * Use the pointer to keep the channel names unique, in case * the handles are shared between multiple channels (stdin/stdout). */ wsprintfA(channelName, "file%lx", (int) infoPtr); infoPtr->channel = Tcl_CreateChannel(&serialChannelType, channelName, (ClientData) infoPtr, permissions); infoPtr->readable = 0; infoPtr->writable = 1; infoPtr->toWrite = infoPtr->writeQueue = 0; infoPtr->blockTime = SERIAL_DEFAULT_BLOCKTIME; infoPtr->lastEventTime = 0; infoPtr->lastError = infoPtr->error = 0; infoPtr->threadId = Tcl_GetCurrentThread(); infoPtr->sysBufRead = infoPtr->sysBufWrite = 4096; SetupComm(handle, infoPtr->sysBufRead, infoPtr->sysBufWrite); PurgeComm(handle, PURGE_TXABORT | PURGE_RXABORT | PURGE_TXCLEAR | PURGE_RXCLEAR); /* * default is blocking */ SetCommTimeouts(handle, &no_timeout); if (permissions & TCL_READABLE) { infoPtr->osRead.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL); } if (permissions & TCL_WRITABLE) { /* * Initially the channel is writable * and the writeThread is idle. */ infoPtr->osWrite.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL); infoPtr->evWritable = CreateEvent(NULL, TRUE, TRUE, NULL); infoPtr->evStartWriter = CreateEvent(NULL, FALSE, FALSE, NULL); InitializeCriticalSection(&infoPtr->csWrite); infoPtr->writeThread = CreateThread(NULL, 8000, SerialWriterThread, infoPtr, 0, &id); } /* * Files have default translation of AUTO and ^Z eof char, which * means that a ^Z will be accepted as EOF when reading. */ Tcl_SetChannelOption(NULL, infoPtr->channel, "-translation", "auto"); Tcl_SetChannelOption(NULL, infoPtr->channel, "-eofchar", "\032 {}"); return infoPtr->channel;}/* *---------------------------------------------------------------------- * * SerialErrorStr -- * * Converts a Win32 serial error code to a list of readable errors * *---------------------------------------------------------------------- */static voidSerialErrorStr(error, dsPtr) DWORD error; /* Win32 serial error code */ Tcl_DString *dsPtr; /* Where to store string */{ if( (error & CE_RXOVER) != 0) { Tcl_DStringAppendElement(dsPtr, "RXOVER"); } if( (error & CE_OVERRUN) != 0) { Tcl_DStringAppendElement(dsPtr, "OVERRUN"); } if( (error & CE_RXPARITY) != 0) { Tcl_DStringAppendElement(dsPtr, "RXPARITY"); } if( (error & CE_FRAME) != 0) { Tcl_DStringAppendElement(dsPtr, "FRAME"); } if( (error & CE_BREAK) != 0) { Tcl_DStringAppendElement(dsPtr, "BREAK"); } if( (error & CE_TXFULL) != 0) { Tcl_DStringAppendElement(dsPtr, "TXFULL"); } if( (error & CE_PTO) != 0) { /* PTO used to signal WRITE-TIMEOUT */ Tcl_DStringAppendElement(dsPtr, "TIMEOUT"); } if( (error & ~(SERIAL_READ_ERRORS | SERIAL_WRITE_ERRORS)) != 0) { char buf[TCL_INTEGER_SPACE + 1]; wsprintfA(buf, "%d", error); Tcl_DStringAppendElement(dsPtr, buf); }}/* *---------------------------------------------------------------------- * * SerialModemStatusStr -- * * Converts a Win32 modem status list of readable flags * *---------------------------------------------------------------------- */static voidSerialModemStatusStr(status, dsPtr) DWORD status; /* Win32 modem status */ Tcl_DString *dsPtr; /* Where to store string */{ Tcl_DStringAppendElement(dsPtr, "CTS"); Tcl_DStringAppendElement(dsPtr, (status & MS_CTS_ON) ? "1" : "0"); Tcl_DStringAppendElement(dsPtr, "DSR"); Tcl_DStringAppendElement(dsPtr, (status & MS_DSR_ON) ? "1" : "0"); Tcl_DStringAppendElement(dsPtr, "RING"); Tcl_DStringAppendElement(dsPtr, (status & MS_RING_ON) ? "1" : "0"); Tcl_DStringAppendElement(dsPtr, "DCD"); Tcl_DStringAppendElement(dsPtr, (status & MS_RLSD_ON) ? "1" : "0");}/* *---------------------------------------------------------------------- * * SerialSetOptionProc -- * * Sets an option on a channel. * * Results: * A standard Tcl result. Also sets the interp's result on error if * interp is not NULL. * * Side effects: * May modify an option on a device. * *---------------------------------------------------------------------- */static intSerialSetOptionProc(instanceData, interp, optionName, value) ClientData instanceData; /* File state. */ Tcl_Interp *interp; /* For error reporting - can be NULL. */ CONST char *optionName; /* Which option to set? */ CONST char *value; /* New value for option. */{ SerialInfo *infoPtr; DCB dcb; BOOL result, flag; size_t len, vlen; Tcl_DString ds; CONST TCHAR *native; int argc; char **argv; infoPtr = (SerialInfo *) instanceData; /* * Parse options */ len = strlen(optionName); vlen = strlen(value); /* * Option -mode baud,parity,databits,stopbits */ if ((len > 2) && (strncmp(optionName, "-mode", len) == 0)) { if (! GetCommState(infoPtr->handle, &dcb)) { if (interp) { Tcl_AppendResult(interp, "can't get comm state", (char *) NULL); } return TCL_ERROR; } native = Tcl_WinUtfToTChar(value, -1, &ds); result = (*tclWinProcs->buildCommDCBProc)(native, &dcb); Tcl_DStringFree(&ds); if (result == FALSE) { if (interp) { Tcl_AppendResult(interp, "bad value for -mode: should be baud,parity,data,stop", (char *) NULL); } return TCL_ERROR; } /* Default settings for serial communications */ dcb.fBinary = TRUE; dcb.fErrorChar = FALSE; dcb.fNull = FALSE; dcb.fAbortOnError = FALSE; if (! SetCommState(infoPtr->handle, &dcb) ) { if (interp) { Tcl_AppendResult(interp, "can't set comm state", (char *) NULL); } return TCL_ERROR; } return TCL_OK; } /* * Option -handshake none|xonxoff|rtscts|dtrdsr */ if ((len > 1) && (strncmp(optionName, "-handshake", len) == 0)) { if (! GetCommState(infoPtr->handle, &dcb)) { if (interp) { Tcl_AppendResult(interp, "can't get comm state", (char *) NULL); } return TCL_ERROR; } /* * Reset all handshake options * DTR and RTS are ON by default */ dcb.fOutX = dcb.fInX = FALSE; dcb.fOutxCtsFlow = dcb.fOutxDsrFlow = dcb.fDsrSensitivity = FALSE; dcb.fDtrControl = DTR_CONTROL_ENABLE; dcb.fRtsControl = RTS_CONTROL_ENABLE; dcb.fTXContinueOnXoff = FALSE; /* * Adjust the handshake limits. * Yes, the XonXoff limits seem to influence even hardware handshake */ dcb.XonLim = (WORD) (infoPtr->sysBufRead*1/2); dcb.XoffLim = (WORD) (infoPtr->sysBufRead*1/4); if (strnicmp(value, "NONE", vlen) == 0) { /* leave all handshake options disabled */ } else if (strnicmp(value, "XONXOFF", vlen) == 0) { dcb.fOutX = dcb.fInX = TRUE; } else if (strnicmp(value, "RTSCTS", vlen) == 0) { dcb.fOutxCtsFlow = TRUE; dcb.fRtsControl = RTS_CONTROL_HANDSHAKE; } else if (strnicmp(value, "DTRDSR", vlen) == 0) { dcb.fOutxDsrFlow = TRUE; dcb.fDtrControl = DTR_CONTROL_HANDSHAKE; } else { if (interp) { Tcl_AppendResult(interp, "bad value for -handshake: ", "must be one of xonxoff, rtscts, dtrdsr or none", (char *) NULL); return TCL_ERROR; } } if (! SetCommState(infoPtr->handle, &dcb)) { if (interp) { Tcl_AppendResult(interp, "can't set comm state", (char *) NULL); } return TCL_ERROR; } return TCL_OK; } /* * Option -xchar {\x11 \x13} */ if ((len > 1) && (strncmp(optionName, "-xchar", len) == 0)) {
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -