/* * Copyright (C) 2017 The Android Open Source Project * * Portions copyright (C) 2017 Broadcom Limited * * 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 #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "wifi_hal.h" #include "common.h" #include "cpp_bindings.h" /* test mode flag for halutil only */ bool halutil_mode = false; interface_info *getIfaceInfo(wifi_interface_handle handle) { return (interface_info *)handle; } wifi_handle getWifiHandle(wifi_interface_handle handle) { return getIfaceInfo(handle)->handle; } hal_info *getHalInfo(wifi_handle handle) { return (hal_info *)handle; } hal_info *getHalInfo(wifi_interface_handle handle) { return getHalInfo(getWifiHandle(handle)); } wifi_handle getWifiHandle(hal_info *info) { return (wifi_handle)info; } wifi_interface_handle getIfaceHandle(interface_info *info) { return (wifi_interface_handle)info; } wifi_error wifi_register_handler(wifi_handle handle, int cmd, nl_recvmsg_msg_cb_t func, void *arg) { hal_info *info = (hal_info *)handle; /* TODO: check for multiple handlers? */ pthread_mutex_lock(&info->cb_lock); wifi_error result = WIFI_ERROR_OUT_OF_MEMORY; if (info->num_event_cb < info->alloc_event_cb) { info->event_cb[info->num_event_cb].nl_cmd = cmd; info->event_cb[info->num_event_cb].vendor_id = 0; info->event_cb[info->num_event_cb].vendor_subcmd = 0; info->event_cb[info->num_event_cb].cb_func = func; info->event_cb[info->num_event_cb].cb_arg = arg; ALOGV("Successfully added event handler %p:%p for command %d at %d", arg, func, cmd, info->num_event_cb); info->num_event_cb++; result = WIFI_SUCCESS; } pthread_mutex_unlock(&info->cb_lock); return result; } wifi_error wifi_register_vendor_handler(wifi_handle handle, uint32_t id, int subcmd, nl_recvmsg_msg_cb_t func, void *arg) { hal_info *info = (hal_info *)handle; /* TODO: check for multiple handlers? */ pthread_mutex_lock(&info->cb_lock); wifi_error result = WIFI_ERROR_OUT_OF_MEMORY; if (info->num_event_cb < info->alloc_event_cb) { /* To avoid an unwanted duplication of the record, find first. * Update it if the same record is already exist. * KEY => [nl_cmd, vendor_id, vendor_subcmd] */ int i = 0; bool is_update = false; for (i = 0; i < info->num_event_cb; i++) { if ((info->event_cb[i].nl_cmd == NL80211_CMD_VENDOR) && (info->event_cb[i].vendor_id == id) && (info->event_cb[i].vendor_subcmd == subcmd)) { is_update = true; break; } } if (is_update) { info->event_cb[i].cb_func = func; info->event_cb[i].cb_arg = arg; } else { info->event_cb[info->num_event_cb].nl_cmd = NL80211_CMD_VENDOR; info->event_cb[info->num_event_cb].vendor_id = id; info->event_cb[info->num_event_cb].vendor_subcmd = subcmd; info->event_cb[info->num_event_cb].cb_func = func; info->event_cb[info->num_event_cb].cb_arg = arg; info->num_event_cb++; } ALOGI("%s ""event handler %p:%p for vendor 0x%0x and subcmd 0x%0x at %d", is_update ? "Updated" : "Added", arg, func, id, subcmd, info->num_event_cb); result = WIFI_SUCCESS; } pthread_mutex_unlock(&info->cb_lock); return result; } void wifi_unregister_handler(wifi_handle handle, int cmd) { hal_info *info = (hal_info *)handle; if (cmd == NL80211_CMD_VENDOR) { ALOGE("Must use wifi_unregister_vendor_handler to remove vendor handlers"); return; } pthread_mutex_lock(&info->cb_lock); for (int i = 0; i < info->num_event_cb; i++) { if (info->event_cb[i].nl_cmd == cmd) { ALOGV("Successfully removed event handler %p:%p for cmd = 0x%0x from %d", info->event_cb[i].cb_arg, info->event_cb[i].cb_func, cmd, i); memmove(&info->event_cb[i], &info->event_cb[i+1], (info->num_event_cb - i - 1) * sizeof(cb_info)); info->num_event_cb--; break; } } pthread_mutex_unlock(&info->cb_lock); } void wifi_unregister_vendor_handler_without_lock(wifi_handle handle, uint32_t id, int subcmd) { hal_info *info = (hal_info *)handle; for (int i = 0; i < info->num_event_cb; i++) { if (info->event_cb[i].nl_cmd == NL80211_CMD_VENDOR && info->event_cb[i].vendor_id == id && info->event_cb[i].vendor_subcmd == subcmd) { ALOGI("Successfully removed event handler %p:%p for vendor 0x%0x, subcmd 0x%0x from %d", info->event_cb[i].cb_arg, info->event_cb[i].cb_func, id, subcmd, i); memmove(&info->event_cb[i], &info->event_cb[i+1], (info->num_event_cb - i - 1) * sizeof(cb_info)); info->num_event_cb--; break; } } } void wifi_unregister_vendor_handler(wifi_handle handle, uint32_t id, int subcmd) { hal_info *info = (hal_info *)handle; pthread_mutex_lock(&info->cb_lock); wifi_unregister_vendor_handler_without_lock(handle, id, subcmd); pthread_mutex_unlock(&info->cb_lock); } wifi_error wifi_register_cmd(wifi_handle handle, int id, WifiCommand *cmd) { hal_info *info = (hal_info *)handle; ALOGV("registering command %d", id); wifi_error result = WIFI_ERROR_OUT_OF_MEMORY; if (info->num_cmd < info->alloc_cmd) { info->cmd[info->num_cmd].id = id; info->cmd[info->num_cmd].cmd = cmd; ALOGV("Successfully added command %d: %p at %d", id, cmd, info->num_cmd); info->num_cmd++; result = WIFI_SUCCESS; } else { ALOGE("Failed to add command %d: %p at %d, reached max limit %d", id, cmd, info->num_cmd, info->alloc_cmd); } return result; } WifiCommand *wifi_unregister_cmd(wifi_handle handle, int id) { hal_info *info = (hal_info *)handle; ALOGV("un-registering command %d", id); WifiCommand *cmd = NULL; for (int i = 0; i < info->num_cmd; i++) { if (info->cmd[i].id == id) { cmd = info->cmd[i].cmd; memmove(&info->cmd[i], &info->cmd[i+1], (info->num_cmd - i - 1) * sizeof(cmd_info)); info->num_cmd--; ALOGV("Successfully removed command %d: %p from %d", id, cmd, i); break; } } if (!cmd) { ALOGI("Failed to remove command %d: %p", id, cmd); } return cmd; } WifiCommand *wifi_get_cmd(wifi_handle handle, int id) { hal_info *info = (hal_info *)handle; WifiCommand *cmd = NULL; for (int i = 0; i < info->num_cmd; i++) { if (info->cmd[i].id == id) { cmd = info->cmd[i].cmd; break; } } return cmd; } void wifi_unregister_cmd(wifi_handle handle, WifiCommand *cmd) { hal_info *info = (hal_info *)handle; for (int i = 0; i < info->num_cmd; i++) { if (info->cmd[i].cmd == cmd) { int id = info->cmd[i].id; memmove(&info->cmd[i], &info->cmd[i+1], (info->num_cmd - i - 1) * sizeof(cmd_info)); info->num_cmd--; ALOGV("Successfully removed command %d: %p from %d", id, cmd, i); break; } } } wifi_error wifi_cancel_cmd(wifi_request_id id, wifi_interface_handle iface) { wifi_handle handle = getWifiHandle(iface); WifiCommand *cmd = wifi_unregister_cmd(handle, id); ALOGV("Cancel WifiCommand = %p", cmd); if (cmd) { cmd->cancel(); cmd->releaseRef(); return WIFI_SUCCESS; } return WIFI_ERROR_INVALID_ARGS; } wifi_error wifi_get_cancel_cmd(wifi_request_id id, wifi_interface_handle iface) { wifi_handle handle = getWifiHandle(iface); WifiCommand *cmd = wifi_get_cmd(handle, id); ALOGV("Get Cancel WifiCommand = %p", cmd); if (cmd) { cmd->cancel(); cmd->releaseRef(); return WIFI_SUCCESS; } return WIFI_ERROR_INVALID_ARGS; } void set_hautil_mode(bool util_mode) { halutil_mode = util_mode; } bool get_halutil_mode() { return halutil_mode; }