/* * 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 /* 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, i=0; GScanCommand *gScanCommand; struct nlattr *nlData; interface_info *ifaceInfo = getIfaceInfo(handle); wifi_handle wifiHandle = getWifiHandle(handle); hal_info *info = getHalInfo(wifiHandle); 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) { ALOGD("%s: Sending cmd directly to host", __FUNCTION__); } else { ret = lowiWifiHalApi->get_valid_channels(handle, band, max_channels, channels, num_channels); ALOGI("%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(); ALOGI("%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) { int requestId, ret = 0; GScanCommand *gScanCommand; struct nlattr *nlData; wifi_gscan_capabilities tCapabilities; interface_info *ifaceInfo = getIfaceInfo(handle); 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; } /* Route GSCAN request through LOWI if supported */ lowiWifiHalApi = getLowiCallbackTable(GSCAN_SUPPORTED); if (lowiWifiHalApi == NULL || lowiWifiHalApi->get_gscan_capabilities == NULL) { ALOGD("%s: Sending cmd directly to host", __FUNCTION__); } else { ret = lowiWifiHalApi->get_gscan_capabilities(handle, capabilities); ALOGI("%s: lowi get_gscan_capabilities " "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(); ALOGI("%s: RequestId:%d", __FUNCTION__, requestId); if (capabilities == NULL) { ALOGE("%s: NULL capabilities pointer provided. Exit.", __FUNCTION__); return WIFI_ERROR_INVALID_ARGS; } gScanCommand = new GScanCommand( wifiHandle, requestId, OUI_QCA, QCA_NL80211_VENDOR_SUBCMD_GSCAN_GET_CAPABILITIES); 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, requestId); if (ret < 0) goto cleanup; gScanCommand->attr_end(nlData); ret = gScanCommand->allocRspParams(eGScanGetCapabilitiesRspParams); if (ret != 0) { ALOGE("%s: Failed to allocate memory fo response struct. Error:%d", __FUNCTION__, ret); goto cleanup; } ret = gScanCommand->requestResponse(); if (ret != 0) { ALOGE("%s: requestResponse Error:%d",__FUNCTION__, ret); goto cleanup; } gScanCommand->getGetCapabilitiesRspParams(capabilities); cleanup: gScanCommand->freeRspParams(eGScanGetCapabilitiesRspParams); delete gScanCommand; return (wifi_error)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; bool previousGScanRunning = false; 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) { ALOGD("%s: Sending cmd directly to host", __FUNCTION__); } else { ret = lowiWifiHalApi->start_gscan(id, iface, params, handler); ALOGI("%s: lowi start_gscan " "returned: %d. Exit.", __FUNCTION__, ret); return (wifi_error)ret; } ALOGI("%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; ALOGI("%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; ALOGI("%s: Index: %d Bucket Id:%d Band:%d Period:%d ReportEvent:%d " "numChannelSpecs:%d max_period:%d exponent:%d step_count:%d", __FUNCTION__, i, bucketSpec.bucket, bucketSpec.band, bucketSpec.period, bucketSpec.report_events, numChannelSpecs, bucketSpec.max_period, bucketSpec.exponent, 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_EXPONENT, bucketSpec.exponent) || 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]; ALOGI("%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_scan_results_available = handler.on_scan_results_available; 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) { ALOGD("%s: Sending cmd directly to host", __FUNCTION__); } else { ret = lowiWifiHalApi->stop_gscan(id, iface); ALOGI("%s: lowi stop_gscan " "returned: %d. Exit.", __FUNCTION__, ret); return (wifi_error)ret; } ALOGI("%s: RequestId:%d", __FUNCTION__, id); 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); bool previousGScanSetBssidRunning = false; 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) { ALOGD("%s: Sending cmd directly to host", __FUNCTION__); } else { ret = lowiWifiHalApi->set_bssid_hotlist(id, iface, params,handler); ALOGI("%s: lowi set_bssid_hotlist " "returned: %d. Exit.", __FUNCTION__, ret); return (wifi_error)ret; } ALOGI("%s: RequestId:%d", __FUNCTION__, id); /* 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; } ALOGI("%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; } ALOGI("%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; ALOGD("%s: Handler object was created for HOTLIST_AP_FOUND.", __FUNCTION__); } 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) { ALOGD("%s: Sending cmd directly to host", __FUNCTION__); } else { ret = lowiWifiHalApi->reset_bssid_hotlist(id, iface); ALOGI("%s: lowi reset_bssid_hotlist " "returned: %d. Exit.", __FUNCTION__, ret); return (wifi_error)ret; } ALOGI("%s: RequestId:%d", __FUNCTION__, id); 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); bool previousGScanSetSigChangeRunning = false; 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) { ALOGD("%s: Sending cmd directly to host", __FUNCTION__); } else { ret = lowiWifiHalApi->set_significant_change_handler(id, iface, params, handler); ALOGI("%s: lowi set_significant_change_handler " "returned: %d. Exit.", __FUNCTION__, ret); return (wifi_error)ret; } ALOGI("%s: RequestId:%d", __FUNCTION__, id); /* 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; } ALOGI("%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; } ALOGI("%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; ALOGD("%s: Event handler object was created for SIGNIFICANT_CHANGE.", __FUNCTION__); } 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) { ALOGD("%s: Sending cmd directly to host", __FUNCTION__); } else { ret = lowiWifiHalApi->reset_significant_change_handler(id, iface); ALOGI("%s: lowi reset_significant_change_handler " "returned: %d. Exit.", __FUNCTION__, ret); return (wifi_error)ret; } ALOGI("%s: RequestId:%d", __FUNCTION__, id); 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; wifi_cached_scan_results *result = results; u32 j = 0; int i = 0; u8 moreData = 0; u16 waitTime = GSCAN_EVENT_WAIT_TIME_SECONDS; GScanCommand *gScanCommand; struct nlattr *nlData; wifi_cached_scan_results *cached_results; 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) { ALOGD("%s: Sending cmd directly to host", __FUNCTION__); } else { ret = lowiWifiHalApi->get_cached_gscan_results(iface, flush, max, results, num); ALOGI("%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(); ALOGI("%s: RequestId:%d", __FUNCTION__, 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; } ALOGI("%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); ALOGI("%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; ALOGD("%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; ALOGI("%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; } /* Set the GSCAN SSID Hotlist. */ wifi_error wifi_set_ssid_hotlist(wifi_request_id id, wifi_interface_handle iface, wifi_ssid_hotlist_params params, wifi_hotlist_ssid_handler handler) { int i, numSsid, ret = 0; GScanCommand *gScanCommand; struct nlattr *nlData, *nlSsidThresholdParamList; interface_info *ifaceInfo = getIfaceInfo(iface); wifi_handle wifiHandle = getWifiHandle(iface); bool previousGScanSetSsidRunning = false; hal_info *info = getHalInfo(wifiHandle); lowi_cb_table_t *lowiWifiHalApi = NULL; gscan_event_handlers* event_handlers; GScanCommandEventHandler *gScanSetSsidHotlistCmdEventHandler; event_handlers = (gscan_event_handlers*)info->gscan_handlers; gScanSetSsidHotlistCmdEventHandler = event_handlers->gScanSetSsidHotlistCmdEventHandler; 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_ssid_hotlist == NULL) { ALOGD("%s: Sending cmd directly to host", __FUNCTION__); } else { ret = lowiWifiHalApi->set_ssid_hotlist(id, iface, params,handler); ALOGI("%s: lowi set_ssid_hotlist " "returned: %d. Exit.", __FUNCTION__, ret); return (wifi_error)ret; } ALOGI("%s: RequestId:%d", __FUNCTION__, id); /* Wi-Fi HAL doesn't need to check if a similar request to set ssid * hotlist was made earlier. If set_ssid_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 gScanSetSsidHotlistCmdEventHandler * object. */ gScanCommand = new GScanCommand( wifiHandle, id, OUI_QCA, QCA_NL80211_VENDOR_SUBCMD_GSCAN_SET_SSID_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; numSsid = (unsigned int)params.num_ssid > MAX_HOTLIST_SSID ? MAX_HOTLIST_SSID : params.num_ssid; if (gScanCommand->put_u32( QCA_WLAN_VENDOR_ATTR_GSCAN_SUBCMD_CONFIG_PARAM_REQUEST_ID, id) || gScanCommand->put_u32( QCA_WLAN_VENDOR_ATTR_GSCAN_SSID_HOTLIST_PARAMS_LOST_SSID_SAMPLE_SIZE, params.lost_ssid_sample_size) || gScanCommand->put_u32( QCA_WLAN_VENDOR_ATTR_GSCAN_SSID_HOTLIST_PARAMS_NUM_SSID, numSsid)) { goto cleanup; } ALOGI("%s: numSsid:%d " "lost_ssid_sameple_size: %d", __FUNCTION__, numSsid, params.lost_ssid_sample_size); /* Add the vendor specific attributes for the NL command. */ nlSsidThresholdParamList = gScanCommand->attr_start( QCA_WLAN_VENDOR_ATTR_GSCAN_SSID_THRESHOLD_PARAM); if (!nlSsidThresholdParamList) goto cleanup; /* Add nested NL attributes for SSID Threshold Param. */ for (i = 0; i < numSsid; i++) { ssid_threshold_param ssidThreshold = params.ssid[i]; struct nlattr *nlSsidThresholdParam = gScanCommand->attr_start(i); if (!nlSsidThresholdParam) goto cleanup; if (gScanCommand->put_string( QCA_WLAN_VENDOR_ATTR_GSCAN_SSID_THRESHOLD_PARAM_SSID, ssidThreshold.ssid) || gScanCommand->put_u8( QCA_WLAN_VENDOR_ATTR_GSCAN_SSID_THRESHOLD_PARAM_BAND, ssidThreshold.band) || gScanCommand->put_s32( QCA_WLAN_VENDOR_ATTR_GSCAN_SSID_THRESHOLD_PARAM_RSSI_LOW, ssidThreshold.low) || gScanCommand->put_s32( QCA_WLAN_VENDOR_ATTR_GSCAN_SSID_THRESHOLD_PARAM_RSSI_HIGH, ssidThreshold.high)) { goto cleanup; } ALOGI("%s: SSID[%d].ssid:%s " "SSID[%d].band:%d SSID[%d].low:%d " "SSID[%d].high:%d", __FUNCTION__, i, ssidThreshold.ssid, i, ssidThreshold.band, i, ssidThreshold.low, i, ssidThreshold.high); gScanCommand->attr_end(nlSsidThresholdParam); } gScanCommand->attr_end(nlSsidThresholdParamList); gScanCommand->attr_end(nlData); GScanCallbackHandler callbackHandler; memset(&callbackHandler, 0, sizeof(callbackHandler)); callbackHandler.on_hotlist_ssid_found = handler.on_hotlist_ssid_found; callbackHandler.on_hotlist_ssid_lost = handler.on_hotlist_ssid_lost; /* Create an object of the event handler class to take care of the * asychronous events on the north-bound. */ if (gScanSetSsidHotlistCmdEventHandler == NULL) { gScanSetSsidHotlistCmdEventHandler = new GScanCommandEventHandler( wifiHandle, id, OUI_QCA, QCA_NL80211_VENDOR_SUBCMD_GSCAN_SET_SSID_HOTLIST, callbackHandler); if (gScanSetSsidHotlistCmdEventHandler == NULL) { ALOGE("%s: Error instantiating " "gScanSetSsidHotlistCmdEventHandler.", __FUNCTION__); ret = WIFI_ERROR_UNKNOWN; goto cleanup; } ALOGD("%s: Handler object was created for HOTLIST_AP_FOUND.", __FUNCTION__); event_handlers->gScanSetSsidHotlistCmdEventHandler = gScanSetSsidHotlistCmdEventHandler; } else { gScanSetSsidHotlistCmdEventHandler->setCallbackHandler(callbackHandler); } ret = gScanCommand->requestResponse(); if (ret != 0) { ALOGE("%s: requestResponse Error:%d",__FUNCTION__, ret); goto cleanup; } if (gScanSetSsidHotlistCmdEventHandler != NULL) { gScanSetSsidHotlistCmdEventHandler->set_request_id(id); gScanSetSsidHotlistCmdEventHandler->enableEventHandling(); } cleanup: delete gScanCommand; /* Disable Event Handling if ret != 0 */ if (ret && gScanSetSsidHotlistCmdEventHandler) { ALOGI("%s: Error ret:%d, disable event handling", __FUNCTION__, ret); gScanSetSsidHotlistCmdEventHandler->disableEventHandling(); } return (wifi_error)ret; } wifi_error wifi_reset_ssid_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 *gScanSetSsidHotlistCmdEventHandler; event_handlers = (gscan_event_handlers*)info->gscan_handlers; gScanSetSsidHotlistCmdEventHandler = event_handlers->gScanSetSsidHotlistCmdEventHandler; 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_ssid_hotlist == NULL) { ALOGD("%s: Sending cmd directly to host", __FUNCTION__); } else { ret = lowiWifiHalApi->reset_ssid_hotlist(id, iface); ALOGI("%s: lowi reset_ssid_hotlist " "returned: %d. Exit.", __FUNCTION__, ret); return (wifi_error)ret; } ALOGI("%s: RequestId:%d", __FUNCTION__, id); if (gScanSetSsidHotlistCmdEventHandler == NULL || (gScanSetSsidHotlistCmdEventHandler->isEventHandlingEnabled() == false)) { ALOGE("wifi_reset_ssid_hotlist: GSCAN ssid_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_SSID_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 (gScanSetSsidHotlistCmdEventHandler) { gScanSetSsidHotlistCmdEventHandler->disableEventHandling(); } cleanup: delete gScanCommand; return (wifi_error)ret; } GScanCommand::GScanCommand(wifi_handle handle, int id, u32 vendor_id, u32 subcmd) : WifiVendorCommand(handle, id, vendor_id, subcmd) { #ifdef QC_HAL_DEBUG ALOGD("GScanCommand %p constructed", this); #endif /* Initialize the member data variables here */ mGetCapabilitiesRspParams = NULL; mGetCachedResultsRspParams = NULL; mChannels = NULL; mMaxChannels = 0; mNumChannelsPtr = NULL; mRequestId = id; memset(&mHandler, 0,sizeof(mHandler)); } GScanCommand::~GScanCommand() { #ifdef QC_HAL_DEBUG ALOGD("GScanCommand %p destructor", this); #endif 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; #ifdef QC_HAL_DEBUG ALOGI("%s: mVendor_id = %d, Subcmd = %d.", __FUNCTION__, mVendor_id, mSubcmd); #endif out: return ret; } /* Callback handlers registered for nl message send */ static int error_handler_gscan(struct sockaddr_nl *nla, struct nlmsgerr *err, void *arg) { struct sockaddr_nl *tmp; int *ret = (int *)arg; tmp = nla; *ret = err->error; ALOGE("%s: Error code:%d (%s)", __FUNCTION__, *ret, strerror(-(*ret))); return NL_STOP; } /* Callback handlers registered for nl message send */ static int ack_handler_gscan(struct nl_msg *msg, void *arg) { int *ret = (int *)arg; struct nl_msg * a; ALOGE("%s: called", __FUNCTION__); a = msg; *ret = 0; return NL_STOP; } /* Callback handlers registered for nl message send */ static int finish_handler_gscan(struct nl_msg *msg, void *arg) { int *ret = (int *)arg; struct nl_msg * a; ALOGE("%s: called", __FUNCTION__); a = msg; *ret = 0; return NL_SKIP; } int GScanCommand::requestResponse() { return WifiCommand::requestResponse(mMsg); } int GScanCommand::handleResponse(WifiEvent &reply) { u32 status; 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)); } ALOGD("%s: Get valid channels response received.", __FUNCTION__); ALOGD("%s: Num channels : %d", __FUNCTION__, *mNumChannelsPtr); ALOGD("%s: List of valid channels are: ", __FUNCTION__); for(i = 0; i < *mNumChannelsPtr; i++) { ALOGD("%u", *(mChannels + i)); } } break; case QCA_NL80211_VENDOR_SUBCMD_GSCAN_GET_CAPABILITIES: { ret = gscan_parse_capabilities(tbVendor); if (ret) { break; } if (mGetCapabilitiesRspParams) { wifi_gscan_capabilities capa = mGetCapabilitiesRspParams->capabilities; ALOGI("%s: max_ap_cache_per_scan:%d\n" "max_bssid_history_entries:%d\n" "max_hotlist_bssids:%d\n" "max_hotlist_ssids:%d\n" "max_rssi_sample_size:%d\n" "max_scan_buckets:%d\n" "max_scan_cache_size:%d\n" "max_scan_reporting_threshold:%d\n" "max_significant_wifi_change_aps:%d\n" "max_number_epno_networks:%d\n" "max_number_epno_networks_by_ssid:%d\n" "max_number_of_white_listed_ssid:%d.", __FUNCTION__, capa.max_ap_cache_per_scan, capa.max_bssid_history_entries, capa.max_hotlist_bssids, capa.max_hotlist_ssids, capa.max_rssi_sample_size, capa.max_scan_buckets, capa.max_scan_cache_size, capa.max_scan_reporting_threshold, capa.max_significant_wifi_change_aps, capa.max_number_epno_networks, capa.max_number_epno_networks_by_ssid, capa.max_number_of_white_listed_ssid); } } break; case QCA_NL80211_VENDOR_SUBCMD_GSCAN_GET_CACHED_RESULTS: { wifi_request_id id; u32 numResults = 0; u32 startingIndex; 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) { ALOGE("%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]); ALOGE("%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]); ALOGE("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; case QCA_NL80211_VENDOR_SUBCMD_GSCAN_GET_CAPABILITIES: { ALOGE("%s: Parsing error, free CapabilitiesRspParams", __FUNCTION__); freeRspParams(eGScanGetCapabilitiesRspParams); } break; default: ALOGE("%s: Wrong GScan subcmd received %d", __FUNCTION__, mSubcmd); } } return NL_SKIP; } /* Parses and extracts gscan capabilities results. */ int GScanCommand::gscan_parse_capabilities(struct nlattr **tbVendor) { if (!mGetCapabilitiesRspParams){ ALOGE("%s: mGetCapabilitiesRspParams ptr is NULL. Exit.", __FUNCTION__); return WIFI_ERROR_INVALID_ARGS; } if (!tbVendor[ QCA_WLAN_VENDOR_ATTR_GSCAN_RESULTS_CAPABILITIES_MAX_SCAN_CACHE_SIZE ]) { ALOGE("%s: QCA_WLAN_VENDOR_ATTR_GSCAN_RESULTS_" "CAPABILITIES_MAX_SCAN_CACHE_SIZE not found", __FUNCTION__); return WIFI_ERROR_INVALID_ARGS; } mGetCapabilitiesRspParams->capabilities.max_scan_cache_size = nla_get_u32(tbVendor[ QCA_WLAN_VENDOR_ATTR_GSCAN_RESULTS_CAPABILITIES_MAX_SCAN_CACHE_SIZE]); if (!tbVendor[ QCA_WLAN_VENDOR_ATTR_GSCAN_RESULTS_CAPABILITIES_MAX_SCAN_BUCKETS ]) { ALOGE("%s: QCA_WLAN_VENDOR_ATTR_GSCAN_RESULTS_CAPABILITIES_MAX" "_SCAN_BUCKETS not found", __FUNCTION__); return WIFI_ERROR_INVALID_ARGS; } mGetCapabilitiesRspParams->capabilities.max_scan_buckets = nla_get_u32(tbVendor[ QCA_WLAN_VENDOR_ATTR_GSCAN_RESULTS_CAPABILITIES_MAX_SCAN_BUCKETS] ); if (!tbVendor[ QCA_WLAN_VENDOR_ATTR_GSCAN_RESULTS_CAPABILITIES_MAX_AP_CACHE_PER_SCAN ]) { ALOGE("%s: QCA_WLAN_VENDOR_ATTR_GSCAN_RESULTS_CAPABILITIES_MAX" "_AP_CACHE_PER_SCAN not found", __FUNCTION__); return WIFI_ERROR_INVALID_ARGS; } mGetCapabilitiesRspParams->capabilities.max_ap_cache_per_scan = nla_get_u32(tbVendor[ QCA_WLAN_VENDOR_ATTR_GSCAN_RESULTS_CAPABILITIES_MAX_AP_CACHE_PER_SCAN]); if (!tbVendor[ QCA_WLAN_VENDOR_ATTR_GSCAN_RESULTS_CAPABILITIES_MAX_RSSI_SAMPLE_SIZE ]) { ALOGE("%s: QCA_WLAN_VENDOR_ATTR_GSCAN_RESULTS_CAPABILITIES_MAX" "_RSSI_SAMPLE_SIZE not found", __FUNCTION__); return WIFI_ERROR_INVALID_ARGS; } mGetCapabilitiesRspParams->capabilities.max_rssi_sample_size = nla_get_u32(tbVendor[ QCA_WLAN_VENDOR_ATTR_GSCAN_RESULTS_CAPABILITIES_MAX_RSSI_SAMPLE_SIZE]); if (!tbVendor[ QCA_WLAN_VENDOR_ATTR_GSCAN_RESULTS_CAPABILITIES_MAX_SCAN_REPORTING_THRESHOLD ]) { ALOGE("%s: QCA_WLAN_VENDOR_ATTR_GSCAN_RESULTS_CAPABILITIES_" "MAX_SCAN_REPORTING_THRESHOLD not found", __FUNCTION__); return WIFI_ERROR_INVALID_ARGS; } mGetCapabilitiesRspParams->capabilities.max_scan_reporting_threshold = nla_get_u32(tbVendor[ QCA_WLAN_VENDOR_ATTR_GSCAN_RESULTS_CAPABILITIES_MAX_SCAN_REPORTING_THRESHOLD ]); if (!tbVendor[ QCA_WLAN_VENDOR_ATTR_GSCAN_RESULTS_CAPABILITIES_MAX_HOTLIST_BSSIDS ]) { ALOGE("%s: QCA_WLAN_VENDOR_ATTR_GSCAN_RESULTS_CAPABILITIES_" "MAX_HOTLIST_BSSIDS not found", __FUNCTION__); return WIFI_ERROR_INVALID_ARGS; } mGetCapabilitiesRspParams->capabilities.max_hotlist_bssids = nla_get_u32(tbVendor[ QCA_WLAN_VENDOR_ATTR_GSCAN_RESULTS_CAPABILITIES_MAX_HOTLIST_BSSIDS]); if (!tbVendor[ QCA_WLAN_VENDOR_ATTR_GSCAN_RESULTS_CAPABILITIES_MAX_SIGNIFICANT_WIFI_CHANGE_APS ]) { ALOGE("%s: QCA_WLAN_VENDOR_ATTR_GSCAN_RESULTS_CAPABILITIES_MAX" "_SIGNIFICANT_WIFI_CHANGE_APS not found", __FUNCTION__); return WIFI_ERROR_INVALID_ARGS; } mGetCapabilitiesRspParams->capabilities.max_significant_wifi_change_aps = nla_get_u32(tbVendor[ QCA_WLAN_VENDOR_ATTR_GSCAN_RESULTS_CAPABILITIES_MAX_SIGNIFICANT_WIFI_CHANGE_APS]); if (!tbVendor[ QCA_WLAN_VENDOR_ATTR_GSCAN_RESULTS_CAPABILITIES_MAX_BSSID_HISTORY_ENTRIES ]) { ALOGE("%s: QCA_WLAN_VENDOR_ATTR_GSCAN_RESULTS_CAPABILITIES_MAX" "_BSSID_HISTORY_ENTRIES not found", __FUNCTION__); return WIFI_ERROR_INVALID_ARGS; } mGetCapabilitiesRspParams->capabilities.max_bssid_history_entries = nla_get_u32(tbVendor[ QCA_WLAN_VENDOR_ATTR_GSCAN_RESULTS_CAPABILITIES_MAX_BSSID_HISTORY_ENTRIES ]); if (!tbVendor[ QCA_WLAN_VENDOR_ATTR_GSCAN_RESULTS_CAPABILITIES_MAX_HOTLIST_SSIDS ]) { ALOGE("%s: QCA_WLAN_VENDOR_ATTR_GSCAN_RESULTS_CAPABILITIES" "_MAX_HOTLIST_SSIDS not found. Set to 0.", __FUNCTION__); mGetCapabilitiesRspParams->capabilities.max_hotlist_ssids = 0; } else { mGetCapabilitiesRspParams->capabilities.max_hotlist_ssids = nla_get_u32(tbVendor[ QCA_WLAN_VENDOR_ATTR_GSCAN_RESULTS_CAPABILITIES_MAX_HOTLIST_SSIDS ]); } if (!tbVendor[ QCA_WLAN_VENDOR_ATTR_GSCAN_RESULTS_CAPABILITIES_MAX_NUM_EPNO_NETS ]) { ALOGE("%s: QCA_WLAN_VENDOR_ATTR_GSCAN_RESULTS_CAPABILITIES_MAX" "_NUM_EPNO_NETS not found. Set to 0.", __FUNCTION__); mGetCapabilitiesRspParams->capabilities.\ max_number_epno_networks = 0; } else { mGetCapabilitiesRspParams->capabilities.max_number_epno_networks = nla_get_u32(tbVendor[ QCA_WLAN_VENDOR_ATTR_GSCAN_RESULTS_CAPABILITIES_MAX_NUM_EPNO_NETS ]); } if (!tbVendor[ QCA_WLAN_VENDOR_ATTR_GSCAN_RESULTS_CAPABILITIES_MAX_NUM_EPNO_NETS_BY_SSID ]) { ALOGE("%s: QCA_WLAN_VENDOR_ATTR_GSCAN_RESULTS_CAPABILITIES_MAX" "_NUM_EPNO_NETS_BY_SSID not found. Set to 0.", __FUNCTION__); mGetCapabilitiesRspParams->capabilities.\ max_number_epno_networks_by_ssid = 0; } else { mGetCapabilitiesRspParams->capabilities.max_number_epno_networks_by_ssid = nla_get_u32(tbVendor[ QCA_WLAN_VENDOR_ATTR_GSCAN_RESULTS_CAPABILITIES_MAX_NUM_EPNO_NETS_BY_SSID ]); } if (!tbVendor[ QCA_WLAN_VENDOR_ATTR_GSCAN_RESULTS_CAPABILITIES_MAX_NUM_WHITELISTED_SSID ]) { ALOGE("%s: QCA_WLAN_VENDOR_ATTR_GSCAN_RESULTS_CAPABILITIES_MAX" "_NUM_WHITELISTED_SSID not found. Set to 0.", __FUNCTION__); mGetCapabilitiesRspParams->capabilities.\ max_number_of_white_listed_ssid = 0; } else { mGetCapabilitiesRspParams->capabilities.max_number_of_white_listed_ssid = nla_get_u32(tbVendor[ QCA_WLAN_VENDOR_ATTR_GSCAN_RESULTS_CAPABILITIES_MAX_NUM_WHITELISTED_SSID ]); } return WIFI_SUCCESS; } /* 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; ALOGE("%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_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; ALOGD("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; ALOGD("parsing: *lastProcessedScanId [%d] == " "cached_results[%d].scan_id:%d, j:%d " "numScanResults:%d", mGetCachedResultsRspParams->lastProcessedScanId, i, cached_results[i].scan_id, j, numScanResults); } if (!cached_results[i].results) { ALOGE("%s: NULL cached_results[%d].results" ". Abort.", __FUNCTION__, i); return WIFI_ERROR_OUT_OF_MEMORY; } #ifdef QC_HAL_DEBUG ALOGE("%s: scan_id %d ", __FUNCTION__, cached_results[i].scan_id); ALOGE("%s: flags %u ", __FUNCTION__, cached_results[i].flags); #endif 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 %lld ", __FUNCTION__, cached_results[i].results[j].ts); ALOGD("%s: SSID %s ", __FUNCTION__, cached_results[i].results[j].ssid); ALOGD("%s: ", __FUNCTION__, "BSSID: %02x:%02x:%02x:%02x:%02x:%02x \n", 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 %lld ", __FUNCTION__, cached_results[i].results[j].rtt); ALOGD("%s: rtt_sd %lld ", __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); } } ALOGE("%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, int num_networks, wifi_epno_network *networks, wifi_epno_handler handler) { int i, ret = 0; GScanCommand *gScanCommand; struct nlattr *nlData, *nlPnoParamList; interface_info *ifaceInfo = getIfaceInfo(iface); wifi_handle wifiHandle = getWifiHandle(iface); bool previousGScanSetEpnoListRunning = false; 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; } ALOGI("%s: RequestId:%d", __FUNCTION__, id); /* 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)num_networks > MAX_PNO_SSID ? MAX_PNO_SSID : num_networks; 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, 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 = 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_s8( QCA_WLAN_VENDOR_ATTR_PNO_SET_LIST_PARAM_EPNO_NETWORK_RSSI_THRESHOLD, pnoNetwork.rssi_threshold) || 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; ALOGD("%s: Handler object was created for PNO_NETWORK_FOUND.", __FUNCTION__); } 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; } /* 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, numAp, ret = 0; GScanCommand *gScanCommand; struct nlattr *nlData, *nlPasspointNetworksParamList; interface_info *ifaceInfo = getIfaceInfo(iface); wifi_handle wifiHandle = getWifiHandle(iface); bool previousGScanPnoSetPasspointListRunning = false; 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; } ALOGI("%s: RequestId:%d", __FUNCTION__, id); /* 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; ALOGD("%s: Handler object was created for PNO_PASSPOINT_" "NETWORK_FOUND.", __FUNCTION__); } 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; } ALOGI("%s: RequestId:%d", __FUNCTION__, id); 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) { wifi_cached_scan_results *tempCachedResults = NULL; /* 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 eGScanGetCapabilitiesRspParams: mGetCapabilitiesRspParams = (GScanGetCapabilitiesRspParams *) malloc(sizeof(GScanGetCapabilitiesRspParams)); if (!mGetCapabilitiesRspParams) ret = -1; else { memset(&mGetCapabilitiesRspParams->capabilities, 0, sizeof(wifi_gscan_capabilities)); } break; 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) { u32 i = 0; wifi_cached_scan_results *cached_results = NULL; switch(cmd) { case eGScanGetCapabilitiesRspParams: if (mGetCapabilitiesRspParams) { free(mGetCapabilitiesRspParams); mGetCapabilitiesRspParams = NULL; } break; 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; if (!cached_results[i].num_results) { ALOGI("Error: cached_results[%d].num_results=0", i); continue; } ALOGI("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::getGetCapabilitiesRspParams( wifi_gscan_capabilities *capabilities) { if (mGetCapabilitiesRspParams && capabilities) { memcpy(capabilities, &mGetCapabilitiesRspParams->capabilities, sizeof(wifi_gscan_capabilities)); } else { ALOGD("%s: mGetCapabilitiesRspParams is NULL", __FUNCTION__); } } 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; } wifi_error wifi_set_ssid_white_list(wifi_request_id id, wifi_interface_handle iface, int num_networks, wifi_ssid *ssids) { int ret = 0, i; GScanCommand *roamCommand; struct nlattr *nlData, *nlSsids; 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; } ALOGI("%s: RequestId:%d", __FUNCTION__, id); ALOGI("Number of SSIDs : %d", num_networks); for (i = 0; i < num_networks; i++) { ALOGI("ssid %d : %s", i, ssids[i].ssid); } roamCommand = new GScanCommand( wifiHandle, id, OUI_QCA, QCA_NL80211_VENDOR_SUBCMD_ROAM); if (roamCommand == NULL) { ALOGE("wifi_set_ssid_white_list(): Error roamCommand NULL"); return WIFI_ERROR_UNKNOWN; } /* Create the NL message. */ ret = roamCommand->create(); if (ret < 0) goto cleanup; /* Set the interface Id of the message. */ ret = roamCommand->set_iface_id(ifaceInfo->name); if (ret < 0) goto cleanup; /* Add the vendor specific attributes for the NL command. */ nlData = roamCommand->attr_start(NL80211_ATTR_VENDOR_DATA); if (!nlData) goto cleanup; if (roamCommand->put_u32(QCA_WLAN_VENDOR_ATTR_ROAMING_SUBCMD, QCA_WLAN_VENDOR_ATTR_ROAM_SUBCMD_SSID_WHITE_LIST) || roamCommand->put_u32( QCA_WLAN_VENDOR_ATTR_ROAMING_REQ_ID, id) || roamCommand->put_u32( QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_WHITE_LIST_SSID_NUM_NETWORKS, num_networks)) { goto cleanup; } nlSsids = roamCommand->attr_start( QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_WHITE_LIST_SSID_LIST); for (i = 0; i < num_networks; i++) { struct nlattr *nl_ssid = roamCommand->attr_start(i); if ( roamCommand->put_string( QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_WHITE_LIST_SSID, ssids[i].ssid)) { goto cleanup; } roamCommand->attr_end(nl_ssid); } roamCommand->attr_end(nlSsids); roamCommand->attr_end(nlData); ret = roamCommand->requestResponse(); if (ret != 0) { ALOGE("wifi_set_ssid_white_list(): requestResponse Error:%d", ret); } cleanup: delete roamCommand; return (wifi_error)ret; } wifi_error wifi_set_gscan_roam_params(wifi_request_id id, wifi_interface_handle iface, wifi_roam_params * params) { int ret = 0; GScanCommand *roamCommand; 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_GSCAN)) { ALOGE("%s: GSCAN is not supported by driver", __FUNCTION__); return WIFI_ERROR_NOT_SUPPORTED; } ALOGI("%s: RequestId:%d", __FUNCTION__, id); if(params) { ALOGI("A_band_boost_threshold %d", params->A_band_boost_threshold); ALOGI("A_band_penalty_threshol %d", params->A_band_penalty_threshold); ALOGI("A_band_boost_factor %u", params->A_band_boost_factor); ALOGI("A_band_penalty_factor %u", params->A_band_penalty_factor); ALOGI("A_band_max_boost %u", params->A_band_max_boost); ALOGI("lazy_roam_histeresys %u", params->lazy_roam_hysteresis); ALOGI("alert_roam_rssi_trigger %d", params->alert_roam_rssi_trigger); } else { ALOGE("wifi_roam_params is NULL"); return WIFI_ERROR_INVALID_ARGS; } roamCommand = new GScanCommand(wifiHandle, id, OUI_QCA, QCA_NL80211_VENDOR_SUBCMD_ROAM); if (roamCommand == NULL) { ALOGE("wifi_set_gscan_roam_params(): Error roamCommand NULL"); return WIFI_ERROR_UNKNOWN; } /* Create the NL message. */ ret = roamCommand->create(); if (ret < 0) goto cleanup; /* Set the interface Id of the message. */ ret = roamCommand->set_iface_id(ifaceInfo->name); if (ret < 0) goto cleanup; /* Add the vendor specific attributes for the NL command. */ nlData = roamCommand->attr_start(NL80211_ATTR_VENDOR_DATA); if (!nlData) goto cleanup; if (roamCommand->put_u32(QCA_WLAN_VENDOR_ATTR_ROAMING_SUBCMD, QCA_WLAN_VENDOR_ATTR_ROAM_SUBCMD_SET_GSCAN_ROAM_PARAMS) || roamCommand->put_u32( QCA_WLAN_VENDOR_ATTR_ROAMING_REQ_ID, id) || roamCommand->put_s32( QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_A_BAND_BOOST_THRESHOLD, params->A_band_boost_threshold) || roamCommand->put_s32( QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_A_BAND_PENALTY_THRESHOLD, params->A_band_penalty_threshold) || roamCommand->put_u32( QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_A_BAND_BOOST_FACTOR, params->A_band_boost_factor) || roamCommand->put_u32( QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_A_BAND_PENALTY_FACTOR, params->A_band_penalty_factor) || roamCommand->put_u32( QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_A_BAND_MAX_BOOST, params->A_band_max_boost) || roamCommand->put_u32( QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_LAZY_ROAM_HISTERESYS, params->lazy_roam_hysteresis) || roamCommand->put_s32( QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_ALERT_ROAM_RSSI_TRIGGER, params->alert_roam_rssi_trigger)) { goto cleanup; } roamCommand->attr_end(nlData); ret = roamCommand->requestResponse(); if (ret != 0) { ALOGE("wifi_set_gscan_roam_params(): requestResponse Error:%d", ret); } cleanup: delete roamCommand; return (wifi_error)ret; } wifi_error wifi_enable_lazy_roam(wifi_request_id id, wifi_interface_handle iface, int enable) { int ret = 0; GScanCommand *roamCommand; 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_GSCAN)) { ALOGE("%s: GSCAN is not supported by driver", __FUNCTION__); return WIFI_ERROR_NOT_SUPPORTED; } ALOGI("%s: RequestId:%d Setting lazy roam: %s", __FUNCTION__, id, enable?"ENABLE":"DISABLE"); roamCommand = new GScanCommand(wifiHandle, id, OUI_QCA, QCA_NL80211_VENDOR_SUBCMD_ROAM); if (roamCommand == NULL) { ALOGE("%s: Error roamCommand NULL", __FUNCTION__); return WIFI_ERROR_UNKNOWN; } /* Create the NL message. */ ret = roamCommand->create(); if (ret < 0) goto cleanup; /* Set the interface Id of the message. */ ret = roamCommand->set_iface_id(ifaceInfo->name); if (ret < 0) goto cleanup; /* Add the vendor specific attributes for the NL command. */ nlData = roamCommand->attr_start(NL80211_ATTR_VENDOR_DATA); if (!nlData) goto cleanup; if (roamCommand->put_u32(QCA_WLAN_VENDOR_ATTR_ROAMING_SUBCMD, QCA_WLAN_VENDOR_ATTR_ROAM_SUBCMD_SET_LAZY_ROAM) || roamCommand->put_u32( QCA_WLAN_VENDOR_ATTR_ROAMING_REQ_ID, id) || roamCommand->put_u32( QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_SET_LAZY_ROAM_ENABLE, enable)) { goto cleanup; } roamCommand->attr_end(nlData); ret = roamCommand->requestResponse(); if (ret != 0) { ALOGE("wifi_enable_lazy_roam(): requestResponse Error:%d", ret); } cleanup: delete roamCommand; return (wifi_error)ret; } wifi_error wifi_set_bssid_preference(wifi_request_id id, wifi_interface_handle iface, int num_bssid, wifi_bssid_preference *prefs) { int ret = 0, i; GScanCommand *roamCommand; struct nlattr *nlData, *nlBssids; 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; } ALOGI("%s: RequestId:%d", __FUNCTION__, id); ALOGI("Number of BSSIDs: %d", num_bssid); if(prefs && num_bssid) { for (i = 0; i < num_bssid; i++) { ALOGI("BSSID: %d : %02x:%02x:%02x:%02x:%02x:%02x", i, prefs[i].bssid[0], prefs[i].bssid[1], prefs[i].bssid[2], prefs[i].bssid[3], prefs[i].bssid[4], prefs[i].bssid[5]); ALOGI("alert_roam_rssi_trigger : %d", prefs[i].rssi_modifier); } } else { ALOGE("wifi_bssid_preference is NULL"); return WIFI_ERROR_INVALID_ARGS; } roamCommand = new GScanCommand(wifiHandle, id, OUI_QCA, QCA_NL80211_VENDOR_SUBCMD_ROAM); if (roamCommand == NULL) { ALOGE("%s: Error roamCommand NULL", __FUNCTION__); return WIFI_ERROR_UNKNOWN; } /* Create the NL message. */ ret = roamCommand->create(); if (ret < 0) goto cleanup; /* Set the interface Id of the message. */ ret = roamCommand->set_iface_id(ifaceInfo->name); if (ret < 0) goto cleanup; /* Add the vendor specific attributes for the NL command. */ nlData = roamCommand->attr_start(NL80211_ATTR_VENDOR_DATA); if (!nlData) goto cleanup; if (roamCommand->put_u32(QCA_WLAN_VENDOR_ATTR_ROAMING_SUBCMD, QCA_WLAN_VENDOR_ATTR_ROAM_SUBCMD_SET_BSSID_PREFS) || roamCommand->put_u32( QCA_WLAN_VENDOR_ATTR_ROAMING_REQ_ID, id) || roamCommand->put_u32( QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_SET_LAZY_ROAM_NUM_BSSID, num_bssid)) { goto cleanup; } nlBssids = roamCommand->attr_start( QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_SET_BSSID_PREFS); for (i = 0; i < num_bssid; i++) { struct nlattr *nl_ssid = roamCommand->attr_start(i); if (roamCommand->put_addr( QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_SET_LAZY_ROAM_BSSID, (u8 *)prefs[i].bssid) || roamCommand->put_s32( QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_SET_LAZY_ROAM_RSSI_MODIFIER, prefs[i].rssi_modifier)) { goto cleanup; } roamCommand->attr_end(nl_ssid); } roamCommand->attr_end(nlBssids); roamCommand->attr_end(nlData); ret = roamCommand->requestResponse(); if (ret != 0) { ALOGE("wifi_set_bssid_preference(): requestResponse Error:%d", ret); } cleanup: delete roamCommand; return (wifi_error)ret; } wifi_error wifi_set_bssid_blacklist(wifi_request_id id, wifi_interface_handle iface, wifi_bssid_params params) { int ret = 0, i; GScanCommand *roamCommand; struct nlattr *nlData, *nlBssids; 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; } ALOGI("%s: RequestId:%d", __FUNCTION__, id); for (i = 0; i < params.num_bssid; i++) { ALOGI("BSSID: %d : %02x:%02x:%02x:%02x:%02x:%02x", i, params.bssids[i][0], params.bssids[i][1], params.bssids[i][2], params.bssids[i][3], params.bssids[i][4], params.bssids[i][5]); } roamCommand = new GScanCommand(wifiHandle, id, OUI_QCA, QCA_NL80211_VENDOR_SUBCMD_ROAM); if (roamCommand == NULL) { ALOGE("%s: Error roamCommand NULL", __FUNCTION__); return WIFI_ERROR_UNKNOWN; } /* Create the NL message. */ ret = roamCommand->create(); if (ret < 0) goto cleanup; /* Set the interface Id of the message. */ ret = roamCommand->set_iface_id(ifaceInfo->name); if (ret < 0) goto cleanup; /* Add the vendor specific attributes for the NL command. */ nlData = roamCommand->attr_start(NL80211_ATTR_VENDOR_DATA); if (!nlData) goto cleanup; if (roamCommand->put_u32(QCA_WLAN_VENDOR_ATTR_ROAMING_SUBCMD, QCA_WLAN_VENDOR_ATTR_ROAM_SUBCMD_SET_BLACKLIST_BSSID) || roamCommand->put_u32( QCA_WLAN_VENDOR_ATTR_ROAMING_REQ_ID, id) || roamCommand->put_u32( QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_SET_BSSID_PARAMS_NUM_BSSID, params.num_bssid)) { goto cleanup; } nlBssids = roamCommand->attr_start( QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_SET_BSSID_PARAMS); for (i = 0; i < params.num_bssid; i++) { struct nlattr *nl_ssid = roamCommand->attr_start(i); if (roamCommand->put_addr( QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_SET_BSSID_PARAMS_BSSID, (u8 *)params.bssids[i])) { goto cleanup; } roamCommand->attr_end(nl_ssid); } roamCommand->attr_end(nlBssids); roamCommand->attr_end(nlData); ret = roamCommand->requestResponse(); if (ret != 0) { ALOGE("wifi_set_bssid_blacklist(): requestResponse Error:%d", ret); } cleanup: delete roamCommand; return (wifi_error)ret; }