#include "pmacAsynIPPort.h" #include "asynPortDriver.h" #include #include PmacPvt::PmacPvt(const char *portName, int maxAddr, int interfaceMask, int interruptMask, int asynFlags, int autoConnect, int priority, int stackSize) : asynPortDriver(portName, maxAddr, interfaceMask, interruptMask, asynFlags, autoConnect, priority, stackSize), circularBuffer(2097152) {}; asynStatus PmacPvt::writeOctet(asynUser *pasynUser, const char *value, size_t nChars, size_t *nActual) { if (nChars == 1 && isCharControlCommand(value[0])) { std::vector serialized = EthernetCommand::serialize( EthernetCommand::RequestType::VR_UPLOAD, EthernetCommand::Request::VR_CTRL_RESPONSE, static_cast(value[0]), 0, htons(0), {}); return asynPortDriver::writeOctet(pasynUser, serialized.data(), serialized.size(), nActual); } std::vector payload(value, value + nChars); std::vector serialized = EthernetCommand::serialize( EthernetCommand::RequestType::VR_DOWNLOAD, EthernetCommand::Request::VR_PMAC_GETRESPONSE, 0, 0, htons(nChars), payload); return asynPortDriver::writeOctet(pasynUser, serialized.data(), serialized.size(), nActual); } asynStatus PmacPvt::readOctet(asynUser *pasynUser, char *value, size_t maxChars, size_t *nActual, int *eomReason) { asynStatus status = asynSuccess; size_t thisRead = 0; size_t nRead = 0; bool isErrorMessagePending = false; int initialRead = 1; asynPrint(pasynUser, ASYN_TRACE_FLOW, "pmacAsynIPPort::readItOctet. START\n"); // When ask to read none, return if (maxChars <= 0) { *nActual = 0; return status; } for (;;) { if (!circularBuffer.isEmpty()) { *value = circularBuffer.read(); if (*value == BELL || *value == STX) isErrorMessagePending = true; if (*value == '\r' && isErrorMessagePending) { /* xxxxxx or xxxxx received - its * probably an error response (ERRxxx) - assume * there is no more response data to come */ nRead++; /* make sure the is passed to the client app */ /*Add on ACK, because that's what we expect to be EOS in EOS * interpose layer.*/ if ((nRead + 1) > maxChars) { /*If maxchars is reached overwrite with ACK, so * that no more reads will be done from EOS layer.*/ *value = ACK; } else { value++; nRead++; *value = ACK; } break; } if (*value == ACK || *value == '\n') { /* or received - assume there is no more response * data to come */ /* If , replace with an ACK.*/ if (*value == '\n') { *value = ACK; } asynPrint(pasynUser, ASYN_TRACE_FLOW, "Message was terminated with ACK in " "pmacAsynIPPort::readItOctet.\n"); /*Pass ACK up to Asyn EOS handling layer.*/ value++; nRead++; break; } value++; nRead++; if (nRead >= maxChars) break; continue; } asynPrint(pasynUser, ASYN_TRACE_FLOW, "pmacAsynIPPort::readItOctet. Calling readResponse().\n"); if (!initialRead) { if (pmacReadReady(pasynUser)) { status = sendPmacGetBuffer(pasynUser, maxChars, nActual); } } status = readResponse(pasynUser, maxChars - nRead, &thisRead, eomReason); initialRead = 0; if (status != asynSuccess || thisRead == 0) break; } *nActual = nRead; asynPrintIO(pasynUser, ASYN_TRACE_FLOW, value, *nActual, "%s pmacAsynIPPort readItOctet nActual=%zd, eomReason=%d, " "status=%d\n", pPmacPvt->portName, *nActual, *eomReason, status); asynPrint(pasynUser, ASYN_TRACE_FLOW, "pmacAsynIPPort::readItOctet. END\n"); return status; } asynStatus PmacPvt::flushOctet(asynUser *pasynUser) { return asynPortDriver::flushOctet(pasynUser); } /* PMAC specific function */ asynStatus PmacPvt::readResponse(asynUser *pasynUser, size_t maxchars, size_t *nbytesTransfered, int *eomReason) { asynStatus status = asynSuccess; size_t thisRead = 0; *nbytesTransfered = 0; if (maxchars > INPUT_SIZE) maxchars = INPUT_SIZE; asynPrint( pasynUser, ASYN_TRACE_FLOW, "pmacAsynIPPort::readResponse. Performing pPmacPvt->poctet->read().\n"); status = pPmacPvt->poctet->read(pPmacPvt->octetPvt, pasynUser, pPmacPvt->inBuf, maxchars, &thisRead, eomReason); asynPrint(pasynUser, ASYN_TRACE_FLOW, "%s readResponse1 maxchars=%zd, thisRead=%zd, eomReason=%d, " "status=%u\n", pPmacPvt->portName, maxchars, thisRead, *eomReason, status); if (status == asynTimeout && thisRead == 0 && pasynUser->timeout > 0) { /* failed to read as many characters as required into the input buffer, check for more response data on the PMAC */ if (pmacReadReady(pPmacPvt, pasynUser)) { status = sendPmacGetBuffer(pPmacPvt, pasynUser, maxchars, nbytesTransfered); asynPrintIO(pasynUser, ASYN_TRACE_FLOW, (char *)pPmacPvt->pinCmd, ETHERNET_CMD_HEADER, "%s write GETBUFFER\n", pPmacPvt->portName); /* We have nothing to return at the moment so read again */ status = pPmacPvt->poctet->read(pPmacPvt->octetPvt, pasynUser, pPmacPvt->inBuf, maxchars, &thisRead, eomReason); asynPrint(pasynUser, ASYN_TRACE_FLOW, "%s readResponse2 maxchars=%zd, thisRead=%zd, " "eomReason=%d, status=%u\n", pPmacPvt->portName, maxchars, thisRead, *eomReason, status); } } if (thisRead > 0) { if (status == asynTimeout) status = asynSuccess; *nbytesTransfered = thisRead; pPmacPvt->inBufTail = 0; pPmacPvt->inBufHead = thisRead; } asynPrint(pasynUser, ASYN_TRACE_FLOW, "pmacAsynIPPort::readResponse. END\n"); return status; } int PmacPvt::pmacReadReady(asynUser *pasynUser) { std::vector serialized = EthernetCommand::serialize( EthernetCommand::RequestType::VR_UPLOAD, EthernetCommand::Request::VR_PMAC_READREADY, 0, 0, htons(2), {}); asynStatus status = asynPortDriver::writeOctet( pasynUser, serialized.data(), serialized.size(), nbytesTransfered); if (status != asynSuccess) { asynPrintIO(pasynUser, ASYN_TRACE_ERROR, (char *)&cmd, ETHERNET_CMD_HEADER, "%s write pmacReadReady fail\n", pPmacPvt->portName); } int retval = 0; char data[2] = {0}; size_t dataSize = 0; int eomReason = 0; status = asynPortDriver::readOctet(pasynUser, data, 2, &dataSize, &eomReason); if (status == asynSuccess) { if (dataSize == 2 && data[0] != 0) { retval = 1; } asynPrintIO(pasynUser, ASYN_TRACE_FLOW, data, dataSize, "%s read pmacReadReady OK thisRead=%zd\n", pPmacPvt->portName, dataSize); } else { asynPrint(pasynUser, ASYN_TRACE_ERROR, "%s read pmacReadReady failed status=%d, retval=%d\n", pPmacPvt->portName, status, retval); } return retval; } int PmacPvt::pmacFlush(asynUser *pasynUser) { std::vector serialized = EthernetCommand::serialize( EthernetCommand::RequestType::VR_DOWNLOAD, EthernetCommand::Request::VR_PMAC_FLUSH, 0, 0, 0, {}); asynStatus status = asynPortDriver::writeOctet( pasynUser, serialized.data(), serialized.size(), nbytesTransfered); if (status != asynSuccess) { asynPrintIO(pasynUser, ASYN_TRACE_ERROR, (char *)&cmd, ETHERNET_CMD_HEADER, "%s write pmacFlush fail\n", pPmacPvt->portName); } char data[2]; size_t dataSize = 0; int eomReason = 0; status = asynPortDriver::readOctet(pasynUser, data, 1, &dataSize, &eomReason); if (status == asynSuccess) { asynPrint(pasynUser, ASYN_TRACE_FLOW, "%s read pmacFlush OK\n", pPmacPvt->portName); retval = 1; } else { asynPrint(pasynUser, ASYN_TRACE_ERROR, "%s read pmacFlush failed - thisRead=%zd, eomReason=%d, " "status=%d\n", pPmacPvt->portName, thisRead, eomReason, status); } circularBuffer.clear(); return retval; } asynStatus PmacPvt::sendPmacGetBuffer(asynUser *pasynUser, size_t maxchars, size_t *nbytesTransfered) { std::vector serialized = EthernetCommand::serialize( EthernetCommand::RequestType::VR_UPLOAD, EthernetCommand::Request::VR_PMAC_GETBUFFER, 0, 0, htons(maxchars), {}); return asynPortDriver::writeOctet(pasynUser, serialized.data(), serialized.size(), nbytesTransfered); } bool PmacPvt::isCharControlCommand(char c) { switch (c) { case CTRLB: case CTRLC: case CTRLF: case CTRLG: case CTRLP: case CTRLV: return true; default: return false; } }