/* * NetX.cpp * * Created on: 30 ����. 2021 �. * Author: titov */ #include #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(pointer); }; driver::netx::dpm::HIL_DPM_DEFAULT_COMM_CHANNEL & ch(peripheral::protected_char * pointer ) { return *reinterpret_cast(pointer); }; driver::netx::dpm::HIL_DPM_HANDSHAKE_CELL & hs(peripheral::protected_char * pointer ) { return *reinterpret_cast(pointer); }; template void read_to(const Pointer * dpm_pointer, Field & field) { while( not peripheral::read_lock( reinterpret_cast( dpm_pointer ), sizeof(Field) ) ); std::memcpy( &field, dpm_pointer, sizeof(Field) ); peripheral::read_unlock( reinterpret_cast( dpm_pointer ), sizeof(Field) ); } template void read_to(const volatile Pointer * dpm_pointer_v, Field & field) { const Pointer * dpm_pointer = const_cast(dpm_pointer_v); while( not peripheral::read_lock( reinterpret_cast( dpm_pointer ), sizeof(Field) ) ); std::memcpy( &field, dpm_pointer, sizeof(Field) ); peripheral::read_unlock( reinterpret_cast( dpm_pointer ), sizeof(Field) ); } template void write_from(Pointer * dpm_pointer, const Field & field) { while( not peripheral::write_lock( reinterpret_cast( dpm_pointer ), sizeof(Field) ) ); std::memcpy( dpm_pointer, &field, sizeof(Field) ); peripheral::write_unlock( reinterpret_cast( dpm_pointer ), sizeof(Field) ); } template void write_from(volatile Pointer * dpm_pointer_v, const volatile Field & field_v) { Pointer * dpm_pointer = const_cast(dpm_pointer_v); const Field & field = const_cast(field_v); while( not peripheral::write_lock( reinterpret_cast( dpm_pointer ), sizeof(Field) ) ); std::memcpy( dpm_pointer, &field, sizeof(Field) ); peripheral::write_unlock( reinterpret_cast( dpm_pointer ), sizeof(Field) ); } } driver::netx::NetX::NetX( std::pair name, std::pair 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(&df(dpm.base).SystemChannel); layout.system.channel.second = sizeof(df(dpm.base).SystemChannel); layout.system.handshake.first = reinterpret_cast(&df(dpm.base).HandshakeChannel.tSysFlags); layout.system.handshake.second = sizeof(df(dpm.base).HandshakeChannel.tSysFlags); //todo: implement mailboxes layout.handshake.channel.first = reinterpret_cast(&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(&df(dpm.base).CommChannel[id]); layout.communications[id].channel.second = sizeof(df(dpm.base).CommChannel[id]); layout.communications[id].handshake.first = reinterpret_cast(&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(&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(&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(&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(&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 system_error = checkChannelError(Channel::System); errors[Channel::System] = system_error.second; return system_error.first; } std::pair 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(true, code); } else return std::pair(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(true, code); } else return std::pair(false, 0); } break; default: { //todo: Add exception. while(true); } break; } return std::pair(); } 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(*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( &reinterpret_cast(base)->HandshakeChannel.tSysFlags ); case CommunicationAlfa: case CommunicationBeta: case CommunicationDelta: case CommunicationGamma: return reinterpret_cast( &(&reinterpret_cast(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: ��������� ������� ������������� ���������� ��������. } 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(this) & static_cast(-1); header.ulCmd = packet::HIL_REGISTER_APP_REQ; if( transmit.set( reinterpret_cast(&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(&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(&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(&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(), 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 > & 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) ); } }