diff --git a/src/pmacAsynIPPort.cpp b/src/pmacAsynIPPort.cpp index 7fc1bd1..ea83923 100644 --- a/src/pmacAsynIPPort.cpp +++ b/src/pmacAsynIPPort.cpp @@ -75,7 +75,6 @@ the higher level `turboPmac` driver to manually call the `pmacFlush` function. #include #include #include -#include #include #include #include @@ -124,13 +123,11 @@ constexpr double SEND_RETRY_DELAY = 0.01; */ static int setNonBlock(SOCKET fd, int nonBlockFlag) { #if defined(vxWorks) - int flags; - flags = nonBlockFlag; + int flags = nonBlockFlag; if (ioctl(fd, FIONBIO, (int)&flags) < 0) return -1; #elif defined(_WIN32) - unsigned long int flags; - flags = nonBlockFlag; + unsigned long int flags = nonBlockFlag; if (socket_ioctl(fd, FIONBIO, &flags) < 0) return -1; #else @@ -148,10 +145,9 @@ static int setNonBlock(SOCKET fd, int nonBlockFlag) { } pmacAsynIPPort::pmacAsynIPPort(const char *portName, const char *hostInfo) - : asynPortDriver( - portName, 1 /* maxAddr */, asynOctetMask | asynInt32Mask, - 0 /* interruptMask */, ASYN_CANBLOCK, 1 /* autoConnect */, - 0 /* priority */, 0 /* stackSize */), + : asynPortDriver(portName, 1 /* maxAddr */, asynOctetMask | asynInt32Mask, + 0 /* interruptMask */, ASYN_CANBLOCK, 1 /* autoConnect */, + 0 /* priority */, 0 /* stackSize */), inBuf_(MAX_BUFFER_SIZE) { // The base class constructor mirrors drvAsynIPPortConfigure(portName, // hostInfo, priority, noAutoConnect) with ASYN_CANBLOCK and autoConnect @@ -196,7 +192,7 @@ void pmacAsynIPPort::parseHostInfo(const char *hostInfo) { asynStatus pmacAsynIPPort::connectIt(asynUser *pasynUser) { SOCKET fd; - int i; + int i = 1; char sockerrmsg[256]; osiSockAddr farAddr; @@ -237,15 +233,14 @@ asynStatus pmacAsynIPPort::connectIt(asynUser *pasynUser) { if (setNonBlock(fd, 1) < 0) { epicsSocketConvertErrnoToString(sockerrmsg, sizeof(sockerrmsg)); epicsSnprintf(pasynUser->errorMessage, pasynUser->errorMessageSize, - "Can't set %s O_NONBLOCK option: %s", - IPHostName_.c_str(), sockerrmsg); + "Can't set %s O_NONBLOCK option: %s", IPHostName_.c_str(), + sockerrmsg); epicsSocketDestroy(fd); return asynError; } { - int connectResult = - ::connect(fd, &farAddr.sa, sizeof(farAddr.ia)); + int connectResult = ::connect(fd, &farAddr.sa, sizeof(farAddr.ia)); if (connectResult < 0 && (SOCKERRNO == SOCK_EWOULDBLOCK || SOCKERRNO == SOCK_EINPROGRESS)) { int msConnectTimeout; @@ -285,7 +280,6 @@ asynStatus pmacAsynIPPort::connectIt(asynUser *pasynUser) { } } - i = 1; if (setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, (void *)&i, sizeof i) < 0) { epicsSocketConvertErrnoToString(sockerrmsg, sizeof(sockerrmsg)); epicsSnprintf(pasynUser->errorMessage, pasynUser->errorMessageSize, @@ -336,14 +330,37 @@ asynStatus pmacAsynIPPort::disconnect(asynUser *pasynUser) { return asynSuccess; } +asynStatus pmacAsynIPPort::pollSocket(asynUser *pasynUser, + struct pollfd *pollfd, int pollmsec, + int *pollstatus) { + epicsTimeStamp startTime; + epicsTimeStamp endTime; + + epicsTimeGetCurrent(&startTime); + while ((*pollstatus = poll(pollfd, 1, pollmsec)) < 0) { + if (SOCKERRNO != SOCK_EINTR) { + char sockerrmsg[256]; + epicsSocketConvertErrnoToString(sockerrmsg, sizeof(sockerrmsg)); + epicsSnprintf(pasynUser->errorMessage, pasynUser->errorMessageSize, + "%s poll() failed: %s", IPHostName_.c_str(), + sockerrmsg); + return asynError; + } + epicsTimeGetCurrent(&endTime); + if (epicsTimeDiffInSeconds(&endTime, &startTime) * 1000. > pollmsec) + break; + } + return asynSuccess; +} + asynStatus pmacAsynIPPort::socketRead(asynUser *pasynUser, char *data, size_t maxchars, size_t *nbytesRead) { int thisRead; int readPollmsec; + int pollstatus; asynStatus status = asynSuccess; char sockerrmsg[256]; - epicsTimeStamp startTime; - epicsTimeStamp endTime; + struct pollfd pollfd; *nbytesRead = 0; if (fd_ == INVALID_SOCKET) { @@ -363,25 +380,11 @@ asynStatus pmacAsynIPPort::socketRead(asynUser *pasynUser, char *data, if (readPollmsec < 0) readPollmsec = -1; - { - struct pollfd pollfd; - pollfd.fd = fd_; - pollfd.events = POLLIN; - epicsTimeGetCurrent(&startTime); - while (poll(&pollfd, 1, readPollmsec) < 0) { - if (SOCKERRNO != SOCK_EINTR) { - epicsSocketConvertErrnoToString(sockerrmsg, sizeof(sockerrmsg)); - epicsSnprintf(pasynUser->errorMessage, - pasynUser->errorMessageSize, "Poll() failed: %s", - sockerrmsg); - return asynError; - } - epicsTimeGetCurrent(&endTime); - if (epicsTimeDiffInSeconds(&endTime, &startTime) * 1000. > - readPollmsec) - break; - } - } + pollfd.fd = fd_; + pollfd.events = POLLIN; + status = pollSocket(pasynUser, &pollfd, readPollmsec, &pollstatus); + if (status != asynSuccess) + return status; thisRead = recv(fd_, data, (int)maxchars, 0); if (thisRead < 0) { @@ -419,10 +422,12 @@ asynStatus pmacAsynIPPort::socketWrite(asynUser *pasynUser, const char *data, asynStatus status = asynSuccess; int thisWrite; int writePollmsec; + int pollstatus; epicsTimeStamp startTime; epicsTimeStamp endTime; int haveStartTime = 0; char sockerrmsg[256]; + struct pollfd pollfd; asynPrint(pasynUser, ASYN_TRACE_FLOW, "%s write.\n", IPHostName_.c_str()); *nbytesTransferred = 0; @@ -439,25 +444,11 @@ asynStatus pmacAsynIPPort::socketWrite(asynUser *pasynUser, const char *data, if (writePollmsec < 0) writePollmsec = -1; for (;;) { - int pollstatus; - struct pollfd pollfd; pollfd.fd = fd_; pollfd.events = POLLOUT; - epicsTimeGetCurrent(&startTime); - while ((pollstatus = poll(&pollfd, 1, writePollmsec)) < 0) { - if (SOCKERRNO != SOCK_EINTR) { - epicsSocketConvertErrnoToString(sockerrmsg, sizeof(sockerrmsg)); - epicsSnprintf(pasynUser->errorMessage, - pasynUser->errorMessageSize, - "%s poll() failed: %s", IPHostName_.c_str(), - sockerrmsg); - return asynError; - } - epicsTimeGetCurrent(&endTime); - if (epicsTimeDiffInSeconds(&endTime, &startTime) * 1000 > - writePollmsec) - break; - } + status = pollSocket(pasynUser, &pollfd, writePollmsec, &pollstatus); + if (status != asynSuccess) + return status; if (pollstatus == 0) { epicsSnprintf(pasynUser->errorMessage, pasynUser->errorMessageSize, "%s poll() timed out", IPHostName_.c_str()); @@ -504,8 +495,7 @@ asynStatus pmacAsynIPPort::socketWrite(asynUser *pasynUser, const char *data, break; } } - asynPrint(pasynUser, ASYN_TRACE_FLOW, - "wrote %lu to %s, return %s.\n", + asynPrint(pasynUser, ASYN_TRACE_FLOW, "wrote %lu to %s, return %s.\n", (unsigned long)*nbytesTransferred, IPHostName_.c_str(), pasynManager->strStatus(status)); return status; @@ -700,9 +690,9 @@ asynStatus pmacAsynIPPort::writeOctet(asynUser *pasynUser, const char *data, poutCmd_.wValue = data[0]; poutCmd_.wIndex = 0; poutCmd_.wLength = htons(0); - status = socketWrite(pasynUser, - reinterpret_cast(&poutCmd_), - ETHERNET_CMD_HEADER, &nbytesActual); + status = + socketWrite(pasynUser, reinterpret_cast(&poutCmd_), + ETHERNET_CMD_HEADER, &nbytesActual); *nbytesTransfered = nbytesActual == ETHERNET_CMD_HEADER ? numchars : 0; } else { if (numchars > ETHERNET_DATA_SIZE) { @@ -743,9 +733,9 @@ asynStatus pmacAsynIPPort::writeOctet(asynUser *pasynUser, const char *data, poutCmd_.wIndex = 0; poutCmd_.wLength = htons(numchars); memcpy(poutCmd_.bData, data, numchars); - status = socketWrite(pasynUser, - reinterpret_cast(&poutCmd_), - numchars + ETHERNET_CMD_HEADER, &nbytesActual); + status = + socketWrite(pasynUser, reinterpret_cast(&poutCmd_), + numchars + ETHERNET_CMD_HEADER, &nbytesActual); if (nbytesActual > ETHERNET_CMD_HEADER) *nbytesTransfered = nbytesActual - ETHERNET_CMD_HEADER; else @@ -833,8 +823,8 @@ asynStatus pmacAsynIPPort::readOctet(asynUser *pasynUser, char *data, nbytesTransfered); } } - status = readResponse(pasynUser, maxchars - nRead, &thisRead, - eomReason); + status = + readResponse(pasynUser, maxchars - nRead, &thisRead, eomReason); initialRead = false; if (status != asynSuccess || thisRead == 0) break; @@ -880,19 +870,13 @@ asynStatus pmacAsynIPPort::flushOctet(asynUser *pasynUser) { asynStatus pmacAsynIPPort::writeInt32(asynUser *pasynUser, epicsInt32 value) { asynPrint(pasynUser, ASYN_TRACE_FLOW, "pmacAsynIPPort::writeInt32\n"); - if (pasynUser->reason == FLUSH_HARDWARE) { - /* - According to the Turbo PMAC user manual, p. 414: - 0 - failed 1 - success - */ - if (pmacFlush(pasynUser) == 1) { - return asynSuccess; - } else { - return asynError; - } - } else { + if (pasynUser->reason != FLUSH_HARDWARE) return asynPortDriver::writeInt32(pasynUser, value); - } + + /* Turbo PMAC User Manual, p. 414: 0 - failed, 1 - success */ + if (pmacFlush(pasynUser) == 1) + return asynSuccess; + return asynError; } // ============================================================================= diff --git a/src/pmacAsynIPPort.h b/src/pmacAsynIPPort.h index dc15aa6..19b025a 100644 --- a/src/pmacAsynIPPort.h +++ b/src/pmacAsynIPPort.h @@ -120,6 +120,16 @@ class HIDDEN pmacAsynIPPort : public asynPortDriver { asynStatus socketWrite(asynUser *pasynUser, const char *data, size_t numchars, size_t *nbytesWritten); + /** + * @brief Poll the socket fd, retrying while poll() is interrupted by + * EINTR. + * + * @return asynSuccess with *pollstatus holding poll()'s return value, or + * asynError if poll() failed for a different reason. + */ + asynStatus pollSocket(asynUser *pasynUser, struct pollfd *pollfd, + int pollmsec, int *pollstatus); + /** * @brief Read response data from the PMAC into the internal input buffer * diff --git a/src/turboPmacController.cpp b/src/turboPmacController.cpp index 0b69aa5..0cf65b7 100644 --- a/src/turboPmacController.cpp +++ b/src/turboPmacController.cpp @@ -36,8 +36,6 @@ struct turboPmacControllerImpl { // Timeout for the communication process in seconds double comTimeout; - char lastResponse[sinqController::MAXBUF_]; - // User for writing int32 values to the port driver. asynUser *pasynInt32SyncIOipPort; @@ -46,6 +44,8 @@ struct turboPmacControllerImpl { int readConfig; int flushHardware; int limFromHardware; + + char lastResponse[sinqController::MAXBUF_]; }; #define NUM_turboPmac_DRIVER_PARAMS 5 @@ -65,12 +65,12 @@ turboPmacController::turboPmacController(const char *portName, pTurboPmacC_( std::make_unique((turboPmacControllerImpl){ .comTimeout = comTimeout, - .lastResponse = {0}, .pasynInt32SyncIOipPort = nullptr, // Populated in constructor .rereadEncoderPosition = 0, // Populated in constructor .readConfig = 0, // Populated in constructor .flushHardware = 0, // Populated in constructor .limFromHardware = 0, // Populated in constructor + .lastResponse = {0}, })) { // Initialization of local variables asynStatus status = asynSuccess;