624 lines
17 KiB
C++
624 lines
17 KiB
C++
|
|
#include "DRI/Dri_Cdc.h"
|
|||
|
|
|
|||
|
|
#include "COM/Com_Protocol.h"
|
|||
|
|
#include <QtCore/QDir>
|
|||
|
|
#include <QtCore/QQueue>
|
|||
|
|
#include <QtCore/QStringList>
|
|||
|
|
#include <QtCore/QVector>
|
|||
|
|
#include <QtSerialPort/QSerialPort>
|
|||
|
|
#include <QtSerialPort/QSerialPortInfo>
|
|||
|
|
|
|||
|
|
struct Dri_Cdc_Struct_Candidate
|
|||
|
|
{
|
|||
|
|
QSerialPort* p_SerialPort = nullptr;
|
|||
|
|
QString PortName;
|
|||
|
|
QString EndpointId;
|
|||
|
|
QString SystemLocation;
|
|||
|
|
QByteArray PendingBytes;
|
|||
|
|
QQueue<Com_Struct_RawPacket> PacketQueue;
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
struct Dri_Cdc_Struct_Context
|
|||
|
|
{
|
|||
|
|
QVector<Dri_Cdc_Struct_Candidate> CandidateList;
|
|||
|
|
int ActiveCandidateIndex = -1;
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
namespace
|
|||
|
|
{
|
|||
|
|
|
|||
|
|
QString Dri_Cdc_FormatPortLabel(const QSerialPortInfo& PortInfo)
|
|||
|
|
{
|
|||
|
|
const QString Description = PortInfo.description().trimmed();
|
|||
|
|
return Description.isEmpty()
|
|||
|
|
? QStringLiteral("USB CDC (%1)").arg(PortInfo.portName())
|
|||
|
|
: QStringLiteral("USB CDC (%1 - %2)").arg(PortInfo.portName(), Description);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
QString Dri_Cdc_BuildEndpointId(const QSerialPortInfo& PortInfo)
|
|||
|
|
{
|
|||
|
|
const QString SystemLocation = QDir::cleanPath(PortInfo.systemLocation());
|
|||
|
|
if (!SystemLocation.isEmpty())
|
|||
|
|
{
|
|||
|
|
return SystemLocation;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
return PortInfo.portName().trimmed();
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
bool Dri_Cdc_IsPortStillPresent(const Dri_Cdc_Struct_Candidate& Candidate)
|
|||
|
|
{
|
|||
|
|
for (const QSerialPortInfo& PortInfo : QSerialPortInfo::availablePorts())
|
|||
|
|
{
|
|||
|
|
if (!Candidate.SystemLocation.isEmpty() &&
|
|||
|
|
(QDir::cleanPath(PortInfo.systemLocation())
|
|||
|
|
.compare(Candidate.SystemLocation, Qt::CaseInsensitive) == 0))
|
|||
|
|
{
|
|||
|
|
return true;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
return false;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
void Dri_Cdc_DeleteCandidate(Dri_Cdc_Struct_Candidate* p_Candidate)
|
|||
|
|
{
|
|||
|
|
if (p_Candidate == nullptr)
|
|||
|
|
{
|
|||
|
|
return;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
if ((p_Candidate->p_SerialPort != nullptr) && p_Candidate->p_SerialPort->isOpen())
|
|||
|
|
{
|
|||
|
|
p_Candidate->p_SerialPort->setDataTerminalReady(false);
|
|||
|
|
p_Candidate->p_SerialPort->close();
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
delete p_Candidate->p_SerialPort;
|
|||
|
|
p_Candidate->p_SerialPort = nullptr;
|
|||
|
|
p_Candidate->PendingBytes.clear();
|
|||
|
|
p_Candidate->PacketQueue.clear();
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
void Dri_Cdc_DeleteContext(Dri_Cdc_Struct_Context* p_Context)
|
|||
|
|
{
|
|||
|
|
if (p_Context == nullptr)
|
|||
|
|
{
|
|||
|
|
return;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
for (int Index = 0; Index < p_Context->CandidateList.size(); ++Index)
|
|||
|
|
{
|
|||
|
|
Dri_Cdc_DeleteCandidate(&p_Context->CandidateList[Index]);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
delete p_Context;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
int Dri_Cdc_FindCandidateIndex(
|
|||
|
|
const Dri_Cdc_Struct_Context* p_Context,
|
|||
|
|
const QString& EndpointId)
|
|||
|
|
{
|
|||
|
|
if ((p_Context == nullptr) || EndpointId.trimmed().isEmpty())
|
|||
|
|
{
|
|||
|
|
return -1;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
for (int Index = 0; Index < p_Context->CandidateList.size(); ++Index)
|
|||
|
|
{
|
|||
|
|
const Dri_Cdc_Struct_Candidate& Candidate = p_Context->CandidateList.at(Index);
|
|||
|
|
if (Candidate.EndpointId.compare(EndpointId, Qt::CaseInsensitive) == 0)
|
|||
|
|
{
|
|||
|
|
return Index;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
return -1;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
void Dri_Cdc_RefreshPortSummary(
|
|||
|
|
Dri_Cdc_Struct_Port* p_Port,
|
|||
|
|
Dri_Cdc_Struct_Context* p_Context)
|
|||
|
|
{
|
|||
|
|
if ((p_Port == nullptr) || (p_Context == nullptr))
|
|||
|
|
{
|
|||
|
|
return;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
if ((p_Context->ActiveCandidateIndex >= 0) &&
|
|||
|
|
((p_Context->ActiveCandidateIndex >= p_Context->CandidateList.size()) ||
|
|||
|
|
(p_Context->CandidateList.at(p_Context->ActiveCandidateIndex).p_SerialPort == nullptr) ||
|
|||
|
|
!p_Context->CandidateList.at(p_Context->ActiveCandidateIndex).p_SerialPort->isOpen()))
|
|||
|
|
{
|
|||
|
|
p_Context->ActiveCandidateIndex = -1;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
QStringList OpenedPortList;
|
|||
|
|
for (const Dri_Cdc_Struct_Candidate& Candidate : p_Context->CandidateList)
|
|||
|
|
{
|
|||
|
|
if ((Candidate.p_SerialPort != nullptr) && Candidate.p_SerialPort->isOpen())
|
|||
|
|
{
|
|||
|
|
OpenedPortList.append(Candidate.PortName);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
if (OpenedPortList.isEmpty())
|
|||
|
|
{
|
|||
|
|
p_Port->IsOpened = false;
|
|||
|
|
p_Port->PortName.clear();
|
|||
|
|
return;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
p_Port->IsOpened = true;
|
|||
|
|
if (p_Context->ActiveCandidateIndex >= 0)
|
|||
|
|
{
|
|||
|
|
p_Port->PortName =
|
|||
|
|
p_Context->CandidateList.at(p_Context->ActiveCandidateIndex).PortName;
|
|||
|
|
return;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
p_Port->PortName = OpenedPortList.size() == 1
|
|||
|
|
? OpenedPortList.first()
|
|||
|
|
: QStringLiteral("USB CDC candidates (%1)").arg(OpenedPortList.join(QStringLiteral(", ")));
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
bool Dri_Cdc_WritePacket(
|
|||
|
|
Dri_Cdc_Struct_Candidate* p_Candidate,
|
|||
|
|
const QByteArray& PacketBody)
|
|||
|
|
{
|
|||
|
|
if ((p_Candidate == nullptr) ||
|
|||
|
|
(p_Candidate->p_SerialPort == nullptr) ||
|
|||
|
|
!p_Candidate->p_SerialPort->isOpen())
|
|||
|
|
{
|
|||
|
|
return false;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
const qint64 BytesWritten = p_Candidate->p_SerialPort->write(PacketBody);
|
|||
|
|
if (BytesWritten != PacketBody.size())
|
|||
|
|
{
|
|||
|
|
return false;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
p_Candidate->p_SerialPort->flush();
|
|||
|
|
return p_Candidate->p_SerialPort->waitForBytesWritten(100);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
void Dri_Cdc_EnqueuePacket(
|
|||
|
|
Dri_Cdc_Struct_Candidate* p_Candidate,
|
|||
|
|
const QByteArray& PacketBody,
|
|||
|
|
Com_Enum_ProtocolType Type)
|
|||
|
|
{
|
|||
|
|
if ((p_Candidate == nullptr) || PacketBody.isEmpty() ||
|
|||
|
|
(Type == Com_Enum_ProtocolType_None))
|
|||
|
|
{
|
|||
|
|
return;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
Com_Struct_RawPacket Packet;
|
|||
|
|
Packet.IsValid = true;
|
|||
|
|
Packet.Source = Com_Enum_RawPacketSource_UsbCdc;
|
|||
|
|
Packet.ProtocolType = Type;
|
|||
|
|
Packet.ByteArray = PacketBody;
|
|||
|
|
Packet.PortName = p_Candidate->PortName;
|
|||
|
|
Packet.EndpointId = p_Candidate->EndpointId;
|
|||
|
|
p_Candidate->PacketQueue.enqueue(Packet);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
void Dri_Cdc_BufferIncomingBytes(
|
|||
|
|
Dri_Cdc_Struct_Candidate* p_Candidate,
|
|||
|
|
const QByteArray& IncomingBytes)
|
|||
|
|
{
|
|||
|
|
if ((p_Candidate == nullptr) || IncomingBytes.isEmpty())
|
|||
|
|
{
|
|||
|
|
return;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
p_Candidate->PendingBytes.append(IncomingBytes);
|
|||
|
|
|
|||
|
|
while (true)
|
|||
|
|
{
|
|||
|
|
QByteArray PacketBody;
|
|||
|
|
Com_Enum_ProtocolType Type = Com_Enum_ProtocolType_None;
|
|||
|
|
if (!Com_Protocol_TryTakePacket(&p_Candidate->PendingBytes, &PacketBody, &Type))
|
|||
|
|
{
|
|||
|
|
break;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
Dri_Cdc_EnqueuePacket(p_Candidate, PacketBody, Type);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
bool Dri_Cdc_DequeuePacket(
|
|||
|
|
Dri_Cdc_Struct_Candidate* p_Candidate,
|
|||
|
|
Com_Struct_RawPacket* p_Packet,
|
|||
|
|
QString* p_TextStatus)
|
|||
|
|
{
|
|||
|
|
if ((p_Candidate == nullptr) || (p_Packet == nullptr) || p_Candidate->PacketQueue.isEmpty())
|
|||
|
|
{
|
|||
|
|
return false;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
*p_Packet = p_Candidate->PacketQueue.dequeue();
|
|||
|
|
if (p_TextStatus != nullptr)
|
|||
|
|
{
|
|||
|
|
*p_TextStatus =
|
|||
|
|
QStringLiteral("%1 RX %2")
|
|||
|
|
.arg(
|
|||
|
|
p_Candidate->PortName,
|
|||
|
|
QString::fromLatin1(p_Packet->ByteArray.toHex(' ').toUpper()));
|
|||
|
|
}
|
|||
|
|
return true;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
} // namespace
|
|||
|
|
|
|||
|
|
void Dri_Cdc_Close(Dri_Cdc_Struct_Port* p_Port)
|
|||
|
|
{
|
|||
|
|
Dri_Cdc_DeleteContext(p_Port->p_Context);
|
|||
|
|
*p_Port = Dri_Cdc_Struct_Port();
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
bool Dri_Cdc_Init(
|
|||
|
|
Dri_Cdc_Struct_Port* p_Port,
|
|||
|
|
const Com_Struct_DeviceConfig& DeviceConfig,
|
|||
|
|
QString* p_TextStatus)
|
|||
|
|
{
|
|||
|
|
Dri_Cdc_Close(p_Port);
|
|||
|
|
|
|||
|
|
const QVector<QSerialPortInfo> PortInfoList = QSerialPortInfo::availablePorts().toVector();
|
|||
|
|
if (PortInfoList.isEmpty())
|
|||
|
|
{
|
|||
|
|
if (p_TextStatus != nullptr)
|
|||
|
|
{
|
|||
|
|
*p_TextStatus = QStringLiteral("No USB CDC port is available.");
|
|||
|
|
}
|
|||
|
|
return false;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
auto* p_Context = new Dri_Cdc_Struct_Context();
|
|||
|
|
QStringList ErrorList;
|
|||
|
|
for (const QSerialPortInfo& PortInfo : PortInfoList)
|
|||
|
|
{
|
|||
|
|
Dri_Cdc_Struct_Candidate Candidate;
|
|||
|
|
Candidate.p_SerialPort = new QSerialPort(PortInfo);
|
|||
|
|
Candidate.PortName = Dri_Cdc_FormatPortLabel(PortInfo);
|
|||
|
|
Candidate.EndpointId = Dri_Cdc_BuildEndpointId(PortInfo);
|
|||
|
|
Candidate.SystemLocation = QDir::cleanPath(PortInfo.systemLocation());
|
|||
|
|
|
|||
|
|
Candidate.p_SerialPort->setBaudRate(QSerialPort::Baud115200);
|
|||
|
|
Candidate.p_SerialPort->setDataBits(QSerialPort::Data8);
|
|||
|
|
Candidate.p_SerialPort->setParity(QSerialPort::NoParity);
|
|||
|
|
Candidate.p_SerialPort->setStopBits(QSerialPort::OneStop);
|
|||
|
|
Candidate.p_SerialPort->setFlowControl(QSerialPort::NoFlowControl);
|
|||
|
|
|
|||
|
|
if (!Candidate.p_SerialPort->open(QIODevice::ReadWrite))
|
|||
|
|
{
|
|||
|
|
ErrorList.append(
|
|||
|
|
QStringLiteral("%1: %2")
|
|||
|
|
.arg(PortInfo.portName(), Candidate.p_SerialPort->errorString()));
|
|||
|
|
Dri_Cdc_DeleteCandidate(&Candidate);
|
|||
|
|
continue;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
Candidate.p_SerialPort->setDataTerminalReady(true);
|
|||
|
|
p_Context->CandidateList.append(Candidate);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
if (p_Context->CandidateList.isEmpty())
|
|||
|
|
{
|
|||
|
|
if (p_TextStatus != nullptr)
|
|||
|
|
{
|
|||
|
|
*p_TextStatus = ErrorList.isEmpty()
|
|||
|
|
? QStringLiteral("USB CDC ports were found, but all opens failed.")
|
|||
|
|
: QStringLiteral("USB CDC open failed: %1").arg(ErrorList.join(QStringLiteral(" | ")));
|
|||
|
|
}
|
|||
|
|
Dri_Cdc_DeleteContext(p_Context);
|
|||
|
|
return false;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
p_Port->p_Context = p_Context;
|
|||
|
|
Dri_Cdc_RefreshPortSummary(p_Port, p_Context);
|
|||
|
|
Q_UNUSED(DeviceConfig);
|
|||
|
|
if (p_TextStatus != nullptr)
|
|||
|
|
{
|
|||
|
|
*p_TextStatus = QStringLiteral("%1 connected.").arg(p_Port->PortName);
|
|||
|
|
}
|
|||
|
|
return true;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
bool Dri_Cdc_Read(
|
|||
|
|
Dri_Cdc_Struct_Port* p_Port,
|
|||
|
|
Com_Struct_RawPacket* p_Packet,
|
|||
|
|
QString* p_TextStatus)
|
|||
|
|
{
|
|||
|
|
*p_Packet = Com_Struct_RawPacket();
|
|||
|
|
p_Packet->Source = Com_Enum_RawPacketSource_UsbCdc;
|
|||
|
|
p_Packet->PortName = p_Port->PortName;
|
|||
|
|
|
|||
|
|
Dri_Cdc_Struct_Context* p_Context = p_Port->p_Context;
|
|||
|
|
if (!p_Port->IsOpened || (p_Context == nullptr) || p_Context->CandidateList.isEmpty())
|
|||
|
|
{
|
|||
|
|
return false;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
const int StartIndex =
|
|||
|
|
p_Context->ActiveCandidateIndex >= 0 ? p_Context->ActiveCandidateIndex : 0;
|
|||
|
|
const int EndIndex =
|
|||
|
|
p_Context->ActiveCandidateIndex >= 0
|
|||
|
|
? (p_Context->ActiveCandidateIndex + 1)
|
|||
|
|
: p_Context->CandidateList.size();
|
|||
|
|
|
|||
|
|
for (int Index = StartIndex; Index < EndIndex; ++Index)
|
|||
|
|
{
|
|||
|
|
Dri_Cdc_Struct_Candidate& Candidate = p_Context->CandidateList[Index];
|
|||
|
|
if ((Candidate.p_SerialPort == nullptr) || !Candidate.p_SerialPort->isOpen())
|
|||
|
|
{
|
|||
|
|
continue;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
if (!Dri_Cdc_IsPortStillPresent(Candidate))
|
|||
|
|
{
|
|||
|
|
if (p_TextStatus != nullptr)
|
|||
|
|
{
|
|||
|
|
*p_TextStatus = QStringLiteral("%1 disconnected.").arg(Candidate.PortName);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
Dri_Cdc_DeleteCandidate(&Candidate);
|
|||
|
|
if (p_Context->ActiveCandidateIndex == Index)
|
|||
|
|
{
|
|||
|
|
Dri_Cdc_Close(p_Port);
|
|||
|
|
return false;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
Dri_Cdc_RefreshPortSummary(p_Port, p_Context);
|
|||
|
|
continue;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
if (Dri_Cdc_DequeuePacket(&Candidate, p_Packet, p_TextStatus))
|
|||
|
|
{
|
|||
|
|
return true;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
const QByteArray IncomingBytes = Candidate.p_SerialPort->readAll();
|
|||
|
|
if (IncomingBytes.isEmpty())
|
|||
|
|
{
|
|||
|
|
continue;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
Dri_Cdc_BufferIncomingBytes(&Candidate, IncomingBytes);
|
|||
|
|
if (Dri_Cdc_DequeuePacket(&Candidate, p_Packet, p_TextStatus))
|
|||
|
|
{
|
|||
|
|
return true;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
if ((p_TextStatus != nullptr) && !Candidate.PendingBytes.isEmpty())
|
|||
|
|
{
|
|||
|
|
*p_TextStatus =
|
|||
|
|
QStringLiteral("%1 has pending protobuf bytes and is waiting for the next CDC chunk.")
|
|||
|
|
.arg(Candidate.PortName);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
Dri_Cdc_RefreshPortSummary(p_Port, p_Context);
|
|||
|
|
if (!p_Port->IsOpened)
|
|||
|
|
{
|
|||
|
|
Dri_Cdc_Close(p_Port);
|
|||
|
|
}
|
|||
|
|
return false;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
bool Dri_Cdc_LockCandidate(
|
|||
|
|
Dri_Cdc_Struct_Port* p_Port,
|
|||
|
|
const QString& EndpointId,
|
|||
|
|
QString* p_TextStatus)
|
|||
|
|
{
|
|||
|
|
Dri_Cdc_Struct_Context* p_Context = p_Port->p_Context;
|
|||
|
|
if (!p_Port->IsOpened || (p_Context == nullptr))
|
|||
|
|
{
|
|||
|
|
if (p_TextStatus != nullptr)
|
|||
|
|
{
|
|||
|
|
*p_TextStatus = QStringLiteral("USB CDC is not open, so the candidate cannot be locked.");
|
|||
|
|
}
|
|||
|
|
return false;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
const int ActiveIndex = Dri_Cdc_FindCandidateIndex(p_Context, EndpointId);
|
|||
|
|
if (ActiveIndex < 0)
|
|||
|
|
{
|
|||
|
|
if (p_TextStatus != nullptr)
|
|||
|
|
{
|
|||
|
|
*p_TextStatus = QStringLiteral("The target USB CDC candidate was not found.");
|
|||
|
|
}
|
|||
|
|
return false;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
if ((p_Context->CandidateList.at(ActiveIndex).p_SerialPort == nullptr) ||
|
|||
|
|
!p_Context->CandidateList.at(ActiveIndex).p_SerialPort->isOpen())
|
|||
|
|
{
|
|||
|
|
if (p_TextStatus != nullptr)
|
|||
|
|
{
|
|||
|
|
*p_TextStatus = QStringLiteral("The target USB CDC candidate is already closed.");
|
|||
|
|
}
|
|||
|
|
return false;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
if (p_Context->ActiveCandidateIndex == ActiveIndex)
|
|||
|
|
{
|
|||
|
|
if (p_TextStatus != nullptr)
|
|||
|
|
{
|
|||
|
|
*p_TextStatus =
|
|||
|
|
QStringLiteral("USB CDC already locked to %1.")
|
|||
|
|
.arg(p_Context->CandidateList.at(ActiveIndex).PortName);
|
|||
|
|
}
|
|||
|
|
return true;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
for (int Index = 0; Index < p_Context->CandidateList.size(); ++Index)
|
|||
|
|
{
|
|||
|
|
if (Index == ActiveIndex)
|
|||
|
|
{
|
|||
|
|
continue;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
Dri_Cdc_DeleteCandidate(&p_Context->CandidateList[Index]);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
p_Context->ActiveCandidateIndex = ActiveIndex;
|
|||
|
|
Dri_Cdc_RefreshPortSummary(p_Port, p_Context);
|
|||
|
|
if (p_TextStatus != nullptr)
|
|||
|
|
{
|
|||
|
|
*p_TextStatus = QStringLiteral("USB CDC locked to %1.").arg(p_Port->PortName);
|
|||
|
|
}
|
|||
|
|
return true;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
bool Dri_Cdc_DiscardCandidate(
|
|||
|
|
Dri_Cdc_Struct_Port* p_Port,
|
|||
|
|
const QString& EndpointId,
|
|||
|
|
QString* p_TextStatus)
|
|||
|
|
{
|
|||
|
|
Dri_Cdc_Struct_Context* p_Context = p_Port->p_Context;
|
|||
|
|
if (!p_Port->IsOpened || (p_Context == nullptr))
|
|||
|
|
{
|
|||
|
|
return false;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
const int CandidateIndex = Dri_Cdc_FindCandidateIndex(p_Context, EndpointId);
|
|||
|
|
if (CandidateIndex < 0)
|
|||
|
|
{
|
|||
|
|
return false;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
const QString PortLabel = p_Context->CandidateList.at(CandidateIndex).PortName;
|
|||
|
|
const bool IsActiveCandidate = p_Context->ActiveCandidateIndex == CandidateIndex;
|
|||
|
|
Dri_Cdc_DeleteCandidate(&p_Context->CandidateList[CandidateIndex]);
|
|||
|
|
|
|||
|
|
if (IsActiveCandidate)
|
|||
|
|
{
|
|||
|
|
Dri_Cdc_Close(p_Port);
|
|||
|
|
if (p_TextStatus != nullptr)
|
|||
|
|
{
|
|||
|
|
*p_TextStatus = QStringLiteral("Discarded the locked USB CDC candidate %1.").arg(PortLabel);
|
|||
|
|
}
|
|||
|
|
return true;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
Dri_Cdc_RefreshPortSummary(p_Port, p_Context);
|
|||
|
|
if (!p_Port->IsOpened)
|
|||
|
|
{
|
|||
|
|
Dri_Cdc_Close(p_Port);
|
|||
|
|
if (p_TextStatus != nullptr)
|
|||
|
|
{
|
|||
|
|
*p_TextStatus = QStringLiteral("All USB CDC candidates were discarded.");
|
|||
|
|
}
|
|||
|
|
return true;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
if (p_TextStatus != nullptr)
|
|||
|
|
{
|
|||
|
|
*p_TextStatus = QStringLiteral("Discarded USB CDC candidate %1.").arg(PortLabel);
|
|||
|
|
}
|
|||
|
|
return true;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
bool Dri_Cdc_Write(
|
|||
|
|
Dri_Cdc_Struct_Port* p_Port,
|
|||
|
|
const QByteArray& PacketBody,
|
|||
|
|
QString* p_TextStatus)
|
|||
|
|
{
|
|||
|
|
Dri_Cdc_Struct_Context* p_Context = p_Port->p_Context;
|
|||
|
|
if (!p_Port->IsOpened || (p_Context == nullptr) || p_Context->CandidateList.isEmpty())
|
|||
|
|
{
|
|||
|
|
if (p_TextStatus != nullptr)
|
|||
|
|
{
|
|||
|
|
*p_TextStatus = QStringLiteral("USB CDC is not open. Skip send.");
|
|||
|
|
}
|
|||
|
|
return false;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
if (PacketBody.isEmpty())
|
|||
|
|
{
|
|||
|
|
if (p_TextStatus != nullptr)
|
|||
|
|
{
|
|||
|
|
*p_TextStatus = QStringLiteral("USB CDC packet body is empty.");
|
|||
|
|
}
|
|||
|
|
return false;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
if (p_Context->ActiveCandidateIndex >= 0)
|
|||
|
|
{
|
|||
|
|
Dri_Cdc_Struct_Candidate& ActiveCandidate =
|
|||
|
|
p_Context->CandidateList[p_Context->ActiveCandidateIndex];
|
|||
|
|
if (!Dri_Cdc_WritePacket(&ActiveCandidate, PacketBody))
|
|||
|
|
{
|
|||
|
|
if (p_TextStatus != nullptr)
|
|||
|
|
{
|
|||
|
|
*p_TextStatus =
|
|||
|
|
QStringLiteral("%1 鍐欏叆澶辫触锛?2")
|
|||
|
|
.arg(
|
|||
|
|
ActiveCandidate.PortName,
|
|||
|
|
ActiveCandidate.p_SerialPort == nullptr
|
|||
|
|
? QStringLiteral("port already closed")
|
|||
|
|
: ActiveCandidate.p_SerialPort->errorString());
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
Dri_Cdc_Close(p_Port);
|
|||
|
|
return false;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
if (p_TextStatus != nullptr)
|
|||
|
|
{
|
|||
|
|
*p_TextStatus =
|
|||
|
|
QStringLiteral("%1 TX %2")
|
|||
|
|
.arg(
|
|||
|
|
ActiveCandidate.PortName,
|
|||
|
|
QString::fromLatin1(PacketBody.toHex(' ').toUpper()));
|
|||
|
|
}
|
|||
|
|
return true;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
int SuccessCount = 0;
|
|||
|
|
QStringList SuccessPortList;
|
|||
|
|
QStringList ErrorList;
|
|||
|
|
for (Dri_Cdc_Struct_Candidate& Candidate : p_Context->CandidateList)
|
|||
|
|
{
|
|||
|
|
if ((Candidate.p_SerialPort == nullptr) || !Candidate.p_SerialPort->isOpen())
|
|||
|
|
{
|
|||
|
|
continue;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
if (Dri_Cdc_WritePacket(&Candidate, PacketBody))
|
|||
|
|
{
|
|||
|
|
++SuccessCount;
|
|||
|
|
SuccessPortList.append(Candidate.PortName);
|
|||
|
|
continue;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
ErrorList.append(
|
|||
|
|
QStringLiteral("%1: %2")
|
|||
|
|
.arg(Candidate.PortName, Candidate.p_SerialPort->errorString()));
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
if (SuccessCount > 0)
|
|||
|
|
{
|
|||
|
|
if (p_TextStatus != nullptr)
|
|||
|
|
{
|
|||
|
|
*p_TextStatus =
|
|||
|
|
QStringLiteral("USB CDC 骞挎挱 TX %1锛岀洰鏍囷細%2")
|
|||
|
|
.arg(
|
|||
|
|
QString::fromLatin1(PacketBody.toHex(' ').toUpper()),
|
|||
|
|
SuccessPortList.join(QStringLiteral(", ")));
|
|||
|
|
}
|
|||
|
|
return true;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
if (p_TextStatus != nullptr)
|
|||
|
|
{
|
|||
|
|
*p_TextStatus = ErrorList.isEmpty()
|
|||
|
|
? QStringLiteral("USB CDC send failed.")
|
|||
|
|
: QStringLiteral("USB CDC 鍙戦€佸け璐ワ細%1").arg(ErrorList.join(QStringLiteral(" | ")));
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
return false;
|
|||
|
|
}
|