1 /*
2 * Copyright © 2022 Imagination Technologies Ltd.
3 *
4 * Permission is hereby granted, free of charge, to any person obtaining a copy
5 * of this software and associated documentation files (the "Software"), to deal
6 * in the Software without restriction, including without limitation the rights
7 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8 * copies of the Software, and to permit persons to whom the Software is
9 * furnished to do so, subject to the following conditions:
10 *
11 * The above copyright notice and this permission notice (including the next
12 * paragraph) shall be included in all copies or substantial portions of the
13 * Software.
14 *
15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 * SOFTWARE.
22 */
23
24 #ifndef PVR_DUMP_H
25 #define PVR_DUMP_H
26
27 #include <inttypes.h>
28 #include <stdarg.h>
29 #include <stdbool.h>
30 #include <stdint.h>
31 #include <stdio.h>
32
33 #include "pvr_types.h"
34 #include "pvr_util.h"
35 #include "util/macros.h"
36 #include "util/u_math.h"
37
38 /** BASIC PRINTING **/
39
40 #define PVR_DUMP_OFFSET_PREFIX "[%0*" PRIx64 "] "
41
42 /** CONTEXTS **/
43
44 #define PVR_DUMP_INDENT_SIZE 2U
45 #define PVR_DUMP_FIELD_COLUMN_WIDTH 36U
46
47 /* This is an invalid context used to permanently mark popped contexts as
48 * unusable. All operations on a context check that it's the "top" context
49 * by ensuring it has no active child. The only way to remove the active child
50 * of a context is by popping the active child directly. Assigning an invalid
51 * context as the active child of a context therefore makes it impossible to
52 * use.
53 */
54 extern const struct pvr_dump_ctx __pvr_dump_ctx_invalid;
55
56 struct pvr_dump_ctx {
57 /* This is const because only the "top" context should ever be modified. It's
58 * fine to extract information from the parent context, but not to modify it.
59 * There is *one* exception: pvr_dump_ctx_pop() must cast away the const to
60 * return the parent context as the new "top" context. This is considered
61 * sound because the parent context was not const when assigned here in
62 * pvr_dump_ctx_push().
63 */
64 const struct pvr_dump_ctx *parent;
65
66 /* This is const because it's not meant to be used for access - it's just a
67 * way of checking if this context is the "top" context (see the comment on
68 * __pvr_dump_ctx_invalid for more details). Unlike parent, the const
69 * qualifier here should never be cast away.
70 */
71 const struct pvr_dump_ctx *active_child;
72
73 FILE *file;
74 const char *name;
75
76 uint32_t allowed_child_depth;
77 uint32_t parent_indent;
78
79 /* User-modifiable values */
80 uint32_t indent;
81 bool ok;
82 };
83
84 static inline uint32_t
__pvr_dump_ctx_get_indent(const struct pvr_dump_ctx * const ctx)85 __pvr_dump_ctx_get_indent(const struct pvr_dump_ctx *const ctx)
86 {
87 return (ctx->parent_indent + ctx->indent) * PVR_DUMP_INDENT_SIZE;
88 }
89
90 struct pvr_dump_buffer_ctx {
91 struct pvr_dump_ctx base;
92
93 const void *initial_ptr;
94 uint64_t capacity;
95
96 /* User-modifiable values */
97 const void *ptr;
98 uint64_t remaining_size;
99 };
100
101 #define pvr_dump_printf(ctx, format, args...) \
102 pvr_dump_printf_cont(ctx, \
103 "%*s" format, \
104 __pvr_dump_ctx_get_indent(ctx), \
105 "", \
106 ##args)
107
108 /* Same as pvr_dump_printf(), but with no indent.
109 * Intended for continuation lines.
110 */
111 #define pvr_dump_printf_cont(ctx, format, args...) \
112 fprintf((ctx)->file, format, ##args)
113
114 #define pvr_dump_println(ctx, format, args...) \
115 pvr_dump_printf(ctx, format "\n", ##args)
116
117 #define pvr_dump_println_cont(ctx, format, args...) \
118 pvr_dump_printf_cont(ctx, format "\n", ##args)
119
120 #define pvr_dump_print_eol(ctx) fprintf((ctx)->file, "\n")
121
122 #define pvr_dump_mark_section(ctx, format, args...) \
123 do { \
124 pvr_dump_print_eol(ctx); \
125 pvr_dump_println(ctx, "------- " format " -------", ##args); \
126 } while (0)
127
128 #define pvr_dump_buffer_print_header_prefix(ctx) \
129 do { \
130 struct pvr_dump_buffer_ctx *_prefix_ctx = (ctx); \
131 pvr_dump_printf(&_prefix_ctx->base, \
132 PVR_DUMP_OFFSET_PREFIX, \
133 u64_dec_digits(_prefix_ctx->capacity), \
134 _prefix_ctx->capacity - _prefix_ctx->remaining_size); \
135 } while (0)
136
137 #define pvr_dump_buffer_print_header_line(ctx, format, args...) \
138 do { \
139 struct pvr_dump_buffer_ctx *_ctx = (ctx); \
140 pvr_dump_buffer_print_header_prefix(_ctx); \
141 pvr_dump_printf_cont(&_ctx->base, format "\n", ##args); \
142 } while (0)
143
144 #define pvr_dump_msg(ctx, prefix, ret, format, args...) \
145 ({ \
146 bool _ret = (ret); \
147 struct pvr_dump_ctx *_ctx = (ctx); \
148 pvr_dump_println(_ctx, "<!" prefix "! " format ">", ##args); \
149 if (!_ret) \
150 _ctx->ok = _ret; \
151 _ret; \
152 })
153
154 #define pvr_dump_error(ctx, format, args...) \
155 pvr_dump_msg(ctx, "ERROR", false, format, ##args)
156
157 #define pvr_dump_warn(ctx, format, args...) \
158 pvr_dump_msg(ctx, "WARN", true, format, ##args)
159
pvr_dump_ctx_require_top(struct pvr_dump_ctx * const ctx)160 static inline bool pvr_dump_ctx_require_top(struct pvr_dump_ctx *const ctx)
161 {
162 if (ctx->active_child != NULL)
163 return pvr_dump_error(ctx, "use of non-top context");
164
165 return true;
166 }
167
pvr_dump_indent(struct pvr_dump_ctx * const ctx)168 static inline void pvr_dump_indent(struct pvr_dump_ctx *const ctx)
169 {
170 ctx->indent++;
171 }
172
pvr_dump_dedent(struct pvr_dump_ctx * const ctx)173 static inline void pvr_dump_dedent(struct pvr_dump_ctx *const ctx)
174 {
175 if (ctx->indent)
176 ctx->indent--;
177 }
178
__pvr_dump_ctx_init(struct pvr_dump_ctx * const ctx,const struct pvr_dump_ctx * const parent,FILE * const file,const char * const name,const uint32_t allowed_child_depth,const uint32_t parent_indent)179 static inline void __pvr_dump_ctx_init(struct pvr_dump_ctx *const ctx,
180 const struct pvr_dump_ctx *const parent,
181 FILE *const file,
182 const char *const name,
183 const uint32_t allowed_child_depth,
184 const uint32_t parent_indent)
185 {
186 ctx->parent = parent;
187 ctx->active_child = NULL;
188
189 ctx->file = file;
190 ctx->name = name;
191
192 ctx->allowed_child_depth = allowed_child_depth;
193 ctx->parent_indent = parent_indent;
194 ctx->indent = 0;
195 ctx->ok = true;
196 }
197
__pvr_dump_ctx_mark_popped(struct pvr_dump_ctx * const ctx)198 static inline void __pvr_dump_ctx_mark_popped(struct pvr_dump_ctx *const ctx)
199 {
200 ctx->active_child = &__pvr_dump_ctx_invalid;
201 }
202
pvr_dump_begin(struct pvr_dump_ctx * const root_ctx,FILE * const file,const char * const name,const uint32_t max_depth)203 static inline void pvr_dump_begin(struct pvr_dump_ctx *const root_ctx,
204 FILE *const file,
205 const char *const name,
206 const uint32_t max_depth)
207 {
208 __pvr_dump_ctx_init(root_ctx, NULL, file, name, max_depth, 0);
209
210 flockfile(file);
211 pvr_dump_println(root_ctx, "======= BEGIN %s =======", name);
212 }
213
pvr_dump_end(struct pvr_dump_ctx * const root_ctx)214 static inline bool pvr_dump_end(struct pvr_dump_ctx *const root_ctx)
215 {
216 /* In order to end a dump, we must be in a root context (no parent) and have
217 * no active child context.
218 */
219 if (!pvr_dump_ctx_require_top(root_ctx))
220 return false;
221
222 if (root_ctx->parent)
223 return pvr_dump_error(root_ctx, "ending non-root context");
224
225 pvr_dump_println(root_ctx, "======= END %s =======", root_ctx->name);
226 funlockfile(root_ctx->file);
227
228 __pvr_dump_ctx_mark_popped(root_ctx);
229
230 return true;
231 }
232
pvr_dump_ctx_push(struct pvr_dump_ctx * const ctx,struct pvr_dump_ctx * const parent_ctx)233 static inline bool pvr_dump_ctx_push(struct pvr_dump_ctx *const ctx,
234 struct pvr_dump_ctx *const parent_ctx)
235 {
236 if (!parent_ctx->ok)
237 return false;
238
239 if (!parent_ctx->allowed_child_depth)
240 return pvr_dump_error(parent_ctx, "context stack depth limit reached");
241
242 __pvr_dump_ctx_init(ctx,
243 parent_ctx,
244 parent_ctx->file,
245 parent_ctx->name,
246 parent_ctx->allowed_child_depth - 1,
247 parent_ctx->parent_indent + parent_ctx->indent);
248
249 parent_ctx->active_child = ctx;
250
251 return true;
252 }
253
254 static inline struct pvr_dump_ctx *
pvr_dump_ctx_pop(struct pvr_dump_ctx * const ctx)255 pvr_dump_ctx_pop(struct pvr_dump_ctx *const ctx)
256 {
257 struct pvr_dump_ctx *const parent = (struct pvr_dump_ctx *)ctx->parent;
258
259 if (!pvr_dump_ctx_require_top(ctx))
260 return NULL;
261
262 if (!parent) {
263 pvr_dump_error(ctx, "popped root context");
264 return NULL;
265 }
266
267 parent->active_child = NULL;
268
269 __pvr_dump_ctx_mark_popped(ctx);
270
271 return parent;
272 }
273
274 static inline bool
pvr_dump_buffer_ctx_push(struct pvr_dump_buffer_ctx * const ctx,struct pvr_dump_ctx * const parent_ctx,const void * const initial_ptr,const uint64_t size)275 pvr_dump_buffer_ctx_push(struct pvr_dump_buffer_ctx *const ctx,
276 struct pvr_dump_ctx *const parent_ctx,
277 const void *const initial_ptr,
278 const uint64_t size)
279 {
280 if (!pvr_dump_ctx_push(&ctx->base, parent_ctx))
281 return false;
282
283 ctx->initial_ptr = initial_ptr;
284 ctx->capacity = size;
285
286 ctx->ptr = initial_ptr;
287 ctx->remaining_size = size;
288
289 return true;
290 }
291
292 static inline struct pvr_dump_ctx *
pvr_dump_buffer_ctx_pop(struct pvr_dump_buffer_ctx * const ctx)293 pvr_dump_buffer_ctx_pop(struct pvr_dump_buffer_ctx *const ctx)
294 {
295 return pvr_dump_ctx_pop(&ctx->base);
296 }
297
298 bool pvr_dump_buffer_hex(struct pvr_dump_buffer_ctx *ctx, uint64_t nr_bytes);
299
__pvr_dump_buffer_advance(struct pvr_dump_buffer_ctx * ctx,const uint64_t nr_bytes)300 static inline void __pvr_dump_buffer_advance(struct pvr_dump_buffer_ctx *ctx,
301 const uint64_t nr_bytes)
302 {
303 ctx->ptr = (uint8_t *)ctx->ptr + nr_bytes;
304 ctx->remaining_size -= nr_bytes;
305 }
306
pvr_dump_buffer_advance(struct pvr_dump_buffer_ctx * ctx,const uint64_t nr_bytes)307 static inline bool pvr_dump_buffer_advance(struct pvr_dump_buffer_ctx *ctx,
308 const uint64_t nr_bytes)
309 {
310 if (!ctx->base.ok || !pvr_dump_ctx_require_top(&ctx->base))
311 return false;
312
313 if (nr_bytes > ctx->remaining_size)
314 return pvr_dump_error(&ctx->base, "advanced past end of context buffer");
315
316 __pvr_dump_buffer_advance(ctx, nr_bytes);
317
318 return true;
319 }
320
__pvr_dump_buffer_rewind(struct pvr_dump_buffer_ctx * ctx,const uint32_t nr_bytes)321 static inline void __pvr_dump_buffer_rewind(struct pvr_dump_buffer_ctx *ctx,
322 const uint32_t nr_bytes)
323 {
324 ctx->ptr = (uint8_t *)ctx->ptr - nr_bytes;
325 ctx->remaining_size += nr_bytes;
326 }
327
pvr_dump_buffer_rewind(struct pvr_dump_buffer_ctx * ctx,const uint32_t nr_bytes)328 static inline bool pvr_dump_buffer_rewind(struct pvr_dump_buffer_ctx *ctx,
329 const uint32_t nr_bytes)
330 {
331 if (!ctx->base.ok || !pvr_dump_ctx_require_top(&ctx->base))
332 return false;
333
334 if (nr_bytes > ctx->capacity - ctx->remaining_size)
335 return pvr_dump_error(&ctx->base, "rewound past start of context buffer");
336
337 __pvr_dump_buffer_rewind(ctx, nr_bytes);
338
339 return true;
340 }
341
pvr_dump_buffer_truncate(struct pvr_dump_buffer_ctx * ctx,const uint64_t remaining_size)342 static inline bool pvr_dump_buffer_truncate(struct pvr_dump_buffer_ctx *ctx,
343 const uint64_t remaining_size)
344 {
345 if (!ctx->base.ok || !pvr_dump_ctx_require_top(&ctx->base))
346 return false;
347
348 if (remaining_size > ctx->remaining_size)
349 return pvr_dump_error(&ctx->base, "truncated to larger size");
350
351 ctx->remaining_size = remaining_size;
352
353 return true;
354 }
355
356 static inline const void *restrict
pvr_dump_buffer_peek(struct pvr_dump_buffer_ctx * const restrict ctx,const uint64_t nr_bytes)357 pvr_dump_buffer_peek(struct pvr_dump_buffer_ctx *const restrict ctx,
358 const uint64_t nr_bytes)
359 {
360 if (!ctx->base.ok || !pvr_dump_ctx_require_top(&ctx->base))
361 return NULL;
362
363 if (nr_bytes > ctx->remaining_size) {
364 pvr_dump_error(&ctx->base, "peeked past end of context buffer");
365 return NULL;
366 }
367
368 return ctx->ptr;
369 }
370
371 static inline const void *restrict
pvr_dump_buffer_take(struct pvr_dump_buffer_ctx * const restrict ctx,const uint64_t nr_bytes)372 pvr_dump_buffer_take(struct pvr_dump_buffer_ctx *const restrict ctx,
373 const uint64_t nr_bytes)
374 {
375 const void *const ptr = pvr_dump_buffer_peek(ctx, nr_bytes);
376
377 if (ptr)
378 __pvr_dump_buffer_advance(ctx, nr_bytes);
379
380 return ptr;
381 }
382
383 static inline void
pvr_dump_buffer_restart(struct pvr_dump_buffer_ctx * const ctx)384 pvr_dump_buffer_restart(struct pvr_dump_buffer_ctx *const ctx)
385 {
386 ctx->ptr = ctx->initial_ptr;
387 ctx->remaining_size = ctx->capacity;
388 }
389
390 /*****************************************************************************
391 Field printers
392 *****************************************************************************/
393
394 #define pvr_dump_field(ctx, name, format, args...) \
395 pvr_dump_println(ctx, \
396 "%-*s : " format, \
397 PVR_DUMP_FIELD_COLUMN_WIDTH - \
398 __pvr_dump_ctx_get_indent(ctx), \
399 name, \
400 ##args)
401
402 #define pvr_dump_field_computed(ctx, name, format, raw_format, args...) \
403 pvr_dump_field(ctx, name, format " (" raw_format ")", ##args)
404
405 #define pvr_dump_field_error(ctx, format, args...) \
406 ({ \
407 struct pvr_dump_ctx *_ctx = (ctx); \
408 pvr_dump_field(_ctx, "<!ERROR!>", "<" format ">", ##args); \
409 _ctx->ok = false; \
410 false; \
411 })
412
413 /*****************************************************************************
414 Field printers: integers
415 *****************************************************************************/
416
pvr_dump_field_u32(struct pvr_dump_ctx * const ctx,const char * const name,const uint32_t value)417 static inline void pvr_dump_field_u32(struct pvr_dump_ctx *const ctx,
418 const char *const name,
419 const uint32_t value)
420 {
421 pvr_dump_field(ctx, name, "%" PRIu32, value);
422 }
423
pvr_dump_field_u32_units(struct pvr_dump_ctx * const ctx,const char * const name,const uint32_t value,const char * const units)424 static inline void pvr_dump_field_u32_units(struct pvr_dump_ctx *const ctx,
425 const char *const name,
426 const uint32_t value,
427 const char *const units)
428 {
429 pvr_dump_field(ctx, name, "%" PRIu32 " %s", value, units);
430 }
431
pvr_dump_field_u32_offset(struct pvr_dump_ctx * const ctx,const char * const name,const uint32_t value,const uint32_t offset)432 static inline void pvr_dump_field_u32_offset(struct pvr_dump_ctx *const ctx,
433 const char *const name,
434 const uint32_t value,
435 const uint32_t offset)
436 {
437 pvr_dump_field_computed(ctx,
438 name,
439 "%" PRIu32,
440 "%" PRIu32 " + %" PRIu32,
441 value + offset,
442 value,
443 offset);
444 }
445
pvr_dump_field_u32_scaled(struct pvr_dump_ctx * const ctx,const char * const name,const uint32_t value,const uint32_t scale)446 static inline void pvr_dump_field_u32_scaled(struct pvr_dump_ctx *const ctx,
447 const char *const name,
448 const uint32_t value,
449 const uint32_t scale)
450 {
451 pvr_dump_field_computed(ctx,
452 name,
453 "%" PRIu32,
454 "%" PRIu32 " x %" PRIu32,
455 value * scale,
456 value,
457 scale);
458 }
459
460 static inline void
pvr_dump_field_u32_scaled_units(struct pvr_dump_ctx * const ctx,const char * const name,const uint32_t value,const uint32_t scale,const char * const units)461 pvr_dump_field_u32_scaled_units(struct pvr_dump_ctx *const ctx,
462 const char *const name,
463 const uint32_t value,
464 const uint32_t scale,
465 const char *const units)
466 {
467 pvr_dump_field_computed(ctx,
468 name,
469 "%" PRIu32 " %s",
470 "%" PRIu32 " x %" PRIu32 " %s",
471 value * scale,
472 units,
473 value,
474 scale,
475 units);
476 }
477
pvr_dump_field_u32_zero(struct pvr_dump_ctx * const ctx,const char * const name,const uint32_t value,const uint32_t zero_value)478 static inline void pvr_dump_field_u32_zero(struct pvr_dump_ctx *const ctx,
479 const char *const name,
480 const uint32_t value,
481 const uint32_t zero_value)
482 {
483 if (value)
484 pvr_dump_field_u32(ctx, name, value);
485 else
486 pvr_dump_field_computed(ctx, name, "%" PRIu32, "0", zero_value);
487 }
488
pvr_dump_field_x32(struct pvr_dump_ctx * const ctx,const char * const name,const uint32_t value,const uint32_t chars)489 static inline void pvr_dump_field_x32(struct pvr_dump_ctx *const ctx,
490 const char *const name,
491 const uint32_t value,
492 const uint32_t chars)
493 {
494 pvr_dump_field(ctx,
495 name,
496 "0x%0*" PRIx32,
497 chars,
498 value & BITFIELD_MASK(chars * 4));
499 }
500
pvr_dump_field_u64(struct pvr_dump_ctx * const ctx,const char * const name,const uint64_t value)501 static inline void pvr_dump_field_u64(struct pvr_dump_ctx *const ctx,
502 const char *const name,
503 const uint64_t value)
504 {
505 pvr_dump_field(ctx, name, "%" PRIu64, value);
506 }
507
pvr_dump_field_u64_units(struct pvr_dump_ctx * const ctx,const char * const name,const uint64_t value,const char * const units)508 static inline void pvr_dump_field_u64_units(struct pvr_dump_ctx *const ctx,
509 const char *const name,
510 const uint64_t value,
511 const char *const units)
512 {
513 pvr_dump_field(ctx, name, "%" PRIu64 " %s", value, units);
514 }
515
516 /*****************************************************************************
517 Field printers: floating point
518 *****************************************************************************/
519
pvr_dump_field_f32(struct pvr_dump_ctx * const ctx,const char * const name,const float value)520 static inline void pvr_dump_field_f32(struct pvr_dump_ctx *const ctx,
521 const char *const name,
522 const float value)
523 {
524 pvr_dump_field_computed(ctx, name, "%f", "0x%08" PRIx32, value, fui(value));
525 }
526
527 /*****************************************************************************
528 Field printers: fixed point
529 *****************************************************************************/
530
531 /* clang-format off */
532 static const char *const __fixed_frac_str_table_4[1 << 4] = {
533 "0", "0625", "125", "1875", "25", "3125", "375", "4375",
534 "5", "5625", "625", "6875", "75", "8125", "875", "9375",
535 };
536 /* clang-format on */
537
pvr_dump_field_uq4_4(struct pvr_dump_ctx * const ctx,const char * const name,const uint32_t raw_value)538 static inline void pvr_dump_field_uq4_4(struct pvr_dump_ctx *const ctx,
539 const char *const name,
540 const uint32_t raw_value)
541 {
542 const uint32_t int_part = (raw_value & BITFIELD_RANGE(4, 4)) >> 4;
543 const uint32_t frac_part = raw_value & BITFIELD_MASK(4);
544
545 pvr_dump_field_computed(ctx,
546 name,
547 "%" PRIu32 ".%s",
548 "0x%02" PRIx32, /* Or %0*x where *=(nr_bits+3)/4 */
549 int_part,
550 __fixed_frac_str_table_4[frac_part],
551 raw_value & BITFIELD_MASK(8));
552 }
553
pvr_dump_field_uq4_4_offset(struct pvr_dump_ctx * const ctx,const char * const name,const uint32_t raw_value,const uint32_t raw_offset)554 static inline void pvr_dump_field_uq4_4_offset(struct pvr_dump_ctx *const ctx,
555 const char *const name,
556 const uint32_t raw_value,
557 const uint32_t raw_offset)
558 {
559 const uint32_t raw_offset_value = raw_value + raw_offset;
560
561 const uint32_t int_part = (raw_offset_value & BITFIELD_RANGE(4, 4)) >> 4;
562 const uint32_t frac_part = raw_offset_value & BITFIELD_MASK(4);
563
564 pvr_dump_field_computed(ctx,
565 name,
566 "%" PRIu32 ".%s",
567 "0x%02" PRIx32 " + 0x%02" PRIx32,
568 int_part,
569 __fixed_frac_str_table_4[frac_part],
570 raw_value & BITFIELD_MASK(8),
571 raw_offset);
572 }
573
574 /*****************************************************************************
575 Field printers: device address
576 *****************************************************************************/
577
pvr_dump_field_addr_non_null(struct pvr_dump_ctx * const ctx,const char * const name,const pvr_dev_addr_t value)578 static inline void pvr_dump_field_addr_non_null(struct pvr_dump_ctx *const ctx,
579 const char *const name,
580 const pvr_dev_addr_t value)
581 {
582 pvr_dump_field(ctx, name, PVR_DEV_ADDR_FMT, value.addr);
583 }
584
pvr_dump_field_addr(struct pvr_dump_ctx * const ctx,const char * const name,const pvr_dev_addr_t value)585 static inline void pvr_dump_field_addr(struct pvr_dump_ctx *const ctx,
586 const char *const name,
587 const pvr_dev_addr_t value)
588 {
589 if (value.addr)
590 pvr_dump_field_addr_non_null(ctx, name, value);
591 else
592 pvr_dump_field(ctx, name, "<null>");
593 }
594
pvr_dump_field_addr_split(struct pvr_dump_ctx * const ctx,const char * const name,const pvr_dev_addr_t msb,const pvr_dev_addr_t lsb)595 static inline void pvr_dump_field_addr_split(struct pvr_dump_ctx *const ctx,
596 const char *const name,
597 const pvr_dev_addr_t msb,
598 const pvr_dev_addr_t lsb)
599 {
600 pvr_dump_field_addr(ctx, name, PVR_DEV_ADDR(msb.addr | lsb.addr));
601
602 pvr_dump_indent(ctx);
603 pvr_dump_field_addr_non_null(ctx, "msb", msb);
604 pvr_dump_field_addr_non_null(ctx, "lsb", lsb);
605 pvr_dump_dedent(ctx);
606 }
607
pvr_dump_field_addr_offset(struct pvr_dump_ctx * const ctx,const char * const name,const pvr_dev_addr_t value,const pvr_dev_addr_t base)608 static inline void pvr_dump_field_addr_offset(struct pvr_dump_ctx *const ctx,
609 const char *const name,
610 const pvr_dev_addr_t value,
611 const pvr_dev_addr_t base)
612 {
613 pvr_dump_field_computed(ctx,
614 name,
615 PVR_DEV_ADDR_FMT,
616 PVR_DEV_ADDR_FMT " + " PVR_DEV_ADDR_FMT,
617 PVR_DEV_ADDR_OFFSET(base, value.addr).addr,
618 base.addr,
619 value.addr);
620 }
621
622 /*****************************************************************************
623 Field printers: enums
624 *****************************************************************************/
625
626 #define pvr_dump_field_enum(ctx, name, value, to_str) \
627 do { \
628 __typeof__(value) _value = (value); \
629 const char *_str = to_str(_value); \
630 if (!_str) \
631 _str = "<unknown>"; \
632 pvr_dump_field_computed(ctx, name, "%s", "%u", _str, _value); \
633 } while (0)
634
__bool_to_str(const bool b)635 static inline const char *__bool_to_str(const bool b)
636 {
637 return b ? "yes" : "no";
638 }
639
640 /* A bool is just an enum with two values. */
pvr_dump_field_bool(struct pvr_dump_ctx * const ctx,const char * const name,const bool value)641 static inline void pvr_dump_field_bool(struct pvr_dump_ctx *const ctx,
642 const char *const name,
643 const bool value)
644 {
645 pvr_dump_field_enum(ctx, name, value, __bool_to_str);
646 }
647
648 /*****************************************************************************
649 Field printers: string
650 *****************************************************************************/
651
pvr_dump_field_string(struct pvr_dump_ctx * const ctx,const char * const name,const char * const value)652 static inline void pvr_dump_field_string(struct pvr_dump_ctx *const ctx,
653 const char *const name,
654 const char *const value)
655 {
656 pvr_dump_field(ctx, name, "%s", value);
657 }
658
659 /*****************************************************************************
660 Field printers: not present
661 *****************************************************************************/
662
pvr_dump_field_no_fields(struct pvr_dump_ctx * const ctx)663 static inline void pvr_dump_field_no_fields(struct pvr_dump_ctx *const ctx)
664 {
665 pvr_dump_println(ctx, "<no fields>");
666 }
667
pvr_dump_field_not_present(struct pvr_dump_ctx * const ctx,const char * const name)668 static inline void pvr_dump_field_not_present(struct pvr_dump_ctx *const ctx,
669 const char *const name)
670 {
671 pvr_dump_field(ctx, name, "<not present>");
672 }
673
674 /*****************************************************************************
675 Field printers: helpers for members
676 *****************************************************************************/
677
678 /* clang-format off */
679
680 #define pvr_dump_field_member_u32(ctx, compound, member) \
681 pvr_dump_field_u32(ctx, #member, (compound)->member)
682
683 #define pvr_dump_field_member_u32_units(ctx, compound, member, units) \
684 pvr_dump_field_u32_units(ctx, #member, (compound)->member, units)
685
686 #define pvr_dump_field_member_u32_offset(ctx, compound, member, offset) \
687 pvr_dump_field_u32_offset(ctx, #member, (compound)->member, offset)
688
689 #define pvr_dump_field_member_u32_scaled(ctx, compound, member, scale) \
690 pvr_dump_field_u32_scaled(ctx, #member, (compound)->member, scale)
691
692 #define pvr_dump_field_member_u32_scaled_units(ctx, compound, member, scale, units) \
693 pvr_dump_field_u32_scaled_units(ctx, #member, (compound)->member, scale, units)
694
695 #define pvr_dump_field_member_u32_zero(ctx, compound, member, zero_value) \
696 pvr_dump_field_u32_zero(ctx, #member, (compound)->member, zero_value)
697
698 #define pvr_dump_field_member_x32(ctx, compound, member, chars) \
699 pvr_dump_field_x32(ctx, #member, (compound)->member, chars)
700
701 #define pvr_dump_field_member_u64(ctx, compound, member) \
702 pvr_dump_field_u64(ctx, #member, (compound)->member)
703
704 #define pvr_dump_field_member_u64_units(ctx, compound, member, units) \
705 pvr_dump_field_u64_units(ctx, #member, (compound)->member, units)
706
707 #define pvr_dump_field_member_f32(ctx, compound, member) \
708 pvr_dump_field_f32(ctx, #member, (compound)->member)
709
710 #define pvr_dump_field_member_uq4_4(ctx, compound, member) \
711 pvr_dump_field_uq4_4(ctx, #member, (compound)->member)
712
713 #define pvr_dump_field_member_uq4_4_offset(ctx, compound, member, raw_offset) \
714 pvr_dump_field_uq4_4_offset(ctx, #member, (compound)->member, raw_offset)
715
716 #define pvr_dump_field_member_addr(ctx, compound, member) \
717 pvr_dump_field_addr(ctx, #member, (compound)->member)
718
719 #define pvr_dump_field_member_addr_offset(ctx, compound, member, base) \
720 pvr_dump_field_addr_offset(ctx, #member, (compound)->member, base)
721
722 #define pvr_dump_field_member_enum(ctx, compound, member, to_str) \
723 pvr_dump_field_enum(ctx, #member, (compound)->member, to_str)
724
725 #define pvr_dump_field_member_bool(ctx, compound, member) \
726 pvr_dump_field_bool(ctx, #member, (compound)->member)
727
728 #define pvr_dump_field_member_string(ctx, compound, member) \
729 pvr_dump_field_string(ctx, #member, (compound)->member)
730
731 /* clang-format on */
732
733 #define pvr_dump_field_member_not_present(ctx, compound, member) \
734 do { \
735 (void)&(compound)->member; \
736 pvr_dump_field_not_present(ctx, #member); \
737 } while (0)
738
739 #endif /* PVR_DUMP_H */
740