summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--qcwcn/wifi_hal/common.h1
-rw-r--r--qcwcn/wifi_hal/ifaceeventhandler.cpp74
-rw-r--r--qcwcn/wifi_hal/ifaceeventhandler.h5
-rw-r--r--qcwcn/wifi_hal/vendor_definitions.h12
-rw-r--r--qcwcn/wifi_hal/wifi_hal.cpp285
5 files changed, 361 insertions, 16 deletions
diff --git a/qcwcn/wifi_hal/common.h b/qcwcn/wifi_hal/common.h
index 9e0961c..a67ef83 100644
--- a/qcwcn/wifi_hal/common.h
+++ b/qcwcn/wifi_hal/common.h
@@ -144,6 +144,7 @@ typedef struct hal_info_s {
struct rssi_monitor_event_handler_s *rssi_handlers;
wifi_capa capa;
struct cld80211_ctx *cldctx;
+ bool apf_enabled;
} hal_info;
wifi_error wifi_register_handler(wifi_handle handle, int cmd, nl_recvmsg_msg_cb_t func, void *arg);
diff --git a/qcwcn/wifi_hal/ifaceeventhandler.cpp b/qcwcn/wifi_hal/ifaceeventhandler.cpp
index e325a83..54845c0 100644
--- a/qcwcn/wifi_hal/ifaceeventhandler.cpp
+++ b/qcwcn/wifi_hal/ifaceeventhandler.cpp
@@ -203,6 +203,8 @@ WifihalGeneric::WifihalGeneric(wifi_handle handle, int id, u32 vendor_id,
filterLength = 0;
firmware_bus_max_size = 0;
mCapa = &(info->capa);
+ mfilter_packet_read_buffer = NULL;
+ mfilter_packet_length = 0;
}
WifihalGeneric::~WifihalGeneric()
@@ -287,31 +289,69 @@ int WifihalGeneric::handleResponse(WifiEvent &reply)
break;
case QCA_NL80211_VENDOR_SUBCMD_PACKET_FILTER:
{
+ int subCmd;
struct nlattr *tb_vendor[
QCA_WLAN_VENDOR_ATTR_PACKET_FILTER_MAX + 1];
nla_parse(tb_vendor, QCA_WLAN_VENDOR_ATTR_PACKET_FILTER_MAX,
(struct nlattr *)mVendorData,
mDataLen, NULL);
- if (!tb_vendor[QCA_WLAN_VENDOR_ATTR_PACKET_FILTER_VERSION])
+ if (tb_vendor[QCA_WLAN_VENDOR_ATTR_PACKET_FILTER_SUB_CMD])
{
- ALOGE("%s: QCA_WLAN_VENDOR_ATTR_PACKET_FILTER_VERSION"
- " not found", __FUNCTION__);
- return -EINVAL;
+ subCmd = nla_get_u32(
+ tb_vendor[QCA_WLAN_VENDOR_ATTR_PACKET_FILTER_SUB_CMD]);
+ } else {
+ /*
+ * The older drivers may not send PACKET_FILTER_SUB_CMD as
+ * they support QCA_WLAN_GET_PACKET_FILTER_SIZE only.
+ */
+ subCmd = QCA_WLAN_GET_PACKET_FILTER_SIZE;
}
- filterVersion = nla_get_u32(
- tb_vendor[QCA_WLAN_VENDOR_ATTR_PACKET_FILTER_VERSION]);
- ALOGV("Current version : %u", filterVersion);
+ if (subCmd == QCA_WLAN_GET_PACKET_FILTER_SIZE) {
+ if (!tb_vendor[QCA_WLAN_VENDOR_ATTR_PACKET_FILTER_VERSION])
+ {
+ ALOGE("%s: QCA_WLAN_VENDOR_ATTR_PACKET_FILTER_VERSION"
+ " not found", __FUNCTION__);
+ return -EINVAL;
+ }
+ filterVersion = nla_get_u32(
+ tb_vendor[QCA_WLAN_VENDOR_ATTR_PACKET_FILTER_VERSION]);
+ ALOGV("Current version : %u", filterVersion);
- if (!tb_vendor[QCA_WLAN_VENDOR_ATTR_PACKET_FILTER_TOTAL_LENGTH])
- {
- ALOGE("%s: QCA_WLAN_VENDOR_ATTR_PACKET_FILTER_TOTAL_LENGTH"
- " not found", __FUNCTION__);
- return -EINVAL;
+ if (!tb_vendor[QCA_WLAN_VENDOR_ATTR_PACKET_FILTER_TOTAL_LENGTH])
+ {
+ ALOGE("%s: QCA_WLAN_VENDOR_ATTR_PACKET_FILTER_TOTAL_LENGTH"
+ " not found", __FUNCTION__);
+ return -EINVAL;
+ }
+ filterLength = nla_get_u32(
+ tb_vendor[QCA_WLAN_VENDOR_ATTR_PACKET_FILTER_TOTAL_LENGTH]);
+ ALOGV("Max filter length Supported : %u", filterLength);
+ } else if (subCmd == QCA_WLAN_READ_PACKET_FILTER) {
+
+ if (!tb_vendor[QCA_WLAN_VENDOR_ATTR_PACKET_FILTER_PROGRAM])
+ {
+ ALOGE("%s: QCA_WLAN_VENDOR_ATTR_PACKET_FILTER_PROGRAM"
+ " not found", __FUNCTION__);
+ return -EINVAL;
+ }
+ if (nla_len(tb_vendor[QCA_WLAN_VENDOR_ATTR_PACKET_FILTER_PROGRAM])
+ < mfilter_packet_length)
+ {
+ ALOGE("%s: Expected packet filter length :%d but received only: %d bytes",
+ __FUNCTION__, mfilter_packet_length,
+ nla_len(tb_vendor[QCA_WLAN_VENDOR_ATTR_PACKET_FILTER_PROGRAM]));
+ return -EINVAL;
+ }
+ memcpy(mfilter_packet_read_buffer,
+ nla_data(tb_vendor[QCA_WLAN_VENDOR_ATTR_PACKET_FILTER_PROGRAM]),
+ mfilter_packet_length);
+ ALOGV("Filter Program length : %u", mfilter_packet_length);
+ } else {
+ ALOGE("%s: Unknown APF sub command received",
+ __FUNCTION__);
+ return -EINVAL;
}
- filterLength = nla_get_u32(
- tb_vendor[QCA_WLAN_VENDOR_ATTR_PACKET_FILTER_TOTAL_LENGTH]);
- ALOGV("Max filter length Supported : %u", filterLength);
}
break;
@@ -522,6 +562,10 @@ int WifihalGeneric::getFilterVersion() {
int WifihalGeneric::getFilterLength() {
return filterLength;
}
+void WifihalGeneric::setPacketBufferParams(u8 *host_packet_buffer, int packet_length) {
+ mfilter_packet_read_buffer = host_packet_buffer;
+ mfilter_packet_length = packet_length;
+}
int WifihalGeneric::getBusSize() {
return firmware_bus_max_size;
diff --git a/qcwcn/wifi_hal/ifaceeventhandler.h b/qcwcn/wifi_hal/ifaceeventhandler.h
index b7c7297..cdbd59d 100644
--- a/qcwcn/wifi_hal/ifaceeventhandler.h
+++ b/qcwcn/wifi_hal/ifaceeventhandler.h
@@ -90,7 +90,9 @@ private:
int filterLength;
int firmware_bus_max_size;
wifi_capa *mCapa;
-
+ /* Packet Filter buffer and length */
+ u8 *mfilter_packet_read_buffer;
+ int mfilter_packet_length;
virtual wifi_error wifiParseCapabilities(struct nlattr **tbVendor);
public:
@@ -101,6 +103,7 @@ public:
virtual void getResponseparams(feature_set *pset);
virtual void setMaxSetSize(int set_size_max);
virtual void setSizePtr(int *set_size);
+ virtual void setPacketBufferParams(u8 *host_packet_buffer, int packet_length);
virtual void setConcurrencySet(feature_set set[]);
virtual int getFilterVersion();
virtual int getFilterLength();
diff --git a/qcwcn/wifi_hal/vendor_definitions.h b/qcwcn/wifi_hal/vendor_definitions.h
index 525e8e4..928a159 100644
--- a/qcwcn/wifi_hal/vendor_definitions.h
+++ b/qcwcn/wifi_hal/vendor_definitions.h
@@ -428,6 +428,14 @@ enum packet_filter_sub_cmd
{
QCA_WLAN_SET_PACKET_FILTER = 1,
QCA_WLAN_GET_PACKET_FILTER_SIZE = 2,
+ /* For writing packet filter program + data */
+ QCA_WLAN_WRITE_PACKET_FILTER = 3,
+ /* For reading packet filter data */
+ QCA_WLAN_READ_PACKET_FILTER = 4,
+ /* Enable APF faeature */
+ QCA_WLAN_ENABLE_PACKET_FILTER = 5,
+ /* Disable APF faeature */
+ QCA_WLAN_DISABLE_PACKET_FILTER = 6,
};
enum qca_wlan_vendor_attr_packet_filter
@@ -440,6 +448,10 @@ enum qca_wlan_vendor_attr_packet_filter
QCA_WLAN_VENDOR_ATTR_PACKET_FILTER_TOTAL_LENGTH,
QCA_WLAN_VENDOR_ATTR_PACKET_FILTER_CURRENT_OFFSET,
QCA_WLAN_VENDOR_ATTR_PACKET_FILTER_PROGRAM,
+ /* The length of the program in the write buffer,
+ * the write buffer may have program+data
+ */
+ QCA_WLAN_VENDOR_ATTR_PACKET_FILTER_PROG_LENGTH,
/* keep last */
QCA_WLAN_VENDOR_ATTR_PACKET_FILTER_AFTER_LAST,
diff --git a/qcwcn/wifi_hal/wifi_hal.cpp b/qcwcn/wifi_hal/wifi_hal.cpp
index f46a22c..1a5811c 100644
--- a/qcwcn/wifi_hal/wifi_hal.cpp
+++ b/qcwcn/wifi_hal/wifi_hal.cpp
@@ -85,6 +85,8 @@ static wifi_error wifi_set_packet_filter(wifi_interface_handle iface,
const u8 *program, u32 len);
static wifi_error wifi_get_packet_filter_capabilities(wifi_interface_handle handle,
u32 *version, u32 *max_len);
+static wifi_error wifi_read_packet_filter(wifi_interface_handle handle,
+ u32 src_offset, u8 *host_dst, u32 length);
static wifi_error wifi_configure_nd_offload(wifi_interface_handle iface,
u8 enable);
wifi_error wifi_get_wake_reason_stats(wifi_interface_handle iface,
@@ -404,6 +406,7 @@ wifi_error init_wifi_vendor_hal_func_table(wifi_hal_fn *fn) {
fn->wifi_nan_get_version = nan_get_version;
fn->wifi_set_packet_filter = wifi_set_packet_filter;
fn->wifi_get_packet_filter_capabilities = wifi_get_packet_filter_capabilities;
+ fn->wifi_read_packet_filter = wifi_read_packet_filter;
fn->wifi_nan_get_capabilities = nan_get_capabilities;
fn->wifi_nan_data_interface_create = nan_data_interface_create;
fn->wifi_nan_data_interface_delete = nan_data_interface_delete;
@@ -1492,6 +1495,8 @@ static wifi_error wifi_set_packet_filter(wifi_interface_handle iface,
current_offset += min(info->firmware_bus_max_size, len);
} while (current_offset < len);
+ info->apf_enabled = !!len;
+
cleanup:
if (vCommand)
delete vCommand;
@@ -1599,3 +1604,283 @@ cleanup:
delete vCommand;
return ret;
}
+
+/**
+ * Copy 'len' bytes of raw data from host memory at source address 'program'
+ * to APF (Android Packet Filter) working memory starting at offset 'dst_offset'.
+ * The size of the program lenght passed to the interpreter is set to
+ * 'progaram_lenght'
+ *
+ * The implementation is allowed to tranlate this wrtie into a series of smaller
+ * writes,but this function is not allowed to return untill all write operations
+ * have been completed
+ * additionally visible memory not targeted by this function must remain
+ * unchanged
+
+ * @param dst_offset write offset in bytes relative to the beginning of the APF
+ * working memory with logical address 0X000. Must be a multiple of 4
+ *
+ * @param program host memory to copy bytes from. Must be 4B aligned
+ *
+ * @param len the number of bytes to copy from the bost into the APF working
+ * memory
+ *
+ * @param program_length new length of the program instructions in bytes to pass
+ * to the interpreter
+ */
+
+wifi_error wifi_write_packet_filter(wifi_interface_handle iface,
+ u32 dst_offset, const u8 *program,
+ u32 len, u32 program_length)
+{
+ wifi_error ret;
+ struct nlattr *nlData;
+ WifiVendorCommand *vCommand = NULL;
+ u32 current_offset = 0;
+ wifi_handle wifiHandle = getWifiHandle(iface);
+ hal_info *info = getHalInfo(wifiHandle);
+
+ /* len=0 clears the filters in driver/firmware */
+ if (len != 0 && program == NULL) {
+ ALOGE("%s: No valid program provided. Exit.",
+ __func__);
+ return WIFI_ERROR_INVALID_ARGS;
+ }
+
+ do {
+ ret = initialize_vendor_cmd(iface, get_requestid(),
+ QCA_NL80211_VENDOR_SUBCMD_PACKET_FILTER,
+ &vCommand);
+ if (ret != WIFI_SUCCESS) {
+ ALOGE("%s: Initialization failed", __FUNCTION__);
+ return ret;
+ }
+
+ /* Add the vendor specific attributes for the NL command. */
+ nlData = vCommand->attr_start(NL80211_ATTR_VENDOR_DATA);
+ if (!nlData)
+ goto cleanup;
+
+ ret = vCommand->put_u32(QCA_WLAN_VENDOR_ATTR_PACKET_FILTER_SUB_CMD,
+ QCA_WLAN_WRITE_PACKET_FILTER);
+ if (ret != WIFI_SUCCESS)
+ goto cleanup;
+ ret = vCommand->put_u32(QCA_WLAN_VENDOR_ATTR_PACKET_FILTER_ID,
+ PACKET_FILTER_ID);
+ if (ret != WIFI_SUCCESS)
+ goto cleanup;
+ ret = vCommand->put_u32(QCA_WLAN_VENDOR_ATTR_PACKET_FILTER_TOTAL_LENGTH,
+ len);
+ if (ret != WIFI_SUCCESS)
+ goto cleanup;
+ ret = vCommand->put_u32(
+ QCA_WLAN_VENDOR_ATTR_PACKET_FILTER_CURRENT_OFFSET,
+ dst_offset + current_offset);
+ if (ret != WIFI_SUCCESS)
+ goto cleanup;
+ ret = vCommand->put_u32(
+ QCA_WLAN_VENDOR_ATTR_PACKET_FILTER_PROG_LENGTH,
+ program_length);
+ if (ret != WIFI_SUCCESS)
+ goto cleanup;
+
+ ret = vCommand->put_bytes(
+ QCA_WLAN_VENDOR_ATTR_PACKET_FILTER_PROGRAM,
+ (char *)&program[current_offset],
+ min(info->firmware_bus_max_size,
+ len - current_offset));
+ if (ret!= WIFI_SUCCESS) {
+ ALOGE("%s: failed to put program", __FUNCTION__);
+ goto cleanup;
+ }
+
+ vCommand->attr_end(nlData);
+
+ ret = vCommand->requestResponse();
+ if (ret != WIFI_SUCCESS) {
+ ALOGE("%s: requestResponse Error:%d",__func__, ret);
+ goto cleanup;
+ }
+
+ /* destroy the object after sending each fragment to driver */
+ delete vCommand;
+ vCommand = NULL;
+
+ current_offset += min(info->firmware_bus_max_size,
+ len - current_offset);
+ } while (current_offset < len);
+
+cleanup:
+ if (vCommand)
+ delete vCommand;
+ return ret;
+}
+
+wifi_error wifi_enable_packet_filter(wifi_interface_handle handle,
+ u32 enable)
+{
+ wifi_error ret;
+ struct nlattr *nlData;
+ WifiVendorCommand *vCommand = NULL;
+ u32 subcmd;
+ wifi_handle wifiHandle = getWifiHandle(handle);
+ hal_info *info = getHalInfo(wifiHandle);
+
+ ret = initialize_vendor_cmd(handle, get_requestid(),
+ QCA_NL80211_VENDOR_SUBCMD_PACKET_FILTER,
+ &vCommand);
+
+ if (ret != WIFI_SUCCESS) {
+ ALOGE("%s: Initialization failed", __func__);
+ return ret;
+ }
+ /* Add the vendor specific attributes for the NL command. */
+ nlData = vCommand->attr_start(NL80211_ATTR_VENDOR_DATA);
+ if (!nlData)
+ goto cleanup;
+
+ subcmd = enable ? QCA_WLAN_ENABLE_PACKET_FILTER :
+ QCA_WLAN_DISABLE_PACKET_FILTER;
+ ret = vCommand->put_u32(QCA_WLAN_VENDOR_ATTR_PACKET_FILTER_SUB_CMD,
+ subcmd);
+ if (ret != WIFI_SUCCESS)
+ goto cleanup;
+
+ vCommand->attr_end(nlData);
+ ret = vCommand->requestResponse();
+
+ if (ret != WIFI_SUCCESS) {
+ ALOGE("%s: requestResponse() error: %d", __FUNCTION__, ret);
+ goto cleanup;
+ }
+
+ info->apf_enabled = !!enable;
+
+cleanup:
+ if (vCommand)
+ delete vCommand;
+ return ret;
+
+}
+
+/**
+ * Copy 'length' bytes of raw data from APF (Android Packet Filter) working
+ * memory to host memory starting at offset src_offset into host memory
+ * pointed to by host_dst.
+ * Memory can be text, data or some combination of the two. The implementiion is
+ * allowed to translate this read into a series of smaller reads, but this
+ * function is not allowed to return untill all the reads operations
+ * into host_dst have been completed.
+ *
+ * @param src_offset offset in bytes of destination memory within APF working
+ * memory
+ *
+ * @param host_dst host memory to copy into. Must be 4B aligned.
+ *
+ * @param length the number of bytes to copy from the APF working memory to the
+ * host.
+ */
+
+static wifi_error wifi_read_packet_filter(wifi_interface_handle handle,
+ u32 src_offset, u8 *host_dst, u32 length)
+{
+ wifi_error ret;
+ struct nlattr *nlData;
+ WifihalGeneric *vCommand = NULL;
+ interface_info *ifaceInfo = getIfaceInfo(handle);
+ wifi_handle wifiHandle = getWifiHandle(handle);
+ hal_info *info = getHalInfo(wifiHandle);
+
+ /*Temporary varibles to support the read complete length in chunks */
+ u8 *temp_host_dst;
+ u32 remainingLengthToBeRead, currentLength;
+ u8 apf_locally_disabled = 0;
+
+ /*Initializing the temporary variables*/
+ temp_host_dst = host_dst;
+ remainingLengthToBeRead = length;
+
+ if (info->apf_enabled) {
+ /* Disable APF only when not disabled by framework before calling
+ * wifi_read_packet_filter()
+ */
+ ret = wifi_enable_packet_filter(handle, 0);
+ if (ret != WIFI_SUCCESS) {
+ ALOGE("%s: Failed to disable APF", __FUNCTION__);
+ return ret;
+ }
+ apf_locally_disabled = 1;
+ }
+ /**
+ * Read the complete length in chunks of size less or equal to firmware bus
+ * max size
+ */
+ while (remainingLengthToBeRead)
+ {
+ vCommand = new WifihalGeneric(wifiHandle, 0, OUI_QCA,
+ QCA_NL80211_VENDOR_SUBCMD_PACKET_FILTER);
+
+ if (vCommand == NULL) {
+ ALOGE("%s: Error vCommand NULL", __FUNCTION__);
+ ret = WIFI_ERROR_OUT_OF_MEMORY;
+ break;
+ }
+
+ /* Create the message */
+ ret = vCommand->create();
+ if (ret != WIFI_SUCCESS)
+ break;
+ ret = vCommand->set_iface_id(ifaceInfo->name);
+ if (ret != WIFI_SUCCESS)
+ break;
+ /* Add the vendor specific attributes for the NL command. */
+ nlData = vCommand->attr_start(NL80211_ATTR_VENDOR_DATA);
+ if (!nlData)
+ break;
+ ret = vCommand->put_u32(QCA_WLAN_VENDOR_ATTR_PACKET_FILTER_SUB_CMD,
+ QCA_WLAN_READ_PACKET_FILTER);
+ if (ret != WIFI_SUCCESS)
+ break;
+
+ currentLength = min(remainingLengthToBeRead, info->firmware_bus_max_size);
+
+ ret = vCommand->put_u32(QCA_WLAN_VENDOR_ATTR_PACKET_FILTER_TOTAL_LENGTH,
+ currentLength);
+ if (ret != WIFI_SUCCESS)
+ break;
+ ret = vCommand->put_u32(QCA_WLAN_VENDOR_ATTR_PACKET_FILTER_CURRENT_OFFSET,
+ src_offset);
+ if (ret != WIFI_SUCCESS)
+ break;
+
+ vCommand->setPacketBufferParams(temp_host_dst, currentLength);
+ vCommand->attr_end(nlData);
+ ret = vCommand->requestResponse();
+
+ if (ret != WIFI_SUCCESS) {
+ ALOGE("%s: requestResponse() error: %d current_len = %u, src_offset = %u",
+ __FUNCTION__, ret, currentLength, src_offset);
+ break;
+ }
+
+ remainingLengthToBeRead -= currentLength;
+ temp_host_dst += currentLength;
+ src_offset += currentLength;
+ delete vCommand;
+ vCommand = NULL;
+ }
+
+ /* Re enable APF only when disabled above within this API */
+ if (apf_locally_disabled) {
+ wifi_error status;
+ status = wifi_enable_packet_filter(handle, 1);
+ if (status != WIFI_SUCCESS)
+ ALOGE("%s: Failed to enable APF", __FUNCTION__);
+ /* Prefer to return read status if read fails */
+ if (ret == WIFI_SUCCESS)
+ ret = status;
+ }
+
+ delete vCommand;
+ return ret;
+}