blob: fbaa284f56c0c52f10b54c2c5a8bb5670c56b04d [file] [log] [blame]
/*
* Copyright (C) 2020 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <dirent.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <android-base/logging.h>
#include <dlfcn.h>
#include <libxml/parser.h>
#include <libxml/tree.h>
#include <libxml/xmlmemory.h>
#include "wifi_legacy_hal_factory.h"
#include "wifi_legacy_hal_stubs.h"
namespace {
static constexpr char kVendorHalsDescPath[] = "/vendor/etc/wifi/vendor_hals";
static constexpr char kVendorHalsDescExt[] = ".xml";
static constexpr uint32_t kVendorHalsDescVersion = 1;
bool isDirectory(struct dirent* entryPtr) {
bool isDir = false;
if (entryPtr->d_type != DT_UNKNOWN && entryPtr->d_type != DT_LNK) {
isDir = (entryPtr->d_type == DT_DIR);
} else {
struct stat entryStat;
stat(entryPtr->d_name, &entryStat);
isDir = S_ISDIR(entryStat.st_mode);
}
return isDir;
}
bool isFileExtension(const char* name, const char* ext) {
if (name == NULL) return false;
if (ext == NULL) return false;
size_t extLen = strlen(ext);
size_t nameLen = strlen(name);
if (extLen > nameLen) return false;
if (strncmp(name + nameLen - extLen, ext, extLen) != 0) return false;
return true;
}
}; // namespace
namespace android {
namespace hardware {
namespace wifi {
namespace V1_5 {
namespace implementation {
namespace legacy_hal {
WifiLegacyHalFactory::WifiLegacyHalFactory(
const std::weak_ptr<wifi_system::InterfaceTool> iface_tool)
: iface_tool_(iface_tool) {}
std::vector<std::shared_ptr<WifiLegacyHal>> WifiLegacyHalFactory::getHals() {
if (legacy_hals_.empty()) {
if (!initVendorHalDescriptorFromLinked())
initVendorHalsDescriptorList();
for (auto& desc : descs_) {
std::shared_ptr<WifiLegacyHal> hal =
std::make_shared<WifiLegacyHal>(iface_tool_, desc.fn,
desc.primary);
legacy_hals_.push_back(hal);
}
}
return legacy_hals_;
}
bool WifiLegacyHalFactory::initVendorHalDescriptorFromLinked() {
wifi_hal_lib_desc desc;
if (!initLinkedHalFunctionTable(&desc.fn)) return false;
desc.primary = true;
desc.handle = NULL;
descs_.push_back(desc);
return true;
}
bool WifiLegacyHalFactory::initLinkedHalFunctionTable(wifi_hal_fn* hal_fn) {
init_wifi_vendor_hal_func_table_t initfn;
initfn = (init_wifi_vendor_hal_func_table_t)dlsym(
RTLD_DEFAULT, "init_wifi_vendor_hal_func_table");
if (!initfn) {
LOG(INFO) << "no vendor HAL library linked, will try dynamic load";
return false;
}
if (!initHalFuncTableWithStubs(hal_fn)) {
LOG(ERROR) << "Can not initialize the basic function pointer table";
return false;
}
if (initfn(hal_fn) != WIFI_SUCCESS) {
LOG(ERROR) << "Can not initialize the vendor function pointer table";
return false;
}
return true;
}
/*
* Overall structure of the HAL descriptor XML schema
*
* <?xml version="1.0" encoding="UTF-8"?>
* <WifiVendorHal version="1">
* <path>/vendor/lib64/libwifi-hal-qcom.so</path>
* <primary>1</primary>
* </WifiVendorHal>
*/
void WifiLegacyHalFactory::initVendorHalsDescriptorList() {
xmlDocPtr xml;
xmlNodePtr node, cnode;
char* version;
std::string path;
xmlChar* value;
wifi_hal_lib_desc desc;
LOG(INFO) << "processing vendor HALs descriptions in "
<< kVendorHalsDescPath;
DIR* dirPtr = ::opendir(kVendorHalsDescPath);
if (dirPtr == NULL) {
LOG(ERROR) << "failed to open " << kVendorHalsDescPath;
return;
}
for (struct dirent* entryPtr = ::readdir(dirPtr); entryPtr != NULL;
entryPtr = ::readdir(dirPtr)) {
if (isDirectory(entryPtr)) continue;
if (!isFileExtension(entryPtr->d_name, kVendorHalsDescExt))
continue; // only process .xml files
LOG(INFO) << "processing config file: " << entryPtr->d_name;
std::string fullPath(kVendorHalsDescPath);
fullPath.append("/");
fullPath.append(entryPtr->d_name);
xml = xmlReadFile(fullPath.c_str(), "UTF-8", XML_PARSE_RECOVER);
if (!xml) {
LOG(ERROR) << "failed to parse: " << entryPtr->d_name
<< " skipping...";
continue;
}
node = xmlDocGetRootElement(xml);
if (!node) {
LOG(ERROR) << "empty config file: " << entryPtr->d_name
<< " skipping...";
goto skip;
}
if (xmlStrcmp(node->name, BAD_CAST "WifiVendorHal")) {
LOG(ERROR) << "bad config, root element not WifiVendorHal: "
<< entryPtr->d_name << " skipping...";
goto skip;
}
version = (char*)xmlGetProp(node, BAD_CAST "version");
if (!version || strtoul(version, NULL, 0) != kVendorHalsDescVersion) {
LOG(ERROR) << "conf file: " << entryPtr->d_name
<< "must have version: " << kVendorHalsDescVersion
<< ", skipping...";
goto skip;
}
cnode = node->children;
path.clear();
desc.primary = false;
while (cnode) {
if (!xmlStrcmp(cnode->name, BAD_CAST "path")) {
value = xmlNodeListGetString(xml, cnode->children, 1);
if (value) path = (char*)value;
xmlFree(value);
} else if (!xmlStrcmp(cnode->name, BAD_CAST "primary")) {
value = xmlNodeListGetString(xml, cnode->children, 1);
desc.primary = !xmlStrcmp(value, BAD_CAST "1");
xmlFree(value);
}
cnode = cnode->next;
}
if (path.empty()) {
LOG(ERROR) << "hal library path not provided in: "
<< entryPtr->d_name << ", skipping...";
goto skip;
}
if (loadVendorHalLib(path, desc)) {
if (desc.primary)
descs_.insert(descs_.begin(), desc);
else
descs_.push_back(desc);
}
skip:
xmlFreeDoc(xml);
}
::closedir(dirPtr);
}
bool WifiLegacyHalFactory::loadVendorHalLib(const std::string& path,
wifi_hal_lib_desc& desc) {
void* h = dlopen(path.c_str(), RTLD_NOW | RTLD_LOCAL);
init_wifi_vendor_hal_func_table_t initfn;
wifi_error res;
if (!h) {
LOG(ERROR) << "failed to open vendor hal library: " << path;
return false;
}
initfn = (init_wifi_vendor_hal_func_table_t)dlsym(
h, "init_wifi_vendor_hal_func_table");
if (!initfn) {
LOG(ERROR) << "init_wifi_vendor_hal_func_table not found in: " << path;
goto out_err;
}
if (!initHalFuncTableWithStubs(&desc.fn)) {
LOG(ERROR) << "Can not initialize the basic function pointer table";
goto out_err;
}
res = initfn(&desc.fn);
if (res != WIFI_SUCCESS) {
LOG(ERROR) << "failed to initialize the vendor func table in: " << path
<< " error: " << res;
goto out_err;
}
res = desc.fn.wifi_early_initialize();
// vendor HALs which do not implement early_initialize will return
// WIFI_ERROR_NOT_SUPPORTED, treat this as success.
if (res != WIFI_SUCCESS && res != WIFI_ERROR_NOT_SUPPORTED) {
LOG(ERROR) << "early initialization failed in: " << path
<< " error: " << res;
goto out_err;
}
desc.handle = h;
return true;
out_err:
dlclose(h);
return false;
}
} // namespace legacy_hal
} // namespace implementation
} // namespace V1_5
} // namespace wifi
} // namespace hardware
} // namespace android