📄 tclwinserial.c
字号:
} } if (!block) { SerialBlockTime(msec); }}/* *---------------------------------------------------------------------- * * SerialCheckProc -- * * This procedure is called by Tcl_DoOneEvent to check the serial * event source for events. * * Results: * None. * * Side effects: * May queue an event. * *---------------------------------------------------------------------- */static voidSerialCheckProc( ClientData data, /* Not used. */ int flags) /* Event flags as passed to Tcl_DoOneEvent. */{ SerialInfo *infoPtr; SerialEvent *evPtr; int needEvent; ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey); COMSTAT cStat; unsigned int time; if (!(flags & TCL_FILE_EVENTS)) { return; } /* * Queue events for any ready serials that don't already have events * queued. */ for (infoPtr = tsdPtr->firstSerialPtr; infoPtr != NULL; infoPtr = infoPtr->nextPtr) { if (infoPtr->flags & SERIAL_PENDING) { continue; } needEvent = 0; /* * If WRITABLE watch mask is set * look for infoPtr->evWritable object */ if (infoPtr->watchMask & TCL_WRITABLE) { if (WaitForSingleObject(infoPtr->evWritable, 0) != WAIT_TIMEOUT) { infoPtr->writable = 1; needEvent = 1; } } /* * If READABLE watch mask is set * call ClearCommError to poll cbInQue * Window errors are ignored here */ if( infoPtr->watchMask & TCL_READABLE ) { if( ClearCommError( infoPtr->handle, &infoPtr->error, &cStat ) ) { /* * Look for characters already pending in windows queue. * If they are, poll. */ if( infoPtr->watchMask & TCL_READABLE ) { /* * force fileevent after serial read error */ if( (cStat.cbInQue > 0) || (infoPtr->error & SERIAL_READ_ERRORS) ) { infoPtr->readable = 1; time = SerialGetMilliseconds(); if ((unsigned int) (time - infoPtr->lastEventTime) >= (unsigned int) infoPtr->blockTime) { needEvent = 1; infoPtr->lastEventTime = time; } } } } } /* * Queue an event if the serial is signaled for reading or writing. */ if (needEvent) { infoPtr->flags |= SERIAL_PENDING; evPtr = (SerialEvent *) ckalloc(sizeof(SerialEvent)); evPtr->header.proc = SerialEventProc; evPtr->infoPtr = infoPtr; Tcl_QueueEvent((Tcl_Event *) evPtr, TCL_QUEUE_TAIL); } }}/* *---------------------------------------------------------------------- * * SerialBlockProc -- * * Set blocking or non-blocking mode on channel. * * Results: * 0 if successful, errno when failed. * * Side effects: * Sets the device into blocking or non-blocking mode. * *---------------------------------------------------------------------- */static intSerialBlockProc( ClientData instanceData, /* Instance data for channel. */ int mode) /* TCL_MODE_BLOCKING or * TCL_MODE_NONBLOCKING. */{ int errorCode = 0; SerialInfo *infoPtr = (SerialInfo *) instanceData; /* * Only serial READ can be switched between blocking & nonblocking * using COMMTIMEOUTS. * Serial write emulates blocking & nonblocking by the SerialWriterThread. */ if (mode == TCL_MODE_NONBLOCKING) { infoPtr->flags |= SERIAL_ASYNC; } else { infoPtr->flags &= ~(SERIAL_ASYNC); } return errorCode;}/* *---------------------------------------------------------------------- * * SerialCloseProc -- * * Closes a serial based IO channel. * * Results: * 0 on success, errno otherwise. * * Side effects: * Closes the physical channel. * *---------------------------------------------------------------------- */static intSerialCloseProc( ClientData instanceData, /* Pointer to SerialInfo structure. */ Tcl_Interp *interp) /* For error reporting. */{ SerialInfo *serialPtr = (SerialInfo *) instanceData; int errorCode, result = 0; SerialInfo *infoPtr, **nextPtrPtr; ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey); errorCode = 0; if (serialPtr->validMask & TCL_READABLE) { PurgeComm(serialPtr->handle, PURGE_RXABORT | PURGE_RXCLEAR); CloseHandle(serialPtr->osRead.hEvent); } serialPtr->validMask &= ~TCL_READABLE; if (serialPtr->validMask & TCL_WRITABLE) { /* * Generally we cannot wait for a pending write operation * because it may hang due to handshake * WaitForSingleObject(serialPtr->evWritable, INFINITE); */ /* * Forcibly terminate the background thread. We cannot rely on the * thread to cleanly terminate itself because we have no way of * closing the handle without blocking in the case where the * thread is in the middle of an I/O operation. Note that we need * to guard against terminating the thread while it is in the * middle of Tcl_ThreadAlert because it won't be able to release * the notifier lock. */ Tcl_MutexLock(&serialMutex); TerminateThread(serialPtr->writeThread, 0); Tcl_MutexUnlock(&serialMutex); /* * Wait for the thread to terminate. This ensures that we are * completely cleaned up before we leave this function. */ WaitForSingleObject(serialPtr->writeThread, INFINITE); CloseHandle(serialPtr->writeThread); CloseHandle(serialPtr->evWritable); CloseHandle(serialPtr->evStartWriter); serialPtr->writeThread = NULL; PurgeComm(serialPtr->handle, PURGE_TXABORT | PURGE_TXCLEAR); } serialPtr->validMask &= ~TCL_WRITABLE; /* * Don't close the Win32 handle if the handle is a standard channel * during the exit process. Otherwise, one thread may kill the stdio * of another. */ if (!TclInExit() || ((GetStdHandle(STD_INPUT_HANDLE) != serialPtr->handle) && (GetStdHandle(STD_OUTPUT_HANDLE) != serialPtr->handle) && (GetStdHandle(STD_ERROR_HANDLE) != serialPtr->handle))) { if (CloseHandle(serialPtr->handle) == FALSE) { TclWinConvertError(GetLastError()); errorCode = errno; } } serialPtr->watchMask &= serialPtr->validMask; /* * Remove the file from the list of watched files. */ for (nextPtrPtr = &(tsdPtr->firstSerialPtr), infoPtr = *nextPtrPtr; infoPtr != NULL; nextPtrPtr = &infoPtr->nextPtr, infoPtr = *nextPtrPtr) { if (infoPtr == (SerialInfo *)serialPtr) { *nextPtrPtr = infoPtr->nextPtr; break; } } /* * Wrap the error file into a channel and give it to the cleanup * routine. */ if (serialPtr->writeBuf != NULL) { ckfree(serialPtr->writeBuf); serialPtr->writeBuf = NULL; } ckfree((char*) serialPtr); if (errorCode == 0) { return result; } return errorCode;}/* *---------------------------------------------------------------------- * * blockingRead -- * * Perform a blocking read into the buffer given. Returns * count of how many bytes were actually read, and an error indication. * * Results: * A count of how many bytes were read is returned and an error * indication is returned. * * Side effects: * Reads input from the actual channel. * *---------------------------------------------------------------------- */static intblockingRead( SerialInfo *infoPtr, /* Serial info structure */ LPVOID buf, /* The input buffer pointer */ DWORD bufSize, /* The number of bytes to read */ LPDWORD lpRead, /* Returns number of bytes read */ LPOVERLAPPED osPtr ) /* OVERLAPPED structure */{ /* * Perform overlapped blocking read. * 1. Reset the overlapped event * 2. Start overlapped read operation * 3. Wait for completion */ /* * Set Offset to ZERO, otherwise NT4.0 may report an error */ osPtr->Offset = osPtr->OffsetHigh = 0; ResetEvent(osPtr->hEvent); if (! ReadFile(infoPtr->handle, buf, bufSize, lpRead, osPtr) ) { if (GetLastError() != ERROR_IO_PENDING) { /* ReadFile failed, but it isn't delayed. Report error */ return FALSE; } else { /* Read is pending, wait for completion, timeout ? */ if (! GetOverlappedResult(infoPtr->handle, osPtr, lpRead, TRUE) ) { return FALSE; } } } else { /* ReadFile completed immediately. */ } return TRUE;}/* *---------------------------------------------------------------------- * * blockingWrite -- * * Perform a blocking write from the buffer given. Returns * count of how many bytes were actually written, and an error indication. * * Results: * A count of how many bytes were written is returned and an error * indication is returned. * * Side effects: * Writes output to the actual channel. * *---------------------------------------------------------------------- */static intblockingWrite( SerialInfo *infoPtr, /* Serial info structure */ LPVOID buf, /* The output buffer pointer */ DWORD bufSize, /* The number of bytes to write */ LPDWORD lpWritten, /* Returns number of bytes written */ LPOVERLAPPED osPtr ) /* OVERLAPPED structure */{ int result; /* * Perform overlapped blocking write. * 1. Reset the overlapped event * 2. Remove these bytes from the output queue counter * 3. Start overlapped write operation * 3. Remove these bytes from the output queue counter * 4. Wait for completion * 5. Adjust the output queue counter */ ResetEvent(osPtr->hEvent); EnterCriticalSection(&infoPtr->csWrite); infoPtr->writeQueue -= bufSize; /* * Set Offset to ZERO, otherwise NT4.0 may report an error */ osPtr->Offset = osPtr->OffsetHigh = 0; result = WriteFile(infoPtr->handle, buf, bufSize, lpWritten, osPtr); LeaveCriticalSection(&infoPtr->csWrite); if (result == FALSE ) { int err = GetLastError(); switch (err) { case ERROR_IO_PENDING: /* Write is pending, wait for completion */ if (! GetOverlappedResult(infoPtr->handle, osPtr, lpWritten, TRUE) ) { return FALSE; } break; case ERROR_COUNTER_TIMEOUT: /* Write timeout handled in SerialOutputProc */ break; default: /* WriteFile failed, but it isn't delayed. Report error */ return FALSE; } } else { /* WriteFile completed immediately. */ } EnterCriticalSection(&infoPtr->csWrite); infoPtr->writeQueue += (*lpWritten - bufSize); LeaveCriticalSection(&infoPtr->csWrite); return TRUE;}/* *---------------------------------------------------------------------- * * SerialInputProc -- * * Reads input from the IO channel into the buffer given. Returns * count of how many bytes were actually read, and an error indication. * * Results: * A count of how many bytes were read is returned and an error * indication is returned in an output argument. * * Side effects: * Reads input from the actual channel. * *---------------------------------------------------------------------- */static intSerialInputProc( ClientData instanceData, /* Serial state. */ char *buf, /* Where to store data read. */
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -