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