1 //
2 // Copyright 2019 Ettus Research, a National Instruments Brand
3 //
4 // SPDX-License-Identifier: GPL-3.0-or-later
5 //
6 
7 #include <uhdlib/rfnoc/chdr_packet_writer.hpp>
8 #include <cassert>
9 #include <functional>
10 #include <memory>
11 
12 using namespace uhd;
13 using namespace uhd::rfnoc;
14 using namespace uhd::rfnoc::chdr;
15 
16 chdr_packet_writer::~chdr_packet_writer() = default;
17 
18 //------------------------------------------------------------
19 // chdr_packet
20 //------------------------------------------------------------
21 // endianness is the link endianness, not the host endianness
22 template <size_t chdr_w, endianness_t endianness>
23 class chdr_packet_impl : public chdr_packet_writer
24 {
25 public:
26     chdr_packet_impl() = delete;
chdr_packet_impl(size_t mtu_bytes)27     chdr_packet_impl(size_t mtu_bytes) : _mtu_bytes(mtu_bytes) {}
28     ~chdr_packet_impl() = default;
29 
refresh(const void * pkt_buff) const30     virtual void refresh(const void* pkt_buff) const
31     {
32         assert(pkt_buff);
33         _pkt_buff = const_cast<uint64_t*>(reinterpret_cast<const uint64_t*>(pkt_buff));
34         _mdata_offset = _compute_mdata_offset(get_chdr_header());
35     }
36 
refresh(void * pkt_buff,chdr_header & header,uint64_t timestamp=0)37     virtual void refresh(void* pkt_buff, chdr_header& header, uint64_t timestamp = 0)
38     {
39         assert(pkt_buff);
40         _pkt_buff    = reinterpret_cast<uint64_t*>(pkt_buff);
41         _pkt_buff[0] = u64_from_host(header);
42         if (_has_timestamp(header)) {
43             _pkt_buff[1] = u64_from_host(timestamp);
44         }
45         _mdata_offset = _compute_mdata_offset(get_chdr_header());
46     }
47 
update_payload_size(size_t payload_size_bytes)48     virtual void update_payload_size(size_t payload_size_bytes)
49     {
50         chdr_header header = get_chdr_header();
51         header.set_length(((_mdata_offset + header.get_num_mdata()) * chdr_w_bytes)
52                           + payload_size_bytes);
53         _pkt_buff[0] = u64_from_host(header);
54     }
55 
get_byte_order() const56     virtual endianness_t get_byte_order() const
57     {
58         return endianness;
59     }
60 
get_mtu_bytes() const61     virtual size_t get_mtu_bytes() const
62     {
63         return _mtu_bytes;
64     }
65 
get_chdr_header() const66     virtual chdr_header get_chdr_header() const
67     {
68         assert(_pkt_buff);
69         return std::move(chdr_header(u64_to_host(_pkt_buff[0])));
70     }
71 
get_timestamp() const72     virtual boost::optional<uint64_t> get_timestamp() const
73     {
74         if (_has_timestamp(get_chdr_header())) {
75             // In a unit64_t buffer, the timestamp is always immediately after the header
76             // regardless of chdr_w.
77             return u64_to_host(_pkt_buff[1]);
78         } else {
79             return boost::none;
80         }
81     }
82 
get_mdata_size() const83     virtual size_t get_mdata_size() const
84     {
85         return get_chdr_header().get_num_mdata() * chdr_w_bytes;
86     }
87 
get_mdata_const_ptr() const88     virtual const void* get_mdata_const_ptr() const
89     {
90         return const_cast<void*>(
91             const_cast<chdr_packet_impl<chdr_w, endianness>*>(this)->get_mdata_ptr());
92     }
93 
get_mdata_ptr()94     virtual void* get_mdata_ptr()
95     {
96         return reinterpret_cast<void*>(_pkt_buff + (chdr_w_stride * _mdata_offset));
97     }
98 
get_payload_size() const99     virtual size_t get_payload_size() const
100     {
101         return get_chdr_header().get_length() - get_mdata_size()
102                - (chdr_w_bytes * _mdata_offset);
103     }
104 
get_payload_const_ptr() const105     virtual const void* get_payload_const_ptr() const
106     {
107         return const_cast<void*>(
108             const_cast<chdr_packet_impl<chdr_w, endianness>*>(this)->get_payload_ptr());
109     }
110 
get_payload_ptr()111     virtual void* get_payload_ptr()
112     {
113         return reinterpret_cast<void*>(
114             _pkt_buff
115             + (chdr_w_stride * (_mdata_offset + get_chdr_header().get_num_mdata())));
116     }
117 
calculate_payload_offset(const packet_type_t pkt_type,const uint8_t num_mdata=0) const118     virtual size_t calculate_payload_offset(
119         const packet_type_t pkt_type, const uint8_t num_mdata = 0) const
120     {
121         chdr_header header;
122         header.set_pkt_type(pkt_type);
123         return (_compute_mdata_offset(header) + num_mdata) * chdr_w_bytes;
124     }
125 
126 private:
_has_timestamp(const chdr_header & header) const127     inline bool _has_timestamp(const chdr_header& header) const
128     {
129         return (header.get_pkt_type() == PKT_TYPE_DATA_WITH_TS);
130     }
131 
_compute_mdata_offset(const chdr_header & header) const132     inline size_t _compute_mdata_offset(const chdr_header& header) const
133     {
134         // The metadata offset depends on the chdr_w and whether we have a timestamp
135         if (chdr_w == 64) {
136             return _has_timestamp(header) ? 2 : 1;
137         } else {
138             return 1;
139         }
140     }
141 
u64_to_host(uint64_t word)142     inline static uint64_t u64_to_host(uint64_t word)
143     {
144         return (endianness == ENDIANNESS_BIG) ? uhd::ntohx<uint64_t>(word)
145                                               : uhd::wtohx<uint64_t>(word);
146     }
147 
u64_from_host(uint64_t word)148     inline static uint64_t u64_from_host(uint64_t word)
149     {
150         return (endianness == ENDIANNESS_BIG) ? uhd::htonx<uint64_t>(word)
151                                               : uhd::htowx<uint64_t>(word);
152     }
153 
154     static const size_t chdr_w_bytes  = (chdr_w / 8);
155     static const size_t chdr_w_stride = (chdr_w / 64);
156 
157     // Packet state
158     const size_t _mtu_bytes      = 0;
159     mutable uint64_t* _pkt_buff  = nullptr;
160     mutable size_t _mdata_offset = 0;
161 };
162 
chdr_packet_factory(chdr_w_t chdr_w,endianness_t endianness)163 chdr_packet_factory::chdr_packet_factory(chdr_w_t chdr_w, endianness_t endianness)
164     : _chdr_w(chdr_w), _endianness(endianness)
165 {
166 }
167 
make_generic(size_t mtu_bytes) const168 chdr_packet_writer::uptr chdr_packet_factory::make_generic(size_t mtu_bytes) const
169 {
170     if (_endianness == ENDIANNESS_BIG) {
171         switch (_chdr_w) {
172             case CHDR_W_512:
173                 return std::make_unique<chdr_packet_impl<512, ENDIANNESS_BIG>>(mtu_bytes);
174             case CHDR_W_256:
175                 return std::make_unique<chdr_packet_impl<256, ENDIANNESS_BIG>>(mtu_bytes);
176             case CHDR_W_128:
177                 return std::make_unique<chdr_packet_impl<128, ENDIANNESS_BIG>>(mtu_bytes);
178             case CHDR_W_64:
179                 return std::make_unique<chdr_packet_impl<64, ENDIANNESS_BIG>>(mtu_bytes);
180             default:
181                 assert(0);
182         }
183     } else {
184         switch (_chdr_w) {
185             case CHDR_W_512:
186                 return std::make_unique<chdr_packet_impl<512, ENDIANNESS_LITTLE>>(
187                     mtu_bytes);
188             case CHDR_W_256:
189                 return std::make_unique<chdr_packet_impl<256, ENDIANNESS_LITTLE>>(
190                     mtu_bytes);
191             case CHDR_W_128:
192                 return std::make_unique<chdr_packet_impl<128, ENDIANNESS_LITTLE>>(
193                     mtu_bytes);
194             case CHDR_W_64:
195                 return std::make_unique<chdr_packet_impl<64, ENDIANNESS_LITTLE>>(
196                     mtu_bytes);
197             default:
198                 assert(0);
199         }
200     }
201     return chdr_packet_writer::uptr();
202 }
203 
make_ctrl(size_t mtu_bytes) const204 chdr_ctrl_packet::uptr chdr_packet_factory::make_ctrl(size_t mtu_bytes) const
205 {
206     return std::make_unique<chdr_ctrl_packet>(make_generic(mtu_bytes));
207 }
208 
make_strs(size_t mtu_bytes) const209 chdr_strs_packet::uptr chdr_packet_factory::make_strs(size_t mtu_bytes) const
210 {
211     return std::make_unique<chdr_strs_packet>(make_generic(mtu_bytes));
212 }
213 
make_strc(size_t mtu_bytes) const214 chdr_strc_packet::uptr chdr_packet_factory::make_strc(size_t mtu_bytes) const
215 {
216     return std::make_unique<chdr_strc_packet>(make_generic(mtu_bytes));
217 }
218 
make_mgmt(size_t mtu_bytes) const219 chdr_mgmt_packet::uptr chdr_packet_factory::make_mgmt(size_t mtu_bytes) const
220 {
221     return std::make_unique<chdr_mgmt_packet>(make_generic(mtu_bytes));
222 }
223