Skip to content

Commit

Permalink
Adds helper functions for generating the logon packets (#563)
Browse files Browse the repository at this point in the history
Updates dcc::Packet to have helper functions for generating packets related to DCC logon:
- Logon Enable
- Select / get short info
- Logon Assign 

Moves the definitions of DCC packet related constants into a header so they are reusable.

===

* Moves the bit definitions for dcc packet into a header.

* Implements the CRC checksumming rule in the dcc packet generation.

* Adds implementation for LOGON_ENABLE to Packet.

* Adds implementations for genetaring select info and logon assign.

* Fixes comments.
  • Loading branch information
balazsracz authored Aug 13, 2021
1 parent bd2f9ec commit ddcb125
Show file tree
Hide file tree
Showing 5 changed files with 309 additions and 54 deletions.
99 changes: 97 additions & 2 deletions src/dcc/Defs.hxx
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,8 @@

#include <stdint.h>

namespace dcc {
namespace dcc
{

/// Which address type this legacy train node uses. These address types
/// translate to mutually independent packets on the track.
Expand All @@ -56,6 +57,100 @@ enum class TrainAddressType : uint8_t
UNSPECIFIED = 254,
};

} // namespace dcc
namespace Defs
{

enum
{
MARKLIN_DEFAULT_CMD = 0b00100110,
// Direction change bits for marklin-old format.
MARKLIN_CHANGE_DIR_B2 = 0b11000000,

DCC_DEFAULT_CMD = 0,
DCC_LONG_PREAMBLE_CMD = 0b00001100,
DCC_SERVICE_MODE_5X_WITH_ACK_CMD = 0b00111000,
// standard dcc packet with 5x repeat.
DCC_SERVICE_MODE_5X_CMD = 0b00101000,
DCC_SERVICE_MODE_1X_CMD = 0b00001000,
/// Prefix for DCC long addresses (first byte).
DCC_LONG_ADDRESS_FIRST = 0b11000000,

// Baseline packet: speed and direction.
DCC_BASELINE_SPEED = 0b01000000,
DCC_BASELINE_SPEED_FORWARD = 0b00100000,
DCC_BASELINE_SPEED_LIGHT = 0b00010000,
DCC_FUNCTION1 = 0b10000000,
DCC_FUNCTION1_F0 = 0b00010000,
DCC_FUNCTION2_F5 = 0b10110000,
DCC_FUNCTION2_F9 = 0b10100000,
DCC_FEATURE_EXP_F13 = 0b11011110,
DCC_FEATURE_EXP_F21 = 0b11011111,
DCC_FEATURE_EXP_FNHI = 0b11011000,
DCC_BINARY_SHORT = 0b11011101,
DCC_BINARY_LONG = 0b11000000,
DCC_ANALOG_FN = 0b00111101,

DCC_PROG_READ1 = 0b11100100,
DCC_PROG_WRITE1 = 0b11101100,
DCC_PROG_READ4 = 0b11100000,

DCC_SVC_BIT_MANIPULATE = 0b01111000,
DCC_SVC_WRITE = 0b01111100,
DCC_SVC_VERIFY = 0b01110100,

DCC_SVC_BITVAL_WRITE = 0b11110000,
DCC_SVC_BITVAL_VERIFY = 0b11100000,
DCC_SVC_BITVAL_VALUE = 0b00001000,

DCC_SVC_PAGED_WRITE = 0b01111000,
DCC_SVC_PAGED_VERIFY = 0b01110000,

DCC_BASIC_ACCESSORY_B1 = 0b10000000,
DCC_BASIC_ACCESSORY_B2 = 0b10000000,

// Extended packet: 128-step speed.
DCC_EXT_SPEED = 0b00111111,
DCC_EXT_SPEED_FORWARD = 0x80,

/// Address partition used for logon features per S-9.2.1.1 and RCN-218
ADDRESS_LOGON = 254,
/// Address partition used for advanced extended packets per S-9.2.1.1
ADDRESS_EXT = 253,

// Logon commands
/// Logon enable in the 254 address partition
DCC_LOGON_ENABLE = 0b11111100,
/// Select command in the 254 address partition
DCC_SELECT = 0b11010000,
/// Get Data Start command in the 254 address partition
DCC_GET_DATA_START = 0,
/// Get Data Continue command in the 254 address partition
DCC_GET_DATA_CONT = 1,
/// Logon Assign command the 254 address partition
DCC_LOGON_ASSIGN = 0b11100000,

// Commands in 254 Select and 253 addressed.
CMD_READ_SHORT_INFO = 0b11111111,
CMD_READ_BLOCK = 0b11111110,
CMD_READ_BACKGROUND = 0b11111101,
CMD_WRITE_BLOCK = 0b11111100,
};

/// Parameters for the Logon Enable command.
enum class LogonEnableParam
{
/// All decoders respond and ignore backoff.
NOW = 0b11,
/// Locomotive decoders only.
LOCO = 0b01,
/// Accessory decoders only.
ACC = 0b10,
/// All decoders respond.
ALL = 0b00,
};

} // namespace dcc::Defs

} // namespace dcc

