diff --git a/src/pmacAsynIPPort.cpp b/src/pmacAsynIPPort.cpp index ea83923..15bbd58 100644 --- a/src/pmacAsynIPPort.cpp +++ b/src/pmacAsynIPPort.cpp @@ -144,6 +144,15 @@ static int setNonBlock(SOCKET fd, int nonBlockFlag) { return 0; } +/* Clamp the asyn I/O timeout to a poll() timeout in milliseconds: negative + * values (infinite wait) pass through, a zero timeout becomes a 1 ms poll. */ +static int pollmsecFromTimeout(double timeoutSeconds) { + int pollmsec = (int)(timeoutSeconds * 1000.0); + if (pollmsec == 0) + return 1; + return pollmsec; +} + pmacAsynIPPort::pmacAsynIPPort(const char *portName, const char *hostInfo) : asynPortDriver(portName, 1 /* maxAddr */, asynOctetMask | asynInt32Mask, 0 /* interruptMask */, ASYN_CANBLOCK, 1 /* autoConnect */, @@ -374,11 +383,7 @@ asynStatus pmacAsynIPPort::socketRead(asynUser *pasynUser, char *data, (int)maxchars); return asynError; } - readPollmsec = (int)(pasynUser->timeout * 1000.0); - if (readPollmsec == 0) - readPollmsec = 1; - if (readPollmsec < 0) - readPollmsec = -1; + readPollmsec = pollmsecFromTimeout(pasynUser->timeout); pollfd.fd = fd_; pollfd.events = POLLIN; @@ -430,7 +435,8 @@ asynStatus pmacAsynIPPort::socketWrite(asynUser *pasynUser, const char *data, struct pollfd pollfd; asynPrint(pasynUser, ASYN_TRACE_FLOW, "%s write.\n", IPHostName_.c_str()); - *nbytesTransferred = 0; + if (nbytesTransferred) + *nbytesTransferred = 0; if (fd_ == INVALID_SOCKET) { epicsSnprintf(pasynUser->errorMessage, pasynUser->errorMessageSize, "%s disconnected", IPHostName_.c_str()); @@ -438,11 +444,7 @@ asynStatus pmacAsynIPPort::socketWrite(asynUser *pasynUser, const char *data, } if (numchars == 0) return asynSuccess; - writePollmsec = (int)(pasynUser->timeout * 1000.0); - if (writePollmsec == 0) - writePollmsec = 1; - if (writePollmsec < 0) - writePollmsec = -1; + writePollmsec = pollmsecFromTimeout(pasynUser->timeout); for (;;) { pollfd.fd = fd_; pollfd.events = POLLOUT; @@ -475,7 +477,8 @@ asynStatus pmacAsynIPPort::socketWrite(asynUser *pasynUser, const char *data, break; } if (thisWrite > 0) { - *nbytesTransferred += thisWrite; + if (nbytesTransferred) + *nbytesTransferred += thisWrite; numchars -= thisWrite; if (numchars == 0) break; @@ -496,11 +499,25 @@ asynStatus pmacAsynIPPort::socketWrite(asynUser *pasynUser, const char *data, } } asynPrint(pasynUser, ASYN_TRACE_FLOW, "wrote %lu to %s, return %s.\n", - (unsigned long)*nbytesTransferred, IPHostName_.c_str(), - pasynManager->strStatus(status)); + (unsigned long)(nbytesTransferred ? *nbytesTransferred : 0), + IPHostName_.c_str(), pasynManager->strStatus(status)); return status; } +asynStatus pmacAsynIPPort::sendPmacCommand(asynUser *pasynUser, + EthernetCmd &cmd, + uint8_t requestType, uint8_t request, + uint16_t wValue, uint16_t wLength, + size_t *nbytesTransfered) { + cmd.RequestType = requestType; + cmd.Request = request; + cmd.wValue = wValue; + cmd.wIndex = 0; + cmd.wLength = htons(wLength); + return socketWrite(pasynUser, reinterpret_cast(&cmd), + ETHERNET_CMD_HEADER, nbytesTransfered); +} + /* Read reponse data from PMAC into buffer inBuf_. If there is no data in the socket buffer then issue pmac GETBUFFER command to get any outstanding data @@ -530,7 +547,7 @@ asynStatus pmacAsynIPPort::readResponse(asynUser *pasynUser, size_t maxchars, check for more response data on the PMAC */ if (pmacReadReady(pasynUser)) { - status = sendPmacGetBuffer(pasynUser, maxchars, nbytesTransfered); + status = sendPmacGetBuffer(pasynUser, maxchars); asynPrintIO(pasynUser, ASYN_TRACE_FLOW, reinterpret_cast(&pinCmd_), ETHERNET_CMD_HEADER, "%s write GETBUFFER\n", portName); @@ -569,17 +586,10 @@ int pmacAsynIPPort::pmacReadReady(asynUser *pasynUser) { char data[2] = {0}; asynStatus status; size_t thisRead = 0; - size_t nbytesTransfered = 0; int retval = 0; - cmd.RequestType = VR_UPLOAD; - cmd.Request = VR_PMAC_READREADY; - cmd.wValue = 0; - cmd.wIndex = 0; - cmd.wLength = htons(2); - - status = socketWrite(pasynUser, reinterpret_cast(&cmd), - ETHERNET_CMD_HEADER, &nbytesTransfered); + status = sendPmacCommand(pasynUser, cmd, VR_UPLOAD, VR_PMAC_READREADY, 0, 2, + nullptr); if (status != asynSuccess) { asynPrintIO(pasynUser, ASYN_TRACE_ERROR, @@ -617,17 +627,10 @@ int pmacAsynIPPort::pmacFlush(asynUser *pasynUser) { char data[2] = {0}; asynStatus status = asynSuccess; size_t thisRead = 0; - size_t nbytesTransfered = 0; int retval = 0; - cmd.RequestType = VR_DOWNLOAD; - cmd.Request = VR_PMAC_FLUSH; - cmd.wValue = 0; - cmd.wIndex = 0; - cmd.wLength = 0; - - status = socketWrite(pasynUser, reinterpret_cast(&cmd), - ETHERNET_CMD_HEADER, &nbytesTransfered); + status = sendPmacCommand(pasynUser, cmd, VR_DOWNLOAD, VR_PMAC_FLUSH, 0, 0, + nullptr); if (status != asynSuccess) { asynPrintIO(pasynUser, ASYN_TRACE_ERROR, @@ -657,15 +660,9 @@ int pmacAsynIPPort::pmacFlush(asynUser *pasynUser) { } asynStatus pmacAsynIPPort::sendPmacGetBuffer(asynUser *pasynUser, - size_t maxchars, - size_t *nbytesTransfered) { - pinCmd_.RequestType = VR_UPLOAD; - pinCmd_.Request = VR_PMAC_GETBUFFER; - pinCmd_.wValue = 0; - pinCmd_.wIndex = 0; - pinCmd_.wLength = htons(maxchars); - return socketWrite(pasynUser, reinterpret_cast(&pinCmd_), - ETHERNET_CMD_HEADER, nbytesTransfered); + size_t maxchars) { + return sendPmacCommand(pasynUser, pinCmd_, VR_UPLOAD, VR_PMAC_GETBUFFER, 0, + maxchars, nullptr); } /* @@ -685,14 +682,8 @@ asynStatus pmacAsynIPPort::writeOctet(asynUser *pasynUser, const char *data, buffer for control commands and do PMAC_GETRESPONSE and CTRL_RESPONSE commands as necessary, */ if (numchars == 1 && strchr(ctrlCommands, data[0])) { - poutCmd_.RequestType = VR_UPLOAD; - poutCmd_.Request = VR_CTRL_RESPONSE; - poutCmd_.wValue = data[0]; - poutCmd_.wIndex = 0; - poutCmd_.wLength = htons(0); - status = - socketWrite(pasynUser, reinterpret_cast(&poutCmd_), - ETHERNET_CMD_HEADER, &nbytesActual); + status = sendPmacCommand(pasynUser, poutCmd_, VR_UPLOAD, + VR_CTRL_RESPONSE, data[0], 0, &nbytesActual); *nbytesTransfered = nbytesActual == ETHERNET_CMD_HEADER ? numchars : 0; } else { if (numchars > ETHERNET_DATA_SIZE) { @@ -819,8 +810,7 @@ asynStatus pmacAsynIPPort::readOctet(asynUser *pasynUser, char *data, "pmacAsynIPPort::readOctet. Calling readResponse().\n"); if (!initialRead) { if (pmacReadReady(pasynUser)) { - status = sendPmacGetBuffer(pasynUser, maxchars, - nbytesTransfered); + status = sendPmacGetBuffer(pasynUser, maxchars); } } status = diff --git a/src/pmacAsynIPPort.h b/src/pmacAsynIPPort.h index 19b025a..1d35b26 100644 --- a/src/pmacAsynIPPort.h +++ b/src/pmacAsynIPPort.h @@ -116,6 +116,9 @@ class HIDDEN pmacAsynIPPort : public asynPortDriver { /** * @brief Raw write to the TCP socket. + * + * If nbytesTransferred is not null, it receives the number of bytes that + * were sent to the socket. */ asynStatus socketWrite(asynUser *pasynUser, const char *data, size_t numchars, size_t *nbytesWritten); @@ -130,6 +133,18 @@ class HIDDEN pmacAsynIPPort : public asynPortDriver { asynStatus pollSocket(asynUser *pasynUser, struct pollfd *pollfd, int pollmsec, int *pollstatus); + /** + * @brief Fill in a PMAC ethernet command header and send it over the + * socket. + * + * Only the 8 byte command header is sent. Commands that carry a payload + * (e.g. GETRESPONSE in writeOctet) are sent directly via socketWrite. + */ + asynStatus sendPmacCommand(asynUser *pasynUser, EthernetCmd &cmd, + uint8_t requestType, uint8_t request, + uint16_t wValue, uint16_t wLength, + size_t *nbytesTransfered); + /** * @brief Read response data from the PMAC into the internal input buffer * @@ -157,8 +172,7 @@ class HIDDEN pmacAsynIPPort : public asynPortDriver { /** * @brief Send a GETBUFFER command to the PMAC. */ - asynStatus sendPmacGetBuffer(asynUser *pasynUser, size_t maxchars, - size_t *nbytesTransfered); + asynStatus sendPmacGetBuffer(asynUser *pasynUser, size_t maxchars); // Host name and TCP port of the PMAC std::string IPHostName_;