-
-
Save apkunpacker/11b881193020fe2a2de0f84725c7fa90 to your computer and use it in GitHub Desktop.
Standalone Device ID provisioning utility for Qualcomm Keymaster
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| /* | |
| * provision_device_ids.c | |
| * Copyright (c) 2026 mhmrdd. All rights reserved. | |
| * | |
| * Standalone Device ID provisioning utility for Qualcomm Keymaster. | |
| * | |
| * This tool provisions Android attestation identifiers (brand, device, | |
| * product, serial, IMEI/MEID, manufacturer, model) into secure storage | |
| * through QSEECom and finalizes Device ID provisioning state. | |
| * | |
| * Runtime sequence: | |
| * 1) GET_VERSION (0x0200) | |
| * 2) SET_VERSION (0x0207) | |
| * 3) PROVISION_DEVICE_IDS (0x220A) | |
| * 4) SET_PROVISIONING_DEVICE_ID_SUCCESS (0x2218) | |
| * | |
| * Build (Android NDK): | |
| * $NDK/toolchains/llvm/prebuilt/linux-x86_64/bin/aarch64-linux-android31-clang \ | |
| * -O2 -Wall -Wextra -o provision_device_ids provision_device_ids.c -ldl | |
| */ | |
| #include <dlfcn.h> | |
| #include <stdint.h> | |
| #include <stdio.h> | |
| #include <stdlib.h> | |
| #include <string.h> | |
| #include <unistd.h> | |
| #define KM_TAG_ATTESTATION_ID_BRAND 0x900002C6u | |
| #define KM_TAG_ATTESTATION_ID_DEVICE 0x900002C7u | |
| #define KM_TAG_ATTESTATION_ID_PRODUCT 0x900002C8u | |
| #define KM_TAG_ATTESTATION_ID_SERIAL 0x900002C9u | |
| #define KM_TAG_ATTESTATION_ID_IMEI 0x900002CAu | |
| #define KM_TAG_ATTESTATION_ID_MEID 0x900002CBu | |
| #define KM_TAG_ATTESTATION_ID_MANUFACTURER 0x900002CCu | |
| #define KM_TAG_ATTESTATION_ID_MODEL 0x900002CDu | |
| #define CMD_GET_VERSION 0x0200u | |
| #define CMD_SET_VERSION 0x0207u | |
| #define CMD_PROVISION_DEVICE_IDS 0x220Au | |
| #define CMD_SET_PROVISIONING_DEVICE_ID_SUCCESS 0x2218u | |
| #define SHARED_BUF_SIZE 0xA000u | |
| #define MAX_PROP_LEN 128 | |
| #define MAX_IDS 10 | |
| #define DEFAULT_LIB_PATH "/vendor/lib64/libQSEEComAPI.so" | |
| #define DEFAULT_LIB_PATH_ALT "/vendor/lib64/hw/libQSEEComAPI.so" | |
| #define DEFAULT_TA_PATH "/vendor/firmware_mnt/image" | |
| #define DEFAULT_TA_NAME "keymaster64" | |
| struct QSEECom_handle { | |
| unsigned char *ion_sbuffer; | |
| }; | |
| typedef int (*fn_start_app)(struct QSEECom_handle **, const char *, const char *, uint32_t); | |
| typedef int (*fn_send_cmd)(struct QSEECom_handle *, void *, uint32_t, void *, uint32_t); | |
| typedef int (*fn_shutdown_app)(struct QSEECom_handle **); | |
| struct device_id { | |
| uint32_t tag; | |
| char value[MAX_PROP_LEN]; | |
| }; | |
| struct km_version { | |
| uint32_t ta_api_major; | |
| uint32_t ta_api_minor; | |
| uint32_t ta_major; | |
| uint32_t ta_minor; | |
| }; | |
| struct km_cmd_response { | |
| int32_t status; | |
| uint32_t data_len; | |
| const uint8_t *data; | |
| }; | |
| static fn_start_app qseecom_start_app; | |
| static fn_send_cmd qseecom_send_cmd; | |
| static fn_shutdown_app qseecom_shutdown_app; | |
| static void *g_qseecom_lib; | |
| static char g_loaded_lib_path[256]; | |
| static struct device_id g_ids[MAX_IDS]; | |
| static int g_id_count; | |
| #ifdef __ANDROID__ | |
| extern int __system_property_get(const char *name, char *value); | |
| #else | |
| static int __system_property_get(const char *name, char *value) | |
| { | |
| (void)name; | |
| value[0] = '\0'; | |
| return 0; | |
| } | |
| #endif | |
| static uint32_t align4(uint32_t v) | |
| { | |
| return (v + 3u) & ~3u; | |
| } | |
| static const char *tag_name(uint32_t tag) | |
| { | |
| switch (tag) { | |
| case KM_TAG_ATTESTATION_ID_BRAND: return "BRAND"; | |
| case KM_TAG_ATTESTATION_ID_DEVICE: return "DEVICE"; | |
| case KM_TAG_ATTESTATION_ID_PRODUCT: return "PRODUCT"; | |
| case KM_TAG_ATTESTATION_ID_SERIAL: return "SERIAL"; | |
| case KM_TAG_ATTESTATION_ID_IMEI: return "IMEI"; | |
| case KM_TAG_ATTESTATION_ID_MEID: return "MEID"; | |
| case KM_TAG_ATTESTATION_ID_MANUFACTURER: return "MANUFACTURER"; | |
| case KM_TAG_ATTESTATION_ID_MODEL: return "MODEL"; | |
| default: return "UNKNOWN"; | |
| } | |
| } | |
| static void hexdump(const uint8_t *data, int len) | |
| { | |
| int i; | |
| for (i = 0; i < len; ++i) { | |
| if (i > 0 && (i % 16) == 0) { | |
| printf("\n"); | |
| } | |
| printf("%02X ", data[i]); | |
| } | |
| printf("\n"); | |
| } | |
| static const char *get_prop(const char *name, char *buf) | |
| { | |
| buf[0] = '\0'; | |
| __system_property_get(name, buf); | |
| return buf; | |
| } | |
| static void get_first_prop(const char *const *keys, size_t nkeys, char *out) | |
| { | |
| char tmp[MAX_PROP_LEN]; | |
| size_t i; | |
| out[0] = '\0'; | |
| for (i = 0; i < nkeys; ++i) { | |
| const char *v = get_prop(keys[i], tmp); | |
| if (v[0]) { | |
| strncpy(out, v, MAX_PROP_LEN - 1); | |
| out[MAX_PROP_LEN - 1] = '\0'; | |
| return; | |
| } | |
| } | |
| } | |
| static void add_id(uint32_t tag, const char *value) | |
| { | |
| if (!value || !value[0] || g_id_count >= MAX_IDS) { | |
| return; | |
| } | |
| g_ids[g_id_count].tag = tag; | |
| strncpy(g_ids[g_id_count].value, value, MAX_PROP_LEN - 1); | |
| g_ids[g_id_count].value[MAX_PROP_LEN - 1] = '\0'; | |
| g_id_count++; | |
| } | |
| static int cbor_encode_uint(uint8_t *buf, uint32_t val) | |
| { | |
| if (val <= 23) { | |
| buf[0] = (uint8_t)val; | |
| return 1; | |
| } | |
| if (val <= 0xFF) { | |
| buf[0] = 0x18; | |
| buf[1] = (uint8_t)val; | |
| return 2; | |
| } | |
| if (val <= 0xFFFF) { | |
| buf[0] = 0x19; | |
| buf[1] = (uint8_t)(val >> 8); | |
| buf[2] = (uint8_t)val; | |
| return 3; | |
| } | |
| buf[0] = 0x1A; | |
| buf[1] = (uint8_t)(val >> 24); | |
| buf[2] = (uint8_t)(val >> 16); | |
| buf[3] = (uint8_t)(val >> 8); | |
| buf[4] = (uint8_t)val; | |
| return 5; | |
| } | |
| static int cbor_encode_nint(uint8_t *buf, uint32_t n) | |
| { | |
| if (n <= 23) { | |
| buf[0] = (uint8_t)(0x20 | n); | |
| return 1; | |
| } | |
| if (n <= 0xFF) { | |
| buf[0] = 0x38; | |
| buf[1] = (uint8_t)n; | |
| return 2; | |
| } | |
| if (n <= 0xFFFF) { | |
| buf[0] = 0x39; | |
| buf[1] = (uint8_t)(n >> 8); | |
| buf[2] = (uint8_t)n; | |
| return 3; | |
| } | |
| buf[0] = 0x3A; | |
| buf[1] = (uint8_t)(n >> 24); | |
| buf[2] = (uint8_t)(n >> 16); | |
| buf[3] = (uint8_t)(n >> 8); | |
| buf[4] = (uint8_t)n; | |
| return 5; | |
| } | |
| static int cbor_encode_tag_key(uint8_t *buf, uint32_t tag) | |
| { | |
| int32_t s = (int32_t)tag; | |
| if (s >= 0) { | |
| return cbor_encode_uint(buf, (uint32_t)s); | |
| } | |
| return cbor_encode_nint(buf, (uint32_t)(-1 - s)); | |
| } | |
| static int cbor_encode_bstr(uint8_t *buf, const void *data, size_t len) | |
| { | |
| int hdr; | |
| if (len <= 23) { | |
| buf[0] = (uint8_t)(0x40 | len); | |
| hdr = 1; | |
| } else if (len <= 0xFF) { | |
| buf[0] = 0x58; | |
| buf[1] = (uint8_t)len; | |
| hdr = 2; | |
| } else { | |
| buf[0] = 0x59; | |
| buf[1] = (uint8_t)(len >> 8); | |
| buf[2] = (uint8_t)len; | |
| hdr = 3; | |
| } | |
| memcpy(buf + hdr, data, len); | |
| return hdr + (int)len; | |
| } | |
| static int build_cbor_payload(uint8_t *buf, size_t cap) | |
| { | |
| int i, off = 0; | |
| int map_count = g_id_count + 1; | |
| if (g_id_count == 0) { | |
| return -1; | |
| } | |
| buf[off++] = (uint8_t)(0xA0 | map_count); | |
| off += cbor_encode_uint(buf + off, 22); | |
| off += cbor_encode_uint(buf + off, (uint32_t)g_id_count); | |
| for (i = 0; i < g_id_count; ++i) { | |
| size_t vlen = strlen(g_ids[i].value); | |
| off += cbor_encode_tag_key(buf + off, g_ids[i].tag); | |
| off += cbor_encode_bstr(buf + off, g_ids[i].value, vlen); | |
| if ((size_t)off >= cap - 16) { | |
| return -1; | |
| } | |
| } | |
| return off; | |
| } | |
| static int load_qseecom(void) | |
| { | |
| const char *cands[2]; | |
| int n = 0; | |
| int i; | |
| cands[n++] = DEFAULT_LIB_PATH; | |
| cands[n++] = DEFAULT_LIB_PATH_ALT; | |
| for (i = 0; i < n; ++i) { | |
| g_qseecom_lib = dlopen(cands[i], RTLD_NOW | RTLD_LOCAL); | |
| if (g_qseecom_lib) { | |
| strncpy(g_loaded_lib_path, cands[i], sizeof(g_loaded_lib_path) - 1); | |
| g_loaded_lib_path[sizeof(g_loaded_lib_path) - 1] = '\0'; | |
| break; | |
| } | |
| } | |
| if (!g_qseecom_lib) { | |
| fprintf(stderr, "Failed to load QSEEComAPI from %s or %s. Last error: %s\n", | |
| DEFAULT_LIB_PATH, DEFAULT_LIB_PATH_ALT, dlerror()); | |
| return -1; | |
| } | |
| qseecom_start_app = (fn_start_app)dlsym(g_qseecom_lib, "QSEECom_start_app"); | |
| qseecom_send_cmd = (fn_send_cmd)dlsym(g_qseecom_lib, "QSEECom_send_cmd"); | |
| qseecom_shutdown_app = (fn_shutdown_app)dlsym(g_qseecom_lib, "QSEECom_shutdown_app"); | |
| if (!qseecom_start_app || !qseecom_send_cmd || !qseecom_shutdown_app) { | |
| fprintf(stderr, "Failed resolving QSEECom symbols.\n"); | |
| return -1; | |
| } | |
| return 0; | |
| } | |
| static int start_ta(struct QSEECom_handle **h, const char *ta_path, const char *ta_name) | |
| { | |
| int ret = qseecom_start_app(h, ta_path, ta_name, SHARED_BUF_SIZE); | |
| if (ret == 0 && *h) { | |
| return 0; | |
| } | |
| if (strcmp(ta_name, DEFAULT_TA_NAME) == 0) { | |
| ret = qseecom_start_app(h, ta_path, "keymaster", SHARED_BUF_SIZE); | |
| } | |
| return ret; | |
| } | |
| static int send_raw(struct QSEECom_handle *h, const void *req, uint32_t req_len, uint8_t **rsp_out, uint32_t *rsp_len_out) | |
| { | |
| uint8_t *sbuf; | |
| uint32_t rsp_off = align4(req_len); | |
| uint32_t rsp_len; | |
| int ret; | |
| if (!h || !h->ion_sbuffer || rsp_off >= SHARED_BUF_SIZE) { | |
| return -1; | |
| } | |
| rsp_len = SHARED_BUF_SIZE - rsp_off; | |
| sbuf = h->ion_sbuffer; | |
| memset(sbuf, 0, SHARED_BUF_SIZE); | |
| memcpy(sbuf, req, req_len); | |
| ret = qseecom_send_cmd(h, sbuf, req_len, sbuf + rsp_off, rsp_len); | |
| if (ret != 0) { | |
| return ret; | |
| } | |
| *rsp_out = sbuf + rsp_off; | |
| *rsp_len_out = rsp_len; | |
| return 0; | |
| } | |
| static int km_get_version(struct QSEECom_handle *h, struct km_version *ver) | |
| { | |
| uint32_t req = CMD_GET_VERSION; | |
| uint8_t *rsp; | |
| uint32_t rsp_len; | |
| uint32_t *u32; | |
| int ret = send_raw(h, &req, 4, &rsp, &rsp_len); | |
| if (ret != 0 || rsp_len < 20) { | |
| return ret ? ret : -1; | |
| } | |
| u32 = (uint32_t *)rsp; | |
| if ((int32_t)u32[0] != 0) { | |
| return (int32_t)u32[0]; | |
| } | |
| ver->ta_api_major = u32[1]; | |
| ver->ta_api_minor = u32[2]; | |
| ver->ta_major = u32[3]; | |
| ver->ta_minor = u32[4]; | |
| return 0; | |
| } | |
| static int km_set_version(struct QSEECom_handle *h) | |
| { | |
| uint32_t req[6] = {CMD_SET_VERSION, 4, 5, 4, 5, 0}; | |
| uint8_t *rsp; | |
| uint32_t rsp_len; | |
| int32_t status; | |
| int ret = send_raw(h, req, sizeof(req), &rsp, &rsp_len); | |
| if (ret != 0 || rsp_len < 4) { | |
| return ret ? ret : -1; | |
| } | |
| memcpy(&status, rsp, 4); | |
| return status; | |
| } | |
| static int send_km_status(struct QSEECom_handle *h, const void *req, uint32_t req_len) | |
| { | |
| uint8_t *rsp; | |
| uint32_t rsp_len; | |
| int32_t status; | |
| int ret = send_raw(h, req, req_len, &rsp, &rsp_len); | |
| if (ret != 0 || rsp_len < 4) { | |
| return ret ? ret : -1; | |
| } | |
| memcpy(&status, rsp, 4); | |
| return status; | |
| } | |
| static int send_km_response(struct QSEECom_handle *h, const void *req, uint32_t req_len, struct km_cmd_response *out) | |
| { | |
| uint8_t *rsp; | |
| uint32_t rsp_len; | |
| int ret = send_raw(h, req, req_len, &rsp, &rsp_len); | |
| if (ret != 0) { | |
| return ret; | |
| } | |
| if (rsp_len < 8) { | |
| return -1; | |
| } | |
| memcpy(&out->status, rsp, 4); | |
| memcpy(&out->data_len, rsp + 4, 4); | |
| out->data = rsp + 8; | |
| if (out->data_len > rsp_len - 8) { | |
| out->data_len = rsp_len - 8; | |
| } | |
| return 0; | |
| } | |
| static void wait_listeners(void) | |
| { | |
| char v[MAX_PROP_LEN]; | |
| int i; | |
| for (i = 0; i < 50; ++i) { | |
| get_prop("vendor.sys.listeners.registered", v); | |
| if (strcmp(v, "true") == 0) { | |
| return; | |
| } | |
| usleep(100000); | |
| } | |
| } | |
| static void usage(const char *p) | |
| { | |
| fprintf(stderr, | |
| "Usage: %s [options]\n" | |
| " -b BRAND -d DEVICE -p PRODUCT -s SERIAL\n" | |
| " -m MANUFACTURER -M MODEL\n" | |
| " -i IMEI -I IMEI2 -e MEID -E MEID2\n" | |
| " -t TA_NAME -P TA_PATH\n" | |
| " -n dry-run\n", p); | |
| } | |
| int main(int argc, char **argv) | |
| { | |
| char brand[MAX_PROP_LEN] = {0}, device[MAX_PROP_LEN] = {0}, product[MAX_PROP_LEN] = {0}; | |
| char serial[MAX_PROP_LEN] = {0}, manufacturer[MAX_PROP_LEN] = {0}, model[MAX_PROP_LEN] = {0}; | |
| char imei[MAX_PROP_LEN] = {0}, imei2[MAX_PROP_LEN] = {0}, meid[MAX_PROP_LEN] = {0}, meid2[MAX_PROP_LEN] = {0}; | |
| const char *ta_name = DEFAULT_TA_NAME, *ta_path = DEFAULT_TA_PATH; | |
| struct QSEECom_handle *h = NULL; | |
| struct km_version ver; | |
| struct km_cmd_response rsp; | |
| uint8_t req[4096]; | |
| uint32_t cmd; | |
| int cbor_len, req_len, dry = 0, opt, ret; | |
| while ((opt = getopt(argc, argv, "b:d:p:s:m:M:i:I:e:E:t:P:nh")) != -1) { | |
| switch (opt) { | |
| case 'b': strncpy(brand, optarg, MAX_PROP_LEN - 1); break; | |
| case 'd': strncpy(device, optarg, MAX_PROP_LEN - 1); break; | |
| case 'p': strncpy(product, optarg, MAX_PROP_LEN - 1); break; | |
| case 's': strncpy(serial, optarg, MAX_PROP_LEN - 1); break; | |
| case 'm': strncpy(manufacturer, optarg, MAX_PROP_LEN - 1); break; | |
| case 'M': strncpy(model, optarg, MAX_PROP_LEN - 1); break; | |
| case 'i': strncpy(imei, optarg, MAX_PROP_LEN - 1); break; | |
| case 'I': strncpy(imei2, optarg, MAX_PROP_LEN - 1); break; | |
| case 'e': strncpy(meid, optarg, MAX_PROP_LEN - 1); break; | |
| case 'E': strncpy(meid2, optarg, MAX_PROP_LEN - 1); break; | |
| case 't': ta_name = optarg; break; | |
| case 'P': ta_path = optarg; break; | |
| case 'n': dry = 1; break; | |
| case 'h': usage(argv[0]); return 0; | |
| default: usage(argv[0]); return 1; | |
| } | |
| } | |
| if (!brand[0]) { const char *k[] = {"ro.product.brand", "ro.product.vendor.brand"}; get_first_prop(k, 2, brand); } | |
| if (!device[0]) { const char *k[] = {"ro.product.device", "ro.product.vendor.device"}; get_first_prop(k, 2, device); } | |
| if (!product[0]) { const char *k[] = {"ro.product.name", "ro.product.vendor.name"}; get_first_prop(k, 2, product); } | |
| if (!serial[0]) { const char *k[] = {"ro.serialno", "ro.boot.serialno"}; get_first_prop(k, 2, serial); } | |
| if (!manufacturer[0]) { const char *k[] = {"ro.product.manufacturer", "ro.product.vendor.manufacturer"}; get_first_prop(k, 2, manufacturer); } | |
| if (!model[0]) { const char *k[] = {"ro.product.model", "ro.product.vendor.model"}; get_first_prop(k, 2, model); } | |
| if (!imei[0]) { const char *k[] = {"persist.vendor.radio.imei", "persist.radio.imei", "vendor.ril.imei", "ril.gsm.imei", "ro.ril.oem.imei", "ro.ril.oem.imei1"}; get_first_prop(k, 6, imei); } | |
| if (!imei2[0]) { const char *k[] = {"persist.vendor.radio.imei2", "persist.radio.imei2", "vendor.ril.imei2", "ril.gsm.imei2", "ro.ril.oem.imei2"}; get_first_prop(k, 5, imei2); } | |
| if (!meid[0]) { const char *k[] = {"persist.vendor.radio.meid", "persist.radio.meid", "vendor.ril.meid", "ro.ril.oem.meid"}; get_first_prop(k, 4, meid); } | |
| if (!meid2[0]) { const char *k[] = {"persist.vendor.radio.meid2", "persist.radio.meid2", "vendor.ril.meid2", "ro.ril.oem.meid2"}; get_first_prop(k, 4, meid2); } | |
| add_id(KM_TAG_ATTESTATION_ID_BRAND, brand); | |
| add_id(KM_TAG_ATTESTATION_ID_DEVICE, device); | |
| add_id(KM_TAG_ATTESTATION_ID_PRODUCT, product); | |
| add_id(KM_TAG_ATTESTATION_ID_SERIAL, serial); | |
| add_id(KM_TAG_ATTESTATION_ID_IMEI, imei); | |
| add_id(KM_TAG_ATTESTATION_ID_IMEI, imei2); | |
| add_id(KM_TAG_ATTESTATION_ID_MEID, meid); | |
| add_id(KM_TAG_ATTESTATION_ID_MEID, meid2); | |
| add_id(KM_TAG_ATTESTATION_ID_MANUFACTURER, manufacturer); | |
| add_id(KM_TAG_ATTESTATION_ID_MODEL, model); | |
| if (!brand[0] || !device[0] || !product[0] || !serial[0] || !manufacturer[0] || !model[0]) { | |
| fprintf(stderr, "Missing required IDs (brand/device/product/serial/manufacturer/model).\n"); | |
| return 1; | |
| } | |
| printf("Device IDs to provision (%d entries):\n", g_id_count); | |
| for (int i = 0; i < g_id_count; ++i) { | |
| printf(" %-14s = \"%s\"\n", tag_name(g_ids[i].tag), g_ids[i].value); | |
| } | |
| printf("\n"); | |
| cmd = CMD_PROVISION_DEVICE_IDS; | |
| memcpy(req, &cmd, 4); | |
| cbor_len = build_cbor_payload(req + 4, sizeof(req) - 4); | |
| if (cbor_len < 0) { | |
| fprintf(stderr, "Failed to build CBOR payload.\n"); | |
| return 1; | |
| } | |
| req_len = 4 + cbor_len; | |
| printf("Command: 0x%04X (%d bytes cmd_id + %d bytes CBOR = %d total)\n", | |
| CMD_PROVISION_DEVICE_IDS, 4, cbor_len, req_len); | |
| printf("CBOR payload (%d bytes):\n", cbor_len); | |
| hexdump(req + 4, cbor_len); | |
| printf("\nFull command buffer:\n"); | |
| hexdump(req, req_len); | |
| if (dry) { | |
| printf("\nDry run: no TA commands sent.\n"); | |
| return 0; | |
| } | |
| printf("\nLoading QSEECom...\n"); | |
| if (load_qseecom() != 0) { | |
| return 1; | |
| } | |
| printf("Loaded QSEEComAPI: %s\n", g_loaded_lib_path); | |
| wait_listeners(); | |
| printf("Starting TA \"%s\" from \"%s\"...\n", ta_name, ta_path); | |
| ret = start_ta(&h, ta_path, ta_name); | |
| if (ret != 0 || !h) { | |
| fprintf(stderr, "QSEECom_start_app failed: %d\n", ret); | |
| return 1; | |
| } | |
| printf("Running keymaster pre-sequence: GET_VERSION (0x200)...\n"); | |
| ret = km_get_version(h, &ver); | |
| if (ret != 0) { | |
| fprintf(stderr, "GET_VERSION failed: %d\n", ret); | |
| return 1; | |
| } | |
| printf(" TA API: %u.%u TA: %u.%u\n", ver.ta_api_major, ver.ta_api_minor, ver.ta_major, ver.ta_minor); | |
| printf("Running keymaster pre-sequence: SET_VERSION (0x207)...\n"); | |
| ret = km_set_version(h); | |
| if (ret != 0) { | |
| fprintf(stderr, "SET_VERSION failed: %d\n", ret); | |
| return 1; | |
| } | |
| printf("Sending PROVISION_DEVICE_IDS (0x220A)...\n"); | |
| ret = send_km_response(h, req, (uint32_t)req_len, &rsp); | |
| if (ret != 0) { | |
| fprintf(stderr, "PROVISION_DEVICE_IDS transport failed: %d\n", ret); | |
| return 1; | |
| } | |
| printf("\nResponse:\n"); | |
| printf(" Status: %d (0x%08X)%s\n", rsp.status, (uint32_t)rsp.status, rsp.status == 0 ? " - SUCCESS" : " - FAILED"); | |
| printf(" Data len: %u\n", rsp.data_len); | |
| if (rsp.data_len > 0 && rsp.data_len <= 64) { | |
| printf(" Data: "); | |
| hexdump(rsp.data, (int)rsp.data_len); | |
| } | |
| if (rsp.status != 0) { | |
| fprintf(stderr, "\nProvisioning failed with status %d.\n", rsp.status); | |
| return 1; | |
| } | |
| cmd = CMD_SET_PROVISIONING_DEVICE_ID_SUCCESS; | |
| printf("Sending device-id success marker (0x2218)...\n"); | |
| ret = send_km_status(h, &cmd, 4); | |
| if (ret != 0) { | |
| fprintf(stderr, "SET_PROVISIONING_DEVICE_ID_SUCCESS failed: %d\n", ret); | |
| return 1; | |
| } | |
| qseecom_shutdown_app(&h); | |
| dlclose(g_qseecom_lib); | |
| printf("\nProvisioning flow finished successfully.\n"); | |
| return 0; | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment