1/*
2 * Copyright (C) 2014 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 <stdlib.h>
18#include <linux/pkt_sched.h>
19#include <netlink/object-api.h>
20#include <netlink-private/object-api.h>
21#include <netlink-private/types.h>
22#include <dlfcn.h>
23#include <pthread.h>
24
25#include "wifi_hal.h"
26#include "common.h"
27#include <errno.h>
28
29interface_info *getIfaceInfo(wifi_interface_handle handle)
30{
31    return (interface_info *)handle;
32}
33
34wifi_handle getWifiHandle(wifi_interface_handle handle)
35{
36    return getIfaceInfo(handle)->handle;
37}
38
39hal_info *getHalInfo(wifi_handle handle)
40{
41    return (hal_info *)handle;
42}
43
44hal_info *getHalInfo(wifi_interface_handle handle)
45{
46    return getHalInfo(getWifiHandle(handle));
47}
48
49wifi_handle getWifiHandle(hal_info *info)
50{
51    return (wifi_handle)info;
52}
53
54wifi_interface_handle getIfaceHandle(interface_info *info)
55{
56    return (wifi_interface_handle)info;
57}
58
59wifi_error wifi_register_handler(wifi_handle handle, int cmd, nl_recvmsg_msg_cb_t func, void *arg)
60{
61    hal_info *info = (hal_info *)handle;
62
63    pthread_mutex_lock(&info->cb_lock);
64
65    wifi_error result = WIFI_ERROR_OUT_OF_MEMORY;
66
67    for (int i = 0; i < info->num_event_cb; i++) {
68        if(info->event_cb[i].nl_cmd == cmd &&
69           info->event_cb[i].cb_arg == arg) {
70            info->event_cb[i].cb_func = func;
71            ALOGV("Updated event handler %p for nl_cmd 0x%0x"
72                    " and arg %p", func, cmd, arg);
73            pthread_mutex_unlock(&info->cb_lock);
74            return WIFI_SUCCESS;
75        }
76    }
77
78    if (info->num_event_cb < info->alloc_event_cb) {
79        info->event_cb[info->num_event_cb].nl_cmd  = cmd;
80        info->event_cb[info->num_event_cb].vendor_id  = 0;
81        info->event_cb[info->num_event_cb].vendor_subcmd  = 0;
82        info->event_cb[info->num_event_cb].cb_func = func;
83        info->event_cb[info->num_event_cb].cb_arg  = arg;
84        info->num_event_cb++;
85        ALOGV("Successfully added event handler %p for command %d", func, cmd);
86        result = WIFI_SUCCESS;
87    } else {
88        result = WIFI_ERROR_OUT_OF_MEMORY;
89    }
90
91    pthread_mutex_unlock(&info->cb_lock);
92    return result;
93}
94
95wifi_error wifi_register_vendor_handler(wifi_handle handle,
96        uint32_t id, int subcmd, nl_recvmsg_msg_cb_t func, void *arg)
97{
98    hal_info *info = (hal_info *)handle;
99
100    pthread_mutex_lock(&info->cb_lock);
101
102    wifi_error result = WIFI_ERROR_OUT_OF_MEMORY;
103
104    for (int i = 0; i < info->num_event_cb; i++) {
105        if(info->event_cb[i].vendor_id  == id &&
106           info->event_cb[i].vendor_subcmd == subcmd)
107        {
108            info->event_cb[i].cb_func = func;
109            info->event_cb[i].cb_arg  = arg;
110            ALOGV("Updated event handler %p for vendor 0x%0x, subcmd 0x%0x"
111                " and arg %p", func, id, subcmd, arg);
112            pthread_mutex_unlock(&info->cb_lock);
113            return WIFI_SUCCESS;
114        }
115    }
116
117    if (info->num_event_cb < info->alloc_event_cb) {
118        info->event_cb[info->num_event_cb].nl_cmd  = NL80211_CMD_VENDOR;
119        info->event_cb[info->num_event_cb].vendor_id  = id;
120        info->event_cb[info->num_event_cb].vendor_subcmd  = subcmd;
121        info->event_cb[info->num_event_cb].cb_func = func;
122        info->event_cb[info->num_event_cb].cb_arg  = arg;
123        info->num_event_cb++;
124        ALOGV("Added event handler %p for vendor 0x%0x, subcmd 0x%0x and arg"
125            " %p", func, id, subcmd, arg);
126        result = WIFI_SUCCESS;
127    } else {
128        result = WIFI_ERROR_OUT_OF_MEMORY;
129    }
130
131    pthread_mutex_unlock(&info->cb_lock);
132    return result;
133}
134
135void wifi_unregister_handler(wifi_handle handle, int cmd)
136{
137    hal_info *info = (hal_info *)handle;
138
139    if (cmd == NL80211_CMD_VENDOR) {
140        ALOGE("Must use wifi_unregister_vendor_handler to remove vendor handlers");
141        return;
142    }
143
144    pthread_mutex_lock(&info->cb_lock);
145
146    for (int i = 0; i < info->num_event_cb; i++) {
147        if (info->event_cb[i].nl_cmd == cmd) {
148            if(i < info->num_event_cb-1) {
149                /* No need to memmove if only one entry exist and deleting
150                 * the same, as the num_event_cb will become 0 in this case.
151                 */
152                memmove(&info->event_cb[i], &info->event_cb[i+1],
153                        (info->num_event_cb - i) * sizeof(cb_info));
154            }
155            info->num_event_cb--;
156            ALOGV("Successfully removed event handler for command %d", cmd);
157            break;
158        }
159    }
160
161    pthread_mutex_unlock(&info->cb_lock);
162}
163
164void wifi_unregister_vendor_handler(wifi_handle handle, uint32_t id, int subcmd)
165{
166    hal_info *info = (hal_info *)handle;
167
168    pthread_mutex_lock(&info->cb_lock);
169
170    for (int i = 0; i < info->num_event_cb; i++) {
171
172        if (info->event_cb[i].nl_cmd == NL80211_CMD_VENDOR
173                && info->event_cb[i].vendor_id == id
174                && info->event_cb[i].vendor_subcmd == subcmd) {
175            if(i < info->num_event_cb-1) {
176                /* No need to memmove if only one entry exist and deleting
177                 * the same, as the num_event_cb will become 0 in this case.
178                 */
179                memmove(&info->event_cb[i], &info->event_cb[i+1],
180                        (info->num_event_cb - i) * sizeof(cb_info));
181            }
182            info->num_event_cb--;
183            ALOGV("Successfully removed event handler for vendor 0x%0x", id);
184            break;
185        }
186    }
187
188    pthread_mutex_unlock(&info->cb_lock);
189}
190
191
192#ifdef __cplusplus
193extern "C"
194{
195#endif /* __cplusplus */
196
197void hexdump(void *buf, u16 len)
198{
199    int i=0;
200    char *bytes = (char *)buf;
201
202    if (len) {
203        ALOGV("******HexDump len:%d*********", len);
204        for (i = 0; ((i + 7) < len); i+=8) {
205            ALOGV("%02x %02x %02x %02x   %02x %02x %02x %02x",
206                bytes[i], bytes[i+1],
207                bytes[i+2], bytes[i+3],
208                bytes[i+4], bytes[i+5],
209                bytes[i+6], bytes[i+7]);
210        }
211        if ((len - i) >= 4) {
212            ALOGV("%02x %02x %02x %02x",
213                bytes[i], bytes[i+1],
214                bytes[i+2], bytes[i+3]);
215            i+=4;
216        }
217        for (;i < len;i++) {
218            ALOGV("%02x", bytes[i]);
219        }
220        ALOGV("******HexDump End***********");
221    } else {
222        return;
223    }
224}
225
226/* Firmware sends RSSI value without noise floor.
227 * Add noise floor to the same and return absolute values.
228 */
229u8 get_rssi(u8 rssi_wo_noise_floor)
230{
231    return abs((int)rssi_wo_noise_floor - 96);
232}
233
234#ifdef __cplusplus
235}
236#endif /* __cplusplus */
237
238/* Pointer to the table of LOWI callback funcs */
239lowi_cb_table_t *LowiWifiHalApi = NULL;
240/* LowiSupportedCapabilities read */
241u32 lowiSupportedCapabilities = 0;
242
243int compareLowiVersion(u16 major, u16 minor, u16 micro)
244{
245    u32 currVersion = 0x10000*(WIFIHAL_LOWI_MAJOR_VERSION) + \
246                      0x100*(WIFIHAL_LOWI_MINOR_VERSION) + \
247                      WIFIHAL_LOWI_MICRO_VERSION;
248
249    u32 lowiVersion = 0x10000*(major) + \
250                      0x100*(minor) + \
251                      micro;
252
253    return (memcmp(&currVersion, &lowiVersion, sizeof(u32)));
254}
255
256/*
257 * This function will open the lowi shared library and obtain the
258 * Lowi Callback table and the capabilities supported.
259 * A version check is also performed in this function and if the version
260 * check fails then the callback table returned will be NULL.
261 */
262wifi_error fetchLowiCbTableAndCapabilities(lowi_cb_table_t **lowi_wifihal_api,
263                                           bool *lowi_get_capa_supported)
264{
265    getCbTable_t* lowiCbTable = NULL;
266    int ret = 0;
267    wifi_error retVal = WIFI_SUCCESS;
268
269    *lowi_wifihal_api = NULL;
270    *lowi_get_capa_supported = false;
271
272#if __WORDSIZE == 64
273    void* lowi_handle = dlopen("/vendor/lib64/liblowi_wifihal.so", RTLD_NOW);
274#else
275    void* lowi_handle = dlopen("/vendor/lib/liblowi_wifihal.so", RTLD_NOW);
276#endif
277    if (!lowi_handle) {
278        ALOGE("%s: NULL lowi_handle, err: %s", __FUNCTION__, dlerror());
279        return WIFI_ERROR_UNKNOWN;
280    }
281
282    lowiCbTable = (getCbTable_t*)dlsym(lowi_handle,
283                                       "lowi_wifihal_get_cb_table");
284    if (!lowiCbTable) {
285        ALOGE("%s: NULL lowi callback table", __FUNCTION__);
286        return WIFI_ERROR_UNKNOWN;
287    }
288
289    *lowi_wifihal_api = lowiCbTable();
290
291    /* First check whether lowi module implements the get_lowi_version
292     * function. All the functions in lowi module starts with
293     * "lowi_wifihal_" prefix thus the below function name.
294     */
295    if ((dlsym(lowi_handle, "lowi_wifihal_get_lowi_version") != NULL) &&
296        ((*lowi_wifihal_api)->get_lowi_version != NULL)) {
297        u16 lowiMajorVersion = WIFIHAL_LOWI_MAJOR_VERSION;
298        u16 lowiMinorVersion = WIFIHAL_LOWI_MINOR_VERSION;
299        u16 lowiMicroVersion = WIFIHAL_LOWI_MICRO_VERSION;
300        int versionCheck = -1;
301
302        ret = (*lowi_wifihal_api)->get_lowi_version(&lowiMajorVersion,
303                                                    &lowiMinorVersion,
304                                                    &lowiMicroVersion);
305        if (ret) {
306            ALOGE("%s: get_lowi_version returned error:%d",
307                  __FUNCTION__, ret);
308            retVal = WIFI_ERROR_NOT_SUPPORTED;
309            goto cleanup;
310        }
311        ALOGV("%s: Lowi version:%d.%d.%d", __FUNCTION__,
312              lowiMajorVersion, lowiMinorVersion,
313              lowiMicroVersion);
314
315        /* Compare the version with version in wifihal_internal.h */
316        versionCheck = compareLowiVersion(lowiMajorVersion,
317                                          lowiMinorVersion,
318                                          lowiMicroVersion);
319        if (versionCheck < 0) {
320            ALOGE("%s: Version Check failed:%d", __FUNCTION__,
321                  versionCheck);
322            retVal = WIFI_ERROR_NOT_SUPPORTED;
323            goto cleanup;
324        }
325    }
326    else {
327        ALOGV("%s: lowi_wifihal_get_lowi_version not present",
328              __FUNCTION__);
329    }
330
331
332    /* Check if get_lowi_capabilities func pointer exists in
333     * the lowi lib and populate lowi_get_capa_supported
334     * All the functions in lowi modules starts with
335     * "lowi_wifihal_ prefix" thus the below function name.
336     */
337    if (dlsym(lowi_handle, "lowi_wifihal_get_lowi_capabilities") != NULL) {
338        *lowi_get_capa_supported = true;
339    }
340    else {
341        ALOGV("lowi_wifihal_get_lowi_capabilities() is not supported.");
342        *lowi_get_capa_supported = false;
343    }
344cleanup:
345    if (retVal) {
346        *lowi_wifihal_api = NULL;
347    }
348    return retVal;
349}
350
351lowi_cb_table_t *getLowiCallbackTable(u32 requested_lowi_capabilities)
352{
353    int ret = WIFI_SUCCESS;
354    bool lowi_get_capabilities_support = false;
355
356    if (LowiWifiHalApi == NULL) {
357        ALOGV("%s: LowiWifiHalApi Null, Initialize Lowi",
358              __FUNCTION__);
359        ret = fetchLowiCbTableAndCapabilities(&LowiWifiHalApi,
360                                              &lowi_get_capabilities_support);
361        if (ret != WIFI_SUCCESS || LowiWifiHalApi == NULL ||
362            LowiWifiHalApi->init == NULL) {
363            ALOGE("%s: LOWI is not supported.", __FUNCTION__);
364            goto cleanup;
365        }
366        /* Initialize LOWI if it isn't up already. */
367        ret = LowiWifiHalApi->init();
368        if (ret) {
369            ALOGE("%s: failed lowi initialization. "
370                "Returned error:%d. Exit.", __FUNCTION__, ret);
371            goto cleanup;
372        }
373        if (!lowi_get_capabilities_support ||
374            LowiWifiHalApi->get_lowi_capabilities == NULL) {
375                ALOGV("%s: Allow rtt APIs thru LOWI to proceed even though "
376                      "get_lowi_capabilities() is not supported. Returning",
377                      __FUNCTION__);
378                lowiSupportedCapabilities |=
379                    (ONE_SIDED_RANGING_SUPPORTED|DUAL_SIDED_RANGING_SUPPORED);
380                return LowiWifiHalApi;
381        }
382        ret =
383            LowiWifiHalApi->get_lowi_capabilities(&lowiSupportedCapabilities);
384        if (ret) {
385            ALOGV("%s: failed to get lowi supported capabilities."
386                "Returned error:%d. Exit.", __FUNCTION__, ret);
387            goto cleanup;
388        }
389    }
390
391    if ((lowiSupportedCapabilities & requested_lowi_capabilities) == 0) {
392        return NULL;
393    }
394    return LowiWifiHalApi;
395
396cleanup:
397    if (LowiWifiHalApi && LowiWifiHalApi->destroy) {
398        ret = LowiWifiHalApi->destroy();
399    }
400    LowiWifiHalApi = NULL;
401    lowiSupportedCapabilities = 0;
402    return LowiWifiHalApi;
403}
404
405wifi_error mapKernelErrortoWifiHalError(int kern_err)
406{
407    if (kern_err >= 0)
408        return WIFI_SUCCESS;
409
410    switch (kern_err) {
411        case -EOPNOTSUPP:
412            return WIFI_ERROR_NOT_SUPPORTED;
413        case -EAGAIN:
414            return WIFI_ERROR_NOT_AVAILABLE;
415        case -EINVAL:
416            return WIFI_ERROR_INVALID_ARGS;
417        case -ETIMEDOUT:
418            return WIFI_ERROR_TIMED_OUT;
419        case -ENOMEM:
420            return WIFI_ERROR_OUT_OF_MEMORY;
421        case -EBUSY:
422            return WIFI_ERROR_BUSY;
423    }
424    return WIFI_ERROR_UNKNOWN;
425}
426