halpai: Add support of reference count for hal_common_(get|put)_backend function
[platform/hal/api/common.git] / src / hal-api-common.c
1 /*
2  * HAL Common API
3  *
4  * Copyright (c) 2020 Samsung Electronics Co., Ltd.
5  *
6  * Licensed under the Apache License, Version 2.0 (the License);
7  * you may not use this file except in compliance with the License.
8  * You may obtain a copy of the License at
9  *
10  *      http://www.apache.org/licenses/LICENSE-2.0
11  *
12  * Unless required by applicable law or agreed to in writing, software
13  * distributed under the License is distributed on an "AS IS" BASIS,
14  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15  * See the License for the specific language governing permissions and
16  * limitations under the License.
17  */
18
19 #include <stdio.h>
20 #include <stdint.h>
21 #include <stdbool.h>
22 #include <dlfcn.h>
23 #include <dlog.h>
24
25 #include <glib-object.h>
26
27 #include "common.h"
28 #include "hal-api-conf.h"
29
30 #ifndef EXPORT
31 #define EXPORT __attribute__ ((visibility("default")))
32 #endif
33
34 static enum hal_abi_version g_platform_curr_abi_version;
35 G_LOCK_DEFINE_STATIC(hal_common_lock);
36
37 EXPORT
38 const char *hal_common_get_backend_library_name(enum hal_module module)
39 {
40         char *library_name;
41         struct __hal_module_info *info = NULL;
42
43         /* Check parameter whether is valid or not */
44         if (module <= HAL_MODULE_UNKNOWN || module >= HAL_MODULE_END) {
45                 _E("Invalid parameter of HAL module (%d)\n", module);
46                 return NULL;
47         }
48
49         info = _hal_api_conf_get_module_info(module);
50         if (info == NULL) {
51                 _E("Failed to get HAL module(%d) information\n", module);
52                 return NULL;
53         }
54
55 #if defined(__aarch64__)
56         library_name = info->library_name_64bit;
57 #else
58         library_name = info->library_name;
59 #endif
60
61         if (!library_name) {
62                 _E("%s backend library name is NULL\n", info->module_name);
63                 return NULL;
64         }
65         return library_name;
66 }
67
68 EXPORT
69 const char *hal_common_get_backend_symbol_name(enum hal_module module)
70 {
71         struct __hal_module_info *info = NULL;
72
73         /* Check parameter whether is valid or not */
74         if (module <= HAL_MODULE_UNKNOWN || module >= HAL_MODULE_END) {
75                 _E("Invalid paramer of HAL module (%d)\n", module);
76                 return NULL;
77         }
78
79         info = _hal_api_conf_get_module_info(module);
80         if (info == NULL) {
81                 _E("Failed to get HAL module(%d) information\n", module);
82                 return NULL;
83         }
84
85         if (!info->symbol_name) {
86                 _E("%s backend library name is NULL\n", info->module_name);
87                 return NULL;
88         }
89
90         return info->symbol_name;
91 }
92
93 EXPORT
94 int hal_common_get_backend(enum hal_module module, void **data)
95 {
96         struct __hal_module_info *info = NULL;
97         void *handle = NULL;
98         hal_backend *backend;
99         const char *library_name, *symbol_name;
100         int ret = 0;
101
102         /* Check parameter whether is valid or not */
103         if (module <= HAL_MODULE_UNKNOWN || module >= HAL_MODULE_END) {
104                 _E("Invalid parameter of HAL module (%d)\n", module);
105                 return TIZEN_ERROR_INVALID_PARAMETER;
106         }
107
108         G_LOCK(hal_common_lock);
109
110         info = _hal_api_conf_get_module_info(module);
111         if (info == NULL) {
112                 _E("Failed to get HAL module(%d) information\n", module);
113                 ret = TIZEN_ERROR_UNKNOWN;
114                 goto err;
115         }
116
117         if (info->usage_count == 0) {
118                 /*
119                  * Load HAL backend library at first loading time
120                  * when usage_count is 0.
121                  */
122                 library_name = hal_common_get_backend_library_name(module);
123                 if (!library_name) {
124                         _E("%s: Failed to get backend library name\n",
125                                         info->module_name);
126                         ret = TIZEN_ERROR_INVALID_PARAMETER;
127                         goto err;
128                 }
129
130                 handle = dlopen(library_name, RTLD_LAZY);
131                 if (!handle) {
132                         _E("%s: Failed to load shared library (%s)\n",
133                                         info->module_name, dlerror());
134                         ret = TIZEN_ERROR_INVALID_PARAMETER;
135                         goto err;
136                 }
137
138                 symbol_name = hal_common_get_backend_symbol_name(module);
139                 if (!symbol_name) {
140                         _E("%s: Failed to get backend symbol name\n",
141                                         info->module_name);
142                         ret = TIZEN_ERROR_INVALID_PARAMETER;
143                         goto err_dlclose;
144                 }
145
146                 backend = dlsym(handle, symbol_name);
147                 if (!backend) {
148                         _E("%s: Failed to find backend data (%s)\n",
149                                         info->module_name, dlerror());
150                         ret = TIZEN_ERROR_INVALID_PARAMETER;
151                         goto err_dlclose;
152                 }
153
154                 info->library_backend = backend;
155                 info->library_handle = handle;
156         } else {
157                 /*
158                  * Re-use the already loaded HAL backend instance
159                  * when usage_count is larger than 0.
160                  */
161                 backend = info->library_backend;
162         }
163
164         /* Check HAL ABI Version */
165         ret = hal_common_check_backend_abi_version(module, backend->abi_version);
166         if (ret < 0) {
167                 _E("%s: Failed to check ABI version\n",
168                                 info->module_name);
169                 ret = TIZEN_ERROR_INVALID_PARAMETER;
170                 goto err_dlclose;
171         }
172
173         /* Get the backend module data */
174         if (!backend->init) {
175                 _E("%s: hal_backend->init() is NULL\n",
176                                 info->module_name);
177                 ret = TIZEN_ERROR_INVALID_PARAMETER;
178                 goto err_dlclose;
179         }
180
181         ret = backend->init(data);
182         if (ret < 0) {
183                 _E("%s: Failed to initialize backend: name(%s)/vendor(%s)\n",
184                                 info->module_name, backend->name, backend->vendor);
185                 ret = TIZEN_ERROR_INVALID_PARAMETER;
186                 goto err_dlclose;
187         }
188
189         _I("%s: Get HAL backend: name(%s)/vendor(%s)\n",
190                         info->module_name, backend->name, backend->vendor);
191
192         info->usage_count++;
193
194         G_UNLOCK(hal_common_lock);
195         return TIZEN_ERROR_NONE;
196
197 err_dlclose:
198         dlclose(handle);
199 err:
200         G_UNLOCK(hal_common_lock);
201         return ret;
202 }
203
204 EXPORT
205 int hal_common_put_backend(enum hal_module module, void *data)
206 {
207         struct __hal_module_info *info = NULL;
208         hal_backend *backend = NULL;
209         void *handle = NULL;
210         int ret;
211
212         /* Check parameter whether is valid or not */
213         if (module <= HAL_MODULE_UNKNOWN || module >= HAL_MODULE_END) {
214                 _E("Invalid parameter of HAL module (%d)\n", module);
215                 return TIZEN_ERROR_INVALID_PARAMETER;
216         }
217
218         G_LOCK(hal_common_lock);
219
220         info = _hal_api_conf_get_module_info(module);
221         if (info == NULL) {
222                 _E("Failed to get HAL module(%d) information\n", module);
223                 ret =  TIZEN_ERROR_UNKNOWN;
224                 goto out;
225         }
226
227         backend = info->library_backend;
228         handle = info->library_handle;
229
230         if (!backend || info->usage_count == 0) {
231                 _I("%s: Already fully put for HAL module\n", info->module_name);
232                 ret = TIZEN_ERROR_NONE;
233                 goto out;
234         }
235
236         info->usage_count--;
237         if (info->usage_count > 0) {
238                 ret = TIZEN_ERROR_NONE;
239                 goto out;
240         }
241
242         if (backend->exit) {
243                 ret = backend->exit(data);
244                 if (ret < 0) {
245                         _E("%s: Failed to exit backend: name(%s)/vendor(%s)\n",
246                                 info->module_name, backend->name, backend->vendor);
247                         ret = TIZEN_ERROR_INVALID_PARAMETER;
248                         goto out;
249                 }
250         }
251
252         _I("%s: Put HAL backend: name(%s)/vendor(%s)\n",
253                         info->module_name, backend->name, backend->vendor);
254
255         if (handle)
256                 dlclose(handle);
257
258         info->library_backend = NULL;
259         info->library_handle = NULL;
260
261 out:
262         G_UNLOCK(hal_common_lock);
263         return ret;
264 }
265
266 EXPORT
267 int hal_common_check_backend_abi_version(enum hal_module module,
268                                 enum hal_abi_version abi_version)
269 {
270         struct __hal_module_info *info = NULL;
271         int i;
272
273         /* Check parameter whether is valid or not */
274         if (module <= HAL_MODULE_UNKNOWN || module >= HAL_MODULE_END) {
275                 _E("Invalid paramer of HAL module(%d)\n", module);
276                 return TIZEN_ERROR_INVALID_PARAMETER;
277         }
278
279         if (abi_version <= HAL_ABI_VERSION_UNKNOWN
280                         || abi_version >= HAL_ABI_VERSION_END) {
281                 _E("Invalid paramer of HAL ABI version(%d) for HAL module(%d)\n",
282                                 abi_version, module);
283                 return TIZEN_ERROR_INVALID_PARAMETER;
284         }
285
286         info = _hal_api_conf_get_module_info(module);
287         if (info == NULL) {
288                 _E("Failed to get HAL module(%d) information\n", module);
289                 return TIZEN_ERROR_UNKNOWN;
290         }
291
292         /* Check abi_version whether is supported or not */
293         if (!info->hal_api) {
294                 _E("%s: Doesn't support HAL API\n", info->module_name);
295                 return TIZEN_ERROR_INVALID_PARAMETER;
296         }
297
298         if (!info->num_abi_versions
299                         || !info->abi_versions) {
300                 _E("%s: Doesn't have the ABI version information\n",
301                                                 info->module_name);
302                 return TIZEN_ERROR_INVALID_PARAMETER;
303         }
304
305         g_platform_curr_abi_version = _hal_api_conf_get_platform_abi_version();
306         for (i = 0; i < info->num_abi_versions; i++) {
307                 struct hal_abi_version_match *data
308                                 = &info->abi_versions[i];
309
310                 if (g_platform_curr_abi_version != data->platform_abi_version)
311                         continue;
312
313                 if (data->backend_min_abi_version <= HAL_ABI_VERSION_UNKNOWN ||
314                         data->backend_min_abi_version >= HAL_ABI_VERSION_END) {
315                         _E("%s: abi_versions[%d].backend_min_abi_version(%d) is invalid\n",
316                                 info->module_name, i, data->backend_min_abi_version);
317                         return TIZEN_ERROR_INVALID_PARAMETER;
318                 }
319
320                 if (abi_version <= data->platform_abi_version
321                                 && abi_version >= data->backend_min_abi_version)
322                         return TIZEN_ERROR_NONE;
323
324                 _E("%s: \'%s\' doesn't support \'%s\'\n",
325                                 info->module_name,
326                                 hal_abi_version_str[g_platform_curr_abi_version],
327                                 hal_abi_version_str[abi_version]);
328                 _E("%s: Must use ABI versions from \'%s\' to \'%s\'\n",
329                                 info->module_name,
330                                 hal_abi_version_str[data->backend_min_abi_version],
331                                 hal_abi_version_str[data->platform_abi_version]);
332         }
333
334         return TIZEN_ERROR_INVALID_PARAMETER;
335 }