283 lines
10 KiB
C++
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;
|
|
}
|
|
}
|