1

I am writing a PTP client for an embedded system (little endian). PTP is a network protocol for time synchronization which is in network order (big endian). The PTP header and other datatypes are not generally word aligned (some fields are 4 bits long, others are 10 bytes). Is there some clever way to bring the structures into little endian despite the variable alignment.

An example of the PTP header is below:

typedef struct
{
  uint8_t message_type : 4;
  uint8_t transport_specific : 4;
  uint8_t version_ptp : 4;
  uint8_t reserved_0 : 4;
  uint16_t message_length;
  uint8_t domain_number;
  uint8_t reserved_1;
  uint16_t flags;
  uint64_t correction_field;
  uint32_t reserved_2;
  uint8_t source_port_identiy[10];
  uint16_t sequence_id;
  uint8_t control_field;
  uint8_t log_message_interval;
} __attribute__((packed)) PtpHeader_t;
chris12892
  • 1,634
  • 2
  • 18
  • 36
  • 1
    Typically one uses the `ntohl` and `ntohs` functions to change the endianness of 32-bit and 16-bit values respectively. You'll need to write your own function to change the endianness of `correction_field`. See [this question](https://stackoverflow.com/questions/26283289/changing-endianess-is-union-more-efficient-than-bitshifts) for information about how to write functions to change the endianness of a single value. – user3386109 Dec 19 '19 at 23:00
  • 3
    Why "some clever way" vs. `sequence_id = (uint8_t) (sequence_id >> 8) | sequence_id << 8;` for example? – chux - Reinstate Monica Dec 19 '19 at 23:40
  • 3
    Be clear: is the goal "big to little" or "whatever (native) to little"? – chux - Reinstate Monica Dec 19 '19 at 23:42
  • 1
    Bit-fields are wildly non-portable, so there is no sensible way to use this code in a portable, endianess-independent manner. It has to be replaced with a union of misc types + byte array. Then you can implement serialization in portable ways with bit shifts. You can have "network endianess" to/from "don't care endianess". – Lundin Dec 20 '19 at 07:42

1 Answers1

1

You can develop some serialization function like, e.g.

typedef enum
{
    Lib_Utils_big_endian,
    Lib_Utils_little_endian,
}typ_Endianness;

unsigned char *Lib_Utils_Ser_uint32_t(unsigned char p[sizeof(uint32_t)], const uint32_t value, typ_Endianness to_endianness)
{
    if (to_endianness == Lib_Utils_little_endian)
    {
        p[3] = (value >> 24u) & 0xFFu;
        p[2] = (value >> 16u) & 0xFFu;
        p[1] = (value >>  8u) & 0xFFu;
        p[0] = (value >>  0u) & 0xFFu;
    }
    else
    {
        p[0] = (value >> 24u) & 0xFFu;
        p[1] = (value >> 16u) & 0xFFu;
        p[2] = (value >>  8u) & 0xFFu;
        p[3] = (value >>  0u) & 0xFFu;
    }
    return p+sizeof(uint32_t);
}

And using a union to access the bit-fields data like

typedef union
{
    struct data_t
    {
      uint8_t message_type : 4;
      uint8_t transport_specific : 4;
      uint8_t version_ptp : 4;
      uint8_t reserved_0 : 4;
    }data;
    uint16_t header;
    uint8_t rawData[sizeof(struct data_t)];
}test_t;
LPs
  • 16,045
  • 8
  • 30
  • 61