1 /*
2  * Copyright (C) 2020 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 #include <dirent.h>
18 #include <sys/stat.h>
19 #include <sys/types.h>
20 
21 #include <android-base/logging.h>
22 #include <dlfcn.h>
23 #include <libxml/parser.h>
24 #include <libxml/tree.h>
25 #include <libxml/xmlmemory.h>
26 
27 #include "wifi_legacy_hal_factory.h"
28 #include "wifi_legacy_hal_stubs.h"
29 
30 namespace {
31 static constexpr char kVendorHalsDescPath[] = "/vendor/etc/wifi/vendor_hals";
32 static constexpr char kVendorHalsDescExt[] = ".xml";
33 static constexpr uint32_t kVendorHalsDescVersion = 1;
34 
isDirectory(struct dirent * entryPtr)35 bool isDirectory(struct dirent* entryPtr) {
36     bool isDir = false;
37     if (entryPtr->d_type != DT_UNKNOWN && entryPtr->d_type != DT_LNK) {
38         isDir = (entryPtr->d_type == DT_DIR);
39     } else {
40         struct stat entryStat;
41         stat(entryPtr->d_name, &entryStat);
42         isDir = S_ISDIR(entryStat.st_mode);
43     }
44     return isDir;
45 }
46 
isFileExtension(const char * name,const char * ext)47 bool isFileExtension(const char* name, const char* ext) {
48     if (name == NULL) return false;
49     if (ext == NULL) return false;
50 
51     size_t extLen = strlen(ext);
52     size_t nameLen = strlen(name);
53 
54     if (extLen > nameLen) return false;
55 
56     if (strncmp(name + nameLen - extLen, ext, extLen) != 0) return false;
57 
58     return true;
59 }
60 };  // namespace
61 
62 namespace android {
63 namespace hardware {
64 namespace wifi {
65 namespace V1_5 {
66 namespace implementation {
67 namespace legacy_hal {
68 
WifiLegacyHalFactory(const std::weak_ptr<wifi_system::InterfaceTool> iface_tool)69 WifiLegacyHalFactory::WifiLegacyHalFactory(
70     const std::weak_ptr<wifi_system::InterfaceTool> iface_tool)
71     : iface_tool_(iface_tool) {}
72 
getHals()73 std::vector<std::shared_ptr<WifiLegacyHal>> WifiLegacyHalFactory::getHals() {
74     if (legacy_hals_.empty()) {
75         if (!initVendorHalDescriptorFromLinked())
76             initVendorHalsDescriptorList();
77         for (auto& desc : descs_) {
78             std::shared_ptr<WifiLegacyHal> hal =
79                 std::make_shared<WifiLegacyHal>(iface_tool_, desc.fn,
80                                                 desc.primary);
81             legacy_hals_.push_back(hal);
82         }
83     }
84 
85     return legacy_hals_;
86 }
87 
initVendorHalDescriptorFromLinked()88 bool WifiLegacyHalFactory::initVendorHalDescriptorFromLinked() {
89     wifi_hal_lib_desc desc;
90 
91     if (!initLinkedHalFunctionTable(&desc.fn)) return false;
92 
93     desc.primary = true;
94     desc.handle = NULL;
95     descs_.push_back(desc);
96     return true;
97 }
98 
initLinkedHalFunctionTable(wifi_hal_fn * hal_fn)99 bool WifiLegacyHalFactory::initLinkedHalFunctionTable(wifi_hal_fn* hal_fn) {
100     init_wifi_vendor_hal_func_table_t initfn;
101 
102     initfn = (init_wifi_vendor_hal_func_table_t)dlsym(
103         RTLD_DEFAULT, "init_wifi_vendor_hal_func_table");
104     if (!initfn) {
105         LOG(INFO) << "no vendor HAL library linked, will try dynamic load";
106         return false;
107     }
108 
109     if (!initHalFuncTableWithStubs(hal_fn)) {
110         LOG(ERROR) << "Can not initialize the basic function pointer table";
111         return false;
112     }
113 
114     if (initfn(hal_fn) != WIFI_SUCCESS) {
115         LOG(ERROR) << "Can not initialize the vendor function pointer table";
116         return false;
117     }
118 
119     return true;
120 }
121 
122 /*
123  * Overall structure of the HAL descriptor XML schema
124  *
125  * <?xml version="1.0" encoding="UTF-8"?>
126  * <WifiVendorHal version="1">
127  * <path>/vendor/lib64/libwifi-hal-qcom.so</path>
128  * <primary>1</primary>
129  * </WifiVendorHal>
130  */
initVendorHalsDescriptorList()131 void WifiLegacyHalFactory::initVendorHalsDescriptorList() {
132     xmlDocPtr xml;
133     xmlNodePtr node, cnode;
134     char* version;
135     std::string path;
136     xmlChar* value;
137     wifi_hal_lib_desc desc;
138 
139     LOG(INFO) << "processing vendor HALs descriptions in "
140               << kVendorHalsDescPath;
141     DIR* dirPtr = ::opendir(kVendorHalsDescPath);
142     if (dirPtr == NULL) {
143         LOG(ERROR) << "failed to open " << kVendorHalsDescPath;
144         return;
145     }
146     for (struct dirent* entryPtr = ::readdir(dirPtr); entryPtr != NULL;
147          entryPtr = ::readdir(dirPtr)) {
148         if (isDirectory(entryPtr)) continue;
149 
150         if (!isFileExtension(entryPtr->d_name, kVendorHalsDescExt))
151             continue;  // only process .xml files
152 
153         LOG(INFO) << "processing config file: " << entryPtr->d_name;
154 
155         std::string fullPath(kVendorHalsDescPath);
156         fullPath.append("/");
157         fullPath.append(entryPtr->d_name);
158         xml = xmlReadFile(fullPath.c_str(), "UTF-8", XML_PARSE_RECOVER);
159         if (!xml) {
160             LOG(ERROR) << "failed to parse: " << entryPtr->d_name
161                        << " skipping...";
162             continue;
163         }
164         node = xmlDocGetRootElement(xml);
165         if (!node) {
166             LOG(ERROR) << "empty config file: " << entryPtr->d_name
167                        << " skipping...";
168             goto skip;
169         }
170         if (xmlStrcmp(node->name, BAD_CAST "WifiVendorHal")) {
171             LOG(ERROR) << "bad config, root element not WifiVendorHal: "
172                        << entryPtr->d_name << " skipping...";
173             goto skip;
174         }
175         version = (char*)xmlGetProp(node, BAD_CAST "version");
176         if (!version || strtoul(version, NULL, 0) != kVendorHalsDescVersion) {
177             LOG(ERROR) << "conf file: " << entryPtr->d_name
178                        << "must have version: " << kVendorHalsDescVersion
179                        << ", skipping...";
180             goto skip;
181         }
182         cnode = node->children;
183         path.clear();
184         desc.primary = false;
185         while (cnode) {
186             if (!xmlStrcmp(cnode->name, BAD_CAST "path")) {
187                 value = xmlNodeListGetString(xml, cnode->children, 1);
188                 if (value) path = (char*)value;
189                 xmlFree(value);
190             } else if (!xmlStrcmp(cnode->name, BAD_CAST "primary")) {
191                 value = xmlNodeListGetString(xml, cnode->children, 1);
192                 desc.primary = !xmlStrcmp(value, BAD_CAST "1");
193                 xmlFree(value);
194             }
195             cnode = cnode->next;
196         }
197         if (path.empty()) {
198             LOG(ERROR) << "hal library path not provided in: "
199                        << entryPtr->d_name << ", skipping...";
200             goto skip;
201         }
202         if (loadVendorHalLib(path, desc)) {
203             if (desc.primary)
204                 descs_.insert(descs_.begin(), desc);
205             else
206                 descs_.push_back(desc);
207         }
208     skip:
209         xmlFreeDoc(xml);
210     }
211     ::closedir(dirPtr);
212 }
213 
loadVendorHalLib(const std::string & path,wifi_hal_lib_desc & desc)214 bool WifiLegacyHalFactory::loadVendorHalLib(const std::string& path,
215                                             wifi_hal_lib_desc& desc) {
216     void* h = dlopen(path.c_str(), RTLD_NOW | RTLD_LOCAL);
217     init_wifi_vendor_hal_func_table_t initfn;
218     wifi_error res;
219 
220     if (!h) {
221         LOG(ERROR) << "failed to open vendor hal library: " << path;
222         return false;
223     }
224     initfn = (init_wifi_vendor_hal_func_table_t)dlsym(
225         h, "init_wifi_vendor_hal_func_table");
226     if (!initfn) {
227         LOG(ERROR) << "init_wifi_vendor_hal_func_table not found in: " << path;
228         goto out_err;
229     }
230 
231     if (!initHalFuncTableWithStubs(&desc.fn)) {
232         LOG(ERROR) << "Can not initialize the basic function pointer table";
233         goto out_err;
234     }
235     res = initfn(&desc.fn);
236     if (res != WIFI_SUCCESS) {
237         LOG(ERROR) << "failed to initialize the vendor func table in: " << path
238                    << " error: " << res;
239         goto out_err;
240     }
241 
242     res = desc.fn.wifi_early_initialize();
243     // vendor HALs which do not implement early_initialize will return
244     // WIFI_ERROR_NOT_SUPPORTED, treat this as success.
245     if (res != WIFI_SUCCESS && res != WIFI_ERROR_NOT_SUPPORTED) {
246         LOG(ERROR) << "early initialization failed in: " << path
247                    << " error: " << res;
248         goto out_err;
249     }
250 
251     desc.handle = h;
252     return true;
253 out_err:
254     dlclose(h);
255     return false;
256 }
257 
258 }  // namespace legacy_hal
259 }  // namespace implementation
260 }  // namespace V1_5
261 }  // namespace wifi
262 }  // namespace hardware
263 }  // namespace android
264