📄 tclwinserial.c
字号:
int bufSize, /* How much space is available * in the buffer? */ int *errorCode) /* Where to store error code. */{ SerialInfo *infoPtr = (SerialInfo *) instanceData; DWORD bytesRead = 0; COMSTAT cStat; *errorCode = 0; /* * Check if there is a CommError pending from SerialCheckProc */ if( infoPtr->error & SERIAL_READ_ERRORS ){ goto commError; } /* * Look for characters already pending in windows queue. * This is the mainly restored good old code from Tcl8.0 */ if( ClearCommError( infoPtr->handle, &infoPtr->error, &cStat ) ) { /* * Check for errors here, but not in the evSetup/Check procedures */ if( infoPtr->error & SERIAL_READ_ERRORS ) { goto commError; } if( infoPtr->flags & SERIAL_ASYNC ) { /* * NON_BLOCKING mode: * Avoid blocking by reading more bytes than available * in input buffer */ if( cStat.cbInQue > 0 ) { if( (DWORD) bufSize > cStat.cbInQue ) { bufSize = cStat.cbInQue; } } else { errno = *errorCode = EAGAIN; return -1; } } else { /* * BLOCKING mode: * Tcl trys to read a full buffer of 4 kBytes here */ if( cStat.cbInQue > 0 ) { if( (DWORD) bufSize > cStat.cbInQue ) { bufSize = cStat.cbInQue; } } else { bufSize = 1; } } } if( bufSize == 0 ) { return bytesRead = 0; } /* * Perform blocking read. Doesn't block in non-blocking mode, * because we checked the number of available bytes. */ if (blockingRead(infoPtr, (LPVOID) buf, (DWORD) bufSize, &bytesRead, &infoPtr->osRead) == FALSE) { goto error; } return bytesRead;error: TclWinConvertError(GetLastError()); *errorCode = errno; return -1;commError: infoPtr->lastError = infoPtr->error; /* save last error code */ infoPtr->error = 0; /* reset error code */ *errorCode = EIO; /* to return read-error only once */ return -1;}/* *---------------------------------------------------------------------- * * SerialOutputProc -- * * Writes the given output on the IO channel. Returns count of how * many characters were actually written, and an error indication. * * Results: * A count of how many characters were written is returned and an * error indication is returned in an output argument. * * Side effects: * Writes output on the actual channel. * *---------------------------------------------------------------------- */static intSerialOutputProc( ClientData instanceData, /* Serial state. */ CONST char *buf, /* The data buffer. */ int toWrite, /* How many bytes to write? */ int *errorCode) /* Where to store error code. */{ SerialInfo *infoPtr = (SerialInfo *) instanceData; int bytesWritten, timeout; *errorCode = 0; /* * At EXIT Tcl trys to flush all open channels in blocking mode. * We avoid blocking output after ExitProc or CloseHandler(chan) * has been called by checking the corrresponding variables. */ if( ! initialized || TclInExit() ) { return toWrite; } /* * Check if there is a CommError pending from SerialCheckProc */ if( infoPtr->error & SERIAL_WRITE_ERRORS ){ infoPtr->lastError = infoPtr->error; /* save last error code */ infoPtr->error = 0; /* reset error code */ errno = EIO; goto error; } timeout = (infoPtr->flags & SERIAL_ASYNC) ? 0 : INFINITE; if (WaitForSingleObject(infoPtr->evWritable, timeout) == WAIT_TIMEOUT) { /* * The writer thread is blocked waiting for a write to complete * and the channel is in non-blocking mode. */ errno = EWOULDBLOCK; goto error1; } /* * Check for a background error on the last write. */ if (infoPtr->writeError) { TclWinConvertError(infoPtr->writeError); infoPtr->writeError = 0; goto error1; } /* * Remember the number of bytes in output queue */ EnterCriticalSection(&infoPtr->csWrite); infoPtr->writeQueue += toWrite; LeaveCriticalSection(&infoPtr->csWrite); if (infoPtr->flags & SERIAL_ASYNC) { /* * The serial is non-blocking, so copy the data into the output * buffer and restart the writer thread. */ if (toWrite > infoPtr->writeBufLen) { /* * Reallocate the buffer to be large enough to hold the data. */ if (infoPtr->writeBuf) { ckfree(infoPtr->writeBuf); } infoPtr->writeBufLen = toWrite; infoPtr->writeBuf = ckalloc(toWrite); } memcpy(infoPtr->writeBuf, buf, toWrite); infoPtr->toWrite = toWrite; ResetEvent(infoPtr->evWritable); SetEvent(infoPtr->evStartWriter); bytesWritten = toWrite; } else { /* * In the blocking case, just try to write the buffer directly. * This avoids an unnecessary copy. */ if (! blockingWrite(infoPtr, (LPVOID) buf, (DWORD) toWrite, &bytesWritten, &infoPtr->osWrite) ) { goto writeError; } if (bytesWritten != toWrite) { /* Write timeout */ infoPtr->lastError |= CE_PTO; errno = EIO; goto error; } } return bytesWritten;writeError: TclWinConvertError(GetLastError());error: /* * Reset the output queue counter on error during blocking output *//* EnterCriticalSection(&infoPtr->csWrite); infoPtr->writeQueue = 0; LeaveCriticalSection(&infoPtr->csWrite);*/ error1: *errorCode = errno; return -1;}/* *---------------------------------------------------------------------- * * SerialEventProc -- * * This function is invoked by Tcl_ServiceEvent when a file event * reaches the front of the event queue. This procedure invokes * Tcl_NotifyChannel on the serial. * * Results: * Returns 1 if the event was handled, meaning it should be removed * from the queue. Returns 0 if the event was not handled, meaning * it should stay on the queue. The only time the event isn't * handled is if the TCL_FILE_EVENTS flag bit isn't set. * * Side effects: * Whatever the notifier callback does. * *---------------------------------------------------------------------- */static intSerialEventProc( Tcl_Event *evPtr, /* Event to service. */ int flags) /* Flags that indicate what events to * handle, such as TCL_FILE_EVENTS. */{ SerialEvent *serialEvPtr = (SerialEvent *)evPtr; SerialInfo *infoPtr; int mask; ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey); if (!(flags & TCL_FILE_EVENTS)) { return 0; } /* * Search through the list of watched serials for the one whose handle * matches the event. We do this rather than simply dereferencing * the handle in the event so that serials can be deleted while the * event is in the queue. */ for (infoPtr = tsdPtr->firstSerialPtr; infoPtr != NULL; infoPtr = infoPtr->nextPtr) { if (serialEvPtr->infoPtr == infoPtr) { infoPtr->flags &= ~(SERIAL_PENDING); break; } } /* * Remove stale events. */ if (!infoPtr) { return 1; } /* * Check to see if the serial is readable. Note * that we can't tell if a serial is writable, so we always report it * as being writable unless we have detected EOF. */ mask = 0; if( infoPtr->watchMask & TCL_WRITABLE ) { if( infoPtr->writable ) { mask |= TCL_WRITABLE; infoPtr->writable = 0; } } if( infoPtr->watchMask & TCL_READABLE ) { if( infoPtr->readable ) { mask |= TCL_READABLE; infoPtr->readable = 0; } } /* * Inform the channel of the events. */ Tcl_NotifyChannel(infoPtr->channel, infoPtr->watchMask & mask); return 1;}/* *---------------------------------------------------------------------- * * SerialWatchProc -- * * Called by the notifier to set up to watch for events on this * channel. * * Results: * None. * * Side effects: * None. * *---------------------------------------------------------------------- */static voidSerialWatchProc( ClientData instanceData, /* Serial state. */ int mask) /* What events to watch for, OR-ed * combination of TCL_READABLE, * TCL_WRITABLE and TCL_EXCEPTION. */{ SerialInfo **nextPtrPtr, *ptr; SerialInfo *infoPtr = (SerialInfo *) instanceData; int oldMask = infoPtr->watchMask; ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey); /* * Since the file is always ready for events, we set the block time * so we will poll. */ infoPtr->watchMask = mask & infoPtr->validMask; if (infoPtr->watchMask) { if (!oldMask) { infoPtr->nextPtr = tsdPtr->firstSerialPtr; tsdPtr->firstSerialPtr = infoPtr; } SerialBlockTime(infoPtr->blockTime); } else { if (oldMask) { /* * Remove the serial port from the list of watched serial ports. */ for (nextPtrPtr = &(tsdPtr->firstSerialPtr), ptr = *nextPtrPtr; ptr != NULL; nextPtrPtr = &ptr->nextPtr, ptr = *nextPtrPtr) { if (infoPtr == ptr) { *nextPtrPtr = ptr->nextPtr; break; } } } }}/* *---------------------------------------------------------------------- * * SerialGetHandleProc -- * * Called from Tcl_GetChannelHandle to retrieve OS handles from * inside a command serial port based channel. * * Results: * Returns TCL_OK with the fd in handlePtr, or TCL_ERROR if * there is no handle for the specified direction. * * Side effects: * None. * *---------------------------------------------------------------------- */static intSerialGetHandleProc( ClientData instanceData, /* The serial state. */ int direction, /* TCL_READABLE or TCL_WRITABLE */ ClientData *handlePtr) /* Where to store the handle. */{ SerialInfo *infoPtr = (SerialInfo *) instanceData; *handlePtr = (ClientData) infoPtr->handle; return TCL_OK;}/* *---------------------------------------------------------------------- * * SerialWriterThread -- * * This function runs in a separate thread and writes data * onto a serial. * * Results: * Always returns 0. * * Side effects: * Signals the main thread when an output operation is completed. * May cause the main thread to wake up by posting a message.
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -