Packages

QNN (Qualcomm AI Engine Direct) NIF bindings for the Hexagon NPU (QCS6490/HTP)

Current section

Files

Jump to
hexagon_tpu c_src qnn_api.cpp
Raw

c_src/qnn_api.cpp

#include "qnn_api.hpp"
#include <dlfcn.h>
#include <cstdarg>
#include <cstdio>
#include "QnnLog.h"
namespace {
// QNN log callback -> stderr (visible on the Nerves console / journald).
void qnn_log_callback(const char *fmt, QnnLog_Level_t level, uint64_t timestamp, va_list args) {
(void)timestamp;
static const char *level_names[] = {"?", "error", "warn", "info", "verbose", "debug"};
unsigned idx = (level >= 1 && level <= 5) ? level : 0;
std::fprintf(stderr, "[qnn:%s] ", level_names[idx]);
std::vfprintf(stderr, fmt, args);
std::fputc('\n', stderr);
}
typedef Qnn_ErrorHandle_t (*GetProvidersFn)(const QnnInterface_t ***, uint32_t *);
typedef Qnn_ErrorHandle_t (*GetSystemProvidersFn)(const QnnSystemInterface_t ***, uint32_t *);
} // namespace
std::string qnn_api_open(QnnApi &api,
const std::string &backend_lib,
const std::string &system_lib,
int log_level) {
api.backend_lib_name = backend_lib;
api.core_lib = dlopen(backend_lib.c_str(), RTLD_NOW | RTLD_GLOBAL);
if (!api.core_lib) {
return std::string("dlopen_failed: ") + dlerror();
}
auto get_providers =
reinterpret_cast<GetProvidersFn>(dlsym(api.core_lib, "QnnInterface_getProviders"));
if (!get_providers) {
qnn_api_close(api);
return "missing_symbol: QnnInterface_getProviders";
}
const QnnInterface_t **providers = nullptr;
uint32_t num_providers = 0;
if (get_providers(&providers, &num_providers) != QNN_SUCCESS || !providers ||
num_providers == 0) {
qnn_api_close(api);
return "no_interface_providers";
}
// Pick the provider with a matching major version and the highest minor.
// The calls used here have been stable across all 2.x minors.
const QnnInterface_t *chosen = nullptr;
for (uint32_t i = 0; i < num_providers; i++) {
const Qnn_Version_t &v = providers[i]->apiVersion.coreApiVersion;
if (v.major != QNN_API_VERSION_MAJOR) continue;
if (!chosen || v.minor > chosen->apiVersion.coreApiVersion.minor) {
chosen = providers[i];
}
}
if (!chosen) {
qnn_api_close(api);
return "no_compatible_interface_provider";
}
api.fn = chosen->QNN_INTERFACE_VER_NAME;
api.system_lib = dlopen(system_lib.c_str(), RTLD_NOW | RTLD_GLOBAL);
if (!api.system_lib) {
std::string err = std::string("dlopen_system_failed: ") + dlerror();
qnn_api_close(api);
return err;
}
auto get_sys_providers = reinterpret_cast<GetSystemProvidersFn>(
dlsym(api.system_lib, "QnnSystemInterface_getProviders"));
if (!get_sys_providers) {
qnn_api_close(api);
return "missing_symbol: QnnSystemInterface_getProviders";
}
const QnnSystemInterface_t **sys_providers = nullptr;
uint32_t num_sys = 0;
if (get_sys_providers(&sys_providers, &num_sys) != QNN_SUCCESS || !sys_providers ||
num_sys == 0) {
qnn_api_close(api);
return "no_system_interface_providers";
}
const QnnSystemInterface_t *sys_chosen = nullptr;
for (uint32_t i = 0; i < num_sys; i++) {
const Qnn_Version_t &v = sys_providers[i]->systemApiVersion;
if (v.major != QNN_SYSTEM_API_VERSION_MAJOR) continue;
if (!sys_chosen || v.minor > sys_chosen->systemApiVersion.minor) {
sys_chosen = sys_providers[i];
}
}
if (!sys_chosen) {
qnn_api_close(api);
return "no_compatible_system_provider";
}
api.sys_fn = sys_chosen->QNN_SYSTEM_INTERFACE_VER_NAME;
if (log_level > 0 && api.fn.logCreate) {
// Log handle creation is best-effort; some backends run without it.
api.fn.logCreate(qnn_log_callback, static_cast<QnnLog_Level_t>(log_level), &api.log);
}
if (!api.fn.backendCreate) {
qnn_api_close(api);
return "missing_fn: backendCreate";
}
Qnn_ErrorHandle_t err = api.fn.backendCreate(api.log, nullptr, &api.backend);
if (err != QNN_SUCCESS) {
qnn_api_close(api);
return "backend_create_failed";
}
if (api.fn.backendGetBuildId) {
const char *id = nullptr;
if (api.fn.backendGetBuildId(&id) == QNN_SUCCESS && id) {
api.build_id = id;
}
}
// deviceCreate is optional: the CPU backend reports UNSUPPORTED_FEATURE,
// and HTP auto-detects the local SoC with a null config.
if (api.fn.deviceCreate) {
Qnn_ErrorHandle_t dev_err = api.fn.deviceCreate(api.log, nullptr, &api.device);
if (dev_err != QNN_SUCCESS) {
api.device = nullptr;
}
}
return "";
}
void qnn_api_close(QnnApi &api) {
if (api.device && api.fn.deviceFree) {
api.fn.deviceFree(api.device);
}
api.device = nullptr;
if (api.backend && api.fn.backendFree) {
api.fn.backendFree(api.backend);
}
api.backend = nullptr;
if (api.log && api.fn.logFree) {
api.fn.logFree(api.log);
}
api.log = nullptr;
if (api.system_lib) {
dlclose(api.system_lib);
api.system_lib = nullptr;
}
if (api.core_lib) {
dlclose(api.core_lib);
api.core_lib = nullptr;
}
}