You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
307 lines
8.7 KiB
307 lines
8.7 KiB
/*
|
|
* 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 "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(wifi_handle handle, uint32_t id, int subcmd)
|
|
{
|
|
hal_info *info = (hal_info *)handle;
|
|
|
|
pthread_mutex_lock(&info->cb_lock);
|
|
|
|
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;
|
|
}
|
|
}
|
|
|
|
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;
|
|
}
|