1 /*
2  * Copyright (C) 2016 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *     http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15 
16  * Changes from Qualcomm Innovation Center are provided under the following license:
17 
18  * Copyright (c) 2022 Qualcomm Innovation Center, Inc. All rights reserved.
19  * SPDX-License-Identifier: BSD-3-Clause-Clear
20  */
21 
22 #include <errno.h>
23 
24 #include "common.h"
25 #include "roamcommand.h"
26 
27 #define WLAN_ROAM_MAX_NUM_WHITE_LIST 8
28 #define WLAN_ROAM_MAX_NUM_BLACK_LIST 16
29 
RoamCommand(wifi_handle handle,int id,u32 vendor_id,u32 subcmd)30 RoamCommand::RoamCommand(wifi_handle handle, int id, u32 vendor_id, u32 subcmd)
31         : WifiVendorCommand(handle, id, vendor_id, subcmd)
32 {
33 }
34 
~RoamCommand()35 RoamCommand::~RoamCommand()
36 {
37 }
38 
39 /* This function implements creation of Vendor command */
create()40 wifi_error RoamCommand::create() {
41     wifi_error ret = mMsg.create(NL80211_CMD_VENDOR, 0, 0);
42     if (ret != WIFI_SUCCESS)
43         return ret;
44 
45     /* Insert the oui in the msg */
46     ret = mMsg.put_u32(NL80211_ATTR_VENDOR_ID, mVendor_id);
47     if (ret != WIFI_SUCCESS)
48         return ret;
49     /* Insert the subcmd in the msg */
50     ret = mMsg.put_u32(NL80211_ATTR_VENDOR_SUBCMD, mSubcmd);
51     if (ret != WIFI_SUCCESS)
52         return ret;
53 
54      ALOGV("%s: mVendor_id = %d, Subcmd = %d.",
55         __FUNCTION__, mVendor_id, mSubcmd);
56     return ret;
57 }
58 
requestResponse()59 wifi_error RoamCommand::requestResponse()
60 {
61     return WifiCommand::requestResponse(mMsg);
62 }
63 
wifi_set_bssid_blacklist(wifi_request_id id,wifi_interface_handle iface,wifi_bssid_params params)64 wifi_error wifi_set_bssid_blacklist(wifi_request_id id,
65                                     wifi_interface_handle iface,
66                                     wifi_bssid_params params)
67 {
68     wifi_error ret;
69     int i;
70     RoamCommand *roamCommand;
71     struct nlattr *nlData, *nlBssids;
72     interface_info *ifaceInfo = getIfaceInfo(iface);
73     wifi_handle wifiHandle = getWifiHandle(iface);
74     hal_info *info = getHalInfo(wifiHandle);
75 
76     if (!(info->supported_feature_set & WIFI_FEATURE_CONTROL_ROAMING)) {
77         ALOGE("%s: Roaming is not supported by driver",
78             __FUNCTION__);
79         return WIFI_ERROR_NOT_SUPPORTED;
80     }
81 
82     for (i = 0; i < params.num_bssid; i++) {
83         ALOGV("BSSID: %d : %02x:%02x:%02x:%02x:%02x:%02x", i,
84                 params.bssids[i][0], params.bssids[i][1],
85                 params.bssids[i][2], params.bssids[i][3],
86                 params.bssids[i][4], params.bssids[i][5]);
87     }
88 
89     roamCommand =
90          new RoamCommand(wifiHandle,
91                           id,
92                           OUI_QCA,
93                           QCA_NL80211_VENDOR_SUBCMD_ROAM);
94     if (roamCommand == NULL) {
95         ALOGE("%s: Error roamCommand NULL", __FUNCTION__);
96         return WIFI_ERROR_UNKNOWN;
97     }
98 
99     /* Create the NL message. */
100     ret = roamCommand->create();
101     if (ret != WIFI_SUCCESS)
102         goto cleanup;
103 
104     /* Set the interface Id of the message. */
105     ret = roamCommand->set_iface_id(ifaceInfo->name);
106     if (ret != WIFI_SUCCESS)
107         goto cleanup;
108 
109     /* Add the vendor specific attributes for the NL command. */
110     nlData = roamCommand->attr_start(NL80211_ATTR_VENDOR_DATA);
111     if (!nlData){
112         ret = WIFI_ERROR_UNKNOWN;
113         goto cleanup;
114     }
115 
116     ret = roamCommand->put_u32(QCA_WLAN_VENDOR_ATTR_ROAMING_SUBCMD,
117                           QCA_WLAN_VENDOR_ROAMING_SUBCMD_SET_BLACKLIST_BSSID);
118     if (ret != WIFI_SUCCESS)
119         goto cleanup;
120 
121     ret = roamCommand->put_u32( QCA_WLAN_VENDOR_ATTR_ROAMING_REQ_ID, id);
122     if (ret != WIFI_SUCCESS)
123         goto cleanup;
124 
125     ret = roamCommand->put_u32(
126                   QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_SET_BSSID_PARAMS_NUM_BSSID,
127                   params.num_bssid);
128     if (ret != WIFI_SUCCESS)
129         goto cleanup;
130 
131     nlBssids = roamCommand->attr_start(
132             QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_SET_BSSID_PARAMS);
133     for (i = 0; i < params.num_bssid; i++) {
134         struct nlattr *nl_ssid = roamCommand->attr_start(i);
135 
136         ret = roamCommand->put_addr(
137                       QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_SET_BSSID_PARAMS_BSSID,
138                       (u8 *)params.bssids[i]);
139         if (ret != WIFI_SUCCESS)
140             goto cleanup;
141 
142         roamCommand->attr_end(nl_ssid);
143     }
144     roamCommand->attr_end(nlBssids);
145 
146     roamCommand->attr_end(nlData);
147 
148     ret = roamCommand->requestResponse();
149     if (ret != WIFI_SUCCESS)
150         ALOGE("wifi_set_bssid_blacklist(): requestResponse Error:%d", ret);
151 
152 cleanup:
153     delete roamCommand;
154     return ret;
155 
156 }
157 
wifi_set_ssid_white_list(wifi_request_id id,wifi_interface_handle iface,int num_networks,ssid_t * ssid_list)158 wifi_error wifi_set_ssid_white_list(wifi_request_id id, wifi_interface_handle iface,
159                                     int num_networks, ssid_t *ssid_list)
160 {
161     wifi_error ret;
162     int i;
163     RoamCommand *roamCommand;
164     struct nlattr *nlData, *nlSsids;
165     interface_info *ifaceInfo = getIfaceInfo(iface);
166     wifi_handle wifiHandle = getWifiHandle(iface);
167     char ssid[MAX_SSID_LENGTH + 1];
168 
169     ALOGV("%s: Number of SSIDs : %d", __FUNCTION__, num_networks);
170 
171     roamCommand = new RoamCommand(
172                                 wifiHandle,
173                                 id,
174                                 OUI_QCA,
175                                 QCA_NL80211_VENDOR_SUBCMD_ROAM);
176     if (roamCommand == NULL) {
177         ALOGE("%s: Failed to create object of RoamCommand class", __FUNCTION__);
178         return WIFI_ERROR_UNKNOWN;
179     }
180 
181     /* Create the NL message. */
182     ret = roamCommand->create();
183     if (ret != WIFI_SUCCESS) {
184         ALOGE("%s: Failed to create NL message,  Error: %d", __FUNCTION__, ret);
185         goto cleanup;
186     }
187 
188     /* Set the interface Id of the message. */
189     ret = roamCommand->set_iface_id(ifaceInfo->name);
190     if (ret != WIFI_SUCCESS) {
191         ALOGE("%s: Failed to set interface Id of message, Error: %d", __FUNCTION__, ret);
192         goto cleanup;
193     }
194 
195     /* Add the vendor specific attributes for the NL command. */
196     nlData = roamCommand->attr_start(NL80211_ATTR_VENDOR_DATA);
197     if (!nlData) {
198         ret = WIFI_ERROR_UNKNOWN;
199         goto cleanup;
200     }
201 
202     ret = roamCommand->put_u32(QCA_WLAN_VENDOR_ATTR_ROAMING_SUBCMD,
203                               QCA_WLAN_VENDOR_ROAMING_SUBCMD_SSID_WHITE_LIST);
204     if (ret != WIFI_SUCCESS)
205         goto cleanup;
206     ret = roamCommand->put_u32(QCA_WLAN_VENDOR_ATTR_ROAMING_REQ_ID, id);
207     if (ret != WIFI_SUCCESS)
208         goto cleanup;
209     ret = roamCommand->put_u32(QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_WHITE_LIST_SSID_NUM_NETWORKS,
210                                num_networks);
211     if (ret != WIFI_SUCCESS)
212         goto cleanup;
213 
214     nlSsids = roamCommand->attr_start(QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_WHITE_LIST_SSID_LIST);
215     for (i = 0; i < num_networks; i++) {
216         struct nlattr *nl_ssid = roamCommand->attr_start(i);
217 
218         memcpy(ssid, ssid_list[i].ssid_str, ssid_list[i].length);
219         ssid[ssid_list[i].length] = '\0';
220         ALOGV("ssid[%d] : %s", i, ssid);
221 
222         ret = roamCommand->put_bytes(QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_WHITE_LIST_SSID,
223                                      ssid, (ssid_list[i].length + 1));
224         if (ret != WIFI_SUCCESS) {
225             ALOGE("%s: Failed to add ssid atribute, Error: %d", __FUNCTION__, ret);
226             goto cleanup;
227         }
228 
229         roamCommand->attr_end(nl_ssid);
230     }
231     roamCommand->attr_end(nlSsids);
232 
233     roamCommand->attr_end(nlData);
234 
235     ret = roamCommand->requestResponse();
236     if (ret != WIFI_SUCCESS)
237         ALOGE("%s: Failed to send request, Error:%d", __FUNCTION__, ret);
238 
239 cleanup:
240     delete roamCommand;
241     return ret;
242 }
243 
wifi_get_roaming_capabilities(wifi_interface_handle iface,wifi_roaming_capabilities * caps)244 wifi_error wifi_get_roaming_capabilities(wifi_interface_handle iface,
245                                          wifi_roaming_capabilities *caps)
246 {
247     wifi_handle wifiHandle = getWifiHandle(iface);
248     hal_info *info = getHalInfo(wifiHandle);
249 
250     if (!caps) {
251         ALOGE("%s: Invalid Buffer provided. Exit", __FUNCTION__);
252         return WIFI_ERROR_INVALID_ARGS;
253     }
254 
255     if (!info) {
256         ALOGE("%s: hal_info is NULL", __FUNCTION__);
257         return WIFI_ERROR_INVALID_ARGS;
258     }
259 
260     // Per WiFi HAL design, roaming feature should have nothing to do with Gscan
261     // But for current driver impl, roaming_capa is provided as part of
262     // GSCAN_GET_CAPABILITY query, so if Gscan is not supported, roaming_capa
263     // is not set (uses initial value 0).
264     // To de-couple roaming with Gscan, set default values for roaming_capa
265     // if Gscan is not supported.
266     // TODO: removes below if driver has new API to get roaming_capa.
267     if (!(info->supported_feature_set & WIFI_FEATURE_GSCAN)) {
268         info->capa.roaming_capa.max_whitelist_size = WLAN_ROAM_MAX_NUM_WHITE_LIST;
269         info->capa.roaming_capa.max_blacklist_size = WLAN_ROAM_MAX_NUM_BLACK_LIST;
270     }
271     memcpy(caps, &info->capa.roaming_capa, sizeof(wifi_roaming_capabilities));
272 
273     return WIFI_SUCCESS;
274 }
275 
wifi_configure_roaming(wifi_interface_handle iface,wifi_roaming_config * roaming_config)276 wifi_error wifi_configure_roaming(wifi_interface_handle iface, wifi_roaming_config *roaming_config)
277 {
278     wifi_error ret;
279     int requestId;
280     wifi_bssid_params bssid_params;
281     wifi_handle wifiHandle = getWifiHandle(iface);
282     hal_info *info = getHalInfo(wifiHandle);
283 
284     if (!roaming_config) {
285         ALOGE("%s: Invalid Buffer provided. Exit", __FUNCTION__);
286         return WIFI_ERROR_INVALID_ARGS;
287     }
288 
289     /* No request id from caller, so generate one and pass it on to the driver.
290      * Generate it randomly.
291      */
292     requestId = get_requestid();
293 
294     /* Set bssid blacklist */
295     if (roaming_config->num_blacklist_bssid > info->capa.roaming_capa.max_blacklist_size) {
296         ALOGE("%s: Number of blacklist bssids(%d) provided is more than maximum blacklist bssids(%d)"
297               " supported", __FUNCTION__, roaming_config->num_blacklist_bssid,
298               info->capa.roaming_capa.max_blacklist_size);
299         return WIFI_ERROR_NOT_SUPPORTED;
300     }
301     bssid_params.num_bssid = roaming_config->num_blacklist_bssid;
302 
303     memcpy(bssid_params.bssids, roaming_config->blacklist_bssid,
304            (bssid_params.num_bssid * sizeof(mac_addr)));
305 
306     ret = wifi_set_bssid_blacklist(requestId, iface, bssid_params);
307     if (ret != WIFI_SUCCESS) {
308         ALOGE("%s: Failed to configure blacklist bssids", __FUNCTION__);
309         return WIFI_ERROR_UNKNOWN;
310     }
311 
312     /* Set ssid whitelist */
313     if (roaming_config->num_whitelist_ssid > info->capa.roaming_capa.max_whitelist_size) {
314         ALOGE("%s: Number of whitelist ssid(%d) provided is more than maximum whitelist ssids(%d) "
315               "supported", __FUNCTION__, roaming_config->num_whitelist_ssid,
316               info->capa.roaming_capa.max_whitelist_size);
317         return WIFI_ERROR_NOT_SUPPORTED;
318     }
319 
320     // Framework is always sending SSID length as 32 though null terminated lengths
321     // are lesser. Thus update correct lengths before sending to driver.
322     for (int i = 0; i < roaming_config->num_whitelist_ssid; i++) {
323         int j;
324 
325         for (j = 0; j < roaming_config->whitelist_ssid[i].length; j++) {
326             if (roaming_config->whitelist_ssid[i].ssid_str[j] == '\0')
327                 break;
328         }
329 
330         if (roaming_config->whitelist_ssid[i].length == j)
331             continue;
332 
333         ALOGI("%s: ssid_str %s reported length = %d , null terminated length = %d", __FUNCTION__,
334               roaming_config->whitelist_ssid[i].ssid_str,
335               roaming_config->whitelist_ssid[i].length, j);
336         roaming_config->whitelist_ssid[i].length = j;
337     }
338 
339     ret = wifi_set_ssid_white_list(requestId, iface, roaming_config->num_whitelist_ssid,
340                                    roaming_config->whitelist_ssid);
341     if (ret != WIFI_SUCCESS)
342         ALOGE("%s: Failed to configure whitelist ssids", __FUNCTION__);
343 
344     return ret;
345 }
346 
347 /* Enable/disable firmware roaming */
wifi_enable_firmware_roaming(wifi_interface_handle iface,fw_roaming_state_t state)348 wifi_error wifi_enable_firmware_roaming(wifi_interface_handle iface, fw_roaming_state_t state)
349 {
350     int requestId;
351     wifi_error ret;
352     RoamCommand *roamCommand;
353     struct nlattr *nlData;
354     interface_info *ifaceInfo = getIfaceInfo(iface);
355     wifi_handle wifiHandle = getWifiHandle(iface);
356     qca_roaming_policy policy;
357 
358     ALOGV("%s: set firmware roam state : %d", __FUNCTION__, state);
359 
360     if (state == ROAMING_ENABLE) {
361         policy = QCA_ROAMING_ALLOWED_WITHIN_ESS;
362     } else if(state == ROAMING_DISABLE) {
363         policy = QCA_ROAMING_NOT_ALLOWED;
364     } else {
365         ALOGE("%s: Invalid state provided: %d. Exit \n", __FUNCTION__, state);
366         return WIFI_ERROR_INVALID_ARGS;
367     }
368 
369     /* No request id from caller, so generate one and pass it on to the driver.
370      * Generate it randomly.
371      */
372     requestId = get_requestid();
373 
374     roamCommand =
375          new RoamCommand(wifiHandle,
376                           requestId,
377                           OUI_QCA,
378                           QCA_NL80211_VENDOR_SUBCMD_ROAMING);
379     if (roamCommand == NULL) {
380         ALOGE("%s: Failed to create object of RoamCommand class", __FUNCTION__);
381         return WIFI_ERROR_UNKNOWN;
382     }
383 
384     /* Create the NL message. */
385     ret = roamCommand->create();
386     if (ret != WIFI_SUCCESS) {
387         ALOGE("%s: Failed to create NL message,  Error: %d", __FUNCTION__, ret);
388         goto cleanup;
389     }
390 
391     /* Set the interface Id of the message. */
392     ret = roamCommand->set_iface_id(ifaceInfo->name);
393     if (ret != WIFI_SUCCESS) {
394         ALOGE("%s: Failed to set interface Id of message, Error: %d", __FUNCTION__, ret);
395         goto cleanup;
396     }
397 
398     /* Add the vendor specific attributes for the NL command. */
399     nlData = roamCommand->attr_start(NL80211_ATTR_VENDOR_DATA);
400     if (!nlData) {
401         ret = WIFI_ERROR_UNKNOWN;
402         goto cleanup;
403     }
404 
405     ret = roamCommand->put_u32(QCA_WLAN_VENDOR_ATTR_ROAMING_POLICY, policy);
406     if (ret != WIFI_SUCCESS) {
407         ALOGE("%s: Failed to add roaming policy atribute, Error: %d", __FUNCTION__, ret);
408         goto cleanup;
409     }
410 
411     roamCommand->attr_end(nlData);
412 
413     ret = roamCommand->requestResponse();
414     if (ret != WIFI_SUCCESS)
415         ALOGE("%s: Failed to send request, Error:%d", __FUNCTION__, ret);
416 
417 cleanup:
418     delete roamCommand;
419     return ret;
420 }
421