1120 lines
37 KiB
C++
1120 lines
37 KiB
C++
/*
|
||
* NetX.cpp
|
||
*
|
||
* Created on: 30 <20><><EFBFBD><EFBFBD>. 2021 <20>.
|
||
* Author: titov
|
||
*/
|
||
|
||
#include <algorithm>
|
||
|
||
#include "../../memories/memories.hh"
|
||
|
||
#include "lowcifx/HilscherDualPortMemoryMap.hh"
|
||
#include "NetX.hh"
|
||
|
||
namespace {
|
||
driver::netx::dpm::NETX_DEFAULT_DPM & df(peripheral::protected_char * pointer ) {
|
||
return *reinterpret_cast<driver::netx::dpm::NETX_DEFAULT_DPM *>(pointer);
|
||
};
|
||
|
||
driver::netx::dpm::HIL_DPM_DEFAULT_COMM_CHANNEL & ch(peripheral::protected_char * pointer ) {
|
||
return *reinterpret_cast<driver::netx::dpm::HIL_DPM_DEFAULT_COMM_CHANNEL *>(pointer);
|
||
};
|
||
|
||
driver::netx::dpm::HIL_DPM_HANDSHAKE_CELL & hs(peripheral::protected_char * pointer ) {
|
||
return *reinterpret_cast<driver::netx::dpm::HIL_DPM_HANDSHAKE_CELL *>(pointer);
|
||
};
|
||
|
||
template<typename Pointer, typename Field>
|
||
void read_to(const Pointer * dpm_pointer, Field & field) {
|
||
while( not
|
||
peripheral::read_lock( reinterpret_cast<const peripheral::protected_char *>( dpm_pointer ), sizeof(Field) )
|
||
);
|
||
std::memcpy( &field, dpm_pointer, sizeof(Field) );
|
||
peripheral::read_unlock( reinterpret_cast<const peripheral::protected_char *>( dpm_pointer ), sizeof(Field) );
|
||
}
|
||
|
||
template<typename Pointer, typename Field>
|
||
void read_to(const volatile Pointer * dpm_pointer_v, Field & field) {
|
||
const Pointer * dpm_pointer = const_cast<const Pointer *>(dpm_pointer_v);
|
||
while( not
|
||
peripheral::read_lock( reinterpret_cast<const peripheral::protected_char *>( dpm_pointer ), sizeof(Field) )
|
||
);
|
||
std::memcpy( &field, dpm_pointer, sizeof(Field) );
|
||
peripheral::read_unlock( reinterpret_cast<const peripheral::protected_char *>( dpm_pointer ), sizeof(Field) );
|
||
}
|
||
|
||
template<typename Pointer, typename Field>
|
||
void write_from(Pointer * dpm_pointer, const Field & field) {
|
||
while( not
|
||
peripheral::write_lock( reinterpret_cast<peripheral::protected_char *>( dpm_pointer ), sizeof(Field) )
|
||
);
|
||
std::memcpy( dpm_pointer, &field, sizeof(Field) );
|
||
peripheral::write_unlock( reinterpret_cast<peripheral::protected_char *>( dpm_pointer ), sizeof(Field) );
|
||
}
|
||
|
||
template<typename Pointer, typename Field>
|
||
void write_from(volatile Pointer * dpm_pointer_v, const volatile Field & field_v) {
|
||
Pointer * dpm_pointer = const_cast<Pointer *>(dpm_pointer_v);
|
||
const Field & field = const_cast<const Field &>(field_v);
|
||
while( not
|
||
peripheral::write_lock( reinterpret_cast<peripheral::protected_char *>( dpm_pointer ), sizeof(Field) )
|
||
);
|
||
std::memcpy( dpm_pointer, &field, sizeof(Field) );
|
||
peripheral::write_unlock( reinterpret_cast<peripheral::protected_char *>( dpm_pointer ), sizeof(Field) );
|
||
}
|
||
|
||
}
|
||
|
||
driver::netx::NetX::NetX( std::pair<const char *, std::size_t> name,
|
||
std::pair<peripheral::protected_char *, std::size_t> dual_port_memory )
|
||
{
|
||
layout.system.channel.first = dual_port_memory.first;
|
||
layout.system.channel.second = dpm::HIL_DPM_SYSTEM_CHANNEL_SIZE;
|
||
|
||
dpm::HIL_DPM_SYSTEM_INFO_BLOCK sys_info_block;
|
||
read_to(&df(dual_port_memory.first).SystemChannel.tSystemInfo, sys_info_block);
|
||
|
||
device_info.dip_address = sys_info_block.bDevIdNumber;
|
||
device_info.device_class = sys_info_block.usDeviceClass;
|
||
device_info.device_number = sys_info_block.ulDeviceNumber;
|
||
device_info.serial_number = sys_info_block.ulSerialNumber;
|
||
|
||
dpm.base = dual_port_memory.first;
|
||
dpm.size = std::size_t(sys_info_block.ulDpmTotalSize * 8) / CHAR_BIT;
|
||
|
||
if( hasDefaultDpmLayout() ) {
|
||
|
||
layout.system.channel.first =
|
||
reinterpret_cast<peripheral::protected_char *>(&df(dpm.base).SystemChannel);
|
||
layout.system.channel.second =
|
||
sizeof(df(dpm.base).SystemChannel);
|
||
|
||
layout.system.handshake.first =
|
||
reinterpret_cast<peripheral::protected_char *>(&df(dpm.base).HandshakeChannel.tSysFlags);
|
||
layout.system.handshake.second =
|
||
sizeof(df(dpm.base).HandshakeChannel.tSysFlags);
|
||
|
||
//todo: implement mailboxes
|
||
|
||
layout.handshake.channel.first =
|
||
reinterpret_cast<peripheral::protected_char *>(&df(dpm.base).HandshakeChannel);
|
||
layout.handshake.channel.second =
|
||
sizeof(df(dpm.base).HandshakeChannel);
|
||
|
||
|
||
for( std::size_t id = 0; id < NETX_COMM_CHANNELS_NUM; ++id ) {
|
||
|
||
layout.communications[id].channel.first =
|
||
reinterpret_cast<peripheral::protected_char *>(&df(dpm.base).CommChannel[id]);
|
||
layout.communications[id].channel.second =
|
||
sizeof(df(dpm.base).CommChannel[id]);
|
||
|
||
layout.communications[id].handshake.first =
|
||
reinterpret_cast<peripheral::protected_char *>(&df(dpm.base).HandshakeChannel.tCommFlags0 + id);
|
||
layout.communications[id].handshake.second =
|
||
sizeof(df(dpm.base).HandshakeChannel.tCommFlags0);
|
||
|
||
read_to(&df(dpm.base).SystemChannel.atChannelInfo[id + 2].tCom.usCommunicationClass,
|
||
layout.communications[id].type );
|
||
read_to(&df(dpm.base).SystemChannel.atChannelInfo[id + 2].tCom.usProtocolClass,
|
||
layout.communications[id].protocol );
|
||
read_to(&df(dpm.base).SystemChannel.atChannelInfo[id + 2].tCom.usProtocolConformanceClass,
|
||
layout.communications[id].conformance );
|
||
|
||
layout.communications[id].cyclic_in.first =
|
||
reinterpret_cast<peripheral::protected_char *>(&df(dpm.base).CommChannel[id].abPd0Input);
|
||
layout.communications[id].cyclic_in.second =
|
||
sizeof(df(dpm.base).CommChannel[id].abPd0Input);
|
||
layout.communications[id].cyclic_out.first =
|
||
reinterpret_cast<peripheral::protected_char *>(&df(dpm.base).CommChannel[id].abPd0Output);
|
||
layout.communications[id].cyclic_out.second =
|
||
sizeof(df(dpm.base).CommChannel[id].abPd0Output);
|
||
|
||
layout.communications[id].mailbox_receive.first =
|
||
reinterpret_cast<peripheral::protected_char *>(&df(dpm.base).CommChannel[id].tRecvMbx.abRecvMailbox);
|
||
layout.communications[id].mailbox_receive.second =
|
||
sizeof(df(dpm.base).CommChannel[id].tRecvMbx.abRecvMailbox);
|
||
|
||
layout.communications[id].mailbox_send.first =
|
||
reinterpret_cast<peripheral::protected_char *>(&df(dpm.base).CommChannel[id].tSendMbx.abSendMailbox);
|
||
layout.communications[id].mailbox_send.second =
|
||
sizeof(df(dpm.base).CommChannel[id].tSendMbx.abSendMailbox);
|
||
}
|
||
|
||
for( std::size_t id = 0; id < NETX_APP_CHANNELS_NUM; ++id ) {
|
||
|
||
layout.applications[id].channel.first = nullptr;
|
||
layout.applications[id].channel.second = 0;
|
||
|
||
layout.applications[id].handshake.first = nullptr;
|
||
layout.applications[id].handshake.second = 0;
|
||
}
|
||
|
||
|
||
} else for( std::size_t channel_id = 0; channel_id < dpm::HIL_DPM_MAX_SUPPORTED_CHANNELS; channel_id++ ) {
|
||
|
||
dpm::HIL_DPM_CHANNEL_INFO_BLOCK info_blocks;
|
||
read_to(&df(dpm.base).SystemChannel.atChannelInfo[channel_id], info_blocks );
|
||
|
||
}
|
||
|
||
|
||
|
||
//Check for avalaible handshake block
|
||
dpm::HIL_DPM_COMMUNICATION_CHANNEL_INFO::ChannelInfo info;
|
||
read_to(&df(dpm.base).SystemChannel.atChannelInfo[0].tCom.uChannelInfo.field, info );
|
||
// info.bits.bChannelType != dpm::HIL_CHANNEL_TYPE_HANDSHAKE;
|
||
}
|
||
|
||
bool driver::netx::NetX::isReady( peripheral::protected_char * dpm_origin ) {
|
||
/* Check of DPM is initialized */
|
||
if (!checkDpmCookie(dpm_origin)) {
|
||
return false;
|
||
}
|
||
|
||
/* Check if system channel is ready */
|
||
dpm::HIL_DPM_HANDSHAKE_CELL sys_hs_cell;
|
||
std::memset(&sys_hs_cell, 0, sizeof(sys_hs_cell));
|
||
|
||
read_to(&df(dpm_origin).HandshakeChannel.tSysFlags, sys_hs_cell);
|
||
|
||
return (sys_hs_cell.t8Bit.bNetxFlags & dpm::NSF_READY) != 0;
|
||
}
|
||
|
||
bool driver::netx::NetX::isReady() const {
|
||
const bool new_ready_status =
|
||
checkDpmCookie( dpm.base ) && isChannelReady( Channel::System );
|
||
|
||
notifyChannels(new_ready_status);
|
||
ready_status = new_ready_status;
|
||
|
||
return ready_status;
|
||
}
|
||
|
||
void driver::netx::NetX::notifyChannels( bool new_ready_status ) const {
|
||
if (ready_status != new_ready_status) {
|
||
for (std::size_t i = 0; i < NETX_COMM_CHANNELS_NUM; i++) {
|
||
if (communication_channels[i] != nullptr) {
|
||
communication_channels[i]->setBoardReadyStatus( new_ready_status );
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
bool driver::netx::NetX::checkDpmCookie( peripheral::protected_char * dpm_origin ) {
|
||
uint32_t dpm_cookie;
|
||
read_to( dpm_origin, dpm_cookie );
|
||
|
||
return dpm_cookie == NETX_DPM_COOKIE;
|
||
}
|
||
|
||
bool driver::netx::NetX::isChannelReady(driver::netx::NetX::Channel::Id channel) const {
|
||
if (channel == Channel::System) {
|
||
/* Check system channel status */
|
||
dpm::HIL_DPM_HANDSHAKE_CELL status_flags;
|
||
memset(&status_flags, 0, sizeof(status_flags));
|
||
|
||
read_to( Channel::handshake(dpm.base, channel), status_flags);
|
||
return (status_flags.t8Bit.bNetxFlags & dpm::NSF_READY);
|
||
}
|
||
|
||
const bool is_comm_channel =
|
||
channel == Channel::CommunicationAlfa ||
|
||
channel == Channel::CommunicationBeta ||
|
||
channel == Channel::CommunicationGamma ||
|
||
channel == Channel::CommunicationDelta;
|
||
|
||
if (!is_comm_channel) {
|
||
/* ERROR: invalid channel ID */
|
||
while(true);
|
||
}
|
||
|
||
/* Check communication channel status */
|
||
uint32_t status_flags = 0;
|
||
int id = channel - Channel::CommunicationAlfa;
|
||
|
||
read_to(
|
||
&df(dpm.base).CommChannel[id].tCommonStatus.ulCommunicationCOS,
|
||
status_flags
|
||
);
|
||
|
||
return (status_flags & dpm::HIL_COMM_COS_READY);
|
||
}
|
||
|
||
bool driver::netx::NetX::isChannelRunning(driver::netx::NetX::Channel::Id channel) const {
|
||
const bool is_comm_channel =
|
||
channel == Channel::CommunicationAlfa ||
|
||
channel == Channel::CommunicationBeta ||
|
||
channel == Channel::CommunicationGamma ||
|
||
channel == Channel::CommunicationDelta;
|
||
|
||
if (!is_comm_channel) {
|
||
/* ERROR: invalid channel ID: only communication channels allowed */
|
||
while(true);
|
||
}
|
||
|
||
/* Check communication channel status */
|
||
uint32_t status_flags = 0;
|
||
int id = channel - Channel::CommunicationAlfa;
|
||
|
||
read_to(
|
||
&df(dpm.base).CommChannel[id].tCommonStatus.ulCommunicationCOS,
|
||
status_flags
|
||
);
|
||
|
||
return (status_flags & dpm::HIL_COMM_COS_RUN);
|
||
}
|
||
|
||
bool driver::netx::NetX::isChannelCommunicating(driver::netx::NetX::Channel::Id channel) const {
|
||
const bool is_comm_channel =
|
||
channel == Channel::CommunicationAlfa ||
|
||
channel == Channel::CommunicationBeta ||
|
||
channel == Channel::CommunicationGamma ||
|
||
channel == Channel::CommunicationDelta;
|
||
|
||
if (!is_comm_channel) {
|
||
/* ERROR: invalid channel ID: only communication channels allowed */
|
||
while(true);
|
||
}
|
||
|
||
/* Check communication channel status */
|
||
uint32_t status_flags = 0;
|
||
int id = channel - Channel::CommunicationAlfa;
|
||
|
||
read_to(
|
||
&df(dpm.base).CommChannel[id].tCommonStatus.ulCommunicationCOS,
|
||
status_flags
|
||
);
|
||
|
||
return (status_flags & dpm::HIL_COMM_COS_BUS_ON);
|
||
}
|
||
|
||
bool driver::netx::NetX::isError() const {
|
||
|
||
std::pair<bool, uint32_t> system_error = checkChannelError(Channel::System);
|
||
errors[Channel::System] = system_error.second;
|
||
|
||
return system_error.first;
|
||
}
|
||
|
||
std::pair<bool, uint32_t> driver::netx::NetX::checkChannelError(driver::netx::NetX::Channel::Id channel) const {
|
||
|
||
switch (channel) {
|
||
case Channel::System: {
|
||
//todo: Check handshake size.
|
||
//For system channel this 8 bit.
|
||
|
||
dpm::HIL_DPM_HANDSHAKE_CELL flags;
|
||
read_to( Channel::handshake(dpm.base, channel), flags);
|
||
|
||
if( flags.t8Bit.bNetxFlags & dpm::NSF_ERROR ) {
|
||
|
||
ErrorCode code;
|
||
read_to(&df(dpm.base).SystemChannel.tSystemState.ulSystemError, code);
|
||
|
||
return std::pair<bool, uint32_t>(true, code);
|
||
|
||
} else
|
||
return std::pair<bool, uint32_t>(false, 0);
|
||
|
||
} break;
|
||
case Channel::CommunicationAlfa:
|
||
case Channel::CommunicationBeta:
|
||
case Channel::CommunicationDelta:
|
||
case Channel::CommunicationGamma: {
|
||
|
||
dpm::HIL_DPM_HANDSHAKE_CELL flags;
|
||
read_to( Channel::handshake(dpm.base, channel), flags);
|
||
|
||
if( flags.t16Bit.usNetxFlags & dpm::NCF_ERROR ) {
|
||
|
||
ErrorCode code;
|
||
read_to( &df(dpm.base).CommChannel[channel-Channel::CommunicationAlfa].tCommonStatus.ulCommunicationError,
|
||
code);
|
||
|
||
return std::pair<bool, uint32_t>(true, code);
|
||
|
||
} else
|
||
return std::pair<bool, uint32_t>(false, 0);
|
||
|
||
} break;
|
||
default: {
|
||
//todo: Add exception.
|
||
while(true);
|
||
} break;
|
||
}
|
||
|
||
return std::pair<bool, uint32_t>();
|
||
}
|
||
|
||
void driver::netx::NetX::reset() {
|
||
resetSystem();
|
||
}
|
||
|
||
void driver::netx::NetX::resetSystem() {
|
||
if( isChannelReady(Channel::System) ) {
|
||
//Write Reset Cookie
|
||
write_from( &df(dpm.base).SystemChannel.tSystemControl.ulSystemCommandCOS, dpm::HIL_SYS_RESET_COOKIE);
|
||
|
||
//Set HSF_RESET Bit
|
||
dpm::HIL_DPM_HANDSHAKE_CELL flags;
|
||
read_to( Channel::handshake(dpm.base, Channel::System), flags);
|
||
flags.t8Bit.bHostFlags |= dpm::HSF_RESET;
|
||
write_from( Channel::handshake(dpm.base, Channel::System), flags);
|
||
}
|
||
}
|
||
|
||
driver::netx::NetX::CommunicationChannel *
|
||
driver::netx::NetX::open(driver::netx::NetX::CommunicationChannel::ChannelId id,
|
||
std::pmr::memory_resource * allocator,
|
||
std::pmr::memory_resource * description, std::pmr::memory_resource * buffer ) {
|
||
|
||
if( communication_channels[id] ) {
|
||
return nullptr;
|
||
}
|
||
|
||
communication_channels[id] = memories::instance_object<CommunicationChannel>(*allocator, description, buffer);
|
||
|
||
communication_channels[id]->channel = layout.communications[id].channel.first;
|
||
communication_channels[id]->handshake = layout.communications[id].handshake.first;
|
||
|
||
communication_channels[id]->self_info.id = id;
|
||
communication_channels[id]->self_info.type = layout.communications[id].type;
|
||
communication_channels[id]->self_info.protocol = layout.communications[id].protocol;
|
||
communication_channels[id]->self_info.conformance = layout.communications[id].conformance;
|
||
|
||
communication_channels[id]->pd0_data.input = layout.communications[id].cyclic_in.first;
|
||
communication_channels[id]->pd0_data.output = layout.communications[id].cyclic_out.first;
|
||
communication_channels[id]->pd0_data.capacity = layout.communications[id].cyclic_in.second;
|
||
communication_channels[id]->pd0_data.handshake = layout.communications[id].handshake.first;
|
||
|
||
communication_channels[id]->mb_rec_data.input = layout.communications[id].mailbox_receive.first;
|
||
communication_channels[id]->mb_rec_data.output = nullptr;
|
||
communication_channels[id]->mb_rec_data.capacity = layout.communications[id].mailbox_receive.second;
|
||
communication_channels[id]->mb_rec_data.handshake = layout.communications[id].handshake.first;
|
||
|
||
communication_channels[id]->mb_tx_data.input = nullptr;
|
||
communication_channels[id]->mb_tx_data.output = layout.communications[id].mailbox_send.first;
|
||
communication_channels[id]->mb_tx_data.capacity = layout.communications[id].mailbox_send.second;
|
||
communication_channels[id]->mb_tx_data.handshake = layout.communications[id].handshake.first;
|
||
|
||
communication_channels[id]->setBoardReadyStatus(ready_status);
|
||
|
||
return communication_channels[id];
|
||
|
||
}
|
||
|
||
bool driver::netx::NetX::hasDefaultDpmLayout() const {
|
||
|
||
uint32_t ulSystemCOS;
|
||
read_to( &df(dpm.base).SystemChannel.tSystemState.ulSystemCOS, ulSystemCOS );
|
||
|
||
return ulSystemCOS & dpm::HIL_SYS_COS_DEFAULT_MEMORY;
|
||
|
||
}
|
||
|
||
peripheral::protected_char *
|
||
driver::netx::NetX::Channel::handshake(peripheral::protected_char * base, driver::netx::NetX::Channel::Id id) {
|
||
|
||
switch (id) {
|
||
case System:
|
||
return reinterpret_cast<peripheral::protected_char *>(
|
||
&reinterpret_cast<dpm::NETX_DEFAULT_DPM *>(base)->HandshakeChannel.tSysFlags
|
||
);
|
||
case CommunicationAlfa:
|
||
case CommunicationBeta:
|
||
case CommunicationDelta:
|
||
case CommunicationGamma:
|
||
return reinterpret_cast<peripheral::protected_char *>(
|
||
&(&reinterpret_cast<dpm::NETX_DEFAULT_DPM *>(base)->HandshakeChannel.tCommFlags0)[id-CommunicationAlfa]
|
||
);
|
||
}
|
||
|
||
return nullptr;
|
||
}
|
||
|
||
bool driver::netx::NetX::CommunicationChannel::isRunning() const {
|
||
if (!board_ready_status) {
|
||
cos_cache.second = false;
|
||
return false;
|
||
}
|
||
|
||
uint32_t cos_flags = readNetXCos();
|
||
|
||
return (cos_flags & dpm::HIL_COMM_COS_READY) && (cos_flags & dpm::HIL_COMM_COS_RUN);
|
||
}
|
||
|
||
bool driver::netx::NetX::CommunicationChannel::isCommunicating() const {
|
||
if (!board_ready_status) {
|
||
cos_cache.second = false;
|
||
return false;
|
||
}
|
||
|
||
uint16_t ncf_flags = 0;
|
||
read_to(&hs(handshake).t16Bit.usNetxFlags, ncf_flags);
|
||
|
||
uint32_t cos_flags = readNetXCos();
|
||
|
||
return (cos_flags & dpm::HIL_COMM_COS_READY)
|
||
&& (cos_flags & dpm::HIL_COMM_COS_RUN)
|
||
&& (ncf_flags & dpm::NCF_COMMUNICATING);
|
||
}
|
||
|
||
bool driver::netx::NetX::CommunicationChannel::isError() const {
|
||
if (!board_ready_status) {
|
||
cos_cache.second = false;
|
||
return false;
|
||
}
|
||
|
||
uint16_t ncf_flags = 0;
|
||
read_to(&hs(handshake).t16Bit.usNetxFlags, ncf_flags);
|
||
|
||
return (ncf_flags & dpm::NCF_ERROR);
|
||
}
|
||
|
||
void driver::netx::NetX::CommunicationChannel::start() {
|
||
updateAppCos( dpm::HIL_APP_COS_BUS_ON | dpm::HIL_APP_COS_BUS_ON_ENABLE );
|
||
}
|
||
|
||
void driver::netx::NetX::CommunicationChannel::stop() {
|
||
updateAppCos( dpm::HIL_APP_COS_BUS_ON_ENABLE, dpm::HIL_APP_COS_BUS_ON );
|
||
}
|
||
|
||
uint32_t driver::netx::NetX::CommunicationChannel::readNetXCos() const {
|
||
driver::netx::dpm::HIL_DPM_HANDSHAKE_CELL cell;
|
||
read_to(&hs(handshake), cell);
|
||
|
||
const bool cos_cache_initialized = cos_cache.second;
|
||
const bool cos_cache_invalid =
|
||
(cell.t16Bit.usHostFlags ^ cell.t16Bit.usNetxFlags) & dpm::NCF_NETX_COS_CMD;
|
||
|
||
if(cos_cache_initialized || cos_cache_invalid) {
|
||
read_to(&ch(channel).tCommonStatus.ulCommunicationCOS, cos_cache);
|
||
|
||
cell.t16Bit.usHostFlags ^= dpm::HCF_NETX_COS_ACK;
|
||
write_from(&hs(handshake).t16Bit.usHostFlags, cell.t16Bit.usHostFlags);
|
||
|
||
cos_cache.second = true;
|
||
}
|
||
|
||
return cos_cache.first;
|
||
}
|
||
|
||
void driver::netx::NetX::CommunicationChannel::updateAppCos(uint32_t set, uint32_t clear) {
|
||
driver::netx::dpm::HIL_DPM_HANDSHAKE_CELL cell;
|
||
read_to(&hs(handshake), cell);
|
||
|
||
if( ( cell.t16Bit.usHostFlags ^ cell.t16Bit.usNetxFlags ) & dpm::HCF_HOST_COS_CMD ) {
|
||
|
||
uint32_t ulApplicationCOS;
|
||
read_to( &ch(channel).tControl.ulApplicationCOS, ulApplicationCOS );
|
||
|
||
ulApplicationCOS &= ~clear;
|
||
ulApplicationCOS |= set;
|
||
|
||
write_from( &ch(channel).tControl.ulApplicationCOS, ulApplicationCOS );
|
||
|
||
cell.t16Bit.usHostFlags ^= dpm::HCF_HOST_COS_CMD;
|
||
write_from( &hs(handshake).t16Bit.usHostFlags, cell.t16Bit.usHostFlags );
|
||
|
||
} else ; //todo: <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>.
|
||
}
|
||
|
||
driver::netx::NetX::CommunicationChannel::CommunicationChannel( std::pmr::memory_resource * description, std::pmr::memory_resource * buffer ) :
|
||
pd0_data( dpm::HCF_PD0_IN_ACK, dpm::HCF_PD0_OUT_CMD ),
|
||
pd1_data( dpm::HCF_PD1_IN_ACK, dpm::HCF_PD1_OUT_CMD ),
|
||
mb_rec_data( dpm::HCF_RECV_MBX_ACK, dpm::HCF_RECV_MBX_ACK ),
|
||
mb_tx_data( dpm::HCF_SEND_MBX_CMD, dpm::HCF_SEND_MBX_CMD ),
|
||
mailbox_handler(mb_rec_data, mb_tx_data, description, buffer),
|
||
cos_cache(0, false),
|
||
board_ready_status(false) {}
|
||
|
||
driver::netx::NetX::CommunicationChannel::Info driver::netx::NetX::CommunicationChannel::info() const {
|
||
return self_info;
|
||
}
|
||
|
||
driver::netx::NetX::ErrorCode driver::netx::NetX::CommunicationChannel::getErrorCode() const {
|
||
uint32_t ulCommunicationError;
|
||
read_to( &ch(channel).tCommonStatus.ulCommunicationError, ulCommunicationError );
|
||
|
||
return ulCommunicationError;
|
||
}
|
||
|
||
float driver::netx::NetX::CommunicationChannel::getErrorCounter() const {
|
||
uint32_t ulCommunicationError;
|
||
read_to( &ch(channel).tCommonStatus.ulCommunicationError, ulCommunicationError );
|
||
|
||
return ulCommunicationError;
|
||
}
|
||
|
||
peripheral::IMemoryAccess & driver::netx::NetX::CommunicationChannel::cyclic() {
|
||
|
||
return pd0_data;
|
||
|
||
}
|
||
|
||
float driver::netx::NetX::CommunicationChannel::getWatchdogCounter() const {
|
||
|
||
uint32_t ulHostWatchdog;
|
||
read_to( &ch(channel).tCommonStatus.ulHostWatchdog, ulHostWatchdog);
|
||
|
||
return ulHostWatchdog;
|
||
}
|
||
|
||
void driver::netx::NetX::CommunicationChannel::watchdog() {
|
||
|
||
uint32_t ulHostWatchdog;
|
||
read_to( &ch(channel).tCommonStatus.ulHostWatchdog, ulHostWatchdog);
|
||
write_from( &ch(channel).tControl.ulDeviceWatchdog, ulHostWatchdog );
|
||
|
||
}
|
||
|
||
driver::netx::NetX::CommunicationChannel::Mailbox & driver::netx::NetX::CommunicationChannel::mailbox() {
|
||
|
||
return mailbox_handler;
|
||
|
||
}
|
||
|
||
bool driver::netx::NetX::CommunicationChannel::isProtocolRegistred() const {
|
||
|
||
return mailbox_handler.enable();
|
||
|
||
}
|
||
|
||
void driver::netx::NetX::CommunicationChannel::setBoardReadyStatus( bool ready_status ) {
|
||
board_ready_status = ready_status;
|
||
}
|
||
|
||
namespace {
|
||
|
||
class Lock {
|
||
|
||
peripheral::protected_char * ptr;
|
||
std::size_t sz;
|
||
bool locked;
|
||
|
||
void (* unlock)(peripheral::protected_char *,std::size_t);
|
||
|
||
public:
|
||
Lock(peripheral::protected_char * p, std::size_t s,
|
||
bool (* lock)(peripheral::protected_char *, std::size_t),
|
||
void (* unlock_function)(peripheral::protected_char *, std::size_t)) :
|
||
ptr(p), sz(s), unlock(unlock_function) {
|
||
locked = lock(ptr, sz);
|
||
}
|
||
operator bool() const { return locked; }
|
||
~Lock() {
|
||
if(locked and unlock)
|
||
unlock(ptr, sz);
|
||
}
|
||
};
|
||
|
||
struct WriteLock : public Lock {
|
||
WriteLock(peripheral::protected_char * p, std::size_t s) :
|
||
Lock(p, s, peripheral::write_lock, peripheral::write_unlock) {}
|
||
};
|
||
|
||
struct ReadLock : public Lock {
|
||
ReadLock(peripheral::protected_char * p, std::size_t s) :
|
||
Lock(p, s, peripheral::write_lock, peripheral::write_unlock) {}
|
||
};
|
||
|
||
}
|
||
|
||
bool driver::netx::NetX::CommunicationChannel::HandshakeData::read( char * data, std::size_t begin, std::size_t size ) {
|
||
|
||
if( begin + size > capacity )
|
||
return read_complete = false;
|
||
|
||
// Lock communication channel handshake memory area
|
||
WriteLock hs_lock(handshake, sizeof(driver::netx::dpm::HIL_DPM_HANDSHAKE_CELL));
|
||
ReadLock data_lock(input + begin, size);
|
||
|
||
if( not( hs_lock and data_lock ) )
|
||
return read_complete = false;
|
||
|
||
driver::netx::dpm::HIL_DPM_HANDSHAKE_CELL cell;
|
||
std::memcpy( &cell, &hs(handshake), sizeof(cell) );
|
||
|
||
// Check if communication channel is up and running
|
||
if ( ( cell.t16Bit.usNetxFlags & dpm::NCF_COMMUNICATING) == 0 ) {
|
||
return read_complete = false;
|
||
}
|
||
|
||
// Check if host has access rights to the DPM area
|
||
if( ( ( cell.t16Bit.usHostFlags ^ cell.t16Bit.usNetxFlags ) & dpm::HCF_PD0_IN_ACK ) == 0) {
|
||
|
||
// Read data from channel cyclic input area to the buffer
|
||
std::memcpy( data, input + begin, size );
|
||
|
||
|
||
// Acknowledge device by toggling handshake flag
|
||
cell.t16Bit.usHostFlags ^= read_mask;//dpm::HCF_PD0_IN_ACK;
|
||
|
||
std::memcpy((void *) &hs(handshake).t16Bit.usHostFlags,
|
||
(const void *) &cell.t16Bit.usHostFlags,
|
||
sizeof(cell.t16Bit.usHostFlags) );
|
||
|
||
return read_complete = true;
|
||
} else {
|
||
return read_complete = false;
|
||
}
|
||
}
|
||
|
||
bool driver::netx::NetX::CommunicationChannel::HandshakeData::write(const char * data, std::size_t begin, std::size_t size) {
|
||
|
||
if( begin + size > capacity )
|
||
return write_request = false;
|
||
|
||
// Lock communication channel handshake memory area
|
||
WriteLock hs_lock(handshake, sizeof(driver::netx::dpm::HIL_DPM_HANDSHAKE_CELL));
|
||
WriteLock data_lock(output + begin, size);
|
||
|
||
if( not( hs_lock and data_lock ) )
|
||
return write_request = false;
|
||
|
||
driver::netx::dpm::HIL_DPM_HANDSHAKE_CELL cell;
|
||
std::memcpy( &cell, &hs(handshake), sizeof(cell) );
|
||
|
||
// Check if communication channel is up and running
|
||
if ( ( cell.t16Bit.usNetxFlags & dpm::NCF_COMMUNICATING ) == 0 ) {
|
||
return write_request = false;
|
||
}
|
||
|
||
// Check if host has access rights to the DPM area
|
||
if( ( ( cell.t16Bit.usHostFlags ^ cell.t16Bit.usNetxFlags ) & write_mask ) == 0 ) {
|
||
|
||
// Write data to the channel cyclic output area
|
||
std::memcpy( output + begin, data, size );
|
||
|
||
// Acknowledge device by toggling handshake flag
|
||
cell.t16Bit.usHostFlags ^= write_mask;//dpm::HCF_PD0_OUT_CMD;
|
||
std::memcpy((void *) &hs(handshake).t16Bit.usHostFlags,
|
||
(const void *) &cell.t16Bit.usHostFlags,
|
||
sizeof(cell.t16Bit.usHostFlags) );
|
||
|
||
return write_request = true;
|
||
} else {
|
||
return write_request = false;
|
||
}
|
||
}
|
||
|
||
bool driver::netx::NetX::CommunicationChannel::HandshakeData::isReadComplete() const {
|
||
|
||
return read_complete;
|
||
|
||
}
|
||
|
||
bool driver::netx::NetX::CommunicationChannel::HandshakeData::isWriteComplete() const {
|
||
|
||
if( not write_request )
|
||
return false;
|
||
|
||
ReadLock hs_lock(handshake, sizeof(driver::netx::dpm::HIL_DPM_HANDSHAKE_CELL));
|
||
|
||
if( not hs_lock )
|
||
return false;
|
||
|
||
driver::netx::dpm::HIL_DPM_HANDSHAKE_CELL cell;
|
||
std::memcpy( &cell, &hs(handshake), sizeof(cell) );
|
||
|
||
return not( ( cell.t16Bit.usHostFlags ^ cell.t16Bit.usNetxFlags ) & write_mask ); //dpm::HCF_PD0_OUT_CMD );
|
||
|
||
}
|
||
|
||
std::size_t driver::netx::NetX::CommunicationChannel::HandshakeData::getCapacity() const {
|
||
|
||
return capacity;
|
||
|
||
}
|
||
|
||
driver::netx::NetX::CommunicationChannel::HandshakeData::HandshakeData(uint16_t rmask, uint16_t wmask) :
|
||
read_mask(rmask), write_mask(wmask) {}
|
||
|
||
bool driver::netx::NetX::CommunicationChannel::HandshakeData::view( char * data, std::size_t begin, std::size_t size ) {
|
||
|
||
if( begin + size > capacity )
|
||
return false;
|
||
|
||
ReadLock data_lock(input + begin, size);
|
||
|
||
if( not data_lock )
|
||
return false;
|
||
|
||
std::memcpy( data, input + begin, size );
|
||
|
||
return true;
|
||
|
||
}
|
||
|
||
bool driver::netx::NetX::CommunicationChannel::HandshakeData::accept() {
|
||
|
||
WriteLock hs_lock( handshake, sizeof(driver::netx::dpm::HIL_DPM_HANDSHAKE_CELL) );
|
||
|
||
if( not hs_lock )
|
||
return false;
|
||
|
||
driver::netx::dpm::HIL_DPM_HANDSHAKE_CELL cell;
|
||
std::memcpy( &cell, &hs(handshake), sizeof(cell) );
|
||
|
||
cell.t16Bit.usHostFlags ^= read_mask;//dpm::HCF_PD0_IN_ACK;
|
||
std::memcpy((void *) &hs(handshake).t16Bit.usHostFlags,
|
||
(const void *) &cell.t16Bit.usHostFlags,
|
||
sizeof(cell.t16Bit.usHostFlags) );
|
||
|
||
return true;
|
||
|
||
}
|
||
|
||
bool driver::netx::NetX::CommunicationChannel::HandshakeData::ready_to_be_read() {
|
||
|
||
ReadLock hs_lock(handshake, sizeof(driver::netx::dpm::HIL_DPM_HANDSHAKE_CELL));
|
||
|
||
if( not hs_lock )
|
||
return false;
|
||
|
||
driver::netx::dpm::HIL_DPM_HANDSHAKE_CELL cell;
|
||
std::memcpy( &cell, &hs(handshake), sizeof(cell) );
|
||
|
||
//Check state handshake
|
||
return ( cell.t16Bit.usHostFlags ^ cell.t16Bit.usNetxFlags ) & read_mask;
|
||
|
||
}
|
||
|
||
bool
|
||
driver::netx::NetX::CommunicationChannel::HandshakeData::set(const char * data, std::size_t begin, std::size_t size) {
|
||
|
||
if( begin + size > capacity )
|
||
return false;
|
||
|
||
WriteLock data_lock(output + begin, size);
|
||
|
||
if( not( data_lock ) )
|
||
return false;
|
||
|
||
//Write data to buffer
|
||
std::memcpy( output + begin, data, size );
|
||
|
||
return true;
|
||
|
||
}
|
||
|
||
bool driver::netx::NetX::CommunicationChannel::HandshakeData::commit() {
|
||
|
||
//Lock memory
|
||
WriteLock hs_lock(handshake, sizeof(driver::netx::dpm::HIL_DPM_HANDSHAKE_CELL));
|
||
|
||
if( not( hs_lock ) )
|
||
return false;
|
||
|
||
driver::netx::dpm::HIL_DPM_HANDSHAKE_CELL cell;
|
||
std::memcpy( &cell, &hs(handshake), sizeof(cell) );
|
||
|
||
//Update host flag
|
||
cell.t16Bit.usHostFlags ^= write_mask;
|
||
std::memcpy((void *) &hs(handshake).t16Bit.usHostFlags,
|
||
(const void *) &cell.t16Bit.usHostFlags,
|
||
sizeof(cell.t16Bit.usHostFlags) );
|
||
|
||
return true;
|
||
|
||
}
|
||
|
||
bool driver::netx::NetX::CommunicationChannel::HandshakeData::ready_to_be_written() {
|
||
|
||
//Lock memory
|
||
ReadLock hs_lock(handshake, sizeof(driver::netx::dpm::HIL_DPM_HANDSHAKE_CELL));
|
||
|
||
if( not( hs_lock ) )
|
||
return false;
|
||
|
||
driver::netx::dpm::HIL_DPM_HANDSHAKE_CELL cell;
|
||
std::memcpy( &cell, &hs(handshake), sizeof(cell) );
|
||
|
||
//Check state handshake
|
||
return not( ( cell.t16Bit.usHostFlags ^ cell.t16Bit.usNetxFlags ) & write_mask );
|
||
}
|
||
|
||
void driver::netx::NetX::CommunicationChannel::MailboxHandler::process() {
|
||
switch(state) {
|
||
//Registration phase:
|
||
case RequestRegisterApplication: {
|
||
|
||
packet::HIL_PACKET_HEADER header;
|
||
|
||
header.ulDest = packet::HIL_PACKET_DEST_DEFAULT_CHANNEL;
|
||
header.ulSrc = reinterpret_cast<uintptr_t>(this) & static_cast<uint32_t>(-1);
|
||
header.ulCmd = packet::HIL_REGISTER_APP_REQ;
|
||
|
||
if( transmit.set( reinterpret_cast<char *>(&header), 0, sizeof(packet::HIL_PACKET_HEADER)) )
|
||
state = WaitRegisterApplication;
|
||
else break;
|
||
|
||
}
|
||
case WaitRegisterApplication: {
|
||
|
||
if( receive.ready_to_be_read() )
|
||
state = ConfirmationRegisterApplication;
|
||
else break;
|
||
|
||
}
|
||
case ConfirmationRegisterApplication: {
|
||
|
||
packet::HIL_PACKET_HEADER header;
|
||
if( receive.view(reinterpret_cast<char *>(&header), 0, sizeof(packet::HIL_PACKET_HEADER)) ) {
|
||
application_registred = not(header.ulSta);
|
||
state = AcceptRegisterApplication;
|
||
} else break;
|
||
|
||
}
|
||
case AcceptRegisterApplication: {
|
||
|
||
if( receive.accept() )
|
||
state = WaitReceive;
|
||
else break;
|
||
|
||
}
|
||
//Work phase:
|
||
case WaitReceive: {
|
||
|
||
if( receive.ready_to_be_read() )
|
||
state = ReadHeader;
|
||
else break;
|
||
|
||
} case ReadHeader: {
|
||
|
||
if( receive.view(reinterpret_cast<char *>(&request.header), 0, sizeof(packet::HIL_PACKET_HEADER)) ) {
|
||
|
||
//todo: handle odd size
|
||
std::size_t args_size = request.header.ulLen * 8 / CHAR_BIT;
|
||
request.data.resize( args_size );
|
||
|
||
state = ReadData;
|
||
} else break;
|
||
|
||
} case ReadData: {
|
||
|
||
if( receive.view( &request.data.front(), sizeof(packet::HIL_PACKET_HEADER), request.data.size() ) )
|
||
state = AcceptReceive;
|
||
else break;
|
||
|
||
} case AcceptReceive: {
|
||
|
||
if( receive.accept() )
|
||
state = RequestServer;
|
||
else break;
|
||
|
||
} case RequestServer: {
|
||
|
||
ServerId server_id = request.header.ulCmd;
|
||
|
||
Servers::iterator server = servers.find(server_id);
|
||
if( server != servers.end() ) {
|
||
|
||
Client client(response.data);
|
||
if( server->second->request(&client, &request.data.front(), request.data.size()) ) {
|
||
|
||
//todo: handle errors.
|
||
if( client.flag ) {
|
||
|
||
//Answer by application
|
||
response.header = request.header;
|
||
response.header.ulCmd = request.header.ulCmd + 1;
|
||
response.header.ulSta = 0;
|
||
response.header.ulLen = response.data.size() * CHAR_BIT / 8;
|
||
|
||
} else {
|
||
|
||
//Default answer by stack
|
||
response.header = request.header;
|
||
response.header.ulCmd = request.header.ulCmd + 1;
|
||
response.header.ulSta = packet::ERR_HIL_NO_APPLICATION_REGISTERED;
|
||
|
||
response.data = request.data;
|
||
|
||
}
|
||
|
||
state = WaitSend;
|
||
|
||
} else {
|
||
|
||
//Format fault
|
||
state = Stop;
|
||
|
||
} break;
|
||
|
||
} else {
|
||
|
||
//Unknown server
|
||
state = Stop;
|
||
|
||
} break;
|
||
|
||
} case WaitSend: {
|
||
|
||
if( transmit.ready_to_be_written() )
|
||
state = WriteHeader;
|
||
else break;
|
||
|
||
} case WriteHeader: {
|
||
|
||
if( transmit.set( reinterpret_cast<char *>(&response.header), 0, sizeof(packet::HIL_PACKET_HEADER)) )
|
||
state = WriteData;
|
||
else break;
|
||
|
||
} case WriteData: {
|
||
|
||
if( transmit.set( &response.data.front(), sizeof(packet::HIL_PACKET_HEADER), response.data.size() ) )
|
||
state = AcceptSend;
|
||
else break;
|
||
|
||
} case AcceptSend: {
|
||
|
||
if( transmit.commit() )
|
||
state = WaitReceive;
|
||
else break;
|
||
}
|
||
case Stop: {} break;
|
||
}
|
||
}
|
||
|
||
driver::netx::NetX::CommunicationChannel::MailboxHandler::MailboxHandler(
|
||
driver::netx::NetX::CommunicationChannel::HandshakeData & receive,
|
||
driver::netx::NetX::CommunicationChannel::HandshakeData & transmit, std::pmr::memory_resource * description,
|
||
std::pmr::memory_resource * buffer) :
|
||
receive(receive), transmit(transmit),
|
||
servers( std::greater<ServerId>(), description ), response(buffer), request(buffer),
|
||
state(Stop) {}
|
||
|
||
void driver::netx::NetX::CommunicationChannel::MailboxHandler::attach(communication::IMessageServer * server,
|
||
driver::netx::NetX::ServerId id ) {
|
||
|
||
servers[id] = server;
|
||
|
||
}
|
||
|
||
void driver::netx::NetX::CommunicationChannel::MailboxHandler::start() {
|
||
|
||
if( state == Stop )
|
||
state = WaitReceive;
|
||
|
||
}
|
||
|
||
bool driver::netx::NetX::CommunicationChannel::MailboxHandler::stop() {
|
||
|
||
if( state == WaitReceive )
|
||
state = Stop;
|
||
|
||
return state == Stop;
|
||
|
||
}
|
||
|
||
const bool &driver::netx::NetX::CommunicationChannel::MailboxHandler::enable() const {
|
||
|
||
return application_registred;
|
||
|
||
}
|
||
|
||
driver::netx::NetX::CommunicationChannel::MailboxHandler::Client::Client(
|
||
std::vector< char, std::pmr::polymorphic_allocator<char> > & buff ) : data(buff) {
|
||
|
||
data.clear();
|
||
|
||
}
|
||
|
||
bool driver::netx::NetX::CommunicationChannel::MailboxHandler::Client::response( const char * buff, std::size_t size ) {
|
||
|
||
data.insert( data.begin(), buff, buff + size );
|
||
|
||
flag = true;
|
||
|
||
return true;
|
||
|
||
}
|
||
|
||
driver::netx::NetX::CommunicationChannel::MailboxHandler::Packet::Packet( std::pmr::memory_resource * buffer ) :
|
||
data(buffer) {}
|
||
|
||
communication::IBinaryDataReadEvent & driver::netx::NetX::CommunicationChannel::cyclic_read() {
|
||
|
||
return pd0_data;
|
||
|
||
}
|
||
|
||
communication::IBinaryDataWriteEvent & driver::netx::NetX::CommunicationChannel::cyclic_write() {
|
||
|
||
return pd0_data;
|
||
|
||
}
|
||
|
||
void driver::netx::NetX::CommunicationChannel::HandshakeData::read_event(
|
||
communication::IBinaryDataSubscriber & subscriber ) {
|
||
|
||
// Lock communication channel handshake memory area
|
||
WriteLock hs_lock(handshake, sizeof(driver::netx::dpm::HIL_DPM_HANDSHAKE_CELL));
|
||
// ReadLock data_lock(input, capacity);
|
||
|
||
if( not( hs_lock ) )
|
||
//and data_lock ) )
|
||
return;
|
||
|
||
driver::netx::dpm::HIL_DPM_HANDSHAKE_CELL cell;
|
||
std::memcpy( &cell, &hs(handshake), sizeof(cell) );
|
||
|
||
// // Check if communication channel is up and running
|
||
// if ( ( cell.t16Bit.usNetxFlags & dpm::NCF_COMMUNICATING ) == 0 )
|
||
// return;
|
||
|
||
// Check if host has access rights to the DPM area
|
||
if( ( ( cell.t16Bit.usHostFlags ^ cell.t16Bit.usNetxFlags ) & dpm::HCF_PD0_IN_ACK ) == 0 ) {
|
||
|
||
// Read data from channel cyclic input area to the buffer
|
||
subscriber.read(input, capacity);
|
||
|
||
// Acknowledge device by toggling handshake flag
|
||
cell.t16Bit.usHostFlags ^= read_mask;//dpm::HCF_PD0_IN_ACK;
|
||
|
||
std::memcpy((void *) &hs(handshake).t16Bit.usHostFlags,
|
||
(const void *) &cell.t16Bit.usHostFlags,
|
||
sizeof(cell.t16Bit.usHostFlags) );
|
||
|
||
}
|
||
|
||
}
|
||
|
||
void driver::netx::NetX::CommunicationChannel::HandshakeData::write_event(
|
||
communication::IBinaryDataIssuer & issuer ) {
|
||
|
||
// Lock communication channel handshake memory area
|
||
WriteLock hs_lock(handshake, sizeof(driver::netx::dpm::HIL_DPM_HANDSHAKE_CELL));
|
||
// WriteLock data_lock(output, capacity);
|
||
|
||
if( not( hs_lock ) )
|
||
//and data_lock ) )
|
||
return;
|
||
|
||
driver::netx::dpm::HIL_DPM_HANDSHAKE_CELL cell;
|
||
std::memcpy( &cell, &hs(handshake), sizeof(cell) );
|
||
|
||
// // Check if communication channel is up and running
|
||
// if ( ( cell.t16Bit.usNetxFlags & dpm::NCF_COMMUNICATING ) == 0 )
|
||
// return;
|
||
|
||
// Check if host has access rights to the DPM area
|
||
if( ( ( cell.t16Bit.usHostFlags ^ cell.t16Bit.usNetxFlags ) & write_mask ) == 0 ) {
|
||
|
||
// Write data to the channel cyclic output area
|
||
issuer.write(output, capacity);
|
||
|
||
// Acknowledge device by toggling handshake flag
|
||
cell.t16Bit.usHostFlags ^= write_mask;//dpm::HCF_PD0_OUT_CMD;
|
||
|
||
std::memcpy((void *) &hs(handshake).t16Bit.usHostFlags,
|
||
(const void *) &cell.t16Bit.usHostFlags,
|
||
sizeof(cell.t16Bit.usHostFlags) );
|
||
|
||
}
|
||
|
||
}
|