freedreno/rddecompiler: Add shader disasm/asm support
Now rddecompiler outputs shaders in editable assembly form. Signed-off-by: Danylo Piliaiev <dpiliaiev@igalia.com> Part-of: <https://gitlab.freedesktop.org/mesa/mesa/-/merge_requests/19444>
This commit is contained in:
committed by
Marge Bot
parent
03d80e0a6d
commit
a7773c3c4c
@@ -30,4 +30,7 @@ generate_rd = executable(
|
|||||||
link_with: [
|
link_with: [
|
||||||
mesa_proj.get_variable('libfreedreno_cffdec'),
|
mesa_proj.get_variable('libfreedreno_cffdec'),
|
||||||
],
|
],
|
||||||
|
dependencies : [
|
||||||
|
mesa_proj.get_variable('idep_nir'),
|
||||||
|
],
|
||||||
)
|
)
|
||||||
@@ -22,6 +22,10 @@
|
|||||||
|
|
||||||
#include "a6xx.xml.h"
|
#include "a6xx.xml.h"
|
||||||
|
|
||||||
|
#include "ir3/ir3_assembler.h"
|
||||||
|
#include "ir3/ir3_compiler.h"
|
||||||
|
#include "ir3/ir3_shader.h"
|
||||||
|
|
||||||
#include "util/list.h"
|
#include "util/list.h"
|
||||||
#include "util/vma.h"
|
#include "util/vma.h"
|
||||||
|
|
||||||
@@ -35,14 +39,27 @@ struct cmdstream {
|
|||||||
uint64_t iova;
|
uint64_t iova;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static uint64_t
|
||||||
|
cs_get_cur_iova(struct cmdstream *cs)
|
||||||
|
{
|
||||||
|
return cs->iova + cs->cur * sizeof(uint32_t);
|
||||||
|
}
|
||||||
|
|
||||||
struct replay_context {
|
struct replay_context {
|
||||||
|
void *mem_ctx;
|
||||||
|
|
||||||
struct util_vma_heap vma;
|
struct util_vma_heap vma;
|
||||||
|
|
||||||
struct cmdstream *submit_cs;
|
struct cmdstream *submit_cs;
|
||||||
struct cmdstream *state_cs;
|
struct cmdstream *state_cs;
|
||||||
|
struct cmdstream *shader_cs;
|
||||||
|
|
||||||
struct list_head cs_list;
|
struct list_head cs_list;
|
||||||
|
|
||||||
|
struct ir3_compiler *compiler;
|
||||||
|
|
||||||
|
struct hash_table_u64 *compiled_shaders;
|
||||||
|
|
||||||
const char *output_name;
|
const char *output_name;
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -60,6 +77,18 @@ pkt_qw(struct cmdstream *cs, uint64_t payload)
|
|||||||
pkt(cs, payload >> 32);
|
pkt(cs, payload >> 32);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static uint64_t
|
||||||
|
pkt_blob(struct cmdstream *cs, void *payload, uint32_t size, uint32_t alignment)
|
||||||
|
{
|
||||||
|
cs->cur = align(cs->cur, alignment / sizeof(uint32_t));
|
||||||
|
uint64_t start_iova = cs_get_cur_iova(cs);
|
||||||
|
|
||||||
|
memcpy(cs->mem + cs->cur, payload, size);
|
||||||
|
cs->cur += size;
|
||||||
|
|
||||||
|
return start_iova;
|
||||||
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
pkt4(struct cmdstream *cs, uint16_t regindx, uint16_t cnt, uint32_t payload)
|
pkt4(struct cmdstream *cs, uint16_t regindx, uint16_t cnt, uint32_t payload)
|
||||||
{
|
{
|
||||||
@@ -94,12 +123,6 @@ cs_alloc(struct replay_context *ctx, uint32_t size)
|
|||||||
return cs;
|
return cs;
|
||||||
}
|
}
|
||||||
|
|
||||||
static uint64_t
|
|
||||||
cs_get_cur_iova(struct cmdstream *cs)
|
|
||||||
{
|
|
||||||
return cs->iova + cs->cur * sizeof(uint32_t);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
static void
|
||||||
rd_write_cs_buffer(FILE *out, struct cmdstream *cs)
|
rd_write_cs_buffer(FILE *out, struct cmdstream *cs)
|
||||||
{
|
{
|
||||||
@@ -159,7 +182,8 @@ static const struct option opts[] = {
|
|||||||
/* clang-format on */
|
/* clang-format on */
|
||||||
|
|
||||||
static void
|
static void
|
||||||
replay_context_init(struct replay_context *ctx, int argc, char **argv)
|
replay_context_init(struct replay_context *ctx, struct fd_dev_id *dev_id,
|
||||||
|
int argc, char **argv)
|
||||||
{
|
{
|
||||||
uint64_t va_start = 0;
|
uint64_t va_start = 0;
|
||||||
uint64_t va_size = 0;
|
uint64_t va_size = 0;
|
||||||
@@ -189,12 +213,18 @@ replay_context_init(struct replay_context *ctx, int argc, char **argv)
|
|||||||
exit(1);
|
exit(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ctx->mem_ctx = ralloc_context(NULL);
|
||||||
list_inithead(&ctx->cs_list);
|
list_inithead(&ctx->cs_list);
|
||||||
|
|
||||||
util_vma_heap_init(&ctx->vma, va_start, ROUND_DOWN_TO(va_size, 4096));
|
util_vma_heap_init(&ctx->vma, va_start, ROUND_DOWN_TO(va_size, 4096));
|
||||||
|
|
||||||
ctx->submit_cs = cs_alloc(ctx, 1024 * 1024);
|
ctx->submit_cs = cs_alloc(ctx, 1024 * 1024);
|
||||||
ctx->state_cs = cs_alloc(ctx, 2 * 1024 * 1024);
|
ctx->state_cs = cs_alloc(ctx, 2 * 1024 * 1024);
|
||||||
|
ctx->shader_cs = cs_alloc(ctx, 8 * 1024 * 1024);
|
||||||
|
|
||||||
|
ctx->compiler =
|
||||||
|
ir3_compiler_create(NULL, dev_id, &(struct ir3_compiler_options){});
|
||||||
|
ctx->compiled_shaders = _mesa_hash_table_u64_create(ctx->mem_ctx);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
@@ -219,6 +249,33 @@ replay_context_finish(struct replay_context *ctx)
|
|||||||
fclose(out);
|
fclose(out);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
upload_shader(struct replay_context *ctx, uint64_t id, const char *source)
|
||||||
|
{
|
||||||
|
FILE *in = fmemopen((void *)source, strlen(source), "r");
|
||||||
|
|
||||||
|
struct ir3_kernel_info info = {};
|
||||||
|
struct ir3_shader *shader = ir3_parse_asm(ctx->compiler, &info, in);
|
||||||
|
assert(shader);
|
||||||
|
|
||||||
|
fclose(in);
|
||||||
|
|
||||||
|
uint64_t *shader_iova = ralloc(ctx->mem_ctx, uint64_t);
|
||||||
|
*shader_iova = pkt_blob(ctx->shader_cs, shader->variants->bin,
|
||||||
|
shader->variants->info.size, 128);
|
||||||
|
ralloc_free(shader);
|
||||||
|
|
||||||
|
_mesa_hash_table_u64_insert(ctx->compiled_shaders, id, shader_iova);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
emit_shader_iova(struct replay_context *ctx, struct cmdstream *cs, uint64_t id)
|
||||||
|
{
|
||||||
|
uint64_t *shader_iova =
|
||||||
|
_mesa_hash_table_u64_search(ctx->compiled_shaders, id);
|
||||||
|
pkt_qw(cs, *shader_iova);
|
||||||
|
}
|
||||||
|
|
||||||
#define begin_draw_state() \
|
#define begin_draw_state() \
|
||||||
uint64_t subcs_iova_start = cs_get_cur_iova(ctx.state_cs); \
|
uint64_t subcs_iova_start = cs_get_cur_iova(ctx.state_cs); \
|
||||||
struct cmdstream *prev_cs = cs; \
|
struct cmdstream *prev_cs = cs; \
|
||||||
|
|||||||
@@ -29,12 +29,17 @@
|
|||||||
#include "freedreno_pm4.h"
|
#include "freedreno_pm4.h"
|
||||||
|
|
||||||
#include "a6xx.xml.h"
|
#include "a6xx.xml.h"
|
||||||
|
#include "common/freedreno_dev_info.h"
|
||||||
|
|
||||||
|
#include "util/hash_table.h"
|
||||||
#include "util/os_time.h"
|
#include "util/os_time.h"
|
||||||
|
#include "util/ralloc.h"
|
||||||
#include "util/rb_tree.h"
|
#include "util/rb_tree.h"
|
||||||
|
#include "util/set.h"
|
||||||
#include "util/u_vector.h"
|
#include "util/u_vector.h"
|
||||||
#include "buffers.h"
|
#include "buffers.h"
|
||||||
#include "cffdec.h"
|
#include "cffdec.h"
|
||||||
|
#include "disasm.h"
|
||||||
#include "io.h"
|
#include "io.h"
|
||||||
#include "rdutil.h"
|
#include "rdutil.h"
|
||||||
#include "redump.h"
|
#include "redump.h"
|
||||||
@@ -148,6 +153,10 @@ main(int argc, char **argv)
|
|||||||
}
|
}
|
||||||
|
|
||||||
static struct rnn *rnn;
|
static struct rnn *rnn;
|
||||||
|
static struct fd_dev_id dev_id;
|
||||||
|
static void *mem_ctx;
|
||||||
|
|
||||||
|
static struct set decompiled_shaders;
|
||||||
|
|
||||||
static void
|
static void
|
||||||
init_rnn(const char *gpuname)
|
init_rnn(const char *gpuname)
|
||||||
@@ -162,11 +171,71 @@ pktname(unsigned opc)
|
|||||||
return rnn_enumname(rnn, "adreno_pm4_type3_packets", opc);
|
return rnn_enumname(rnn, "adreno_pm4_type3_packets", opc);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static uint32_t
|
||||||
decompile_register(uint32_t regbase, uint32_t dword, uint16_t cnt, int level)
|
decompile_shader(const char *name, uint32_t regbase, uint32_t *dwords, int level)
|
||||||
|
{
|
||||||
|
uint64_t gpuaddr = ((uint64_t)dwords[1] << 32) | dwords[0];
|
||||||
|
gpuaddr &= 0xfffffffffffffff0;
|
||||||
|
|
||||||
|
/* Shader's iova is referenced in two places, so we have to remember it. */
|
||||||
|
if (_mesa_set_search(&decompiled_shaders, &gpuaddr)) {
|
||||||
|
printlvl(level, "emit_shader_iova(&ctx, cs, 0x%" PRIx64 ");\n", gpuaddr);
|
||||||
|
} else {
|
||||||
|
uint64_t *key = ralloc(mem_ctx, uint64_t);
|
||||||
|
*key = gpuaddr;
|
||||||
|
_mesa_set_add(&decompiled_shaders, key);
|
||||||
|
|
||||||
|
void *buf = hostptr(gpuaddr);
|
||||||
|
assert(buf);
|
||||||
|
|
||||||
|
uint32_t sizedwords = hostlen(gpuaddr) / 4;
|
||||||
|
|
||||||
|
char *stream_data = NULL;
|
||||||
|
size_t stream_size = 0;
|
||||||
|
FILE *stream = open_memstream(&stream_data, &stream_size);
|
||||||
|
|
||||||
|
try_disasm_a3xx(buf, sizedwords, 0, stream, dev_id.gpu_id);
|
||||||
|
fclose(stream);
|
||||||
|
|
||||||
|
printlvl(level, "{\n");
|
||||||
|
printlvl(level + 1, "const char *source = R\"(\n");
|
||||||
|
printf("%s", stream_data);
|
||||||
|
printlvl(level + 1, ")\";\n");
|
||||||
|
printlvl(level + 1, "upload_shader(&ctx, 0x%" PRIx64 ", source);\n", gpuaddr);
|
||||||
|
printlvl(level + 1, "emit_shader_iova(&ctx, cs, 0x%" PRIx64 ");\n", gpuaddr);
|
||||||
|
printlvl(level, "}\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
return 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct {
|
||||||
|
uint32_t regbase;
|
||||||
|
uint32_t (*fxn)(const char *name, uint32_t regbase, uint32_t *dwords, int level);
|
||||||
|
} reg_a6xx[] = {
|
||||||
|
{REG_A6XX_SP_VS_OBJ_START, decompile_shader},
|
||||||
|
{REG_A6XX_SP_HS_OBJ_START, decompile_shader},
|
||||||
|
{REG_A6XX_SP_DS_OBJ_START, decompile_shader},
|
||||||
|
{REG_A6XX_SP_GS_OBJ_START, decompile_shader},
|
||||||
|
{REG_A6XX_SP_FS_OBJ_START, decompile_shader},
|
||||||
|
{REG_A6XX_SP_CS_OBJ_START, decompile_shader},
|
||||||
|
|
||||||
|
{0, NULL},
|
||||||
|
}, *type0_reg;
|
||||||
|
|
||||||
|
static uint32_t
|
||||||
|
decompile_register(uint32_t regbase, uint32_t *dwords, uint16_t cnt, int level)
|
||||||
{
|
{
|
||||||
struct rnndecaddrinfo *info = rnn_reginfo(rnn, regbase);
|
struct rnndecaddrinfo *info = rnn_reginfo(rnn, regbase);
|
||||||
|
|
||||||
|
for (unsigned idx = 0; type0_reg[idx].regbase; idx++) {
|
||||||
|
if (type0_reg[idx].regbase == regbase) {
|
||||||
|
return type0_reg[idx].fxn(info->name, regbase, dwords, level);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const uint32_t dword = *dwords;
|
||||||
|
|
||||||
if (info && info->typeinfo) {
|
if (info && info->typeinfo) {
|
||||||
char *decoded = rnndec_decodeval(rnn->vc, info->typeinfo, dword);
|
char *decoded = rnndec_decodeval(rnn->vc, info->typeinfo, dword);
|
||||||
printlvl(level, "/* pkt4: %s = %s */\n", info->name, decoded);
|
printlvl(level, "/* pkt4: %s = %s */\n", info->name, decoded);
|
||||||
@@ -195,6 +264,8 @@ decompile_register(uint32_t regbase, uint32_t dword, uint16_t cnt, int level)
|
|||||||
printlvl(level, "/* unknown pkt4 */\n");
|
printlvl(level, "/* unknown pkt4 */\n");
|
||||||
printlvl(level, "pkt4(cs, %u, (%u), %u);\n", regbase, cnt, dword);
|
printlvl(level, "pkt4(cs, %u, (%u), %u);\n", regbase, cnt, dword);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
@@ -203,17 +274,19 @@ decompile_registers(uint32_t regbase, uint32_t *dwords, uint32_t sizedwords,
|
|||||||
{
|
{
|
||||||
if (!sizedwords)
|
if (!sizedwords)
|
||||||
return;
|
return;
|
||||||
decompile_register(regbase, *dwords, sizedwords--, level);
|
uint32_t consumed = decompile_register(regbase, dwords, sizedwords, level);
|
||||||
while (sizedwords--) {
|
sizedwords -= consumed;
|
||||||
regbase++;
|
while (sizedwords > 0) {
|
||||||
dwords++;
|
regbase += consumed;
|
||||||
decompile_register(regbase, *dwords, 0, level);
|
dwords += consumed;
|
||||||
|
consumed = decompile_register(regbase, dwords, 0, level);
|
||||||
|
sizedwords -= consumed;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
decompile_domain(uint32_t *dwords, uint32_t sizedwords, const char *dom_name,
|
decompile_domain(uint32_t pkt, uint32_t *dwords, uint32_t sizedwords,
|
||||||
const char *packet_name, int level)
|
const char *dom_name, const char *packet_name, int level)
|
||||||
{
|
{
|
||||||
struct rnndomain *dom;
|
struct rnndomain *dom;
|
||||||
int i;
|
int i;
|
||||||
@@ -222,6 +295,22 @@ decompile_domain(uint32_t *dwords, uint32_t sizedwords, const char *dom_name,
|
|||||||
|
|
||||||
printlvl(level, "pkt7(cs, %s, %u);\n", packet_name, sizedwords);
|
printlvl(level, "pkt7(cs, %s, %u);\n", packet_name, sizedwords);
|
||||||
|
|
||||||
|
if (pkt == CP_LOAD_STATE6_FRAG || pkt == CP_LOAD_STATE6_GEOM) {
|
||||||
|
enum a6xx_state_type state_type =
|
||||||
|
(dwords[0] & CP_LOAD_STATE6_0_STATE_TYPE__MASK) >>
|
||||||
|
CP_LOAD_STATE6_0_STATE_TYPE__SHIFT;
|
||||||
|
enum a6xx_state_src state_src =
|
||||||
|
(dwords[0] & CP_LOAD_STATE6_0_STATE_SRC__MASK) >>
|
||||||
|
CP_LOAD_STATE6_0_STATE_SRC__SHIFT;
|
||||||
|
|
||||||
|
/* TODO: decompile all other state */
|
||||||
|
if (state_type == ST6_SHADER && state_src == SS6_INDIRECT) {
|
||||||
|
printlvl(level, "pkt(cs, %u);\n", dwords[0]);
|
||||||
|
decompile_shader(NULL, 0, dwords + 1, level);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
for (i = 0; i < sizedwords; i++) {
|
for (i = 0; i < sizedwords; i++) {
|
||||||
struct rnndecaddrinfo *info = NULL;
|
struct rnndecaddrinfo *info = NULL;
|
||||||
if (dom) {
|
if (dom) {
|
||||||
@@ -300,7 +389,7 @@ decompile_commands(uint32_t *dwords, uint32_t sizedwords, int level)
|
|||||||
printlvl(level + 1, "end_draw_state(%u);\n", unchanged);
|
printlvl(level + 1, "end_draw_state(%u);\n", unchanged);
|
||||||
printlvl(level, "}\n");
|
printlvl(level, "}\n");
|
||||||
} else {
|
} else {
|
||||||
decompile_domain(dwords + i, 3, "CP_SET_DRAW_STATE",
|
decompile_domain(val, dwords + i, 3, "CP_SET_DRAW_STATE",
|
||||||
"CP_SET_DRAW_STATE", level);
|
"CP_SET_DRAW_STATE", level);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -314,7 +403,7 @@ decompile_commands(uint32_t *dwords, uint32_t sizedwords, int level)
|
|||||||
if (!strcmp(packet_name, "CP_LOAD_STATE6_FRAG") ||
|
if (!strcmp(packet_name, "CP_LOAD_STATE6_FRAG") ||
|
||||||
!strcmp(packet_name, "CP_LOAD_STATE6_GEOM"))
|
!strcmp(packet_name, "CP_LOAD_STATE6_GEOM"))
|
||||||
dom_name = "CP_LOAD_STATE6";
|
dom_name = "CP_LOAD_STATE6";
|
||||||
decompile_domain(dwords + 1, count - 1, dom_name, packet_name,
|
decompile_domain(val, dwords + 1, count - 1, dom_name, packet_name,
|
||||||
level);
|
level);
|
||||||
} else {
|
} else {
|
||||||
errx(1, "unknown pkt7 %u", val);
|
errx(1, "unknown pkt7 %u", val);
|
||||||
@@ -332,6 +421,39 @@ decompile_commands(uint32_t *dwords, uint32_t sizedwords, int level)
|
|||||||
fprintf(stderr, "**** this ain't right!! dwords_left=%d\n", dwords_left);
|
fprintf(stderr, "**** this ain't right!! dwords_left=%d\n", dwords_left);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
emit_header()
|
||||||
|
{
|
||||||
|
if (!dev_id.gpu_id || !dev_id.chip_id)
|
||||||
|
return;
|
||||||
|
|
||||||
|
static bool emitted = false;
|
||||||
|
if (emitted)
|
||||||
|
return;
|
||||||
|
emitted = true;
|
||||||
|
|
||||||
|
printf("#include \"decode/rdcompiler-utils.h\"\n"
|
||||||
|
"int main(int argc, char **argv)\n"
|
||||||
|
"{\n"
|
||||||
|
"\tstruct replay_context ctx;\n"
|
||||||
|
"\tstruct fd_dev_id dev_id = {%u, %" PRIu64 "};\n"
|
||||||
|
"\treplay_context_init(&ctx, &dev_id, argc, argv);\n"
|
||||||
|
"\tstruct cmdstream *cs = ctx.submit_cs;\n\n",
|
||||||
|
dev_id.gpu_id, dev_id.chip_id);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline uint32_t
|
||||||
|
u64_hash(const void *key)
|
||||||
|
{
|
||||||
|
return _mesa_hash_data(key, sizeof(uint64_t));
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline bool
|
||||||
|
u64_compare(const void *key1, const void *key2)
|
||||||
|
{
|
||||||
|
return memcmp(key1, key2, sizeof(uint64_t)) == 0;
|
||||||
|
}
|
||||||
|
|
||||||
static int
|
static int
|
||||||
handle_file(const char *filename, uint32_t submit_to_decompile)
|
handle_file(const char *filename, uint32_t submit_to_decompile)
|
||||||
{
|
{
|
||||||
@@ -351,13 +473,9 @@ handle_file(const char *filename, uint32_t submit_to_decompile)
|
|||||||
}
|
}
|
||||||
|
|
||||||
init_rnn("a6xx");
|
init_rnn("a6xx");
|
||||||
|
type0_reg = reg_a6xx;
|
||||||
printf("#include \"decode/rdcompiler-utils.h\"\n"
|
mem_ctx = ralloc_context(NULL);
|
||||||
"int main(int argc, char **argv)\n"
|
_mesa_set_init(&decompiled_shaders, mem_ctx, u64_hash, u64_compare);
|
||||||
"{\n"
|
|
||||||
"\tstruct replay_context ctx;\n"
|
|
||||||
"\treplay_context_init(&ctx, argc, argv);\n"
|
|
||||||
"\tstruct cmdstream *cs = ctx.submit_cs;\n\n");
|
|
||||||
|
|
||||||
struct {
|
struct {
|
||||||
unsigned int len;
|
unsigned int len;
|
||||||
@@ -369,8 +487,6 @@ handle_file(const char *filename, uint32_t submit_to_decompile)
|
|||||||
case RD_TEST:
|
case RD_TEST:
|
||||||
case RD_VERT_SHADER:
|
case RD_VERT_SHADER:
|
||||||
case RD_FRAG_SHADER:
|
case RD_FRAG_SHADER:
|
||||||
case RD_GPU_ID:
|
|
||||||
case RD_CHIP_ID:
|
|
||||||
case RD_CMD:
|
case RD_CMD:
|
||||||
/* no-op */
|
/* no-op */
|
||||||
break;
|
break;
|
||||||
@@ -398,6 +514,16 @@ handle_file(const char *filename, uint32_t submit_to_decompile)
|
|||||||
submit++;
|
submit++;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
case RD_GPU_ID: {
|
||||||
|
dev_id.gpu_id = parse_gpu_id(ps.buf);
|
||||||
|
emit_header();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case RD_CHIP_ID: {
|
||||||
|
dev_id.chip_id = *(uint64_t *)ps.buf;
|
||||||
|
emit_header();
|
||||||
|
break;
|
||||||
|
}
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user