| #define LOG_TAG "NanohubHAL_Test" |
| |
| #include <cstddef> |
| #include <cstdint> |
| #include <functional> |
| #include <iostream> |
| #include <iomanip> |
| #include <map> |
| #include <memory> |
| #include <cstddef> |
| #include <cstdint> |
| #include <mutex> |
| #include <vector> |
| |
| #include <dlfcn.h> |
| #include <signal.h> |
| #include <unistd.h> |
| |
| #include <log/log.h> |
| #include <sys/endian.h> |
| |
| #include <hardware/hardware.h> |
| #include <hardware/context_hub.h> |
| #include <nanohub/nanoapp.h> |
| |
| inline std::ostream &operator << (std::ostream &os, const hub_app_name_t &appId) |
| { |
| char vendor[6]; |
| __be64 beAppId = htobe64(appId.id); |
| uint32_t seqId = appId.id & NANOAPP_VENDOR_ALL_APPS; |
| |
| std::ios::fmtflags f(os.flags()); |
| memcpy(vendor, (void*)&beAppId, sizeof(vendor) - 1); |
| vendor[sizeof(vendor) - 1] = 0; |
| if (strlen(vendor) == 5) |
| os << vendor << ", " << std::hex << std::setw(6) << seqId; |
| else |
| os << "#" << std::hex << appId.id; |
| os.flags(f); |
| |
| return os; |
| } |
| |
| void dumpBuffer(std::ostream &os, const char *pfx, const hub_app_name_t &appId, uint32_t evtId, const void *data, size_t len, int status) |
| { |
| const uint8_t *p = static_cast<const uint8_t *>(data); |
| os << pfx << ": [ID=" << appId << "; SZ=" << std::dec << len; |
| if (evtId) |
| os << "; EVT=" << std::hex << evtId; |
| os << "]:" << std::hex; |
| for (size_t i = 0; i < len; ++i) { |
| os << " " << std::setfill('0') << std::setw(2) << (unsigned int)p[i]; |
| } |
| if (status) { |
| os << "; status=" << status << " [" << std::setfill('0') << std::setw(8) << status << "]"; |
| } |
| } |
| |
| class CHub |
| { |
| public: |
| class IClient { |
| public: |
| virtual void onMessage(const hub_message_t &msg) = 0; |
| virtual ~IClient(){} |
| }; |
| class Client : IClient { |
| CHub *mParent; |
| const context_hub_t *mHub; |
| std::function<void(const hub_message_t &)> mHandler; |
| |
| public: |
| explicit Client(const context_hub_t *hub, CHub *parent) { |
| mHub = hub; |
| mParent = parent; |
| } |
| ~Client() = default; |
| |
| void setHandler(std::function<void(const hub_message_t &)> handler) { |
| mHandler = handler; |
| } |
| void onMessage(const hub_message_t &msg) { |
| if ((bool)mHandler == true) { |
| mHandler(msg); |
| } |
| } |
| void sendMessage(const hub_message_t &msg) { |
| mParent->sendMessage(mHub->hub_id, msg); |
| } |
| void sendToSystem(uint32_t typ, void *data, uint32_t len) { |
| mParent->sendMessage(mHub->hub_id, mHub->os_app_name, typ, data, len); |
| } |
| void sendToApp(hub_app_name_t app, void *data, uint32_t len) { |
| mParent->sendMessage(mHub->hub_id, app, 0, data, len); |
| } |
| const hub_app_name_t getSystemApp() const { return mHub->os_app_name; } |
| }; |
| private: |
| static int contextHubCallback(uint32_t id, const hub_message_t *msg, void *cookie) |
| { |
| CHub *hub = static_cast<CHub*>(cookie); |
| hub->onMessage(id, msg); |
| return 0; |
| } |
| |
| CHub() { |
| hw_get_module(CONTEXT_HUB_MODULE_ID, (const hw_module_t **)&mMod); |
| if (!mMod) |
| return; |
| mMod->subscribe_messages(0, contextHubCallback, this); |
| mHubArraySize = mMod->get_hubs(mMod, &mHubArray); |
| for (size_t i = 0; i < mHubArraySize; ++i) { |
| auto item = &mHubArray[i]; |
| mHubs[item->hub_id] = std::unique_ptr<Client>(new Client(item, this)); |
| } |
| } |
| |
| ~CHub() { |
| // destroy all clients first |
| mHubs.clear(); |
| if (mMod != nullptr) { |
| // unregister from HAL services |
| mMod->subscribe_messages(0, nullptr, nullptr); |
| // there is no hw_put_module(); release HAL fd directly |
| dlclose(mMod->common.dso); |
| mMod = nullptr; |
| } |
| } |
| |
| void onMessage(uint32_t hubId, const hub_message_t *msg) { |
| Client *cli = getClientById(hubId); |
| if (cli != nullptr && msg != nullptr) { |
| cli->onMessage(*msg); |
| } |
| } |
| |
| int sendMessage(uint32_t id, const hub_message_t &msg) { |
| return (mMod != nullptr) ? mMod->send_message(id, &msg) : 0; |
| } |
| |
| int sendMessage(uint32_t id, hub_app_name_t app, uint32_t typ, void *data, uint32_t len) { |
| hub_message_t msg = { |
| .app_name = app, |
| .message_type = typ, |
| .message_len = len, |
| .message = data, |
| }; |
| return sendMessage(id, msg); |
| } |
| |
| Client *getClientById(size_t id) { return mHubs.count(id) ? mHubs[id].get() : nullptr; } |
| |
| context_hub_module_t *mMod = nullptr; |
| const context_hub_t *mHubArray = nullptr; |
| size_t mHubArraySize = 0; |
| std::map <size_t, std::unique_ptr<Client> > mHubs; |
| |
| public: |
| static CHub *instantiate() { |
| static CHub instance; |
| |
| return &instance; |
| } |
| Client *getClientByIndex(size_t idx) { |
| return idx < mHubArraySize && mHubArray != nullptr ? |
| getClientById(mHubArray[idx].hub_id) : nullptr; |
| } |
| }; |
| |
| class NanoClient |
| { |
| CHub::Client *mClient; |
| std::ostream &log; |
| std::mutex lock; |
| void onMessage(const hub_message_t &msg){ |
| std::lock_guard<std::mutex> _l(lock); |
| dumpBuffer(log, "Rx", msg.app_name, msg.message_type, msg.message, msg.message_len, 0); |
| log << std::endl; |
| } |
| public: |
| NanoClient(int idx = 0) : log(std::clog) { |
| CHub *hub = CHub::instantiate(); |
| mClient = hub->getClientByIndex(idx); |
| if (mClient) |
| mClient->setHandler(std::function<void(const hub_message_t&)>([this] (const hub_message_t&msg) { onMessage(msg); })); |
| } |
| void sendMessage(const hub_message_t &msg) { mClient->sendMessage(msg); } |
| void sendMessageToSystem(uint32_t cmd, void * data, size_t dataSize) { |
| hub_message_t msg; |
| msg.message = data; |
| msg.message_len = dataSize; |
| msg.message_type = cmd; |
| msg.app_name = mClient->getSystemApp(); |
| { |
| std::lock_guard<std::mutex> _l(lock); |
| dumpBuffer(log, "TxCmd", msg.app_name, msg.message_type, msg.message, msg.message_len, 0); |
| log << std::endl; |
| } |
| sendMessage(msg); |
| } |
| void sendMessageToApp(const hub_app_name_t appName, void * data, size_t dataSize, uint32_t msg_type) { |
| hub_message_t msg; |
| msg.message = data; |
| msg.message_len = dataSize; |
| msg.message_type = msg_type; |
| msg.app_name = appName; |
| { |
| std::lock_guard<std::mutex> _l(lock); |
| dumpBuffer(log, "TxMsg", msg.app_name, msg.message_type, msg.message, msg.message_len, 0); |
| log << std::endl; |
| } |
| sendMessage(msg); |
| } |
| }; |
| |
| void sigint_handler(int) |
| { |
| exit(0); |
| } |
| |
| int main(int argc, char *argv[]) |
| { |
| int opt; |
| long cmd = 0; |
| unsigned long msg = 0; |
| uint64_t appId = 0; |
| const char *appFileName = NULL; |
| uint32_t fileSize = 0; |
| |
| while((opt = getopt(argc, argv, "c:i:a:m:")) != -1) { |
| char *end = NULL; |
| switch(opt) { |
| case 'm': |
| msg = strtoul(optarg, &end, 16); |
| break; |
| case 'c': |
| cmd = strtol(optarg, &end, 10); |
| break; |
| case 'i': |
| appId = strtoull(optarg, &end, 16); |
| break; |
| case 'a': |
| appFileName = optarg; |
| break; |
| } |
| if (end && *end != '\0') { |
| std::clog << "Invalid argument: " << optarg << std::endl; |
| return 1; |
| } |
| } |
| |
| NanoClient cli; |
| |
| std::vector<uint8_t> data; |
| for (int i = optind; i < argc; ++i) { |
| char *end; |
| unsigned long v = strtoul(argv[i], &end, 16); |
| // ignore any garbage after parsed hex value; |
| // ignore the fact it may not fit 1 byte; |
| // we're not testing user's ability to pass valid data, |
| // we're testing the system ability to transfer data. |
| data.push_back(v); |
| } |
| if (msg != 0) { |
| // send APP message |
| const hub_app_name_t app_name = { .id = appId }; |
| cli.sendMessageToApp(app_name, data.data(), data.size(), msg); |
| } else { |
| // send HAL command |
| switch(cmd) { |
| case CONTEXT_HUB_APPS_ENABLE: |
| { |
| apps_enable_request_t req; |
| req.app_name.id = appId; |
| cli.sendMessageToSystem(CONTEXT_HUB_APPS_ENABLE, &req, sizeof(req)); |
| } |
| break; |
| case CONTEXT_HUB_APPS_DISABLE: |
| { |
| apps_disable_request_t req; |
| req.app_name.id = appId; |
| cli.sendMessageToSystem(CONTEXT_HUB_APPS_DISABLE, &req, sizeof(req)); |
| } |
| break; |
| case CONTEXT_HUB_LOAD_APP: |
| { |
| load_app_request_t *req = NULL; |
| if (appFileName) |
| req = (load_app_request_t *)loadFile(appFileName, &fileSize); |
| if (!req || fileSize < sizeof(*req) || req->app_binary.magic != NANOAPP_MAGIC) { |
| std::clog << "Invalid nanoapp image: " << |
| (appFileName != nullptr ? appFileName : "<NULL>") << std::endl; |
| return 1; |
| } |
| cli.sendMessageToSystem(CONTEXT_HUB_LOAD_APP, req, fileSize); |
| free(req); |
| } |
| break; |
| case CONTEXT_HUB_UNLOAD_APP: |
| { |
| unload_app_request_t req; |
| req.app_name.id = appId; |
| cli.sendMessageToSystem(CONTEXT_HUB_UNLOAD_APP, &req, sizeof(req)); |
| } |
| break; |
| case CONTEXT_HUB_QUERY_APPS: |
| { |
| query_apps_request_t req; |
| req.app_name.id = appId; |
| cli.sendMessageToSystem(CONTEXT_HUB_QUERY_APPS, &req, sizeof(req)); |
| } |
| break; |
| case CONTEXT_HUB_QUERY_MEMORY: |
| default: |
| std::clog << "Unknown command: " << cmd << std::endl; |
| break; |
| } |
| } |
| |
| signal(SIGINT, sigint_handler); |
| while(1) { |
| sleep(1); |
| } |
| return 0; |
| } |