1/*
2 * Copyright (C) 2008 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 <hardware/hardware.h>
18
19#include <cutils/properties.h>
20
21#include <dlfcn.h>
22#include <string.h>
23#include <pthread.h>
24#include <errno.h>
25#include <limits.h>
26
27#define LOG_TAG "HAL"
28#include <utils/Log.h>
29
30/** Base path of the hal modules */
31#define HAL_LIBRARY_PATH1 "/system/lib/hw"
32#define HAL_LIBRARY_PATH2 "/vendor/lib/hw"
33
34/**
35 * There are a set of variant filename for modules. The form of the filename
36 * is "<MODULE_ID>.variant.so" so for the led module the Dream variants
37 * of base "ro.product.board", "ro.board.platform" and "ro.arch" would be:
38 *
39 * led.trout.so
40 * led.msm7k.so
41 * led.ARMV6.so
42 * led.default.so
43 */
44
45static const char *variant_keys[] = {
46    "ro.hardware",  /* This goes first so that it can pick up a different
47                       file on the emulator. */
48    "ro.product.board",
49    "ro.board.platform",
50    "ro.arch"
51};
52
53static const int HAL_VARIANT_KEYS_COUNT =
54    (sizeof(variant_keys)/sizeof(variant_keys[0]));
55
56/**
57 * Load the file defined by the variant and if successful
58 * return the dlopen handle and the hmi.
59 * @return 0 = success, !0 = failure.
60 */
61static int load(const char *id,
62        const char *path,
63        const struct hw_module_t **pHmi)
64{
65    int status;
66    void *handle;
67    struct hw_module_t *hmi;
68
69    /*
70     * load the symbols resolving undefined symbols before
71     * dlopen returns. Since RTLD_GLOBAL is not or'd in with
72     * RTLD_NOW the external symbols will not be global
73     */
74    handle = dlopen(path, RTLD_NOW);
75    if (handle == NULL) {
76        char const *err_str = dlerror();
77        LOGE("load: module=%s\n%s", path, err_str?err_str:"unknown");
78        status = -EINVAL;
79        goto done;
80    }
81
82    /* Get the address of the struct hal_module_info. */
83    const char *sym = HAL_MODULE_INFO_SYM_AS_STR;
84    hmi = (struct hw_module_t *)dlsym(handle, sym);
85    if (hmi == NULL) {
86        LOGE("load: couldn't find symbol %s", sym);
87        status = -EINVAL;
88        goto done;
89    }
90
91    /* Check that the id matches */
92    if (strcmp(id, hmi->id) != 0) {
93        LOGE("load: id=%s != hmi->id=%s", id, hmi->id);
94        status = -EINVAL;
95        goto done;
96    }
97
98    hmi->dso = handle;
99
100    /* success */
101    status = 0;
102
103    done:
104    if (status != 0) {
105        hmi = NULL;
106        if (handle != NULL) {
107            dlclose(handle);
108            handle = NULL;
109        }
110    } else {
111        LOGV("loaded HAL id=%s path=%s hmi=%p handle=%p",
112                id, path, *pHmi, handle);
113    }
114
115    *pHmi = hmi;
116
117    return status;
118}
119
120int hw_get_module(const char *id, const struct hw_module_t **module)
121{
122    int status;
123    int i;
124    const struct hw_module_t *hmi = NULL;
125    char prop[PATH_MAX];
126    char path[PATH_MAX];
127
128    /*
129     * Here we rely on the fact that calling dlopen multiple times on
130     * the same .so will simply increment a refcount (and not load
131     * a new copy of the library).
132     * We also assume that dlopen() is thread-safe.
133     */
134
135    /* Loop through the configuration variants looking for a module */
136    for (i=0 ; i<HAL_VARIANT_KEYS_COUNT+1 ; i++) {
137        if (i < HAL_VARIANT_KEYS_COUNT) {
138            if (property_get(variant_keys[i], prop, NULL) == 0) {
139                continue;
140            }
141            snprintf(path, sizeof(path), "%s/%s.%s.so",
142                    HAL_LIBRARY_PATH1, id, prop);
143            if (access(path, R_OK) == 0) break;
144
145            snprintf(path, sizeof(path), "%s/%s.%s.so",
146                     HAL_LIBRARY_PATH2, id, prop);
147            if (access(path, R_OK) == 0) break;
148        } else {
149            snprintf(path, sizeof(path), "%s/%s.default.so",
150                     HAL_LIBRARY_PATH1, id);
151            if (access(path, R_OK) == 0) break;
152        }
153    }
154
155    status = -ENOENT;
156    if (i < HAL_VARIANT_KEYS_COUNT+1) {
157        /* load the module, if this fails, we're doomed, and we should not try
158         * to load a different variant. */
159        status = load(id, path, module);
160    }
161
162    return status;
163}
164