#endif
114 changes: 62 additions & 52 deletions src/dcc/Packet.cxx
Original file line number Diff line number Diff line change
Expand Up @@ -36,72 +36,39 @@

#include "dcc/Packet.hxx"

#include "dcc/Defs.hxx"
#include "utils/Crc.hxx"
#include "utils/logging.h"
#include "utils/macros.h"


namespace dcc
{

static_assert(sizeof(Packet) == sizeof(DCCPacket), "DCCPacket size missmatch");
// Imports the bit declarations from the enums in Defs. This import may only be
// performed in a .cxx file.
using namespace Defs;

enum
{
MARKLIN_DEFAULT_CMD = 0b00100110,
// Direction change bits for marklin-old format.
MARKLIN_CHANGE_DIR_B2 = 0b11000000,

DCC_DEFAULT_CMD = 0,
DCC_LONG_PREAMBLE_CMD = 0b00001100,
DCC_SERVICE_MODE_5X_WITH_ACK_CMD = 0b00111000,
// standard dcc packet with 5x repeat.
DCC_SERVICE_MODE_5X_CMD = 0b00101000,
DCC_SERVICE_MODE_1X_CMD = 0b00001000,
DCC_LONG_ADDRESS_FIRST = 0b11000000,

// Baseline packet: speed and direction.
DCC_BASELINE_SPEED = 0b01000000,
DCC_BASELINE_SPEED_FORWARD = 0b00100000,
DCC_BASELINE_SPEED_LIGHT = 0b00010000,
DCC_FUNCTION1 = 0b10000000,
DCC_FUNCTION1_F0 = 0b00010000,
DCC_FUNCTION2_F5 = 0b10110000,
DCC_FUNCTION2_F9 = 0b10100000,
DCC_FEATURE_EXP_F13 = 0b11011110,
DCC_FEATURE_EXP_F21 = 0b11011111,
DCC_FEATURE_EXP_FNHI = 0b11011000,
DCC_BINARY_SHORT = 0b11011101,
DCC_BINARY_LONG = 0b11000000,
DCC_ANALOG_FN = 0b00111101,

DCC_PROG_READ1 = 0b11100100,
DCC_PROG_WRITE1 = 0b11101100,
DCC_PROG_READ4 = 0b11100000,

DCC_SVC_BIT_MANIPULATE = 0b01111000,
DCC_SVC_WRITE = 0b01111100,
DCC_SVC_VERIFY = 0b01110100,

DCC_SVC_BITVAL_WRITE = 0b11110000,
DCC_SVC_BITVAL_VERIFY = 0b11100000,
DCC_SVC_BITVAL_VALUE = 0b00001000,

DCC_SVC_PAGED_WRITE = 0b01111000,
DCC_SVC_PAGED_VERIFY = 0b01110000,

DCC_BASIC_ACCESSORY_B1 = 0b10000000,
DCC_BASIC_ACCESSORY_B2 = 0b10000000,

// Extended packet: 128-step speed.
DCC_EXT_SPEED = 0b00111111,
DCC_EXT_SPEED_FORWARD = 0x80,
};
static_assert(sizeof(Packet) == sizeof(DCCPacket), "DCCPacket size missmatch");

void Packet::add_dcc_checksum()
{
HASSERT(dlc < MAX_PAYLOAD);
// Protects against double call of add checksum.
HASSERT(!packet_header.skip_ec);

// Performs CRC computation if needed.
if ((payload[0] == ADDRESS_LOGON || payload[0] == ADDRESS_EXT) &&
(dlc >= 6))
{
Crc8DallasMaxim m;
for (int i = 0; i < dlc; ++i)
{
m.update16(payload[i]);
}
payload[dlc++] = m.get();
}

uint8_t cs = 0;
for (int i = 0; i < dlc; ++i)
{
Expand Down Expand Up @@ -353,6 +320,49 @@ void Packet::add_dcc_basic_accessory(unsigned address, bool is_activate) {
add_dcc_checksum();
}

void Packet::set_dcc_logon_enable(
Defs::LogonEnableParam param, uint16_t cid, uint8_t session_id)
{
start_dcc_packet();
payload[dlc++] = ADDRESS_LOGON;
payload[dlc++] = DCC_LOGON_ENABLE | ((uint8_t)param & 0x3);
payload[dlc++] = cid >> 8;
payload[dlc++] = cid & 0xff;
payload[dlc++] = session_id;
add_dcc_checksum();
}

void Packet::set_dcc_select_shortinfo(uint64_t decoder_id)
{
start_dcc_packet();
payload[dlc++] = ADDRESS_LOGON;

payload[dlc++] = DCC_SELECT | ((decoder_id >> 40) & 0xf);
payload[dlc++] = (decoder_id >> 32) & 0xff;
payload[dlc++] = (decoder_id >> 24) & 0xff;
payload[dlc++] = (decoder_id >> 16) & 0xff;
payload[dlc++] = (decoder_id >> 8) & 0xff;
payload[dlc++] = (decoder_id) & 0xff;
payload[dlc++] = CMD_READ_SHORT_INFO;
add_dcc_checksum();
}

void Packet::set_dcc_logon_assign(uint64_t decoder_id, uint16_t address)
{
start_dcc_packet();
payload[dlc++] = ADDRESS_LOGON;

payload[dlc++] = DCC_LOGON_ASSIGN | ((decoder_id >> 40) & 0xf);
payload[dlc++] = (decoder_id >> 32) & 0xff;
payload[dlc++] = (decoder_id >> 24) & 0xff;
payload[dlc++] = (decoder_id >> 16) & 0xff;
payload[dlc++] = (decoder_id >> 8) & 0xff;
payload[dlc++] = (decoder_id) & 0xff;
payload[dlc++] = ((address >> 8) & 0xff) ^ 0b11000000;
payload[dlc++] = (address) & 0xff;
add_dcc_checksum();
}

void Packet::start_mm_packet()
{
dlc = 3;
Expand Down
108 changes: 108 additions & 0 deletions src/dcc/Packet.cxxtest
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
#include "dcc/Packet.hxx"
#include "dcc/Loco.hxx"
#include "dcc/UpdateLoop.hxx"
#include "utils/Crc.hxx"

using ::testing::AtLeast;
using ::testing::ElementsAre;
Expand Down Expand Up @@ -33,6 +34,17 @@ protected:
return p == 0;
}

/// @return true if pkt_ has correct CRC8.
bool check_crc()
{
Crc8DallasMaxim m;
for (int i = 0; i < pkt_.dlc - 1; i++)
{
m.update256(pkt_.payload[i]);
}
return m.get() == 0;
}

vector<uint8_t> packet(const std::initializer_list<int> &data)
{
vector<uint8_t> v;
Expand Down Expand Up @@ -76,6 +88,71 @@ TEST_F(PacketTest, ChecksumFF)
EXPECT_EQ(0xFF, pkt_.payload[3]);
}

TEST_F(PacketTest, ChecksumLogon)
{
pkt_.clear();
pkt_.payload[pkt_.dlc++] = 254;
pkt_.payload[pkt_.dlc++] = 0x01;
pkt_.add_dcc_checksum();
EXPECT_EQ(3, pkt_.dlc);
EXPECT_EQ(255, pkt_.payload[2]);

pkt_.clear();
pkt_.payload[pkt_.dlc++] = 253;
pkt_.payload[pkt_.dlc++] = 0x00;
pkt_.add_dcc_checksum();
EXPECT_EQ(3, pkt_.dlc);
EXPECT_EQ(253, pkt_.payload[2]);

pkt_.clear();
pkt_.payload[pkt_.dlc++] = 253;
pkt_.payload[pkt_.dlc++] = 0xaa;
pkt_.payload[pkt_.dlc++] = 0xaa;
pkt_.payload[pkt_.dlc++] = 0xaa;
pkt_.payload[pkt_.dlc++] = 0xaa;
pkt_.add_dcc_checksum();
EXPECT_EQ(6, pkt_.dlc);
EXPECT_EQ(253, pkt_.payload[5]);

pkt_.clear();
pkt_.payload[pkt_.dlc++] = 254;
pkt_.payload[pkt_.dlc++] = 0xaa;
pkt_.payload[pkt_.dlc++] = 0xaa;
pkt_.payload[pkt_.dlc++] = 0xaa;
pkt_.payload[pkt_.dlc++] = 0xaa;
pkt_.add_dcc_checksum();
EXPECT_EQ(6, pkt_.dlc);
EXPECT_EQ(254, pkt_.payload[5]);
}

TEST_F(PacketTest, CrcLogon)
{
pkt_.clear();
pkt_.payload[pkt_.dlc++] = 253;
pkt_.payload[pkt_.dlc++] = 0xaa;
pkt_.payload[pkt_.dlc++] = 0xaa;
pkt_.payload[pkt_.dlc++] = 0xaa;
pkt_.payload[pkt_.dlc++] = 0xaa;
pkt_.payload[pkt_.dlc++] = 0xaa;
pkt_.add_dcc_checksum();
EXPECT_EQ(8, pkt_.dlc);
EXPECT_EQ(0x40, pkt_.payload[6]);
EXPECT_EQ(253 ^ 0xaa ^ 0x40, pkt_.payload[7]);

pkt_.clear();
pkt_.payload[pkt_.dlc++] = 254;
pkt_.payload[pkt_.dlc++] = 0xaa;
pkt_.payload[pkt_.dlc++] = 0xaa;
pkt_.payload[pkt_.dlc++] = 0xaa;
pkt_.payload[pkt_.dlc++] = 0xaa;
pkt_.payload[pkt_.dlc++] = 0xaa;
pkt_.add_dcc_checksum();
EXPECT_EQ(8, pkt_.dlc);
EXPECT_EQ(0x19, pkt_.payload[6]);
EXPECT_EQ(254 ^ 0xaa ^ 0x19, pkt_.payload[7]);
}


TEST_F(PacketTest, DccSpeed28)
{
pkt_.set_dcc_speed28(DccShortAddress(55), true, 6);
Expand Down Expand Up @@ -310,6 +387,37 @@ TEST_F(PacketTest, SvcProgPagedVerify)
EXPECT_TRUE(check_checksum());
}

TEST_F(PacketTest, LogonEnable)
{
pkt_.set_dcc_logon_enable(Defs::LogonEnableParam::LOCO, 0x55aa, 0x31);
EXPECT_THAT(get_packet(), ElementsAre(254, 0b11111101, 0x55, 0xaa, 0x31, _));
EXPECT_TRUE(check_checksum());
}

TEST_F(PacketTest, SelectInfo)
{
constexpr uint64_t decoder_id = 0x39944332211ull;
pkt_.set_dcc_select_shortinfo(decoder_id);
EXPECT_THAT(get_packet(),
ElementsAre(
254, 0b11010000 | 0x3, 0x99, 0x44, 0x33, 0x22, 0x11, 0xff, _, _));
EXPECT_TRUE(check_crc());
EXPECT_TRUE(check_checksum());
}

TEST_F(PacketTest, LogonAssign)
{
constexpr uint64_t decoder_id = 0x39944332211ull;
pkt_.set_dcc_logon_assign(decoder_id, 0x8233);
// There are too many bytes here to use elementsare, this is the longer
// syntax.
::testing::Matcher<uint8_t> exp[] = {
254, 0b11100000 | 0x3, 0x99, 0x44, 0x33, 0x22, 0x11, 0x42, 0x33, _, _};
EXPECT_THAT(get_packet(), ::testing::ElementsAreArray(exp));
EXPECT_TRUE(check_crc());
EXPECT_TRUE(check_checksum());
}

TEST_F(PacketTest, MMOld)
{
pkt_.start_mm_packet();
Expand Down
Loading

0 comments on commit ddcb125

Please sign in to comment.