Files
turboPmac/src/pmacAsynIPPort.cpp
ponsin_h 6f9e2fc06b
Test And Build / Lint (push) Failing after 12s
Test And Build / Build (push) Failing after 13s
update
2026-08-18 13:43:17 +02:00

283 lines
10 KiB
C++

#include "pmacAsynIPPort.h"
#include "asynPortDriver.h"
#include <algorithm>
#include <arpa/inet.h>
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<char> serialized = EthernetCommand::serialize(
EthernetCommand::RequestType::VR_UPLOAD,
EthernetCommand::Request::VR_CTRL_RESPONSE,
static_cast<short>(value[0]), 0, htons(0), {});
return asynPortDriver::writeOctet(pasynUser, serialized.data(),
serialized.size(), nActual);
}
std::vector<char> payload(value, value + nChars);
std::vector<char> 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) {
/* <BELL>xxxxxx<CR> or <STX>xxxxx<CR> received - its
* probably an error response (<BELL>ERRxxx<CR>) - assume
* there is no more response data to come */
nRead++; /* make sure the <CR> 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 <CR> 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') {
/* <ACK> or <LF> received - assume there is no more response
* data to come */
/* If <LF>, 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<char> 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<char> 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<char> 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;
}
}