| /* |
| * 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 <stdint.h> |
| #include <fcntl.h> |
| #include <sys/socket.h> |
| #include <netlink/genl/genl.h> |
| #include <netlink/genl/family.h> |
| #include <netlink/genl/ctrl.h> |
| #include <linux/rtnetlink.h> |
| #include <netpacket/packet.h> |
| #include <linux/filter.h> |
| #include <linux/errqueue.h> |
| |
| #include <linux/pkt_sched.h> |
| #include <netlink/object-api.h> |
| #include <netlink/netlink.h> |
| #include <netlink/socket.h> |
| #include <netlink/handlers.h> |
| |
| #include <hardware_legacy/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; |
| } |