/* * Copyright (C) 2014 The Android Open Source Project * * 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 "sync.h" #define LOG_TAG "WifiHAL" #include <utils/Log.h> #include <time.h> #include <errno.h> #include <stdlib.h> #include "common.h" #include "cpp_bindings.h" #include "gscancommand.h" #include "gscan_event_handler.h" #define GSCAN_EVENT_WAIT_TIME_SECONDS 4 /* BSSID blacklist */ typedef struct { int num_bssid; // number of blacklisted BSSIDs mac_addr bssids[MAX_BLACKLIST_BSSID]; // blacklisted BSSIDs } wifi_bssid_params; /* Used to handle gscan command events from driver/firmware.*/ typedef struct gscan_event_handlers_s { GScanCommandEventHandler *gscanStartCmdEventHandler; GScanCommandEventHandler *gScanSetBssidHotlistCmdEventHandler; GScanCommandEventHandler *gScanSetSignificantChangeCmdEventHandler; GScanCommandEventHandler *gScanSetSsidHotlistCmdEventHandler; GScanCommandEventHandler *gScanSetPnoListCmdEventHandler; GScanCommandEventHandler *gScanPnoSetPasspointListCmdEventHandler; } gscan_event_handlers; wifi_error initializeGscanHandlers(hal_info *info) { info->gscan_handlers = (gscan_event_handlers *)malloc(sizeof(gscan_event_handlers)); if (info->gscan_handlers) { memset(info->gscan_handlers, 0, sizeof(gscan_event_handlers)); } else { ALOGE("%s: Allocation of gscan event handlers failed", __FUNCTION__); return WIFI_ERROR_OUT_OF_MEMORY; } return WIFI_SUCCESS; } wifi_error cleanupGscanHandlers(hal_info *info) { gscan_event_handlers* event_handlers; if (info && info->gscan_handlers) { event_handlers = (gscan_event_handlers*) info->gscan_handlers; if (event_handlers->gscanStartCmdEventHandler) { delete event_handlers->gscanStartCmdEventHandler; } if (event_handlers->gScanSetBssidHotlistCmdEventHandler) { delete event_handlers->gScanSetBssidHotlistCmdEventHandler; } if (event_handlers->gScanSetSignificantChangeCmdEventHandler) { delete event_handlers->gScanSetSignificantChangeCmdEventHandler; } if (event_handlers->gScanSetSsidHotlistCmdEventHandler) { delete event_handlers->gScanSetSsidHotlistCmdEventHandler; } if (event_handlers->gScanSetPnoListCmdEventHandler) { delete event_handlers->gScanSetPnoListCmdEventHandler; } if (event_handlers->gScanPnoSetPasspointListCmdEventHandler) { delete event_handlers->gScanPnoSetPasspointListCmdEventHandler; } memset(event_handlers, 0, sizeof(gscan_event_handlers)); return WIFI_SUCCESS; } ALOGE ("%s: info or info->gscan_handlers NULL", __FUNCTION__); return WIFI_ERROR_UNKNOWN; } /* Implementation of the API functions exposed in gscan.h */ wifi_error wifi_get_valid_channels(wifi_interface_handle handle, int band, int max_channels, wifi_channel *channels, int *num_channels) { int requestId, ret = 0; GScanCommand *gScanCommand; struct nlattr *nlData; interface_info *ifaceInfo = getIfaceInfo(handle); wifi_handle wifiHandle = getWifiHandle(handle); lowi_cb_table_t *lowiWifiHalApi = NULL; /* Route GSCAN request through LOWI if supported */ lowiWifiHalApi = getLowiCallbackTable(GSCAN_SUPPORTED); if (lowiWifiHalApi == NULL || lowiWifiHalApi->get_valid_channels == NULL) { ALOGV("%s: Sending cmd directly to host", __FUNCTION__); } else { ret = lowiWifiHalApi->get_valid_channels(handle, band, max_channels, channels, num_channels); ALOGV("%s: lowi get_valid_channels " "returned: %d. Exit.", __FUNCTION__, ret); return (wifi_error)ret; } /* No request id from caller, so generate one and pass it on to the driver. * Generate one randomly. */ requestId = get_requestid(); ALOGV("%s: RequestId:%d band:%d max_channels:%d", __FUNCTION__, requestId, band, max_channels); if (channels == NULL) { ALOGE("%s: NULL channels pointer provided. Exit.", __FUNCTION__); return WIFI_ERROR_INVALID_ARGS; } gScanCommand = new GScanCommand( wifiHandle, requestId, OUI_QCA, QCA_NL80211_VENDOR_SUBCMD_GSCAN_GET_VALID_CHANNELS); if (gScanCommand == NULL) { ALOGE("%s: Error GScanCommand NULL", __FUNCTION__); return WIFI_ERROR_UNKNOWN; } /* Create the NL message. */ ret = gScanCommand->create(); if (ret < 0) goto cleanup; /* Set the interface Id of the message. */ ret = gScanCommand->set_iface_id(ifaceInfo->name); if (ret < 0) goto cleanup; /* Add the vendor specific attributes for the NL command. */ nlData = gScanCommand->attr_start(NL80211_ATTR_VENDOR_DATA); if (!nlData) goto cleanup; if (gScanCommand->put_u32( QCA_WLAN_VENDOR_ATTR_GSCAN_SUBCMD_CONFIG_PARAM_REQUEST_ID, requestId) || gScanCommand->put_u32( QCA_WLAN_VENDOR_ATTR_GSCAN_GET_VALID_CHANNELS_CONFIG_PARAM_WIFI_BAND, band) || gScanCommand->put_u32( QCA_WLAN_VENDOR_ATTR_GSCAN_GET_VALID_CHANNELS_CONFIG_PARAM_MAX_CHANNELS, max_channels) ) { goto cleanup; } gScanCommand->attr_end(nlData); /* Populate the input received from caller/framework. */ gScanCommand->setMaxChannels(max_channels); gScanCommand->setChannels(channels); gScanCommand->setNumChannelsPtr(num_channels); /* Send the msg and wait for a response. */ ret = gScanCommand->requestResponse(); if (ret) { ALOGE("%s: Error %d happened. ", __FUNCTION__, ret); } cleanup: delete gScanCommand; return (wifi_error)ret; } wifi_error wifi_get_gscan_capabilities(wifi_interface_handle handle, wifi_gscan_capabilities *capabilities) { wifi_error ret = WIFI_SUCCESS; wifi_handle wifiHandle = getWifiHandle(handle); hal_info *info = getHalInfo(wifiHandle); lowi_cb_table_t *lowiWifiHalApi = NULL; if (!(info->supported_feature_set & WIFI_FEATURE_GSCAN)) { ALOGE("%s: GSCAN is not supported by driver", __FUNCTION__); return WIFI_ERROR_NOT_SUPPORTED; } if (capabilities == NULL) { ALOGE("%s: NULL capabilities pointer provided. Exit.", __FUNCTION__); return WIFI_ERROR_INVALID_ARGS; } /* Route GSCAN request through LOWI if supported */ lowiWifiHalApi = getLowiCallbackTable(GSCAN_SUPPORTED); if (lowiWifiHalApi == NULL || lowiWifiHalApi->get_gscan_capabilities == NULL) { ALOGV("%s: Sending cmd directly to host", __FUNCTION__); } else { ret = lowiWifiHalApi->get_gscan_capabilities(handle, capabilities); ALOGV("%s: lowi get_gscan_capabilities returned: %d. Exit.", __FUNCTION__, ret); return ret; } memcpy(capabilities, &info->capa.gscan_capa, sizeof(wifi_gscan_capabilities)); return ret; } wifi_error wifi_start_gscan(wifi_request_id id, wifi_interface_handle iface, wifi_scan_cmd_params params, wifi_scan_result_handler handler) { int ret = 0; u32 i, j; GScanCommand *gScanCommand; struct nlattr *nlData; interface_info *ifaceInfo = getIfaceInfo(iface); wifi_handle wifiHandle = getWifiHandle(iface); u32 num_scan_buckets, numChannelSpecs; wifi_scan_bucket_spec bucketSpec; struct nlattr *nlBuckectSpecList; hal_info *info = getHalInfo(wifiHandle); lowi_cb_table_t *lowiWifiHalApi = NULL; gscan_event_handlers* event_handlers; GScanCommandEventHandler *gScanStartCmdEventHandler; event_handlers = (gscan_event_handlers*)info->gscan_handlers; gScanStartCmdEventHandler = event_handlers->gscanStartCmdEventHandler; if (!(info->supported_feature_set & WIFI_FEATURE_GSCAN)) { ALOGE("%s: GSCAN is not supported by driver", __FUNCTION__); return WIFI_ERROR_NOT_SUPPORTED; } /* Route GSCAN request through LOWI if supported */ lowiWifiHalApi = getLowiCallbackTable(GSCAN_SUPPORTED); if (lowiWifiHalApi == NULL || lowiWifiHalApi->start_gscan == NULL) { ALOGV("%s: Sending cmd directly to host", __FUNCTION__); } else { ret = lowiWifiHalApi->start_gscan(id, iface, params, handler); ALOGV("%s: lowi start_gscan " "returned: %d. Exit.", __FUNCTION__, ret); return (wifi_error)ret; } ALOGV("%s: RequestId:%d ", __FUNCTION__, id); /* Wi-Fi HAL doesn't need to check if a similar request to start gscan was * made earlier. If start_gscan() is called while another gscan is already * running, the request will be sent down to driver and firmware. If new * request is successfully honored, then Wi-Fi HAL will use the new request * id for the gScanStartCmdEventHandler object. */ gScanCommand = new GScanCommand( wifiHandle, id, OUI_QCA, QCA_NL80211_VENDOR_SUBCMD_GSCAN_START); if (gScanCommand == NULL) { ALOGE("%s: Error GScanCommand NULL", __FUNCTION__); return WIFI_ERROR_UNKNOWN; } /* Create the NL message. */ ret = gScanCommand->create(); if (ret < 0) goto cleanup; /* Set the interface Id of the message. */ ret = gScanCommand->set_iface_id(ifaceInfo->name); if (ret < 0) goto cleanup; /* Add the vendor specific attributes for the NL command. */ nlData = gScanCommand->attr_start(NL80211_ATTR_VENDOR_DATA); if (!nlData) goto cleanup; num_scan_buckets = (unsigned int)params.num_buckets > MAX_BUCKETS ? MAX_BUCKETS : params.num_buckets; ALOGV("%s: Base Period:%d Max_ap_per_scan:%d " "Threshold_percent:%d Threshold_num_scans:%d " "num_buckets:%d", __FUNCTION__, params.base_period, params.max_ap_per_scan, params.report_threshold_percent, params.report_threshold_num_scans, num_scan_buckets); if (gScanCommand->put_u32( QCA_WLAN_VENDOR_ATTR_GSCAN_SUBCMD_CONFIG_PARAM_REQUEST_ID, id) || gScanCommand->put_u32( QCA_WLAN_VENDOR_ATTR_GSCAN_SCAN_CMD_PARAMS_BASE_PERIOD, params.base_period) || gScanCommand->put_u32( QCA_WLAN_VENDOR_ATTR_GSCAN_SCAN_CMD_PARAMS_MAX_AP_PER_SCAN, params.max_ap_per_scan) || gScanCommand->put_u8( QCA_WLAN_VENDOR_ATTR_GSCAN_SCAN_CMD_PARAMS_REPORT_THRESHOLD_PERCENT, params.report_threshold_percent) || gScanCommand->put_u8( QCA_WLAN_VENDOR_ATTR_GSCAN_SCAN_CMD_PARAMS_REPORT_THRESHOLD_NUM_SCANS, params.report_threshold_num_scans) || gScanCommand->put_u8( QCA_WLAN_VENDOR_ATTR_GSCAN_SCAN_CMD_PARAMS_NUM_BUCKETS, num_scan_buckets)) { goto cleanup; } nlBuckectSpecList = gScanCommand->attr_start(QCA_WLAN_VENDOR_ATTR_GSCAN_BUCKET_SPEC); /* Add NL attributes for scan bucket specs . */ for (i = 0; i < num_scan_buckets; i++) { bucketSpec = params.buckets[i]; numChannelSpecs = (unsigned int)bucketSpec.num_channels > MAX_CHANNELS ? MAX_CHANNELS : bucketSpec.num_channels; ALOGV("%s: Index: %d Bucket Id:%d Band:%d Period:%d ReportEvent:%d " "numChannelSpecs:%d max_period:%d base:%d step_count:%d", __FUNCTION__, i, bucketSpec.bucket, bucketSpec.band, bucketSpec.period, bucketSpec.report_events, numChannelSpecs, bucketSpec.max_period, bucketSpec.base, bucketSpec.step_count); struct nlattr *nlBucketSpec = gScanCommand->attr_start(i); if (gScanCommand->put_u8( QCA_WLAN_VENDOR_ATTR_GSCAN_BUCKET_SPEC_INDEX, bucketSpec.bucket) || gScanCommand->put_u8( QCA_WLAN_VENDOR_ATTR_GSCAN_BUCKET_SPEC_BAND, bucketSpec.band) || gScanCommand->put_u32( QCA_WLAN_VENDOR_ATTR_GSCAN_BUCKET_SPEC_PERIOD, bucketSpec.period) || gScanCommand->put_u8( QCA_WLAN_VENDOR_ATTR_GSCAN_BUCKET_SPEC_REPORT_EVENTS, bucketSpec.report_events) || gScanCommand->put_u32( QCA_WLAN_VENDOR_ATTR_GSCAN_BUCKET_SPEC_NUM_CHANNEL_SPECS, numChannelSpecs) || gScanCommand->put_u32( QCA_WLAN_VENDOR_ATTR_GSCAN_BUCKET_SPEC_MAX_PERIOD, bucketSpec.max_period) || gScanCommand->put_u32( QCA_WLAN_VENDOR_ATTR_GSCAN_BUCKET_SPEC_BASE, bucketSpec.base) || gScanCommand->put_u32( QCA_WLAN_VENDOR_ATTR_GSCAN_BUCKET_SPEC_STEP_COUNT, bucketSpec.step_count)) { goto cleanup; } struct nlattr *nl_channelSpecList = gScanCommand->attr_start(QCA_WLAN_VENDOR_ATTR_GSCAN_CHANNEL_SPEC); /* Add NL attributes for scan channel specs . */ for (j = 0; j < numChannelSpecs; j++) { struct nlattr *nl_channelSpec = gScanCommand->attr_start(j); wifi_scan_channel_spec channel_spec = bucketSpec.channels[j]; ALOGV("%s: Channel Spec Index:%d Channel:%d Dwell Time:%d " "passive:%d", __FUNCTION__, j, channel_spec.channel, channel_spec.dwellTimeMs, channel_spec.passive); if ( gScanCommand->put_u32( QCA_WLAN_VENDOR_ATTR_GSCAN_CHANNEL_SPEC_CHANNEL, channel_spec.channel) || gScanCommand->put_u32( QCA_WLAN_VENDOR_ATTR_GSCAN_CHANNEL_SPEC_DWELL_TIME, channel_spec.dwellTimeMs) || gScanCommand->put_u8( QCA_WLAN_VENDOR_ATTR_GSCAN_CHANNEL_SPEC_PASSIVE, channel_spec.passive) ) { goto cleanup; } gScanCommand->attr_end(nl_channelSpec); } gScanCommand->attr_end(nl_channelSpecList); gScanCommand->attr_end(nlBucketSpec); } gScanCommand->attr_end(nlBuckectSpecList); gScanCommand->attr_end(nlData); /* Set the callback handler functions for related events. */ GScanCallbackHandler callbackHandler; memset(&callbackHandler, 0, sizeof(callbackHandler)); callbackHandler.on_full_scan_result = handler.on_full_scan_result; callbackHandler.on_scan_event = handler.on_scan_event; /* Create an object to handle the related events from firmware/driver. */ if (gScanStartCmdEventHandler == NULL) { gScanStartCmdEventHandler = new GScanCommandEventHandler( wifiHandle, id, OUI_QCA, QCA_NL80211_VENDOR_SUBCMD_GSCAN_START, callbackHandler); if (gScanStartCmdEventHandler == NULL) { ALOGE("%s: Error gScanStartCmdEventHandler NULL", __FUNCTION__); ret = WIFI_ERROR_UNKNOWN; goto cleanup; } event_handlers->gscanStartCmdEventHandler = gScanStartCmdEventHandler; } else { gScanStartCmdEventHandler->setCallbackHandler(callbackHandler); } ret = gScanCommand->requestResponse(); if (ret != 0) { ALOGE("%s : requestResponse Error:%d", __FUNCTION__, ret); goto cleanup; } if (gScanStartCmdEventHandler != NULL) { gScanStartCmdEventHandler->set_request_id(id); gScanStartCmdEventHandler->enableEventHandling(); } cleanup: delete gScanCommand; /* Disable Event Handling if ret != 0 */ if (ret && gScanStartCmdEventHandler) { ALOGI("%s: Error ret:%d, disable event handling", __FUNCTION__, ret); gScanStartCmdEventHandler->disableEventHandling(); } return (wifi_error)ret; } wifi_error wifi_stop_gscan(wifi_request_id id, wifi_interface_handle iface) { int ret = 0; GScanCommand *gScanCommand; struct nlattr *nlData; lowi_cb_table_t *lowiWifiHalApi = NULL; interface_info *ifaceInfo = getIfaceInfo(iface); wifi_handle wifiHandle = getWifiHandle(iface); hal_info *info = getHalInfo(wifiHandle); gscan_event_handlers* event_handlers; GScanCommandEventHandler *gScanStartCmdEventHandler; event_handlers = (gscan_event_handlers*)info->gscan_handlers; gScanStartCmdEventHandler = event_handlers->gscanStartCmdEventHandler; if (!(info->supported_feature_set & WIFI_FEATURE_GSCAN)) { ALOGE("%s: GSCAN is not supported by driver", __FUNCTION__); return WIFI_ERROR_NOT_SUPPORTED; } /* Route GSCAN request through LOWI if supported */ lowiWifiHalApi = getLowiCallbackTable(GSCAN_SUPPORTED); if (lowiWifiHalApi == NULL || lowiWifiHalApi->stop_gscan == NULL) { ALOGV("%s: Sending cmd directly to host", __FUNCTION__); } else { ret = lowiWifiHalApi->stop_gscan(id, iface); ALOGV("%s: lowi stop_gscan " "returned: %d. Exit.", __FUNCTION__, ret); return (wifi_error)ret; } if (gScanStartCmdEventHandler == NULL || gScanStartCmdEventHandler->isEventHandlingEnabled() == false) { ALOGE("%s: GSCAN isn't running or already stopped. " "Nothing to do. Exit", __FUNCTION__); return WIFI_ERROR_NOT_AVAILABLE; } gScanCommand = new GScanCommand( wifiHandle, id, OUI_QCA, QCA_NL80211_VENDOR_SUBCMD_GSCAN_STOP); if (gScanCommand == NULL) { ALOGE("%s: Error GScanCommand NULL", __FUNCTION__); return WIFI_ERROR_UNKNOWN; } /* Create the NL message. */ ret = gScanCommand->create(); if (ret < 0) goto cleanup; /* Set the interface Id of the message. */ ret = gScanCommand->set_iface_id(ifaceInfo->name); if (ret < 0) goto cleanup; /* Add the vendor specific attributes for the NL command. */ nlData = gScanCommand->attr_start(NL80211_ATTR_VENDOR_DATA); if (!nlData) goto cleanup; ret = gScanCommand->put_u32( QCA_WLAN_VENDOR_ATTR_GSCAN_SUBCMD_CONFIG_PARAM_REQUEST_ID, id); if (ret < 0) goto cleanup; gScanCommand->attr_end(nlData); ret = gScanCommand->requestResponse(); if (ret != 0) { ALOGE("%s: requestResponse Error:%d",__FUNCTION__, ret); } /* Disable Event Handling. */ if (gScanStartCmdEventHandler) { gScanStartCmdEventHandler->disableEventHandling(); } cleanup: delete gScanCommand; return (wifi_error)ret; } /* Set the GSCAN BSSID Hotlist. */ wifi_error wifi_set_bssid_hotlist(wifi_request_id id, wifi_interface_handle iface, wifi_bssid_hotlist_params params, wifi_hotlist_ap_found_handler handler) { int i, numAp, ret = 0; GScanCommand *gScanCommand; struct nlattr *nlData, *nlApThresholdParamList; interface_info *ifaceInfo = getIfaceInfo(iface); wifi_handle wifiHandle = getWifiHandle(iface); hal_info *info = getHalInfo(wifiHandle); lowi_cb_table_t *lowiWifiHalApi = NULL; gscan_event_handlers* event_handlers; GScanCommandEventHandler *gScanSetBssidHotlistCmdEventHandler; event_handlers = (gscan_event_handlers*)info->gscan_handlers; gScanSetBssidHotlistCmdEventHandler = event_handlers->gScanSetBssidHotlistCmdEventHandler; if (!(info->supported_feature_set & WIFI_FEATURE_GSCAN)) { ALOGE("%s: GSCAN is not supported by driver", __FUNCTION__); return WIFI_ERROR_NOT_SUPPORTED; } /* Route request through LOWI if supported*/ lowiWifiHalApi = getLowiCallbackTable(GSCAN_SUPPORTED); if (lowiWifiHalApi == NULL || lowiWifiHalApi->set_bssid_hotlist == NULL) { ALOGV("%s: Sending cmd directly to host", __FUNCTION__); } else { ret = lowiWifiHalApi->set_bssid_hotlist(id, iface, params,handler); ALOGV("%s: lowi set_bssid_hotlist " "returned: %d. Exit.", __FUNCTION__, ret); return (wifi_error)ret; } /* Wi-Fi HAL doesn't need to check if a similar request to set bssid * hotlist was made earlier. If set_bssid_hotlist() is called while * another one is running, the request will be sent down to driver and * firmware. If the new request is successfully honored, then Wi-Fi HAL * will use the new request id for the gScanSetBssidHotlistCmdEventHandler * object. */ gScanCommand = new GScanCommand( wifiHandle, id, OUI_QCA, QCA_NL80211_VENDOR_SUBCMD_GSCAN_SET_BSSID_HOTLIST); if (gScanCommand == NULL) { ALOGE("%s: Error GScanCommand NULL", __FUNCTION__); return WIFI_ERROR_UNKNOWN; } /* Create the NL message. */ ret = gScanCommand->create(); if (ret < 0) goto cleanup; /* Set the interface Id of the message. */ ret = gScanCommand->set_iface_id(ifaceInfo->name); if (ret < 0) goto cleanup; /* Add the vendor specific attributes for the NL command. */ nlData = gScanCommand->attr_start(NL80211_ATTR_VENDOR_DATA); if (!nlData) goto cleanup; numAp = (unsigned int)params.num_bssid > MAX_HOTLIST_APS ? MAX_HOTLIST_APS : params.num_bssid; if (gScanCommand->put_u32( QCA_WLAN_VENDOR_ATTR_GSCAN_SUBCMD_CONFIG_PARAM_REQUEST_ID, id) || gScanCommand->put_u32( QCA_WLAN_VENDOR_ATTR_GSCAN_BSSID_HOTLIST_PARAMS_LOST_AP_SAMPLE_SIZE, params.lost_ap_sample_size) || gScanCommand->put_u32( QCA_WLAN_VENDOR_ATTR_GSCAN_BSSID_HOTLIST_PARAMS_NUM_AP, numAp)) { goto cleanup; } ALOGV("%s: lost_ap_sample_size:%d numAp:%d", __FUNCTION__, params.lost_ap_sample_size, numAp); /* Add the vendor specific attributes for the NL command. */ nlApThresholdParamList = gScanCommand->attr_start( QCA_WLAN_VENDOR_ATTR_GSCAN_AP_THRESHOLD_PARAM); if (!nlApThresholdParamList) goto cleanup; /* Add nested NL attributes for AP Threshold Param. */ for (i = 0; i < numAp; i++) { ap_threshold_param apThreshold = params.ap[i]; struct nlattr *nlApThresholdParam = gScanCommand->attr_start(i); if (!nlApThresholdParam) goto cleanup; if (gScanCommand->put_addr( QCA_WLAN_VENDOR_ATTR_GSCAN_AP_THRESHOLD_PARAM_BSSID, apThreshold.bssid) || gScanCommand->put_s32( QCA_WLAN_VENDOR_ATTR_GSCAN_AP_THRESHOLD_PARAM_RSSI_LOW, apThreshold.low) || gScanCommand->put_s32( QCA_WLAN_VENDOR_ATTR_GSCAN_AP_THRESHOLD_PARAM_RSSI_HIGH, apThreshold.high)) { goto cleanup; } ALOGV("%s: Index:%d BssId: %hhx:%hhx:%hhx:%hhx:%hhx:%hhx " "Threshold low:%d high:%d", __FUNCTION__, i, apThreshold.bssid[0], apThreshold.bssid[1], apThreshold.bssid[2], apThreshold.bssid[3], apThreshold.bssid[4], apThreshold.bssid[5], apThreshold.low, apThreshold.high); gScanCommand->attr_end(nlApThresholdParam); } gScanCommand->attr_end(nlApThresholdParamList); gScanCommand->attr_end(nlData); GScanCallbackHandler callbackHandler; memset(&callbackHandler, 0, sizeof(callbackHandler)); callbackHandler.on_hotlist_ap_found = handler.on_hotlist_ap_found; callbackHandler.on_hotlist_ap_lost = handler.on_hotlist_ap_lost; /* Create an object of the event handler class to take care of the * asychronous events on the north-bound. */ if (gScanSetBssidHotlistCmdEventHandler == NULL) { gScanSetBssidHotlistCmdEventHandler = new GScanCommandEventHandler( wifiHandle, id, OUI_QCA, QCA_NL80211_VENDOR_SUBCMD_GSCAN_SET_BSSID_HOTLIST, callbackHandler); if (gScanSetBssidHotlistCmdEventHandler == NULL) { ALOGE("%s: Error instantiating " "gScanSetBssidHotlistCmdEventHandler.", __FUNCTION__); ret = WIFI_ERROR_UNKNOWN; goto cleanup; } event_handlers->gScanSetBssidHotlistCmdEventHandler = gScanSetBssidHotlistCmdEventHandler; } else { gScanSetBssidHotlistCmdEventHandler->setCallbackHandler(callbackHandler); } ret = gScanCommand->requestResponse(); if (ret != 0) { ALOGE("%s: requestResponse Error:%d",__FUNCTION__, ret); goto cleanup; } if (gScanSetBssidHotlistCmdEventHandler != NULL) { gScanSetBssidHotlistCmdEventHandler->set_request_id(id); gScanSetBssidHotlistCmdEventHandler->enableEventHandling(); } cleanup: delete gScanCommand; /* Disable Event Handling if ret != 0 */ if (ret && gScanSetBssidHotlistCmdEventHandler) { ALOGI("%s: Error ret:%d, disable event handling", __FUNCTION__, ret); gScanSetBssidHotlistCmdEventHandler->disableEventHandling(); } return (wifi_error)ret; } wifi_error wifi_reset_bssid_hotlist(wifi_request_id id, wifi_interface_handle iface) { int ret = 0; GScanCommand *gScanCommand; struct nlattr *nlData; interface_info *ifaceInfo = getIfaceInfo(iface); wifi_handle wifiHandle = getWifiHandle(iface); hal_info *info = getHalInfo(wifiHandle); lowi_cb_table_t *lowiWifiHalApi = NULL; gscan_event_handlers* event_handlers; GScanCommandEventHandler *gScanSetBssidHotlistCmdEventHandler; event_handlers = (gscan_event_handlers*)info->gscan_handlers; gScanSetBssidHotlistCmdEventHandler = event_handlers->gScanSetBssidHotlistCmdEventHandler; if (!(info->supported_feature_set & WIFI_FEATURE_GSCAN)) { ALOGE("%s: GSCAN is not supported by driver", __FUNCTION__); return WIFI_ERROR_NOT_SUPPORTED; } /* Route request through LOWI if supported*/ lowiWifiHalApi = getLowiCallbackTable(GSCAN_SUPPORTED); if (lowiWifiHalApi == NULL || lowiWifiHalApi->reset_bssid_hotlist == NULL) { ALOGV("%s: Sending cmd directly to host", __FUNCTION__); } else { ret = lowiWifiHalApi->reset_bssid_hotlist(id, iface); ALOGV("%s: lowi reset_bssid_hotlist " "returned: %d. Exit.", __FUNCTION__, ret); return (wifi_error)ret; } if (gScanSetBssidHotlistCmdEventHandler == NULL || (gScanSetBssidHotlistCmdEventHandler->isEventHandlingEnabled() == false)) { ALOGE("wifi_reset_bssid_hotlist: GSCAN bssid_hotlist isn't set. " "Nothing to do. Exit"); return WIFI_ERROR_NOT_AVAILABLE; } gScanCommand = new GScanCommand( wifiHandle, id, OUI_QCA, QCA_NL80211_VENDOR_SUBCMD_GSCAN_RESET_BSSID_HOTLIST); if (gScanCommand == NULL) { ALOGE("%s: Error GScanCommand NULL", __FUNCTION__); return WIFI_ERROR_UNKNOWN; } /* Create the NL message. */ ret = gScanCommand->create(); if (ret < 0) goto cleanup; /* Set the interface Id of the message. */ ret = gScanCommand->set_iface_id(ifaceInfo->name); if (ret < 0) goto cleanup; /* Add the vendor specific attributes for the NL command. */ nlData = gScanCommand->attr_start(NL80211_ATTR_VENDOR_DATA); if (!nlData) goto cleanup; ret = gScanCommand->put_u32( QCA_WLAN_VENDOR_ATTR_GSCAN_SUBCMD_CONFIG_PARAM_REQUEST_ID, id); if (ret < 0) goto cleanup; gScanCommand->attr_end(nlData); ret = gScanCommand->requestResponse(); if (ret != 0) { ALOGE("%s: requestResponse Error:%d",__FUNCTION__, ret); } /* Disable Event Handling. */ if (gScanSetBssidHotlistCmdEventHandler) { gScanSetBssidHotlistCmdEventHandler->disableEventHandling(); } cleanup: delete gScanCommand; return (wifi_error)ret; } /* Set the GSCAN Significant AP Change list. */ wifi_error wifi_set_significant_change_handler(wifi_request_id id, wifi_interface_handle iface, wifi_significant_change_params params, wifi_significant_change_handler handler) { int i, numAp, ret = 0; GScanCommand *gScanCommand; struct nlattr *nlData, *nlApThresholdParamList; interface_info *ifaceInfo = getIfaceInfo(iface); wifi_handle wifiHandle = getWifiHandle(iface); hal_info *info = getHalInfo(wifiHandle); lowi_cb_table_t *lowiWifiHalApi = NULL; gscan_event_handlers* event_handlers; GScanCommandEventHandler *gScanSetSignificantChangeCmdEventHandler; event_handlers = (gscan_event_handlers*)info->gscan_handlers; gScanSetSignificantChangeCmdEventHandler = event_handlers->gScanSetSignificantChangeCmdEventHandler; if (!(info->supported_feature_set & WIFI_FEATURE_GSCAN)) { ALOGE("%s: GSCAN is not supported by driver", __FUNCTION__); return WIFI_ERROR_NOT_SUPPORTED; } /* Route request through LOWI if supported*/ lowiWifiHalApi = getLowiCallbackTable(GSCAN_SUPPORTED); if (lowiWifiHalApi == NULL || lowiWifiHalApi->set_significant_change_handler == NULL) { ALOGV("%s: Sending cmd directly to host", __FUNCTION__); } else { ret = lowiWifiHalApi->set_significant_change_handler(id, iface, params, handler); ALOGV("%s: lowi set_significant_change_handler " "returned: %d. Exit.", __FUNCTION__, ret); return (wifi_error)ret; } /* Wi-Fi HAL doesn't need to check if a similar request to set significant * change list was made earlier. If set_significant_change() is called while * another one is running, the request will be sent down to driver and * firmware. If the new request is successfully honored, then Wi-Fi HAL * will use the new request id for the gScanSetSignificantChangeCmdEventHandler * object. */ gScanCommand = new GScanCommand( wifiHandle, id, OUI_QCA, QCA_NL80211_VENDOR_SUBCMD_GSCAN_SET_SIGNIFICANT_CHANGE); if (gScanCommand == NULL) { ALOGE("%s: Error GScanCommand NULL", __FUNCTION__); return WIFI_ERROR_UNKNOWN; } /* Create the NL message. */ ret = gScanCommand->create(); if (ret < 0) goto cleanup; /* Set the interface Id of the message. */ ret = gScanCommand->set_iface_id(ifaceInfo->name); if (ret < 0) goto cleanup; /* Add the vendor specific attributes for the NL command. */ nlData = gScanCommand->attr_start(NL80211_ATTR_VENDOR_DATA); if (!nlData) goto cleanup; numAp = (unsigned int)params.num_bssid > MAX_SIGNIFICANT_CHANGE_APS ? MAX_SIGNIFICANT_CHANGE_APS : params.num_bssid; if (gScanCommand->put_u32( QCA_WLAN_VENDOR_ATTR_GSCAN_SUBCMD_CONFIG_PARAM_REQUEST_ID, id) || gScanCommand->put_u32( QCA_WLAN_VENDOR_ATTR_GSCAN_SIGNIFICANT_CHANGE_PARAMS_RSSI_SAMPLE_SIZE, params.rssi_sample_size) || gScanCommand->put_u32( QCA_WLAN_VENDOR_ATTR_GSCAN_SIGNIFICANT_CHANGE_PARAMS_LOST_AP_SAMPLE_SIZE, params.lost_ap_sample_size) || gScanCommand->put_u32( QCA_WLAN_VENDOR_ATTR_GSCAN_SIGNIFICANT_CHANGE_PARAMS_MIN_BREACHING, params.min_breaching) || gScanCommand->put_u32( QCA_WLAN_VENDOR_ATTR_GSCAN_SIGNIFICANT_CHANGE_PARAMS_NUM_AP, numAp)) { goto cleanup; } ALOGV("%s: Number of AP params:%d Rssi_sample_size:%d " "lost_ap_sample_size:%d min_breaching:%d", __FUNCTION__, numAp, params.rssi_sample_size, params.lost_ap_sample_size, params.min_breaching); /* Add the vendor specific attributes for the NL command. */ nlApThresholdParamList = gScanCommand->attr_start( QCA_WLAN_VENDOR_ATTR_GSCAN_AP_THRESHOLD_PARAM); if (!nlApThresholdParamList) goto cleanup; /* Add nested NL attributes for AP Threshold Param list. */ for (i = 0; i < numAp; i++) { ap_threshold_param apThreshold = params.ap[i]; struct nlattr *nlApThresholdParam = gScanCommand->attr_start(i); if (!nlApThresholdParam) goto cleanup; if ( gScanCommand->put_addr( QCA_WLAN_VENDOR_ATTR_GSCAN_AP_THRESHOLD_PARAM_BSSID, apThreshold.bssid) || gScanCommand->put_s32( QCA_WLAN_VENDOR_ATTR_GSCAN_AP_THRESHOLD_PARAM_RSSI_LOW, apThreshold.low) || gScanCommand->put_s32( QCA_WLAN_VENDOR_ATTR_GSCAN_AP_THRESHOLD_PARAM_RSSI_HIGH, apThreshold.high)) { goto cleanup; } ALOGV("%s: ap[%d].bssid:%hhx:%hhx:%hhx:%hhx:%hhx:%hhx " "ap[%d].low:%d ap[%d].high:%d", __FUNCTION__, i, apThreshold.bssid[0], apThreshold.bssid[1], apThreshold.bssid[2], apThreshold.bssid[3], apThreshold.bssid[4], apThreshold.bssid[5], i, apThreshold.low, i, apThreshold.high); gScanCommand->attr_end(nlApThresholdParam); } gScanCommand->attr_end(nlApThresholdParamList); gScanCommand->attr_end(nlData); GScanCallbackHandler callbackHandler; memset(&callbackHandler, 0, sizeof(callbackHandler)); callbackHandler.on_significant_change = handler.on_significant_change; /* Create an object of the event handler class to take care of the * asychronous events on the north-bound. */ if (gScanSetSignificantChangeCmdEventHandler == NULL) { gScanSetSignificantChangeCmdEventHandler = new GScanCommandEventHandler( wifiHandle, id, OUI_QCA, QCA_NL80211_VENDOR_SUBCMD_GSCAN_SET_SIGNIFICANT_CHANGE, callbackHandler); if (gScanSetSignificantChangeCmdEventHandler == NULL) { ALOGE("%s: Error in instantiating, " "gScanSetSignificantChangeCmdEventHandler.", __FUNCTION__); ret = WIFI_ERROR_UNKNOWN; goto cleanup; } event_handlers->gScanSetSignificantChangeCmdEventHandler = gScanSetSignificantChangeCmdEventHandler; } else { gScanSetSignificantChangeCmdEventHandler->setCallbackHandler(callbackHandler); } ret = gScanCommand->requestResponse(); if (ret != 0) { ALOGE("%s: requestResponse Error:%d",__FUNCTION__, ret); goto cleanup; } if (gScanSetSignificantChangeCmdEventHandler != NULL) { gScanSetSignificantChangeCmdEventHandler->set_request_id(id); gScanSetSignificantChangeCmdEventHandler->enableEventHandling(); } cleanup: /* Disable Event Handling if ret != 0 */ if (ret && gScanSetSignificantChangeCmdEventHandler) { ALOGI("%s: Error ret:%d, disable event handling", __FUNCTION__, ret); gScanSetSignificantChangeCmdEventHandler->disableEventHandling(); } delete gScanCommand; return (wifi_error)ret; } /* Clear the GSCAN Significant AP change list. */ wifi_error wifi_reset_significant_change_handler(wifi_request_id id, wifi_interface_handle iface) { int ret = 0; GScanCommand *gScanCommand; struct nlattr *nlData; interface_info *ifaceInfo = getIfaceInfo(iface); wifi_handle wifiHandle = getWifiHandle(iface); hal_info *info = getHalInfo(wifiHandle); lowi_cb_table_t *lowiWifiHalApi = NULL; gscan_event_handlers* event_handlers; GScanCommandEventHandler *gScanSetSignificantChangeCmdEventHandler; event_handlers = (gscan_event_handlers*)info->gscan_handlers; gScanSetSignificantChangeCmdEventHandler = event_handlers->gScanSetSignificantChangeCmdEventHandler; if (!(info->supported_feature_set & WIFI_FEATURE_GSCAN)) { ALOGE("%s: GSCAN is not supported by driver", __FUNCTION__); return WIFI_ERROR_NOT_SUPPORTED; } /* Route request through LOWI if supported*/ lowiWifiHalApi = getLowiCallbackTable(GSCAN_SUPPORTED); if (lowiWifiHalApi == NULL || lowiWifiHalApi->reset_significant_change_handler == NULL) { ALOGV("%s: Sending cmd directly to host", __FUNCTION__); } else { ret = lowiWifiHalApi->reset_significant_change_handler(id, iface); ALOGV("%s: lowi reset_significant_change_handler " "returned: %d. Exit.", __FUNCTION__, ret); return (wifi_error)ret; } if (gScanSetSignificantChangeCmdEventHandler == NULL || (gScanSetSignificantChangeCmdEventHandler->isEventHandlingEnabled() == false)) { ALOGE("wifi_reset_significant_change_handler: GSCAN significant_change" " isn't set. Nothing to do. Exit"); return WIFI_ERROR_NOT_AVAILABLE; } gScanCommand = new GScanCommand ( wifiHandle, id, OUI_QCA, QCA_NL80211_VENDOR_SUBCMD_GSCAN_RESET_SIGNIFICANT_CHANGE); if (gScanCommand == NULL) { ALOGE("%s: Error GScanCommand NULL", __FUNCTION__); return WIFI_ERROR_UNKNOWN; } /* Create the NL message. */ ret = gScanCommand->create(); if (ret < 0) goto cleanup; /* Set the interface Id of the message. */ ret = gScanCommand->set_iface_id(ifaceInfo->name); if (ret < 0) goto cleanup; /* Add the vendor specific attributes for the NL command. */ nlData = gScanCommand->attr_start(NL80211_ATTR_VENDOR_DATA); if (!nlData) goto cleanup; ret = gScanCommand->put_u32( QCA_WLAN_VENDOR_ATTR_GSCAN_SUBCMD_CONFIG_PARAM_REQUEST_ID, id); if (ret < 0) goto cleanup; gScanCommand->attr_end(nlData); ret = gScanCommand->requestResponse(); if (ret != 0) { ALOGE("%s: requestResponse Error:%d",__FUNCTION__, ret); } /* Disable Event Handling. */ if (gScanSetSignificantChangeCmdEventHandler) { gScanSetSignificantChangeCmdEventHandler->disableEventHandling(); } cleanup: delete gScanCommand; return (wifi_error)ret; } /* Get the GSCAN cached scan results. */ wifi_error wifi_get_cached_gscan_results(wifi_interface_handle iface, byte flush, int max, wifi_cached_scan_results *results, int *num) { int requestId, ret = 0, retRequestRsp = 0; GScanCommand *gScanCommand; struct nlattr *nlData; lowi_cb_table_t *lowiWifiHalApi = NULL; interface_info *ifaceInfo = getIfaceInfo(iface); wifi_handle wifiHandle = getWifiHandle(iface); hal_info *info = getHalInfo(wifiHandle); if (!(info->supported_feature_set & WIFI_FEATURE_GSCAN)) { ALOGE("%s: GSCAN is not supported by driver", __FUNCTION__); return WIFI_ERROR_NOT_SUPPORTED; } /* Route GSCAN request through LOWI if supported */ lowiWifiHalApi = getLowiCallbackTable(GSCAN_SUPPORTED); if (lowiWifiHalApi == NULL || lowiWifiHalApi->get_cached_gscan_results == NULL) { ALOGV("%s: Sending cmd directly to host", __FUNCTION__); } else { ret = lowiWifiHalApi->get_cached_gscan_results(iface, flush, max, results, num); ALOGV("%s: lowi get_cached_gscan_results" "returned: %d. Exit.", __FUNCTION__, ret); return (wifi_error)ret; } /* No request id from caller, so generate one and pass it on to the driver. */ /* Generate it randomly */ requestId = get_requestid(); if (results == NULL || num == NULL) { ALOGE("%s: NULL pointer provided. Exit.", __FUNCTION__); return WIFI_ERROR_INVALID_ARGS; } gScanCommand = new GScanCommand( wifiHandle, requestId, OUI_QCA, QCA_NL80211_VENDOR_SUBCMD_GSCAN_GET_CACHED_RESULTS); if (gScanCommand == NULL) { ALOGE("%s: Error GScanCommand NULL", __FUNCTION__); return WIFI_ERROR_UNKNOWN; } ret = gScanCommand->allocRspParams(eGScanGetCachedResultsRspParams); if (ret != 0) { ALOGE("%s: Failed to allocate memory for response struct. Error:%d", __FUNCTION__, ret); goto cleanup; } ret = gScanCommand->allocCachedResultsTemp(max, results); if (ret != 0) { ALOGE("%s: Failed to allocate memory for temp gscan cached list. " "Error:%d", __FUNCTION__, ret); goto cleanup; } /* Clear the destination cached results list before copying results. */ memset(results, 0, max * sizeof(wifi_cached_scan_results)); /* Create the NL message. */ ret = gScanCommand->create(); if (ret < 0) goto cleanup; /* Set the interface Id of the message. */ ret = gScanCommand->set_iface_id(ifaceInfo->name); if (ret < 0) goto cleanup; /* Add the vendor specific attributes for the NL command. */ nlData = gScanCommand->attr_start(NL80211_ATTR_VENDOR_DATA); if (!nlData) goto cleanup; if (ret < 0) goto cleanup; if (gScanCommand->put_u32( QCA_WLAN_VENDOR_ATTR_GSCAN_SUBCMD_CONFIG_PARAM_REQUEST_ID, requestId) || gScanCommand->put_u8( QCA_WLAN_VENDOR_ATTR_GSCAN_GET_CACHED_SCAN_RESULTS_CONFIG_PARAM_FLUSH, flush) || gScanCommand->put_u32( QCA_WLAN_VENDOR_ATTR_GSCAN_GET_CACHED_SCAN_RESULTS_CONFIG_PARAM_MAX, max)) { goto cleanup; } ALOGV("%s: flush:%d max:%d", __FUNCTION__, flush, max); gScanCommand->attr_end(nlData); retRequestRsp = gScanCommand->requestResponse(); if (retRequestRsp != 0) { ALOGE("%s: requestResponse Error:%d", __FUNCTION__, retRequestRsp); if (retRequestRsp != -ETIMEDOUT) { /* Proceed to cleanup & return no results */ goto cleanup; } } /* No more data, copy the parsed results into the caller's results array */ ret = gScanCommand->copyCachedScanResults(num, results); ALOGV("%s: max: %d, num:%d", __FUNCTION__, max, *num); if (!ret) { /* If requestResponse returned a TIMEOUT */ if (retRequestRsp == -ETIMEDOUT) { if (*num > 0) { /* Mark scan results as incomplete for the last scan_id */ results[(*num)-1].flags = WIFI_SCAN_FLAG_INTERRUPTED; ALOGV("%s: Timeout happened. Mark scan results as incomplete " "for scan_id:%d", __FUNCTION__, results[(*num)-1].scan_id); ret = WIFI_SUCCESS; } else ret = WIFI_ERROR_TIMED_OUT; } } cleanup: gScanCommand->freeRspParams(eGScanGetCachedResultsRspParams); delete gScanCommand; return (wifi_error)ret; } /* Random MAC OUI for PNO */ wifi_error wifi_set_scanning_mac_oui(wifi_interface_handle handle, oui scan_oui) { int ret = 0; struct nlattr *nlData; WifiVendorCommand *vCommand = NULL; interface_info *iinfo = getIfaceInfo(handle); wifi_handle wifiHandle = getWifiHandle(handle); vCommand = new WifiVendorCommand(wifiHandle, 0, OUI_QCA, QCA_NL80211_VENDOR_SUBCMD_SCANNING_MAC_OUI); if (vCommand == NULL) { ALOGE("%s: Error vCommand NULL", __FUNCTION__); return WIFI_ERROR_OUT_OF_MEMORY; } /* create the message */ ret = vCommand->create(); if (ret < 0) goto cleanup; ret = vCommand->set_iface_id(iinfo->name); if (ret < 0) goto cleanup; /* Add the vendor specific attributes for the NL command. */ nlData = vCommand->attr_start(NL80211_ATTR_VENDOR_DATA); if (!nlData) goto cleanup; ALOGV("%s: MAC_OUI - %02x:%02x:%02x", __FUNCTION__, scan_oui[0], scan_oui[1], scan_oui[2]); /* Add the fixed part of the mac_oui to the nl command */ ret = vCommand->put_bytes( QCA_WLAN_VENDOR_ATTR_SET_SCANNING_MAC_OUI, (char *)scan_oui, WIFI_SCANNING_MAC_OUI_LENGTH); if (ret < 0) goto cleanup; vCommand->attr_end(nlData); ret = vCommand->requestResponse(); if (ret != 0) { ALOGE("%s: requestResponse Error:%d",__FUNCTION__, ret); goto cleanup; } cleanup: delete vCommand; return (wifi_error)ret; } GScanCommand::GScanCommand(wifi_handle handle, int id, u32 vendor_id, u32 subcmd) : WifiVendorCommand(handle, id, vendor_id, subcmd) { /* Initialize the member data variables here */ mGetCachedResultsRspParams = NULL; mChannels = NULL; mMaxChannels = 0; mNumChannelsPtr = NULL; mRequestId = id; memset(&mHandler, 0,sizeof(mHandler)); } GScanCommand::~GScanCommand() { unregisterVendorHandler(mVendor_id, mSubcmd); } /* This function implements creation of Vendor command */ int GScanCommand::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; ALOGV("%s: mVendor_id = %d, Subcmd = %d.", __FUNCTION__, mVendor_id, mSubcmd); out: return ret; } int GScanCommand::requestResponse() { return WifiCommand::requestResponse(mMsg); } int GScanCommand::handleResponse(WifiEvent &reply) { int i = 0; int ret = WIFI_SUCCESS; u32 val; WifiVendorCommand::handleResponse(reply); struct nlattr *tbVendor[ QCA_WLAN_VENDOR_ATTR_GSCAN_RESULTS_MAX + 1]; nla_parse(tbVendor, QCA_WLAN_VENDOR_ATTR_GSCAN_RESULTS_MAX, (struct nlattr *)mVendorData,mDataLen, NULL); switch(mSubcmd) { case QCA_NL80211_VENDOR_SUBCMD_GSCAN_GET_VALID_CHANNELS: { if (!tbVendor[QCA_WLAN_VENDOR_ATTR_GSCAN_RESULTS_NUM_CHANNELS]) { ALOGE("%s: QCA_WLAN_VENDOR_ATTR_GSCAN_RESULTS_NUM_CHANNELS" " not found", __FUNCTION__); ret = WIFI_ERROR_INVALID_ARGS; break; } val = nla_get_u32(tbVendor[ QCA_WLAN_VENDOR_ATTR_GSCAN_RESULTS_NUM_CHANNELS]); val = val > (unsigned int)mMaxChannels ? (unsigned int)mMaxChannels : val; *mNumChannelsPtr = val; /* Extract the list of channels. */ if (*mNumChannelsPtr > 0 ) { if (!tbVendor[QCA_WLAN_VENDOR_ATTR_GSCAN_RESULTS_CHANNELS]) { ALOGE("%s: QCA_WLAN_VENDOR_ATTR_GSCAN_RESULTS_CHANNELS" " not found", __FUNCTION__); ret = WIFI_ERROR_INVALID_ARGS; break; } nla_memcpy(mChannels, tbVendor[ QCA_WLAN_VENDOR_ATTR_GSCAN_RESULTS_CHANNELS], sizeof(wifi_channel) * (*mNumChannelsPtr)); } char buf[256]; size_t len = 0; for (i = 0; i < *mNumChannelsPtr && len < sizeof(buf); i++) { len += snprintf(buf + len, sizeof(buf) - len, "%u ", *(mChannels + i)); } ALOGV("%s: Num Channels %d: List of valid channels are: %s", __FUNCTION__, *mNumChannelsPtr, buf); } break; case QCA_NL80211_VENDOR_SUBCMD_GSCAN_GET_CACHED_RESULTS: { wifi_request_id id; u32 numResults = 0; int firstScanIdInPatch = -1; if (!tbVendor[ QCA_WLAN_VENDOR_ATTR_GSCAN_RESULTS_REQUEST_ID]) { ALOGE("%s: GSCAN_RESULTS_REQUEST_ID not" "found", __FUNCTION__); ret = WIFI_ERROR_INVALID_ARGS; break; } id = nla_get_u32( tbVendor[QCA_WLAN_VENDOR_ATTR_GSCAN_RESULTS_REQUEST_ID] ); /* If this is not for us, just ignore it. */ if (id != mRequestId) { ALOGV("%s: Event has Req. ID:%d <> ours:%d", __FUNCTION__, id, mRequestId); break; } if (!tbVendor[ QCA_WLAN_VENDOR_ATTR_GSCAN_RESULTS_NUM_RESULTS_AVAILABLE]) { ALOGE("%s: GSCAN_RESULTS_NUM_RESULTS_AVAILABLE not" "found", __FUNCTION__); ret = WIFI_ERROR_INVALID_ARGS; break; } /* Read num of cached scan results in this data chunk. Note that * this value doesn't represent the number of unique gscan scan Ids * since the first scan id in this new chunk could be similar to * the last scan id in the previous chunk. */ numResults = nla_get_u32(tbVendor[ QCA_WLAN_VENDOR_ATTR_GSCAN_RESULTS_NUM_RESULTS_AVAILABLE]); ALOGV("%s: num Cached results in this fragment:%d", __FUNCTION__, numResults); if (!mGetCachedResultsRspParams) { ALOGE("%s: mGetCachedResultsRspParams is NULL, exit.", __FUNCTION__); ret = WIFI_ERROR_INVALID_ARGS; break; } /* To support fragmentation from firmware, monitor the * MORE_DATA flag and cache results until MORE_DATA = 0. */ if (!tbVendor[ QCA_WLAN_VENDOR_ATTR_GSCAN_RESULTS_SCAN_RESULT_MORE_DATA]) { ALOGE("%s: GSCAN_RESULTS_NUM_RESULTS_MORE_DATA " "not found", __FUNCTION__); ret = WIFI_ERROR_INVALID_ARGS; break; } else { mGetCachedResultsRspParams->more_data = nla_get_u8( tbVendor[ QCA_WLAN_VENDOR_ATTR_GSCAN_RESULTS_SCAN_RESULT_MORE_DATA]); } /* No data in this chunk so skip this chunk */ if (numResults == 0) { return NL_SKIP; } if (!tbVendor[QCA_WLAN_VENDOR_ATTR_GSCAN_CACHED_RESULTS_SCAN_ID]) { ALOGE("GSCAN_CACHED_RESULTS_SCAN_ID not found"); ret = WIFI_ERROR_INVALID_ARGS; break; } /* Get the first Scan-Id in this chuck of cached results. */ firstScanIdInPatch = nla_get_u32(tbVendor[ QCA_WLAN_VENDOR_ATTR_GSCAN_CACHED_RESULTS_SCAN_ID]); ALOGV("More data: %d, firstScanIdInPatch: %d, lastProcessedScanId: %d", mGetCachedResultsRspParams->more_data, firstScanIdInPatch, mGetCachedResultsRspParams->lastProcessedScanId); if (numResults) { if (firstScanIdInPatch != mGetCachedResultsRspParams->lastProcessedScanId) { /* New result scan Id block, update the starting index. */ mGetCachedResultsRspParams->cachedResultsStartingIndex++; } ret = gscan_get_cached_results( mGetCachedResultsRspParams->cached_results, tbVendor); /* If a parsing error occurred, exit and proceed for cleanup. */ if (ret) break; } } break; default: /* Error case should not happen print log */ ALOGE("%s: Wrong GScan subcmd response received %d", __FUNCTION__, mSubcmd); } /* A parsing error occurred, do the cleanup of gscan result lists. */ if (ret) { switch(mSubcmd) { case QCA_NL80211_VENDOR_SUBCMD_GSCAN_GET_CACHED_RESULTS: { ALOGE("%s: Parsing error, free CachedResultsRspParams", __FUNCTION__); freeRspParams(eGScanGetCachedResultsRspParams); } break; default: ALOGE("%s: Wrong GScan subcmd received %d", __FUNCTION__, mSubcmd); } } return NL_SKIP; } /* Called to parse and extract cached results. */ int GScanCommand:: gscan_get_cached_results( wifi_cached_scan_results *cached_results, struct nlattr **tb_vendor) { u32 j = 0; struct nlattr *scanResultsInfo, *wifiScanResultsInfo; int rem = 0, remResults = 0; u32 len = 0, numScanResults = 0; u32 i = mGetCachedResultsRspParams->cachedResultsStartingIndex; ALOGV("%s: starting counter: %d", __FUNCTION__, i); for (scanResultsInfo = (struct nlattr *) nla_data(tb_vendor[ QCA_WLAN_VENDOR_ATTR_GSCAN_CACHED_RESULTS_LIST]), rem = nla_len(tb_vendor[ QCA_WLAN_VENDOR_ATTR_GSCAN_CACHED_RESULTS_LIST]); nla_ok(scanResultsInfo, rem) && i < mGetCachedResultsRspParams->max; scanResultsInfo = nla_next(scanResultsInfo, &(rem))) { struct nlattr *tb2[QCA_WLAN_VENDOR_ATTR_GSCAN_RESULTS_MAX + 1]; nla_parse(tb2, QCA_WLAN_VENDOR_ATTR_GSCAN_RESULTS_MAX, (struct nlattr *) nla_data(scanResultsInfo), nla_len(scanResultsInfo), NULL); if (! tb2[ QCA_WLAN_VENDOR_ATTR_GSCAN_CACHED_RESULTS_SCAN_ID ]) { ALOGE("%s: GSCAN_CACHED_RESULTS_SCAN_ID" " not found", __FUNCTION__); return WIFI_ERROR_INVALID_ARGS; } cached_results[i].scan_id = nla_get_u32( tb2[ QCA_WLAN_VENDOR_ATTR_GSCAN_CACHED_RESULTS_SCAN_ID ]); if (! tb2[ QCA_WLAN_VENDOR_ATTR_GSCAN_CACHED_RESULTS_FLAGS ]) { ALOGE("%s: GSCAN_CACHED_RESULTS_FLAGS " "not found", __FUNCTION__); return WIFI_ERROR_INVALID_ARGS; } cached_results[i].flags = nla_get_u32( tb2[QCA_WLAN_VENDOR_ATTR_GSCAN_CACHED_RESULTS_FLAGS]); if (!tb2[QCA_WLAN_VENDOR_ATTR_GSCAN_RESULTS_BUCKETS_SCANNED]) { ALOGI("%s: GSCAN_RESULTS_BUCKETS_SCANNED" "not found", __FUNCTION__); } else { cached_results[i].buckets_scanned = nla_get_u32( tb2[QCA_WLAN_VENDOR_ATTR_GSCAN_RESULTS_BUCKETS_SCANNED]); } if (! tb2[ QCA_WLAN_VENDOR_ATTR_GSCAN_RESULTS_NUM_RESULTS_AVAILABLE ]) { ALOGE("%s: RESULTS_NUM_RESULTS_AVAILABLE " "not found", __FUNCTION__); return WIFI_ERROR_INVALID_ARGS; } numScanResults = nla_get_u32( tb2[QCA_WLAN_VENDOR_ATTR_GSCAN_RESULTS_NUM_RESULTS_AVAILABLE]); if (mGetCachedResultsRspParams->lastProcessedScanId != cached_results[i].scan_id) { j = 0; /* reset wifi_scan_result counter */ cached_results[i].num_results = 0; ALOGV("parsing: *lastProcessedScanId [%d] !=" " cached_results[%d].scan_id:%d, j:%d " "numScanResults: %d", mGetCachedResultsRspParams->lastProcessedScanId, i, cached_results[i].scan_id, j, numScanResults); mGetCachedResultsRspParams->lastProcessedScanId = cached_results[i].scan_id; mGetCachedResultsRspParams->wifiScanResultsStartingIndex = 0; /* Increment the number of cached scan results received */ mGetCachedResultsRspParams->num_cached_results++; } else { j = mGetCachedResultsRspParams->wifiScanResultsStartingIndex; ALOGV("parsing: *lastProcessedScanId [%d] == " "cached_results[%d].scan_id:%d, j:%d " "numScanResults:%d", mGetCachedResultsRspParams->lastProcessedScanId, i, cached_results[i].scan_id, j, numScanResults); } ALOGV("%s: scan_id %d ", __FUNCTION__, cached_results[i].scan_id); ALOGV("%s: flags %u ", __FUNCTION__, cached_results[i].flags); for (wifiScanResultsInfo = (struct nlattr *) nla_data(tb2[ QCA_WLAN_VENDOR_ATTR_GSCAN_RESULTS_LIST]), remResults = nla_len(tb2[ QCA_WLAN_VENDOR_ATTR_GSCAN_RESULTS_LIST]); nla_ok(wifiScanResultsInfo, remResults); wifiScanResultsInfo = nla_next(wifiScanResultsInfo, &(remResults))) { struct nlattr *tb3[QCA_WLAN_VENDOR_ATTR_GSCAN_RESULTS_MAX + 1]; nla_parse(tb3, QCA_WLAN_VENDOR_ATTR_GSCAN_RESULTS_MAX, (struct nlattr *) nla_data(wifiScanResultsInfo), nla_len(wifiScanResultsInfo), NULL); if (j < MAX_AP_CACHE_PER_SCAN) { if (! tb3[ QCA_WLAN_VENDOR_ATTR_GSCAN_RESULTS_SCAN_RESULT_TIME_STAMP ]) { ALOGE("%s: " "RESULTS_SCAN_RESULT_TIME_STAMP not found", __FUNCTION__); return WIFI_ERROR_INVALID_ARGS; } cached_results[i].results[j].ts = nla_get_u64( tb3[ QCA_WLAN_VENDOR_ATTR_GSCAN_RESULTS_SCAN_RESULT_TIME_STAMP ]); if (! tb3[ QCA_WLAN_VENDOR_ATTR_GSCAN_RESULTS_SCAN_RESULT_SSID ]) { ALOGE("%s: " "RESULTS_SCAN_RESULT_SSID not found", __FUNCTION__); return WIFI_ERROR_INVALID_ARGS; } len = nla_len(tb3[ QCA_WLAN_VENDOR_ATTR_GSCAN_RESULTS_SCAN_RESULT_SSID]); len = sizeof(cached_results[i].results[j].ssid) <= len ? sizeof(cached_results[i].results[j].ssid) : len; memcpy((void *)&cached_results[i].results[j].ssid, nla_data( tb3[ QCA_WLAN_VENDOR_ATTR_GSCAN_RESULTS_SCAN_RESULT_SSID]), len); if (! tb3[ QCA_WLAN_VENDOR_ATTR_GSCAN_RESULTS_SCAN_RESULT_BSSID ]) { ALOGE("%s: " "RESULTS_SCAN_RESULT_BSSID not found", __FUNCTION__); return WIFI_ERROR_INVALID_ARGS; } len = nla_len( tb3[ QCA_WLAN_VENDOR_ATTR_GSCAN_RESULTS_SCAN_RESULT_BSSID]); len = sizeof(cached_results[i].results[j].bssid) <= len ? sizeof(cached_results[i].results[j].bssid) : len; memcpy(&cached_results[i].results[j].bssid, nla_data( tb3[ QCA_WLAN_VENDOR_ATTR_GSCAN_RESULTS_SCAN_RESULT_BSSID]), len); if (! tb3[ QCA_WLAN_VENDOR_ATTR_GSCAN_RESULTS_SCAN_RESULT_CHANNEL ]) { ALOGE("%s: " "RESULTS_SCAN_RESULT_CHANNEL not found", __FUNCTION__); return WIFI_ERROR_INVALID_ARGS; } cached_results[i].results[j].channel = nla_get_u32( tb3[QCA_WLAN_VENDOR_ATTR_GSCAN_RESULTS_SCAN_RESULT_CHANNEL]); if (! tb3[ QCA_WLAN_VENDOR_ATTR_GSCAN_RESULTS_SCAN_RESULT_RSSI ]) { ALOGE("%s: " "RESULTS_SCAN_RESULT_RSSI not found", __FUNCTION__); return WIFI_ERROR_INVALID_ARGS; } cached_results[i].results[j].rssi = get_s32( tb3[QCA_WLAN_VENDOR_ATTR_GSCAN_RESULTS_SCAN_RESULT_RSSI]); if (! tb3[ QCA_WLAN_VENDOR_ATTR_GSCAN_RESULTS_SCAN_RESULT_RTT ]) { ALOGE("%s: " "RESULTS_SCAN_RESULT_RTT not found", __FUNCTION__); return WIFI_ERROR_INVALID_ARGS; } cached_results[i].results[j].rtt = nla_get_u32( tb3[QCA_WLAN_VENDOR_ATTR_GSCAN_RESULTS_SCAN_RESULT_RTT]); if (! tb3[ QCA_WLAN_VENDOR_ATTR_GSCAN_RESULTS_SCAN_RESULT_RTT_SD ]) { ALOGE("%s: " "RESULTS_SCAN_RESULT_RTT_SD not found", __FUNCTION__); return WIFI_ERROR_INVALID_ARGS; } cached_results[i].results[j].rtt_sd = nla_get_u32( tb3[QCA_WLAN_VENDOR_ATTR_GSCAN_RESULTS_SCAN_RESULT_RTT_SD]); #ifdef QC_HAL_DEBUG /* Enable these prints for debugging if needed. */ ALOGD("%s: ts %" PRId64, __FUNCTION__, cached_results[i].results[j].ts); ALOGD("%s: SSID %s ", __FUNCTION__, cached_results[i].results[j].ssid); ALOGD("%s: BSSID: %02x:%02x:%02x:%02x:%02x:%02x \n", __FUNCTION__, cached_results[i].results[j].bssid[0], cached_results[i].results[j].bssid[1], cached_results[i].results[j].bssid[2], cached_results[i].results[j].bssid[3], cached_results[i].results[j].bssid[4], cached_results[i].results[j].bssid[5]); ALOGD("%s: channel %d ", __FUNCTION__, cached_results[i].results[j].channel); ALOGD("%s: rssi %d ", __FUNCTION__, cached_results[i].results[j].rssi); ALOGD("%s: rtt %" PRId64, __FUNCTION__, cached_results[i].results[j].rtt); ALOGD("%s: rtt_sd %" PRId64, __FUNCTION__, cached_results[i].results[j].rtt_sd); #endif /* Increment loop index for next record */ j++; /* For this scan id, update the wifiScanResultsStartingIndex * and number of cached results parsed so far. */ mGetCachedResultsRspParams->wifiScanResultsStartingIndex = j; cached_results[i].num_results++; } else { /* We already parsed and stored up to max wifi_scan_results * specified by the caller. Now, continue to loop over NL * entries in order to properly update NL parsing pointer * so it points to the next scan_id results. */ ALOGD("%s: loop index:%d > max num" " of wifi_scan_results:%d for gscan cached results" " bucket:%d. Dummy loop", __FUNCTION__, j, MAX_AP_CACHE_PER_SCAN, i); } } ALOGV("%s: cached_results[%d].num_results: %d ", __FUNCTION__, i, cached_results[i].num_results); /* Increment loop index for next cached scan result record */ i++; } /* Increment starting index of filling cached results received */ if (mGetCachedResultsRspParams->num_cached_results) mGetCachedResultsRspParams->cachedResultsStartingIndex = mGetCachedResultsRspParams->num_cached_results - 1; return WIFI_SUCCESS; } /* Set the GSCAN BSSID Hotlist. */ wifi_error wifi_set_epno_list(wifi_request_id id, wifi_interface_handle iface, const wifi_epno_params *epno_params, wifi_epno_handler handler) { int i, ret = 0, num_networks; GScanCommand *gScanCommand; struct nlattr *nlData, *nlPnoParamList; interface_info *ifaceInfo = getIfaceInfo(iface); wifi_handle wifiHandle = getWifiHandle(iface); hal_info *info = getHalInfo(wifiHandle); gscan_event_handlers* event_handlers; GScanCommandEventHandler *gScanSetPnoListCmdEventHandler; event_handlers = (gscan_event_handlers*)info->gscan_handlers; gScanSetPnoListCmdEventHandler = event_handlers->gScanSetPnoListCmdEventHandler; if (!(info->supported_feature_set & WIFI_FEATURE_HAL_EPNO)) { ALOGE("%s: Enhanced PNO is not supported by the driver", __FUNCTION__); return WIFI_ERROR_NOT_SUPPORTED; } /* Wi-Fi HAL doesn't need to check if a similar request to set ePNO * list was made earlier. If wifi_set_epno_list() is called while * another one is running, the request will be sent down to driver and * firmware. If the new request is successfully honored, then Wi-Fi HAL * will use the new request id for the gScanSetPnoListCmdEventHandler * object. */ gScanCommand = new GScanCommand( wifiHandle, id, OUI_QCA, QCA_NL80211_VENDOR_SUBCMD_PNO_SET_LIST); if (gScanCommand == NULL) { ALOGE("%s: Error GScanCommand NULL", __FUNCTION__); return WIFI_ERROR_UNKNOWN; } /* Create the NL message. */ ret = gScanCommand->create(); if (ret < 0) { ALOGE("%s: Failed to create the NL msg. Error:%d", __FUNCTION__, ret); goto cleanup; } /* Set the interface Id of the message. */ ret = gScanCommand->set_iface_id(ifaceInfo->name); if (ret < 0) { ALOGE("%s: Failed to set iface id. Error:%d", __FUNCTION__, ret); goto cleanup; } /* Add the vendor specific attributes for the NL command. */ nlData = gScanCommand->attr_start(NL80211_ATTR_VENDOR_DATA); if (!nlData) { ALOGE("%s: Failed to add attribute NL80211_ATTR_VENDOR_DATA. Error:%d", __FUNCTION__, ret); goto cleanup; } num_networks = (unsigned int)epno_params->num_networks > MAX_EPNO_NETWORKS ? MAX_EPNO_NETWORKS : epno_params->num_networks; if (gScanCommand->put_u32( QCA_WLAN_VENDOR_ATTR_GSCAN_SUBCMD_CONFIG_PARAM_REQUEST_ID, id) || gScanCommand->put_u32( QCA_WLAN_VENDOR_ATTR_EPNO_MIN5GHZ_RSSI, epno_params->min5GHz_rssi) || gScanCommand->put_u32( QCA_WLAN_VENDOR_ATTR_EPNO_MIN24GHZ_RSSI, epno_params->min24GHz_rssi) || gScanCommand->put_u32( QCA_WLAN_VENDOR_ATTR_EPNO_INITIAL_SCORE_MAX, epno_params->initial_score_max) || gScanCommand->put_u32( QCA_WLAN_VENDOR_ATTR_EPNO_CURRENT_CONNECTION_BONUS, epno_params->current_connection_bonus) || gScanCommand->put_u32( QCA_WLAN_VENDOR_ATTR_EPNO_SAME_NETWORK_BONUS, epno_params->same_network_bonus) || gScanCommand->put_u32( QCA_WLAN_VENDOR_ATTR_EPNO_SECURE_BONUS, epno_params->secure_bonus) || gScanCommand->put_u32( QCA_WLAN_VENDOR_ATTR_EPNO_BAND5GHZ_BONUS, epno_params->band5GHz_bonus) || gScanCommand->put_u32( QCA_WLAN_VENDOR_ATTR_PNO_SET_LIST_PARAM_NUM_NETWORKS, num_networks)) { ALOGE("%s: Failed to add vendor atributes. Error:%d", __FUNCTION__, ret); goto cleanup; } /* Add the vendor specific attributes for the NL command. */ nlPnoParamList = gScanCommand->attr_start( QCA_WLAN_VENDOR_ATTR_PNO_SET_LIST_PARAM_EPNO_NETWORKS_LIST); if (!nlPnoParamList) { ALOGE("%s: Failed to add attr. PNO_SET_LIST_PARAM_EPNO_NETWORKS_LIST. " "Error:%d", __FUNCTION__, ret); goto cleanup; } /* Add nested NL attributes for ePno List. */ for (i = 0; i < num_networks; i++) { wifi_epno_network pnoNetwork = epno_params->networks[i]; struct nlattr *nlPnoNetwork = gScanCommand->attr_start(i); if (!nlPnoNetwork) { ALOGE("%s: Failed attr_start for nlPnoNetwork. Error:%d", __FUNCTION__, ret); goto cleanup; } if (gScanCommand->put_string( QCA_WLAN_VENDOR_ATTR_PNO_SET_LIST_PARAM_EPNO_NETWORK_SSID, pnoNetwork.ssid) || gScanCommand->put_u8( QCA_WLAN_VENDOR_ATTR_PNO_SET_LIST_PARAM_EPNO_NETWORK_FLAGS, pnoNetwork.flags) || gScanCommand->put_u8( QCA_WLAN_VENDOR_ATTR_PNO_SET_LIST_PARAM_EPNO_NETWORK_AUTH_BIT, pnoNetwork.auth_bit_field)) { ALOGE("%s: Failed to add PNO_SET_LIST_PARAM_EPNO_NETWORK_*. " "Error:%d", __FUNCTION__, ret); goto cleanup; } gScanCommand->attr_end(nlPnoNetwork); } gScanCommand->attr_end(nlPnoParamList); gScanCommand->attr_end(nlData); GScanCallbackHandler callbackHandler; memset(&callbackHandler, 0, sizeof(callbackHandler)); callbackHandler.on_pno_network_found = handler.on_network_found; /* Create an object of the event handler class to take care of the * asychronous events on the north-bound. */ if (gScanSetPnoListCmdEventHandler == NULL) { gScanSetPnoListCmdEventHandler = new GScanCommandEventHandler( wifiHandle, id, OUI_QCA, QCA_NL80211_VENDOR_SUBCMD_PNO_SET_LIST, callbackHandler); if (gScanSetPnoListCmdEventHandler == NULL) { ALOGE("%s: Error instantiating " "gScanSetPnoListCmdEventHandler.", __FUNCTION__); ret = WIFI_ERROR_UNKNOWN; goto cleanup; } event_handlers->gScanSetPnoListCmdEventHandler = gScanSetPnoListCmdEventHandler; } else { gScanSetPnoListCmdEventHandler->setCallbackHandler(callbackHandler); } ret = gScanCommand->requestResponse(); if (ret != 0) { ALOGE("%s: requestResponse Error:%d",__FUNCTION__, ret); goto cleanup; } if (gScanSetPnoListCmdEventHandler != NULL) { gScanSetPnoListCmdEventHandler->set_request_id(id); gScanSetPnoListCmdEventHandler->enableEventHandling(); } cleanup: delete gScanCommand; /* Disable Event Handling if ret != 0 */ if (ret && gScanSetPnoListCmdEventHandler) { ALOGI("%s: Error ret:%d, disable event handling", __FUNCTION__, ret); gScanSetPnoListCmdEventHandler->disableEventHandling(); } return (wifi_error)ret; } /* Reset the ePNO list - no ePNO networks should be matched after this */ wifi_error wifi_reset_epno_list(wifi_request_id id, wifi_interface_handle iface) { int ret = 0; GScanCommand *gScanCommand; struct nlattr *nlData; interface_info *ifaceInfo = getIfaceInfo(iface); wifi_handle wifiHandle = getWifiHandle(iface); hal_info *info = getHalInfo(wifiHandle); if (!(info->supported_feature_set & WIFI_FEATURE_HAL_EPNO)) { ALOGE("%s: Enhanced PNO is not supported by the driver", __FUNCTION__); return WIFI_ERROR_NOT_SUPPORTED; } gScanCommand = new GScanCommand(wifiHandle, id, OUI_QCA, QCA_NL80211_VENDOR_SUBCMD_PNO_SET_LIST); if (gScanCommand == NULL) { ALOGE("%s: Error GScanCommand NULL", __FUNCTION__); return WIFI_ERROR_UNKNOWN; } /* Create the NL message. */ ret = gScanCommand->create(); if (ret < 0) { ALOGE("%s: Failed to create the NL msg. Error:%d", __FUNCTION__, ret); goto cleanup; } /* Set the interface Id of the message. */ ret = gScanCommand->set_iface_id(ifaceInfo->name); if (ret < 0) { ALOGE("%s: Failed to set iface id. Error:%d", __FUNCTION__, ret); goto cleanup; } /* Add the vendor specific attributes for the NL command. */ nlData = gScanCommand->attr_start(NL80211_ATTR_VENDOR_DATA); if (!nlData) { ALOGE("%s: Failed to add attribute NL80211_ATTR_VENDOR_DATA. Error:%d", __FUNCTION__, ret); goto cleanup; } if (gScanCommand->put_u32( QCA_WLAN_VENDOR_ATTR_GSCAN_SUBCMD_CONFIG_PARAM_REQUEST_ID, id) || gScanCommand->put_u32( QCA_WLAN_VENDOR_ATTR_PNO_SET_LIST_PARAM_NUM_NETWORKS, EPNO_NO_NETWORKS)) { ALOGE("%s: Failed to add vendor atributes Error:%d", __FUNCTION__, ret); goto cleanup; } gScanCommand->attr_end(nlData); ret = gScanCommand->requestResponse(); if (ret != 0) { ALOGE("%s: requestResponse Error:%d",__FUNCTION__, ret); } cleanup: delete gScanCommand; return (wifi_error)ret; } /* Set the ePNO Passpoint List. */ wifi_error wifi_set_passpoint_list(wifi_request_id id, wifi_interface_handle iface, int num, wifi_passpoint_network *networks, wifi_passpoint_event_handler handler) { int i, ret = 0; GScanCommand *gScanCommand; struct nlattr *nlData, *nlPasspointNetworksParamList; interface_info *ifaceInfo = getIfaceInfo(iface); wifi_handle wifiHandle = getWifiHandle(iface); hal_info *info = getHalInfo(wifiHandle); gscan_event_handlers* event_handlers; GScanCommandEventHandler *gScanPnoSetPasspointListCmdEventHandler; event_handlers = (gscan_event_handlers*)info->gscan_handlers; gScanPnoSetPasspointListCmdEventHandler = event_handlers->gScanPnoSetPasspointListCmdEventHandler; if (!(info->supported_feature_set & WIFI_FEATURE_HAL_EPNO)) { ALOGE("%s: Enhanced PNO is not supported by the driver", __FUNCTION__); return WIFI_ERROR_NOT_SUPPORTED; } /* Wi-Fi HAL doesn't need to check if a similar request to set ePNO * passpoint list was made earlier. If wifi_set_passpoint_list() is called * while another one is running, the request will be sent down to driver and * firmware. If the new request is successfully honored, then Wi-Fi HAL * will use the new request id for the * gScanPnoSetPasspointListCmdEventHandler object. */ gScanCommand = new GScanCommand( wifiHandle, id, OUI_QCA, QCA_NL80211_VENDOR_SUBCMD_PNO_SET_PASSPOINT_LIST); if (gScanCommand == NULL) { ALOGE("%s: Error GScanCommand NULL", __FUNCTION__); return WIFI_ERROR_UNKNOWN; } /* Create the NL message. */ ret = gScanCommand->create(); if (ret < 0) { ALOGE("%s: Failed to create the NL msg. Error:%d", __FUNCTION__, ret); goto cleanup; } /* Set the interface Id of the message. */ ret = gScanCommand->set_iface_id(ifaceInfo->name); if (ret < 0) { ALOGE("%s: Failed to set iface id. Error:%d", __FUNCTION__, ret); goto cleanup; } /* Add the vendor specific attributes for the NL command. */ nlData = gScanCommand->attr_start(NL80211_ATTR_VENDOR_DATA); if (!nlData) { ALOGE("%s: Failed to add attribute NL80211_ATTR_VENDOR_DATA. Error:%d", __FUNCTION__, ret); goto cleanup; } if (gScanCommand->put_u32( QCA_WLAN_VENDOR_ATTR_GSCAN_SUBCMD_CONFIG_PARAM_REQUEST_ID, id) || gScanCommand->put_u32( QCA_WLAN_VENDOR_ATTR_PNO_PASSPOINT_LIST_PARAM_NUM, num)) { ALOGE("%s: Failed to add vendor atributes. Error:%d", __FUNCTION__, ret); goto cleanup; } /* Add the vendor specific attributes for the NL command. */ nlPasspointNetworksParamList = gScanCommand->attr_start( QCA_WLAN_VENDOR_ATTR_PNO_PASSPOINT_LIST_PARAM_NETWORK_ARRAY); if (!nlPasspointNetworksParamList) { ALOGE("%s: Failed attr_start for PASSPOINT_LIST_PARAM_NETWORK_ARRAY. " "Error:%d", __FUNCTION__, ret); goto cleanup; } /* Add nested NL attributes for Passpoint List param. */ for (i = 0; i < num; i++) { wifi_passpoint_network passpointNetwork = networks[i]; struct nlattr *nlPasspointNetworkParam = gScanCommand->attr_start(i); if (!nlPasspointNetworkParam) { ALOGE("%s: Failed attr_start for nlPasspointNetworkParam. " "Error:%d", __FUNCTION__, ret); goto cleanup; } if (gScanCommand->put_u32( QCA_WLAN_VENDOR_ATTR_PNO_PASSPOINT_NETWORK_PARAM_ID, passpointNetwork.id) || gScanCommand->put_string( QCA_WLAN_VENDOR_ATTR_PNO_PASSPOINT_NETWORK_PARAM_REALM, passpointNetwork.realm) || gScanCommand->put_bytes( QCA_WLAN_VENDOR_ATTR_PNO_PASSPOINT_NETWORK_PARAM_ROAM_CNSRTM_ID, (char*)passpointNetwork.roamingConsortiumIds, 16 * sizeof(int64_t)) || gScanCommand->put_bytes( QCA_WLAN_VENDOR_ATTR_PNO_PASSPOINT_NETWORK_PARAM_ROAM_PLMN, (char*)passpointNetwork.plmn, 3 * sizeof(u8))) { ALOGE("%s: Failed to add PNO_PASSPOINT_NETWORK_PARAM_ROAM_* attr. " "Error:%d", __FUNCTION__, ret); goto cleanup; } gScanCommand->attr_end(nlPasspointNetworkParam); } gScanCommand->attr_end(nlPasspointNetworksParamList); gScanCommand->attr_end(nlData); GScanCallbackHandler callbackHandler; memset(&callbackHandler, 0, sizeof(callbackHandler)); callbackHandler.on_passpoint_network_found = handler.on_passpoint_network_found; /* Create an object of the event handler class to take care of the * asychronous events on the north-bound. */ if (gScanPnoSetPasspointListCmdEventHandler == NULL) { gScanPnoSetPasspointListCmdEventHandler = new GScanCommandEventHandler( wifiHandle, id, OUI_QCA, QCA_NL80211_VENDOR_SUBCMD_PNO_SET_PASSPOINT_LIST, callbackHandler); if (gScanPnoSetPasspointListCmdEventHandler == NULL) { ALOGE("%s: Error instantiating " "gScanPnoSetPasspointListCmdEventHandler.", __FUNCTION__); ret = WIFI_ERROR_UNKNOWN; goto cleanup; } event_handlers->gScanPnoSetPasspointListCmdEventHandler = gScanPnoSetPasspointListCmdEventHandler; } else { gScanPnoSetPasspointListCmdEventHandler->setCallbackHandler(callbackHandler); } ret = gScanCommand->requestResponse(); if (ret != 0) { ALOGE("%s: requestResponse Error:%d",__FUNCTION__, ret); goto cleanup; } if (gScanPnoSetPasspointListCmdEventHandler != NULL) { gScanPnoSetPasspointListCmdEventHandler->set_request_id(id); gScanPnoSetPasspointListCmdEventHandler->enableEventHandling(); } cleanup: delete gScanCommand; /* Disable Event Handling if ret != 0 */ if (ret && gScanPnoSetPasspointListCmdEventHandler) { ALOGI("%s: Error ret:%d, disable event handling", __FUNCTION__, ret); gScanPnoSetPasspointListCmdEventHandler->disableEventHandling(); } return (wifi_error)ret; } wifi_error wifi_reset_passpoint_list(wifi_request_id id, wifi_interface_handle iface) { int ret = 0; GScanCommand *gScanCommand; struct nlattr *nlData; interface_info *ifaceInfo = getIfaceInfo(iface); wifi_handle wifiHandle = getWifiHandle(iface); hal_info *info = getHalInfo(wifiHandle); gscan_event_handlers* event_handlers; GScanCommandEventHandler *gScanPnoSetPasspointListCmdEventHandler; event_handlers = (gscan_event_handlers*)info->gscan_handlers; gScanPnoSetPasspointListCmdEventHandler = event_handlers->gScanPnoSetPasspointListCmdEventHandler; if (!(info->supported_feature_set & WIFI_FEATURE_HAL_EPNO)) { ALOGE("%s: Enhanced PNO is not supported by the driver", __FUNCTION__); return WIFI_ERROR_NOT_SUPPORTED; } if (gScanPnoSetPasspointListCmdEventHandler == NULL || (gScanPnoSetPasspointListCmdEventHandler->isEventHandlingEnabled() == false)) { ALOGE("wifi_reset_passpoint_list: ePNO passpoint_list isn't set. " "Nothing to do. Exit."); return WIFI_ERROR_NOT_AVAILABLE; } gScanCommand = new GScanCommand( wifiHandle, id, OUI_QCA, QCA_NL80211_VENDOR_SUBCMD_PNO_RESET_PASSPOINT_LIST); if (gScanCommand == NULL) { ALOGE("%s: Error GScanCommand NULL", __FUNCTION__); return WIFI_ERROR_UNKNOWN; } /* Create the NL message. */ ret = gScanCommand->create(); if (ret < 0) { ALOGE("%s: Failed to create the NL msg. Error:%d", __FUNCTION__, ret); goto cleanup; } /* Set the interface Id of the message. */ ret = gScanCommand->set_iface_id(ifaceInfo->name); if (ret < 0) { ALOGE("%s: Failed to set iface id. Error:%d", __FUNCTION__, ret); goto cleanup; } /* Add the vendor specific attributes for the NL command. */ nlData = gScanCommand->attr_start(NL80211_ATTR_VENDOR_DATA); if (!nlData) { ALOGE("%s: Failed to add attribute NL80211_ATTR_VENDOR_DATA. Error:%d", __FUNCTION__, ret); goto cleanup; } ret = gScanCommand->put_u32( QCA_WLAN_VENDOR_ATTR_GSCAN_SUBCMD_CONFIG_PARAM_REQUEST_ID, id); if (ret < 0) { ALOGE("%s: Failed to add vendor data attributes. Error:%d", __FUNCTION__, ret); goto cleanup; } gScanCommand->attr_end(nlData); ret = gScanCommand->requestResponse(); if (ret != 0) { ALOGE("%s: requestResponse Error:%d",__FUNCTION__, ret); } /* Disable Event Handling. */ if (gScanPnoSetPasspointListCmdEventHandler) { gScanPnoSetPasspointListCmdEventHandler->disableEventHandling(); } cleanup: delete gScanCommand; return (wifi_error)ret; } int GScanCommand::allocCachedResultsTemp(int max, wifi_cached_scan_results *cached_results) { /* Alloc memory for "max" number of cached results. */ mGetCachedResultsRspParams->cached_results = (wifi_cached_scan_results*) malloc(max * sizeof(wifi_cached_scan_results)); if (!mGetCachedResultsRspParams->cached_results) { ALOGE("%s: Failed to allocate memory for " "mGetCachedResultsRspParams->cached_results.", __FUNCTION__); return WIFI_ERROR_OUT_OF_MEMORY; } memset(mGetCachedResultsRspParams->cached_results, 0, max * sizeof(wifi_cached_scan_results)); mGetCachedResultsRspParams->max = max; return WIFI_SUCCESS; } /* * Allocates memory for the subCmd response struct and initializes status = -1 */ int GScanCommand::allocRspParams(eGScanRspRarams cmd) { int ret = 0; switch(cmd) { case eGScanGetCachedResultsRspParams: mGetCachedResultsRspParams = (GScanGetCachedResultsRspParams *) malloc(sizeof(GScanGetCachedResultsRspParams)); if (!mGetCachedResultsRspParams) ret = -1; else { mGetCachedResultsRspParams->num_cached_results = 0; mGetCachedResultsRspParams->more_data = false; mGetCachedResultsRspParams->cachedResultsStartingIndex = -1; mGetCachedResultsRspParams->lastProcessedScanId = -1; mGetCachedResultsRspParams->wifiScanResultsStartingIndex = -1; mGetCachedResultsRspParams->max = 0; mGetCachedResultsRspParams->cached_results = NULL; } break; default: ALOGD("%s: Wrong request for alloc.", __FUNCTION__); ret = -1; } return ret; } void GScanCommand::freeRspParams(eGScanRspRarams cmd) { switch(cmd) { case eGScanGetCachedResultsRspParams: if (mGetCachedResultsRspParams) { if (mGetCachedResultsRspParams->cached_results) { free(mGetCachedResultsRspParams->cached_results); mGetCachedResultsRspParams->cached_results = NULL; } free(mGetCachedResultsRspParams); mGetCachedResultsRspParams = NULL; } break; default: ALOGD("%s: Wrong request for free.", __FUNCTION__); } } wifi_error GScanCommand::copyCachedScanResults( int *numResults, wifi_cached_scan_results *cached_results) { wifi_error ret = WIFI_SUCCESS; int i; wifi_cached_scan_results *cachedResultRsp; if (mGetCachedResultsRspParams && cached_results) { /* Populate the number of parsed cached results. */ *numResults = mGetCachedResultsRspParams->num_cached_results; for (i = 0; i < *numResults; i++) { cachedResultRsp = &mGetCachedResultsRspParams->cached_results[i]; cached_results[i].scan_id = cachedResultRsp->scan_id; cached_results[i].flags = cachedResultRsp->flags; cached_results[i].num_results = cachedResultRsp->num_results; cached_results[i].buckets_scanned = cachedResultRsp->buckets_scanned; if (!cached_results[i].num_results) { ALOGI("Error: cached_results[%d].num_results=0", i); continue; } ALOGV("copyCachedScanResults: " "cached_results[%d].num_results : %d", i, cached_results[i].num_results); memcpy(cached_results[i].results, cachedResultRsp->results, cached_results[i].num_results * sizeof(wifi_scan_result)); } } else { ALOGE("%s: mGetCachedResultsRspParams is NULL", __FUNCTION__); *numResults = 0; ret = WIFI_ERROR_INVALID_ARGS; } return ret; } void GScanCommand::setMaxChannels(int max_channels) { mMaxChannels = max_channels; } void GScanCommand::setChannels(int *channels) { mChannels = channels; } void GScanCommand::setNumChannelsPtr(int *num_channels) { mNumChannelsPtr = num_channels; }