blob: 3e9abf2a3bb088aadef9c698fd1b938fb32b46e6 [file] [log] [blame]
/* Copyright (c) 2015, The Linux Foundation. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following
* disclaimer in the documentation and/or other materials provided
* with the distribution.
* * Neither the name of The Linux Foundation nor the names of its
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
* BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
* OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
* IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "sync.h"
#include "wifi_hal.h"
#include "common.h"
#include "cpp_bindings.h"
#include <errno.h>
#include <utils/Log.h>
#include "wifiloggercmd.h"
#define WIFI_MEMORY_DUMP_WAIT_TIME_SECONDS 4
#define WIFI_MEMORY_DUMP_MAX_SIZE 300000
//Singleton Static Instance
WifiLoggerCommand* WifiLoggerCommand::mWifiLoggerCommandInstance = NULL;
//Implementation of the functions exposed in wifi_logger.h
/* Function to intiate logging */
wifi_error wifi_start_logging(wifi_interface_handle iface,
u32 verbose_level, u32 flags,
u32 max_interval_sec, u32 min_data_size,
char *buffer_name)
{
int requestId, ret = 0;
WifiLoggerCommand *wifiLoggerCommand;
struct nlattr *nlData;
interface_info *ifaceInfo = getIfaceInfo(iface);
wifi_handle wifiHandle = getWifiHandle(iface);
hal_info *info = getHalInfo(wifiHandle);
/*
* No request id from caller, so generate one and pass it on to the driver.
* Generate one randomly.
*/
srand( time(NULL) );
requestId = rand();
/*
* TBD Include support to send VENDOR_CMD for PKT LOG.
* Create a Ring Buffer
*/
wifiLoggerCommand = new WifiLoggerCommand(
wifiHandle,
requestId,
OUI_QCA,
QCA_NL80211_VENDOR_SUBCMD_WIFI_LOGGER_START);
ALOGI("%s: Sending Start Logging Request. \n", __FUNCTION__);
if (wifiLoggerCommand == NULL) {
ALOGE("%s: Error WifiLoggerCommand NULL", __func__);
return WIFI_ERROR_UNKNOWN;
}
/* Create the NL message. */
ret = wifiLoggerCommand->create();
if (ret < 0)
goto cleanup;
/* Set the interface Id of the message. */
ret = wifiLoggerCommand->set_iface_id(ifaceInfo->name);
if (ret < 0)
goto cleanup;
/* Add the vendor specific attributes for the NL command. */
nlData = wifiLoggerCommand->attr_start(NL80211_ATTR_VENDOR_DATA);
if (!nlData)
goto cleanup;
wifiLoggerCommand->attr_end(nlData);
/* Send the msg and wait for a response. */
ret = wifiLoggerCommand->requestResponse();
if (ret) {
ALOGE("%s: Error %d happened. ", __func__, ret);
}
cleanup:
ALOGI("%s: Delete object.", __func__);
delete wifiLoggerCommand;
return (wifi_error)ret;
}
/* Function to get each ring related info */
wifi_error wifi_get_ring_buffers_status(wifi_interface_handle iface,
u32 *num_buffers,
wifi_ring_buffer_status **status)
{
int requestId, ret = 0;
WifiLoggerCommand *wifiLoggerCommand;
struct nlattr *nlData;
interface_info *ifaceInfo = getIfaceInfo(iface);
wifi_handle wifiHandle = getWifiHandle(iface);
hal_info *info = getHalInfo(wifiHandle);
/* No request id from caller, so generate one and pass it on to the driver.
* Generate one randomly.
*/
srand( time(NULL) );
requestId = rand();
/*
* TBD need to implement the total rings supported
* along with the ring buffer info.
*/
cleanup:
return (wifi_error)ret;
}
/* Function to get the supported feature set for logging.*/
wifi_error wifi_get_logger_supported_feature_set(wifi_interface_handle iface,
u32 *support)
{
int requestId, ret = 0;
WifiLoggerCommand *wifiLoggerCommand;
struct nlattr *nlData;
interface_info *ifaceInfo = getIfaceInfo(iface);
wifi_handle wifiHandle = getWifiHandle(iface);
hal_info *info = getHalInfo(wifiHandle);
/* No request id from caller, so generate one and pass it on to the driver.
* Generate one randomly.
*/
srand( time(NULL) );
requestId = rand();
/*
* TBD need to implement feature set supported in wifi_hal
* for the logger app.
*/
cleanup:
return (wifi_error)ret;
}
/* Function to get the data in each ring for the given ring ID.*/
wifi_error wifi_get_ring_data(wifi_interface_handle iface,
char *ring_name)
{
int requestId, ret = 0;
WifiLoggerCommand *wifiLoggerCommand;
struct nlattr *nlData;
interface_info *ifaceInfo = getIfaceInfo(iface);
wifi_handle wifiHandle = getWifiHandle(iface);
hal_info *info = getHalInfo(wifiHandle);
/* No request id from caller, so generate one and pass it on to the driver.
* Generate one randomly.
*/
srand( time(NULL) );
requestId = rand();
/*
* TBD need to implement get ring data to invoke the CB handler
* for the given ring_id.
*/
cleanup:
return (wifi_error)ret;
}
void WifiLoggerCommand::setVersionInfo(char **buffer, int *buffer_size) {
mVersion = buffer;
mVersionLen = buffer_size;
}
/* Function to send enable request to the wifi driver.*/
wifi_error wifi_get_firmware_version(wifi_interface_handle iface,
char **buffer, int *buffer_size)
{
int requestId, ret = 0;
WifiLoggerCommand *wifiLoggerCommand;
struct nlattr *nlData;
interface_info *ifaceInfo = getIfaceInfo(iface);
wifi_handle wifiHandle = getWifiHandle(iface);
hal_info *info = getHalInfo(wifiHandle);
/* No request id from caller, so generate one and pass it on to the driver.
* Generate one randomly.
*/
srand( time(NULL) );
requestId = rand();
wifiLoggerCommand = new WifiLoggerCommand(
wifiHandle,
requestId,
OUI_QCA,
QCA_NL80211_VENDOR_SUBCMD_GET_WIFI_INFO);
ALOGI("%s: Sending Get Wifi Info Request. \n", __FUNCTION__);
if (wifiLoggerCommand == NULL) {
ALOGE("%s: Error WifiLoggerCommand NULL", __func__);
return WIFI_ERROR_UNKNOWN;
}
/* Create the NL message. */
ret = wifiLoggerCommand->create();
if (ret < 0)
goto cleanup;
/* Set the interface Id of the message. */
ret = wifiLoggerCommand->set_iface_id(ifaceInfo->name);
if (ret < 0)
goto cleanup;
/* Add the vendor specific attributes for the NL command. */
nlData = wifiLoggerCommand->attr_start(NL80211_ATTR_VENDOR_DATA);
if (!nlData)
goto cleanup;
if (wifiLoggerCommand->put_u32(
QCA_WLAN_VENDOR_ATTR_WIFI_INFO_FIRMWARE_VERSION, requestId) )
{
goto cleanup;
}
wifiLoggerCommand->attr_end(nlData);
wifiLoggerCommand->setVersionInfo(buffer, buffer_size);
/* Send the msg and wait for a response. */
ret = wifiLoggerCommand->requestResponse();
if (ret) {
ALOGE("%s: Error %d happened. ", __func__, ret);
}
cleanup:
ALOGI("%s: Delete object.", __func__);
delete wifiLoggerCommand;
return (wifi_error)ret;
}
/* Function to get wlan driver version.*/
wifi_error wifi_get_driver_version(wifi_interface_handle iface,
char **buffer, int *buffer_size)
{
int requestId, ret = 0;
WifiLoggerCommand *wifiLoggerCommand;
struct nlattr *nlData;
interface_info *ifaceInfo = getIfaceInfo(iface);
wifi_handle wifiHandle = getWifiHandle(iface);
hal_info *info = getHalInfo(wifiHandle);
/* No request id from caller, so generate one and pass it on to the driver.
* Generate one randomly.
*/
srand( time(NULL) );
requestId = rand();
wifiLoggerCommand = new WifiLoggerCommand(
wifiHandle,
requestId,
OUI_QCA,
QCA_NL80211_VENDOR_SUBCMD_GET_WIFI_INFO);
ALOGI("%s: Sending Get Wifi Info Request. \n", __FUNCTION__);
if (wifiLoggerCommand == NULL) {
ALOGE("%s: Error WifiLoggerCommand NULL", __func__);
return WIFI_ERROR_UNKNOWN;
}
/* Create the NL message. */
ret = wifiLoggerCommand->create();
if (ret < 0)
goto cleanup;
/* Set the interface Id of the message. */
ret = wifiLoggerCommand->set_iface_id(ifaceInfo->name);
if (ret < 0)
goto cleanup;
/* Add the vendor specific attributes for the NL command. */
nlData = wifiLoggerCommand->attr_start(NL80211_ATTR_VENDOR_DATA);
if (!nlData)
goto cleanup;
if (wifiLoggerCommand->put_u32(
QCA_WLAN_VENDOR_ATTR_WIFI_INFO_DRIVER_VERSION, requestId))
{
goto cleanup;
}
wifiLoggerCommand->attr_end(nlData);
wifiLoggerCommand->setVersionInfo(buffer, buffer_size);
/* Send the msg and wait for a response. */
ret = wifiLoggerCommand->requestResponse();
if (ret) {
ALOGE("%s: Error %d happened. ", __func__, ret);
}
cleanup:
ALOGI("%s: Delete object.", __func__);
delete wifiLoggerCommand;
return (wifi_error)ret;
}
/* Function to get the Firmware memory dump. */
wifi_error wifi_get_firmware_memory_dump(wifi_interface_handle iface,
wifi_firmware_memory_dump_handler handler)
{
int requestId, ret = 0;
WifiLoggerCommand *wifiLoggerCommand;
struct nlattr *nlData;
interface_info *ifaceInfo = getIfaceInfo(iface);
wifi_handle wifiHandle = getWifiHandle(iface);
hal_info *info = getHalInfo(wifiHandle);
/* No request id from caller, so generate one and pass it on to the driver.
* Generate one randomly.
*/
srand( time(NULL) );
requestId = rand();
wifiLoggerCommand = new WifiLoggerCommand(
wifiHandle,
requestId,
OUI_QCA,
QCA_NL80211_VENDOR_SUBCMD_WIFI_LOGGER_MEMORY_DUMP);
ALOGI("%s: Sending Memory Dump Request. \n", __FUNCTION__);
if (wifiLoggerCommand == NULL) {
ALOGE("%s: Error WifiLoggerCommand NULL", __func__);
return WIFI_ERROR_UNKNOWN;
}
/* Create the NL message. */
ret = wifiLoggerCommand->create();
if (ret < 0)
goto cleanup;
/* Set the interface Id of the message. */
ret = wifiLoggerCommand->set_iface_id(ifaceInfo->name);
if (ret < 0)
goto cleanup;
/* Add the vendor specific attributes for the NL command. */
nlData = wifiLoggerCommand->attr_start(NL80211_ATTR_VENDOR_DATA);
if (!nlData)
goto cleanup;
wifiLoggerCommand->attr_end(nlData);
/* Send the msg and wait for a response. */
ret = wifiLoggerCommand->requestResponse();
if (ret) {
ALOGE("%s: Error %d happened. ", __func__, ret);
}
wifiLoggerCommand->mMemoryDumBuffer =(u8*)malloc(WIFI_MEMORY_DUMP_MAX_SIZE);
if (wifiLoggerCommand->mMemoryDumBuffer == NULL) {
ALOGE("%s: Failed to allocate memory", __func__);
goto cleanup;
}
/* Send the msg and wait for a response. */
ret = wifiLoggerCommand->requestResponse();
if (ret) {
ALOGE("%s: Error %d happened. ", __func__, ret);
goto cleanup;
}
cleanup:
if (wifiLoggerCommand->mMemoryDumBuffer != NULL) {
free(wifiLoggerCommand->mMemoryDumBuffer);
wifiLoggerCommand->mMemoryDumBuffer = NULL;
}
ALOGI("%s: Delete object.", __func__);
delete wifiLoggerCommand;
return (wifi_error)ret;
}
WifiLoggerCommand::WifiLoggerCommand(wifi_handle handle, int id, u32 vendor_id, u32 subcmd)
: WifiVendorCommand(handle, id, vendor_id, subcmd)
{
ALOGV("WifiLoggerCommand %p constructed", this);
mVersion = NULL;
mVersionLen = NULL;
}
WifiLoggerCommand::~WifiLoggerCommand()
{
ALOGD("WifiLoggerCommand %p destructor", this);
unregisterVendorHandler(mVendor_id, mSubcmd);
}
WifiLoggerCommand* WifiLoggerCommand::instance(wifi_handle handle)
{
if (handle == NULL) {
ALOGE("Interface Handle is invalid");
return NULL;
}
if (mWifiLoggerCommandInstance == NULL) {
mWifiLoggerCommandInstance = new WifiLoggerCommand(handle, 0,
OUI_QCA,
QCA_NL80211_VENDOR_SUBCMD_WIFI_LOGGER_START);
ALOGV("WifiLoggerCommand %p created", mWifiLoggerCommandInstance);
return mWifiLoggerCommandInstance;
}
else
{
if (handle != getWifiHandle(mWifiLoggerCommandInstance->mInfo))
{
ALOGE("Handle different");
return NULL;
}
}
ALOGV("WifiLoggerCommand %p created already", mWifiLoggerCommandInstance);
return mWifiLoggerCommandInstance;
}
/* This function implements creation of Vendor command */
int WifiLoggerCommand::create() {
int ret = mMsg.create(NL80211_CMD_VENDOR, 0, 0);
if (ret < 0) {
return ret;
}
/* Insert the oui in the msg */
ret = mMsg.put_u32(NL80211_ATTR_VENDOR_ID, mVendor_id);
if (ret < 0)
goto out;
/* Insert the subcmd in the msg */
ret = mMsg.put_u32(NL80211_ATTR_VENDOR_SUBCMD, mSubcmd);
if (ret < 0)
goto out;
ALOGI("%s: mVendor_id = %d, Subcmd = %d.",
__func__, mVendor_id, mSubcmd);
out:
return ret;
}
/* Callback handlers registered for nl message send */
static int error_handler_wifi_logger(struct sockaddr_nl *nla, struct nlmsgerr *err,
void *arg)
{
struct sockaddr_nl *tmp;
int *ret = (int *)arg;
tmp = nla;
*ret = err->error;
ALOGE("%s: Error code:%d (%s)", __func__, *ret, strerror(-(*ret)));
return NL_STOP;
}
/* Callback handlers registered for nl message send */
static int ack_handler_wifi_logger(struct nl_msg *msg, void *arg)
{
int *ret = (int *)arg;
struct nl_msg * a;
ALOGE("%s: called", __func__);
a = msg;
*ret = 0;
return NL_STOP;
}
/* Callback handlers registered for nl message send */
static int finish_handler_wifi_logger(struct nl_msg *msg, void *arg)
{
int *ret = (int *)arg;
struct nl_msg * a;
ALOGE("%s: called", __func__);
a = msg;
*ret = 0;
return NL_SKIP;
}
int WifiLoggerCommand::requestEvent()
{
int res = -1;
struct nl_cb *cb;
ALOGD("%s: Entry.", __func__);
cb = nl_cb_alloc(NL_CB_DEFAULT);
if (!cb) {
ALOGE("%s: Callback allocation failed",__func__);
res = -1;
goto out;
}
/* Send message */
ALOGE("%s:Handle:%p Socket Value:%p", __func__, mInfo, mInfo->cmd_sock);
res = nl_send_auto_complete(mInfo->cmd_sock, mMsg.getMessage());
if (res < 0)
goto out;
res = 1;
nl_cb_err(cb, NL_CB_CUSTOM, error_handler_wifi_logger, &res);
nl_cb_set(cb, NL_CB_FINISH, NL_CB_CUSTOM, finish_handler_wifi_logger, &res);
nl_cb_set(cb, NL_CB_ACK, NL_CB_CUSTOM, ack_handler_wifi_logger, &res);
/* Err is populated as part of finish_handler. */
while (res > 0){
nl_recvmsgs(mInfo->cmd_sock, cb);
}
ALOGD("%s: Msg sent, res=%d, mWaitForRsp=%d", __func__, res, mWaitforRsp);
/* Only wait for the asynchronous event if HDD returns success, res=0 */
if (!res && (mWaitforRsp == true)) {
struct timespec abstime;
abstime.tv_sec = 4;
abstime.tv_nsec = 0;
res = mCondition.wait(abstime);
if (res == ETIMEDOUT)
{
ALOGE("%s: Time out happened.", __func__);
}
ALOGD("%s: Command invoked return value:%d, mWaitForRsp=%d",
__func__, res, mWaitforRsp);
}
out:
/* Cleanup the mMsg */
mMsg.destroy();
return res;
}
int WifiLoggerCommand::requestResponse()
{
ALOGD("%s: request a response", __func__);
return WifiCommand::requestResponse(mMsg);
}
int WifiLoggerCommand::handleResponse(WifiEvent &reply) {
ALOGD("Received a WifiLogger response message from Driver");
u32 status;
int ret = WIFI_SUCCESS;
int i = 0;
u32 len = 0, version;
char version_type[20];
WifiVendorCommand::handleResponse(reply);
switch(mSubcmd)
{
case QCA_NL80211_VENDOR_SUBCMD_GET_WIFI_INFO:
{
struct nlattr *tb_vendor[QCA_WLAN_VENDOR_ATTR_WIFI_INFO_GET_MAX + 1];
nla_parse(tb_vendor, QCA_WLAN_VENDOR_ATTR_WIFI_INFO_GET_MAX,
(struct nlattr *)mVendorData, mDataLen, NULL);
if (tb_vendor[QCA_WLAN_VENDOR_ATTR_WIFI_INFO_DRIVER_VERSION]) {
len = nla_len(tb_vendor[
QCA_WLAN_VENDOR_ATTR_WIFI_INFO_DRIVER_VERSION]);
memcpy(version_type, "Driver", strlen("Driver"));
version = QCA_WLAN_VENDOR_ATTR_WIFI_INFO_DRIVER_VERSION;
} else if (
tb_vendor[QCA_WLAN_VENDOR_ATTR_WIFI_INFO_FIRMWARE_VERSION]) {
len = nla_len(
tb_vendor[
QCA_WLAN_VENDOR_ATTR_WIFI_INFO_FIRMWARE_VERSION]);
memcpy(version_type, "Firmware", strlen("Firmware"));
version = QCA_WLAN_VENDOR_ATTR_WIFI_INFO_FIRMWARE_VERSION;
}
if (len) {
*mVersion = (char *)malloc(len*(sizeof(char)) + 1);
if (!(*mVersion)) {
ALOGE("%s: Failed to allocate memory for Version.",
__func__);
return WIFI_ERROR_OUT_OF_MEMORY;
}
memset(*mVersion, 0, (len*(sizeof(char))) + 1);
*mVersionLen = len;
memcpy(*mVersion, nla_data(tb_vendor[version]), len);
ALOGD("%s: WLAN version len : %d", __func__, len);
ALOGD("%s: WLAN %s version : %s ", __func__,
version_type, *mVersion);
}
}
break;
case QCA_NL80211_VENDOR_SUBCMD_WIFI_LOGGER_MEMORY_DUMP:
{
/*
* TBD malloc buffer.
* Device open, initiate a read from proc
* interface.
* Attributes need to determine the size
*/
}
break;
default :
ALOGE("%s: Wrong Wifi Logger subcmd response received %d",
__func__, mSubcmd);
}
return NL_SKIP;
}
/* This function will be the main handler for incoming (from driver)
* WIFI_LOGGER_SUBCMD.
* Calls the appropriate callback handler after parsing the vendor data.
*/
int WifiLoggerCommand::handleEvent(WifiEvent &event)
{
ALOGI("Got a WifiLogger Event message from the Driver.");
unsigned i = 0;
u32 status;
int ret = WIFI_SUCCESS;
WifiVendorCommand::handleEvent(event);
/* TBD Handle Event */
return NL_SKIP;
}
int WifiLoggerCommand::setCallbackHandler(WifiLoggerCallbackHandler nHandler)
{
int res = 0;
mHandler = nHandler;
res = registerVendorHandler(mVendor_id, mSubcmd);
if (res != 0) {
/* Error case: should not happen, so print a log when it does. */
ALOGE("%s: Unable to register Vendor Handler Vendor Id=0x%x subcmd=%u",
__func__, mVendor_id, mSubcmd);
}
return res;
}
void WifiLoggerCommand::unregisterHandler(u32 subCmd)
{
unregisterVendorHandler(mVendor_id, subCmd);
}
int WifiLoggerCommand::timed_wait(u16 wait_time)
{
struct timespec absTime;
int res;
absTime.tv_sec = wait_time;
absTime.tv_nsec = 0;
return mCondition.wait(absTime);
}
void WifiLoggerCommand::waitForRsp(bool wait)
{
mWaitforRsp = wait;
}