/src/cmp_tool/lib/rdcu_compress/rdcu_rmap.c
Line | Count | Source (jump to first uncovered line) |
1 | | /** |
2 | | * @file rdcu_rmap.c |
3 | | * @author Armin Luntzer (armin.luntzer@univie.ac.at) |
4 | | * @date 2018 |
5 | | * |
6 | | * @copyright GPLv2 |
7 | | * This program is free software; you can redistribute it and/or modify it |
8 | | * under the terms and conditions of the GNU General Public License, |
9 | | * version 2, as published by the Free Software Foundation. |
10 | | * |
11 | | * This program is distributed in the hope it will be useful, but WITHOUT |
12 | | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or |
13 | | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for |
14 | | * more details. |
15 | | * |
16 | | * @brief RMAP RDCU link interface |
17 | | * @see FPGA Requirement Specification PLATO-IWF-PL-RS-005 Issue 0.7 |
18 | | * |
19 | | * |
20 | | * Ideally, we would use asynchronous operations here, with a transaction log |
21 | | * that is served by another thread. However, we cannot assume that such |
22 | | * features will be available, so we'll do this by maintaining a mirror of the |
23 | | * RDCU's registers and memory, where instead of actively blocking with get() |
24 | | * and set() RMAP calls, they operate on the local copy and the user issues |
25 | | * sync() calls. |
26 | | * |
27 | | * To monitor the synchronisation status, we maintaining a transaction log |
28 | | * tracking the submitted command set. Response packets could be processed |
29 | | * by interrupt (or thread), but in this variant, we process the return packets |
30 | | * when the user calls rdcu_ctrl_sync_status() |
31 | | * |
32 | | * This is probably the nicest solution when it comes to call overhead, but it |
33 | | * requires 8 MiB of memory for the SRAM mirror and the some for the registers. |
34 | | * |
35 | | * Note that for simplicity , we assume that there is a working heap allocator |
36 | | * available, please adapt all malloc/free calls to your needs, or ask us |
37 | | * to do that for you. |
38 | | * |
39 | | * @note in order to run this on the GR712RC eval board, we set the SRAM mirror |
40 | | * image to the boards SDRAM in rdcu_ctrl_init() and just malloc() it for |
41 | | * the PC (see rdcu_ctrl_init) |
42 | | * |
43 | | * @note The interface requires that you provide an RX and a TX function, |
44 | | * see rdcu_ctrl_init for the call interface. |
45 | | * The TX function shall to return 0 on success, everything else |
46 | | * is considered an error in submission. The RX function shall return |
47 | | * the size of the packet buffer and accept NULL as call argument, on |
48 | | * which it shall return the buffer size required to store the next |
49 | | * pending packet. |
50 | | * You can use these functions to adapt the actual backend, i.e. use |
51 | | * your particular SpW interface or just redirect RX/TX to files |
52 | | * or via a network connection. |
53 | | * |
54 | | * @note We don't have to serve more than one RDCU at any given time, so we |
55 | | * track addresses and paths internally in a single instance. This also |
56 | | * makes the interface less cluttered. Besides, I'm lazy. |
57 | | * |
58 | | * |
59 | | * @warning when operational, we expect to have exclusive control of the SpW link |
60 | | * |
61 | | * TODO: RMAP command response evaluation |
62 | | */ |
63 | | |
64 | | |
65 | | #include <stdint.h> |
66 | | #include <stdlib.h> |
67 | | #include <string.h> |
68 | | |
69 | | #include "../common/byteorder.h" |
70 | | #include "../common/cmp_debug.h" |
71 | | #include "rmap.h" |
72 | | #include "rdcu_rmap.h" |
73 | | |
74 | 0 | #define RDCU_CONFIG_DEBUG 0 |
75 | | |
76 | | static uint8_t rdcu_addr; |
77 | | static uint8_t icu_addr; |
78 | | |
79 | | static uint8_t *dpath; /* destination path (to the RDCU) */ |
80 | | static uint8_t *rpath; /* return path (to the ICU) */ |
81 | | static uint8_t dpath_len; |
82 | | static uint8_t rpath_len; |
83 | | |
84 | | static uint8_t dst_key; /* destination command key */ |
85 | | |
86 | | |
87 | | |
88 | | |
89 | | /* generic calls, functions must be provided to init() */ |
90 | | static int32_t (*rmap_tx)(const void *hdr, uint32_t hdr_size, |
91 | | const uint8_t non_crc_bytes, |
92 | | const void *data, uint32_t data_size); |
93 | | static uint32_t (*rmap_rx)(uint8_t *pkt); |
94 | | |
95 | | static uint32_t data_mtu; /* maximum data transfer size per unit */ |
96 | | |
97 | | |
98 | | |
99 | | |
100 | | |
101 | | |
102 | | |
103 | | |
104 | | |
105 | | |
106 | | /* For now we maintain a simple transaction log that works like this: |
107 | | * we allow up to 128 transfers, simply because this is how many response |
108 | | * packets the GRSPW2 SpW descriptor table can hold at any one time without |
109 | | * blocking the link. |
110 | | * |
111 | | * Every time a new transfer is to be submitted, we grab the first free |
112 | | * slot we encounter in the "in_use" array and use the index as the transaction |
113 | | * identifier of the RMAP packet. (Yes, this is potentially slow, as we may have |
114 | | * to iterate over the whole array every time if we're very busy with |
115 | | * transmissions, so there is room for improvement.) |
116 | | * |
117 | | * Every time a slot is retrieved, the "pending" counter is incremented to |
118 | | * have a fast indicator of the synchronisation status, i.e. if "pending" |
119 | | * is not set, the synchronisation procedure is complete and the local data may |
120 | | * be read, or the remote data has been written and further commands may be |
121 | | * issued. |
122 | | * |
123 | | * The local (mirror) start address of the requested remote address is stored |
124 | | * into the same slot in the "local_addr" array, so we'll know where to put the |
125 | | * data if we issue an RMAP_read command. This may be omitted for write |
126 | | * commands. |
127 | | * |
128 | | * Every time a response packet is received, the data (if any) is written to the |
129 | | * local address, using the length specified by RMAP packet, so be careful where |
130 | | * you place your buffers or registers. On success, the "in_use" slot is cleared |
131 | | * and the pending counter is improved. |
132 | | * |
133 | | * XXX: careful, no locking is used on any of the log data, so this is |
134 | | * single-thread-use only! |
135 | | * |
136 | | */ |
137 | | /* #define TRANS_LOG_SIZE 64 /1* GRSPW2 TX descriptor limit *1/ */ |
138 | 0 | #define TRANS_LOG_SIZE 64000 /* increased because there are no response packets during packet generation */ |
139 | | static struct { |
140 | | |
141 | | uint8_t in_use[TRANS_LOG_SIZE]; |
142 | | void *local_addr[TRANS_LOG_SIZE]; |
143 | | |
144 | | int pending; |
145 | | } trans_log; |
146 | | |
147 | | |
148 | | /** |
149 | | * @brief grab a slot in the transaction log |
150 | | * |
151 | | * @param local_addr the local memory address |
152 | | * |
153 | | * @returns -1 on no slots, >= 0 for the transaction id |
154 | | */ |
155 | | |
156 | | static int trans_log_grab_slot(void *local_addr) |
157 | 0 | { |
158 | 0 | int i; |
159 | 0 | int slot = -1; |
160 | |
|
161 | 0 | for (i = 0; i < TRANS_LOG_SIZE; i++) { |
162 | |
|
163 | 0 | if (trans_log.in_use[i]) |
164 | 0 | continue; |
165 | | |
166 | | /* got one */ |
167 | 0 | slot = i; |
168 | 0 | trans_log.in_use[slot] = 1; |
169 | 0 | trans_log.local_addr[slot] = local_addr; |
170 | 0 | trans_log.pending++; |
171 | 0 | break; |
172 | 0 | } |
173 | |
|
174 | 0 | return slot; |
175 | 0 | } |
176 | | |
177 | | |
178 | | /** |
179 | | * @brief release a slot in the transaction log |
180 | | * |
181 | | * @param slot the id of the slot |
182 | | * |
183 | | */ |
184 | | |
185 | | static void trans_log_release_slot(int slot) |
186 | 0 | { |
187 | |
|
188 | 0 | if (slot < 0) |
189 | 0 | return; |
190 | | |
191 | 0 | if (slot >= TRANS_LOG_SIZE) |
192 | 0 | return; |
193 | | |
194 | 0 | if (!trans_log.in_use[slot]) |
195 | 0 | return; |
196 | | |
197 | 0 | trans_log.in_use[slot] = 0; |
198 | 0 | trans_log.pending--; |
199 | 0 | } |
200 | | |
201 | | |
202 | | /** |
203 | | * @brief get the local address for a slot |
204 | | * |
205 | | * @param slot the id of the slot |
206 | | * |
207 | | * @returns the address or NULL if not found/slot not in use |
208 | | */ |
209 | | |
210 | | static void *trans_log_get_addr(int slot) |
211 | 0 | { |
212 | 0 | if (slot < 0) |
213 | 0 | return NULL; |
214 | | |
215 | 0 | if (slot >= TRANS_LOG_SIZE) |
216 | 0 | return NULL; |
217 | | |
218 | 0 | if (!trans_log.in_use[slot]) |
219 | 0 | return NULL; |
220 | | |
221 | 0 | return trans_log.local_addr[slot]; |
222 | 0 | } |
223 | | |
224 | | /** |
225 | | * @brief n rmap command transaction |
226 | | * |
227 | | * @returns number of packets processed or < 0 on error |
228 | | */ |
229 | | |
230 | | static int rdcu_process_rx(void) |
231 | 0 | { |
232 | 0 | uint32_t n; |
233 | 0 | int cnt = 0; |
234 | |
|
235 | 0 | uint32_t *local_addr; |
236 | |
|
237 | 0 | uint8_t *spw_pckt; |
238 | |
|
239 | 0 | struct rmap_pkt *rp; |
240 | | |
241 | |
|
242 | 0 | if (!rmap_rx) |
243 | 0 | return -1; |
244 | | |
245 | | /* process all pending responses */ |
246 | 0 | while ((n = rmap_rx(NULL))) { |
247 | | /* we received something, allocate enough space for the packet */ |
248 | 0 | spw_pckt = (uint8_t *) malloc(n); |
249 | 0 | if (!spw_pckt) { |
250 | 0 | debug_print("malloc() for packet failed!"); |
251 | 0 | return -1; |
252 | 0 | } |
253 | | |
254 | | /* read the packet */ |
255 | 0 | n = rmap_rx(spw_pckt); |
256 | |
|
257 | 0 | if (!n) { |
258 | 0 | debug_print("Unknown error in rmap_rx()"); |
259 | 0 | free(spw_pckt); |
260 | 0 | return -1; |
261 | 0 | } |
262 | | |
263 | 0 | cnt++; |
264 | |
|
265 | 0 | if (RDCU_CONFIG_DEBUG) |
266 | 0 | rmap_parse_pkt(spw_pckt); |
267 | | |
268 | | /* convert format */ |
269 | 0 | rp = rmap_pkt_from_buffer(spw_pckt, n); |
270 | 0 | free(spw_pckt); |
271 | |
|
272 | 0 | if (!rp) { |
273 | 0 | debug_print("Error converting to RMAP packet"); |
274 | 0 | continue; |
275 | 0 | } |
276 | | |
277 | 0 | local_addr = trans_log_get_addr(rp->tr_id); |
278 | |
|
279 | 0 | if (!local_addr) { |
280 | 0 | debug_print("Warning: response packet received not in transaction log"); |
281 | 0 | rmap_erase_packet(rp); |
282 | 0 | continue; |
283 | 0 | } |
284 | | |
285 | 0 | if (rp->data_len & 0x3) { |
286 | 0 | debug_print("Error: response packet data size is not a multiple of 4, transaction dropped"); |
287 | 0 | trans_log_release_slot(rp->tr_id); |
288 | 0 | rmap_erase_packet(rp); |
289 | 0 | return -1; |
290 | 0 | } |
291 | | |
292 | 0 | if (rp->data_len) { |
293 | 0 | uint8_t crc8; |
294 | | |
295 | | /* convert endianness in-place if needed */ |
296 | 0 | #ifdef __LITTLE_ENDIAN |
297 | 0 | { |
298 | 0 | uint32_t i, tmp; |
299 | |
|
300 | 0 | for (i = 0; i < rp->data_len; i += sizeof(tmp)) { |
301 | 0 | memcpy(&tmp, &rp->data[i], sizeof(tmp)); |
302 | 0 | be32_to_cpus(&tmp); |
303 | 0 | memcpy(&rp->data[i], &tmp, sizeof(tmp)); |
304 | 0 | } |
305 | 0 | } |
306 | 0 | #endif /* __LITTLE_ENDIAN */ |
307 | |
|
308 | 0 | crc8 = rmap_crc8(rp->data, rp->data_len); |
309 | 0 | if (crc8 != rp->data_crc) { |
310 | 0 | debug_print("Error: data CRC8 mismatch, data invalid or packet truncated. Transaction dropped"); |
311 | |
|
312 | 0 | trans_log_release_slot(rp->tr_id); |
313 | 0 | rmap_erase_packet(rp); |
314 | 0 | return -1; |
315 | 0 | } |
316 | | |
317 | 0 | memcpy(local_addr, rp->data, rp->data_len); |
318 | 0 | } |
319 | | |
320 | | |
321 | 0 | trans_log_release_slot(rp->tr_id); |
322 | 0 | rmap_erase_packet(rp); |
323 | 0 | } |
324 | | |
325 | 0 | return cnt; |
326 | 0 | } |
327 | | |
328 | | |
329 | | /** |
330 | | * @brief submit an rmap command transaction |
331 | | * |
332 | | * @param cmd the rmap command |
333 | | * @param cmd_size the size of the rmap command |
334 | | * @param data the payload (may be NULL) |
335 | | * @param data_size the size of the payload |
336 | | * |
337 | | * @returns 0 on success, otherwise error |
338 | | */ |
339 | | |
340 | | int rdcu_submit_tx(const uint8_t *cmd, uint32_t cmd_size, |
341 | | const uint8_t *data, uint32_t data_size) |
342 | 0 | { |
343 | | /* try to process pending responses */ |
344 | 0 | rdcu_process_rx(); |
345 | |
|
346 | 0 | if (!rmap_tx) |
347 | 0 | return -1; |
348 | | |
349 | 0 | if (RDCU_CONFIG_DEBUG) |
350 | 0 | debug_print("Transmitting RMAP command"); |
351 | |
|
352 | 0 | if (rmap_tx(cmd, cmd_size, dpath_len, data, data_size)) { |
353 | 0 | debug_print("rmap_tx() returned error!"); |
354 | 0 | return -1; |
355 | 0 | } |
356 | | |
357 | 0 | return 0; |
358 | 0 | } |
359 | | |
360 | | |
361 | | /** |
362 | | * @brief generate an rmap command packet |
363 | | * |
364 | | * @param trans_id a transaction identifier |
365 | | * |
366 | | * @param cmd the command buffer; if NULL, the function returns the needed size |
367 | | * |
368 | | * @param rmap_cmd_type the rmap command type of the packet |
369 | | * |
370 | | * @param addr the address to read from or write to |
371 | | * |
372 | | * @param size the number of bytes to read or write |
373 | | * |
374 | | * @returns the size of the command data buffer or 0 on error |
375 | | */ |
376 | | |
377 | | int rdcu_gen_cmd(uint16_t trans_id, uint8_t *cmd, |
378 | | uint8_t rmap_cmd_type, |
379 | | uint32_t addr, uint32_t size) |
380 | 0 | { |
381 | 0 | int n; |
382 | |
|
383 | 0 | struct rmap_pkt *pkt; |
384 | |
|
385 | 0 | pkt = rmap_create_packet(); |
386 | 0 | if (!pkt) { |
387 | 0 | debug_print("Error creating packet"); |
388 | 0 | return 0; |
389 | 0 | } |
390 | | |
391 | 0 | rmap_set_dst(pkt, rdcu_addr); |
392 | 0 | rmap_set_src(pkt, icu_addr); |
393 | 0 | rmap_set_dest_path(pkt, dpath, dpath_len); |
394 | 0 | rmap_set_reply_path(pkt, rpath, rpath_len); |
395 | 0 | rmap_set_key(pkt, dst_key); |
396 | 0 | rmap_set_cmd(pkt, rmap_cmd_type); |
397 | 0 | rmap_set_tr_id(pkt, trans_id); |
398 | 0 | rmap_set_data_addr(pkt, addr); |
399 | 0 | rmap_set_data_len(pkt, size); |
400 | | |
401 | | /* determine header size */ |
402 | 0 | n = rmap_build_hdr(pkt, NULL); |
403 | |
|
404 | 0 | if (!cmd || n <= 0) { |
405 | 0 | rmap_erase_packet(pkt); |
406 | 0 | return n; |
407 | 0 | } |
408 | | |
409 | 0 | memset(cmd, 0, (size_t)n); /* clear command buffer */ |
410 | |
|
411 | 0 | n = rmap_build_hdr(pkt, cmd); |
412 | |
|
413 | 0 | rmap_erase_packet(pkt); |
414 | |
|
415 | 0 | return n; |
416 | 0 | } |
417 | | |
418 | | |
419 | | |
420 | | |
421 | | |
422 | | /** |
423 | | * @brief submit a sync command |
424 | | * |
425 | | * @param fn the RDCU command generation function |
426 | | * @param addr the local address of the corresponding remote address |
427 | | * @param data_len the length of the data payload (0 for read commands) |
428 | | * |
429 | | * @note data_len must be a multiple of 4 |
430 | | * @note all data is treated (and byte swapped) as 32 bit words |
431 | | * |
432 | | * @return 0 on success, otherwise error |
433 | | */ |
434 | | |
435 | | |
436 | | int rdcu_sync(int (*fn)(uint16_t trans_id, uint8_t *cmd), |
437 | | void *addr, uint32_t data_len) |
438 | 0 | { |
439 | 0 | int n; |
440 | 0 | int slot; |
441 | |
|
442 | 0 | uint8_t *rmap_cmd; |
443 | | |
444 | |
|
445 | 0 | if (data_len & 0x3) |
446 | 0 | return -1; |
447 | | |
448 | 0 | slot = trans_log_grab_slot(addr); |
449 | 0 | if (slot < 0 || slot > UINT16_MAX) |
450 | 0 | return -1; |
451 | | |
452 | | |
453 | | /* determine size of command */ |
454 | 0 | n = fn((uint16_t)slot, NULL); |
455 | 0 | if (n <= 0) { |
456 | 0 | debug_print("Error creating command packet"); |
457 | 0 | return -1; |
458 | 0 | } |
459 | | |
460 | 0 | rmap_cmd = (uint8_t *)malloc((size_t)n); |
461 | 0 | if (!rmap_cmd) { |
462 | 0 | debug_print("Error allocating rmap cmd"); |
463 | 0 | return -1; |
464 | 0 | } |
465 | | |
466 | | /* now fill actual command */ |
467 | 0 | n = fn((uint16_t)slot, rmap_cmd); |
468 | 0 | if (n <= 0) { |
469 | 0 | debug_print("Error creating command packet"); |
470 | 0 | free(rmap_cmd); |
471 | 0 | return -1; |
472 | 0 | } |
473 | | |
474 | | /* convert endianness if needed */ |
475 | 0 | #ifdef __LITTLE_ENDIAN |
476 | 0 | if (data_len) { |
477 | 0 | uint32_t i; |
478 | 0 | uint32_t *tmp_buf = alloca(data_len); |
479 | 0 | uint32_t *p = (uint32_t *)addr; |
480 | |
|
481 | 0 | for (i = 0; i < (data_len / 4); i++) |
482 | 0 | tmp_buf[i] = cpu_to_be32(p[i]); |
483 | |
|
484 | 0 | addr = tmp_buf; |
485 | 0 | } |
486 | 0 | #endif /* __LITTLE_ENDIAN */ |
487 | |
|
488 | 0 | n = rdcu_submit_tx(rmap_cmd, (uint32_t)n, addr, data_len); |
489 | 0 | free(rmap_cmd); |
490 | |
|
491 | 0 | return n; |
492 | 0 | } |
493 | | |
494 | | |
495 | | |
496 | | /** |
497 | | * @brief submit a data sync command |
498 | | * |
499 | | * @param fn an RDCU data transfer generation function |
500 | | * @param addr the remote address |
501 | | * @param data the local data address |
502 | | * @param data_len the length of the data payload |
503 | | * @param read 0: write, otherwise read |
504 | | * |
505 | | * @return 0 on success, < 0: error, > 0: retry |
506 | | * |
507 | | * @note this one is a little redundant, but otherwise we'd have a lot of |
508 | | * unused parameters on most of the control functions |
509 | | * |
510 | | * XXX need a parameter for read...meh...must think of something else |
511 | | */ |
512 | | |
513 | | |
514 | | int rdcu_sync_data(int (*fn)(uint16_t trans_id, uint8_t *cmd, |
515 | | uint32_t addr, uint32_t data_len), |
516 | | uint32_t addr, void *data, uint32_t data_len, int read) |
517 | 0 | { |
518 | 0 | int n; |
519 | 0 | int slot; |
520 | |
|
521 | 0 | uint8_t *rmap_cmd; |
522 | |
|
523 | 0 | rdcu_process_rx(); |
524 | |
|
525 | 0 | slot = trans_log_grab_slot(data); |
526 | 0 | if (slot < 0 || slot > UINT16_MAX) { |
527 | 0 | if (RDCU_CONFIG_DEBUG) |
528 | 0 | debug_print("Error: all slots busy!"); |
529 | 0 | return 1; |
530 | 0 | } |
531 | | |
532 | | |
533 | | /* determine size of command */ |
534 | 0 | n = fn((uint16_t)slot, NULL, addr, data_len); |
535 | 0 | if (n <= 0) { |
536 | 0 | debug_print("Error creating command packet"); |
537 | 0 | return -1; |
538 | 0 | } |
539 | | |
540 | | |
541 | 0 | rmap_cmd = (uint8_t *)malloc((size_t)n); |
542 | 0 | if (!rmap_cmd) { |
543 | 0 | debug_print("Error allocating rmap cmd"); |
544 | 0 | return -1; |
545 | 0 | } |
546 | | |
547 | | /* now fill actual command */ |
548 | 0 | n = fn((uint16_t)slot, rmap_cmd, addr, data_len); |
549 | 0 | if (n <= 0) { |
550 | 0 | debug_print("Error creating command packet"); |
551 | 0 | free(rmap_cmd); |
552 | 0 | return -1; |
553 | 0 | } |
554 | | |
555 | 0 | if (read) |
556 | 0 | n = rdcu_submit_tx(rmap_cmd, (uint32_t)n, NULL, 0); |
557 | 0 | else |
558 | 0 | n = rdcu_submit_tx(rmap_cmd, (uint32_t)n, data, data_len); |
559 | |
|
560 | 0 | free(rmap_cmd); |
561 | |
|
562 | 0 | return n; |
563 | 0 | } |
564 | | |
565 | | |
566 | | |
567 | | /** |
568 | | * @brief create a complete package from header and payload data including CRC8 |
569 | | * |
570 | | * @note this is a helper function to generate complete binary RMAP packet dumps |
571 | | * |
572 | | * @param blob the blob buffer; if NULL, the function returns the needed size |
573 | | * |
574 | | * @param[in] cmd an rmap command buffer |
575 | | * @param[in] cmd_size the size of the rmap command buffer |
576 | | * @param[in] non_crc_bytes leading bytes in the header not path of the CRC |
577 | | * @param[in] data a data buffer (may be NULL) |
578 | | * @param[in] data_size the size of the data buffer (ignored if data is NULL) |
579 | | * |
580 | | * @note data_size must be a multiple of 4 |
581 | | * @note this function will convert all data to big endian as 32 bit words |
582 | | * |
583 | | * @returns the size of the blob or 0 on error |
584 | | */ |
585 | | |
586 | | uint32_t rdcu_package(uint8_t *blob, |
587 | | const uint8_t *cmd, uint32_t cmd_size, |
588 | | const uint8_t non_crc_bytes, |
589 | | const uint8_t *data, uint32_t data_size) |
590 | 0 | { |
591 | 0 | uint32_t n; |
592 | 0 | int has_data_crc = 0; |
593 | 0 | const struct rmap_instruction *ri; |
594 | | |
595 | |
|
596 | 0 | if (data_size & 0x3) /* must be multiple of 4 */ |
597 | 0 | return 0; |
598 | | |
599 | 0 | if (!data_size) |
600 | 0 | data = NULL; |
601 | |
|
602 | 0 | if (!cmd_size) { |
603 | 0 | blob = NULL; |
604 | 0 | return 0; |
605 | 0 | } |
606 | | |
607 | | |
608 | | /* allocate space for header, header crc, data, data crc */ |
609 | 0 | n = cmd_size + 1; |
610 | |
|
611 | 0 | ri = (const struct rmap_instruction *)&cmd[non_crc_bytes + RMAP_INSTRUCTION]; |
612 | | |
613 | | /* see if the type of command needs a data crc field at the end */ |
614 | 0 | switch (ri->cmd) { |
615 | 0 | case RMAP_READ_MODIFY_WRITE_ADDR_INC: |
616 | 0 | case RMAP_WRITE_ADDR_SINGLE: |
617 | 0 | case RMAP_WRITE_ADDR_INC: |
618 | 0 | case RMAP_WRITE_ADDR_SINGLE_VERIFY: |
619 | 0 | case RMAP_WRITE_ADDR_INC_VERIFY: |
620 | 0 | case RMAP_WRITE_ADDR_SINGLE_VERIFY_REPLY: |
621 | 0 | case RMAP_WRITE_ADDR_INC_VERIFY_REPLY: |
622 | 0 | case RMAP_WRITE_ADDR_INC_REPLY: |
623 | 0 | has_data_crc = 1; |
624 | 0 | n += 1; |
625 | 0 | break; |
626 | 0 | default: |
627 | 0 | break; |
628 | 0 | } |
629 | | |
630 | 0 | if (data) |
631 | 0 | n += data_size; |
632 | |
|
633 | 0 | if (!blob) |
634 | 0 | return n; |
635 | | |
636 | 0 | memcpy(&blob[0], cmd, cmd_size); |
637 | |
|
638 | 0 | blob[cmd_size] = rmap_crc8(&cmd[non_crc_bytes], |
639 | 0 | cmd_size - non_crc_bytes); |
640 | |
|
641 | 0 | if (data) { |
642 | 0 | memcpy(&blob[cmd_size + 1], data, data_size); |
643 | 0 | blob[cmd_size + 1 + data_size] = rmap_crc8(&blob[cmd_size + 1], data_size); |
644 | 0 | } else { |
645 | | /* if no data is present, data crc is 0x0 */ |
646 | 0 | if (has_data_crc) |
647 | 0 | blob[cmd_size + 1] = 0x0; |
648 | 0 | } |
649 | |
|
650 | 0 | return n; |
651 | 0 | } |
652 | | |
653 | | |
654 | | /** |
655 | | * @brief sets the logical address of the RDCU |
656 | | * @param addr the address |
657 | | */ |
658 | | |
659 | | void rdcu_set_destination_logical_address(uint8_t addr) |
660 | 0 | { |
661 | 0 | rdcu_addr = addr; |
662 | 0 | } |
663 | | |
664 | | /** |
665 | | * @brief sets the logical address of the ICU |
666 | | * @param addr the address |
667 | | */ |
668 | | |
669 | | void rdcu_set_source_logical_address(uint8_t addr) |
670 | 0 | { |
671 | 0 | icu_addr = addr; |
672 | 0 | } |
673 | | |
674 | | |
675 | | /** |
676 | | * @brief set the destination path to the RDCU |
677 | | * @param path a byte array containing the path (may be NULL) |
678 | | * @param len the number of elements in the array |
679 | | * |
680 | | * @returns 0 on success, otherwise error |
681 | | * |
682 | | * @note the path array is taken as a reference, make sure to keep it around |
683 | | * the maximum length of the path is 15 elements |
684 | | * setting either path NULL or len 0 disables destination path addressing |
685 | | */ |
686 | | |
687 | | int rdcu_set_destination_path(uint8_t *path, uint8_t len) |
688 | 0 | { |
689 | 0 | if (len > RMAP_MAX_PATH_LEN) |
690 | 0 | return -1; |
691 | | |
692 | 0 | if (!path || !len) { |
693 | 0 | dpath = NULL; |
694 | 0 | dpath_len = 0; |
695 | 0 | return 0; |
696 | 0 | } |
697 | | |
698 | 0 | dpath = path; |
699 | 0 | dpath_len = len; |
700 | |
|
701 | 0 | return 0; |
702 | 0 | } |
703 | | |
704 | | |
705 | | /** |
706 | | * @brief set the return path to the ICU |
707 | | * @param path a byte array containing the path (may be NULL) |
708 | | * @param len the number of elements in the array |
709 | | * |
710 | | * @returns 0 on success, otherwise error |
711 | | * |
712 | | * @note the path array is taken as a reference, make sure to keep it around |
713 | | * the maximum length of the path is 12 elements |
714 | | * the number of elements must be a multiple of 4 (due to RMAP protocol) |
715 | | * setting either path NULL or len 0 disables return path addressing |
716 | | */ |
717 | | |
718 | | int rdcu_set_return_path(uint8_t *path, uint8_t len) |
719 | 0 | { |
720 | 0 | if (len > RMAP_MAX_REPLY_PATH_LEN) |
721 | 0 | return -1; |
722 | | |
723 | 0 | if (len & 0x3) |
724 | 0 | return -1; /* not a multiple of 4 */ |
725 | | |
726 | 0 | if (!path || !len) { |
727 | 0 | rpath = NULL; |
728 | 0 | rpath_len = 0; |
729 | 0 | return 0; |
730 | 0 | } |
731 | | |
732 | 0 | rpath = path; |
733 | 0 | rpath_len = len; |
734 | |
|
735 | 0 | return 0; |
736 | 0 | } |
737 | | |
738 | | |
739 | | /** |
740 | | * @brief set the destination command key to use |
741 | | * |
742 | | * @param key the destination key |
743 | | */ |
744 | | |
745 | | void rdcu_set_destination_key(uint8_t key) |
746 | 0 | { |
747 | 0 | dst_key = key; |
748 | 0 | } |
749 | | |
750 | | |
751 | | /** |
752 | | * @brief get the configured data MTU |
753 | | * |
754 | | * @returns the mtu |
755 | | */ |
756 | | |
757 | | uint32_t rdcu_get_data_mtu(void) |
758 | 0 | { |
759 | 0 | return data_mtu; |
760 | 0 | } |
761 | | |
762 | | |
763 | | /** |
764 | | * @brief get the RDCU <-> ICU mirror RMAP synchronisation status |
765 | | * |
766 | | * @returns 0: synchronised, > 0: operations pending |
767 | | */ |
768 | | |
769 | | int rdcu_rmap_sync_status(void) |
770 | 0 | { |
771 | | /* try to process pending responses */ |
772 | 0 | rdcu_process_rx(); |
773 | |
|
774 | 0 | return trans_log.pending; |
775 | 0 | } |
776 | | |
777 | | |
778 | | /** |
779 | | * @brief reset all entries in the RMAP transaction log |
780 | | */ |
781 | | |
782 | | void rdcu_rmap_reset_log(void) |
783 | 0 | { |
784 | 0 | memset(trans_log.in_use, 0, sizeof(trans_log.in_use)); /* clear in_use buffer */ |
785 | 0 | trans_log.pending = 0; |
786 | 0 | } |
787 | | |
788 | | |
789 | | /** |
790 | | * @brief initialise the rdcu control library |
791 | | * |
792 | | * @param mtu the maximum data transfer size per unit |
793 | | * |
794 | | * @param tx a function pointer to transmit an rmap command |
795 | | * @param rx function pointer to receive an rmap command |
796 | | * |
797 | | * @note tx is expected to return 0 on success |
798 | | * rmap_rx is expected to return the number of packet bytes |
799 | | * |
800 | | * @returns 0 on success, otherwise error |
801 | | */ |
802 | | |
803 | | int rdcu_rmap_init(uint32_t mtu, |
804 | | int32_t (*tx)(const void *hdr, uint32_t hdr_size, |
805 | | const uint8_t non_crc_bytes, |
806 | | const void *data, uint32_t data_size), |
807 | | uint32_t (*rx)(uint8_t *pkt)) |
808 | 0 | { |
809 | 0 | if (!tx) |
810 | 0 | return -1; |
811 | | |
812 | 0 | if (!rx) |
813 | 0 | return -1; |
814 | | |
815 | 0 | rmap_tx = tx; |
816 | 0 | rmap_rx = rx; |
817 | |
|
818 | 0 | data_mtu = mtu; |
819 | |
|
820 | 0 | return 0; |
821 | 0 | } |