From ce366b22f46bbfa5d6c523e23704d8677ab2b176 Mon Sep 17 00:00:00 2001 From: mazumdar Date: Thu, 29 Mar 2018 19:18:45 +0530 Subject: WiFi-Hal: Add write/read and enable/disable ops to Android Packet Filter Add write/read and enable/disable ops for APF (Android Packet Filter). Change-Id: Iae573751db0ec80d4404dccfd5056e2767ac113d Bug: 36221302 Test: dumpsys wifi with ag/3937960 --- qcwcn/wifi_hal/common.h | 1 + qcwcn/wifi_hal/ifaceeventhandler.cpp | 74 +++++++-- qcwcn/wifi_hal/ifaceeventhandler.h | 5 +- qcwcn/wifi_hal/vendor_definitions.h | 12 ++ qcwcn/wifi_hal/wifi_hal.cpp | 285 +++++++++++++++++++++++++++++++++++ 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; +} -- cgit v1.2.3