* Copyright (c) Huawei Technologies Co., Ltd. 2024. All rights reserved.
* 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 <cerrno>
#include <cstdint>
#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 <unistd.h>
#include "sync.h"
#include "wifi_hal.h"
#include "common.h"
#include "cpp_bindings.h"
#include <sys/stat.h>
#define ARRAYSIZE(a) (unsigned char)(sizeof(a) / sizeof((a)[0]))
#define HAL_VERSION "DEFAULT vendor HAL"
typedef enum {
LOGGER_ATTRIBUTE_INVALID = 0,
LOGGER_ATTRIBUTE_DRIVER_VER = 1,
LOGGER_ATTRIBUTE_FW_VER = 2,
LOGGER_ATTRIBUTE_RING_ID = 3,
LOGGER_ATTRIBUTE_RING_NAME = 4,
LOGGER_ATTRIBUTE_RING_FLAGS = 5,
LOGGER_ATTRIBUTE_LOG_LEVEL = 6,
LOGGER_ATTRIBUTE_LOG_TIME_INTVAL = 7,
LOGGER_ATTRIBUTE_LOG_MIN_DATA_SIZE = 8,
LOGGER_ATTRIBUTE_FW_DUMP_LEN = 9,
LOGGER_ATTRIBUTE_FW_DUMP_DATA = 10,
LOGGER_ATTRIBUTE_FW_ERR_CODE = 11,
LOGGER_ATTRIBUTE_RING_DATA = 12,
LOGGER_ATTRIBUTE_RING_STATUS = 13,
LOGGER_ATTRIBUTE_RING_NUM = 14,
LOGGER_ATTRIBUTE_DRIVER_DUMP_LEN = 15,
LOGGER_ATTRIBUTE_DRIVER_DUMP_DATA = 16,
LOGGER_ATTRIBUTE_PKT_FATE_NUM = 17,
LOGGER_ATTRIBUTE_PKT_FATE_DATA = 18,
LOGGER_ATTRIBUTE_HANG_REASON = 19,
LOGGER_ATTRIBUTE_MAX
} LOGGER_ATTRIBUTE;
constexpr int32_t HAL_RESTART_ID = 2;
class SetRestartHandler : public WifiCommand {
VendorHalRestartHandler mHandler;
char *mBuff;
public:
SetRestartHandler(wifiHandle handle, int id, VendorHalRestartHandler handler)
: WifiCommand("SetRestartHandler", handle, id), mHandler(handler), mBuff(nullptr)
{ }
int Start()
{
HDF_LOGI("Start Restart Handler");
RegisterVendorHandler(BRCM_OUI, BRCM_VENDOR_EVENT_HANGED);
return HAL_SUCCESS;
}
int Cancel() override
{
HDF_LOGI("Clear Restart Handler");
UnregisterVendorHandler(BRCM_OUI, BRCM_VENDOR_EVENT_HANGED);
WifiUnregisterCmd(WifiHandle(), Id());
HDF_LOGI("Success to clear restarthandler");
return HAL_SUCCESS;
}
int HandleResponse(WifiEvent& reply) override
{
return NL_OK;
}
int HandleEvent(WifiEvent& event) override
{
nlattr *vendorData = event.GetAttribute(NL80211_ATTR_VENDOR_DATA);
int len = event.GetVendorDataLen();
int eventId = event.GetVendorSubcmd();
HDF_LOGI("Got event: %d", eventId);
if (vendorData == nullptr || len == 0) {
HDF_LOGE("No Debug data found");
return NL_SKIP;
}
if (eventId == BRCM_VENDOR_EVENT_HANGED) {
for (NlIterator it(vendorData); it.HasNext(); it.Next()) {
if (it.GetType() == LOGGER_ATTRIBUTE_HANG_REASON) {
mBuff = (char *)it.GetData();
} else {
HDF_LOGI("Ignoring invalid attribute type = %d, size = %d",
it.GetType(), it.GetLen());
}
}
if (*mHandler.onVendorHalRestart) {
(*mHandler.onVendorHalRestart)(mBuff);
HDF_LOGI("Hang event received.");
} else {
HDF_LOGI("No Restart handler registered");
}
}
return NL_OK;
}
};
class SubSystemRestart : public WifiCommand {
public:
explicit SubSystemRestart(wifiInterfaceHandle iface)
: WifiCommand("SubSystemRestart", iface, 0)
{ }
int CreateRequest(WifiRequest& request)
{
int result = request.Create(HAL_OUI, WIFI_SUBCMD_TRIGGER_SSR);
if (result < 0) {
return result;
}
nlattr *data = request.AttrStart(NL80211_ATTR_VENDOR_DATA);
request.AttrEnd(data);
return HAL_SUCCESS;
}
int Create() override
{
WifiRequest request(FamilyId(), IfaceId());
int result = CreateRequest(request);
if (result < 0) {
HDF_LOGE("Failed to create ssr request result = %d\n", result);
return result;
}
result = RequestResponse(request);
if (result != HAL_SUCCESS) {
HDF_LOGE("Failed to register ssr response; result = %d\n", result);
}
return result;
}
protected:
int HandleResponse(WifiEvent& reply) override
{
return NL_OK;
}
int HandleEvent(WifiEvent& event) override
{
return NL_SKIP;
}
};
WifiError VendorHalSetRestartHandler(wifiHandle handle, VendorHalRestartHandler handler)
{
HalInfo *info = nullptr;
info = (HalInfo *)handle;
if (info == nullptr) {
HDF_LOGE("Could not find hal info\n");
return HAL_UNKNOWN;
}
SetRestartHandler *cmd = new SetRestartHandler(handle, HAL_RESTART_ID, handler);
NULL_CHECK_RETURN(cmd, "memory allocation failure", HAL_OUT_OF_MEMORY);
WifiError result = WifiRegisterCmd(handle, HAL_RESTART_ID, cmd);
if (result != HAL_SUCCESS) {
cmd->ReleaseRef();
return result;
}
result = (WifiError)cmd->Start();
if (result != HAL_SUCCESS) {
WifiUnregisterCmd(handle, HAL_RESTART_ID);
cmd->ReleaseRef();
return result;
}
HDF_LOGI("Register SSR handler");
info->restartHandler = handler;
return result;
}
WifiError TriggerVendorHalRestart(wifiHandle handle)
{
WifiError result = HAL_SUCCESS;
HalInfo *info = nullptr;
char errorStr[20];
SubSystemRestart *cmd = nullptr;
wifiInterfaceHandle *ifaceHandles = nullptr;
wifiInterfaceHandle wlan0Handle;
int numIfaceHandles = 0;
info = (HalInfo *)handle;
if (handle == NULL || info == nullptr) {
HDF_LOGE("Could not find hal info\n");
result = HAL_UNKNOWN;
goto exit;
}
HDF_LOGI("Trigger subsystem restart\n");
wlan0Handle = WifiGetWlanInterface((wifiHandle)handle, ifaceHandles, numIfaceHandles);
cmd = new SubSystemRestart(wlan0Handle);
NULL_CHECK_RETURN(cmd, "memory allocation failure", HAL_OUT_OF_MEMORY);
result = (WifiError)cmd->Create();
if (result != HAL_SUCCESS) {
cmd->ReleaseRef();
if (strncpy_s(errorStr, sizeof(errorStr), "HAL_UNKNOWN", sizeof("HAL_UNKNOWN")) != EOK) {
return result;
}
HDF_LOGE("Failed to create SSR");
return result;
}
if (strncpy_s(errorStr, sizeof(errorStr), "HAL_SUCCESS", sizeof("HAL_SUCCESS")) != EOK) {
goto exit;
}
exit:
if (cmd != nullptr) {
cmd->ReleaseRef();
}
if (info->restartHandler.onVendorHalRestart) {
HDF_LOGI("Trigger ssr handler registered handler");
(info->restartHandler.onVendorHalRestart)(errorStr);
} else {
HDF_LOGI("No trigger ssr handler registered");
}
return result;
}