1 /* Copyright (c) 2015, 2018 The Linux Foundation. All rights reserved.
2 *
3 * Redistribution and use in source and binary forms, with or without
4 * modification, are permitted provided that the following conditions
5 * are met:
6 * * Redistributions of source code must retain the above copyright
7 * notice, this list of conditions and the following disclaimer.
8 * * Redistributions in binary form must reproduce the above
9 * copyright notice, this list of conditions and the following
10 * disclaimer in the documentation and/or other materials provided
11 * with the distribution.
12 * * Neither the name of The Linux Foundation nor the names of its
13 * contributors may be used to endorse or promote products derived
14 * from this software without specific prior written permission.
15 *
16 * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
17 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
18 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
19 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
20 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
21 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
22 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
23 * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
24 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
25 * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
26 * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 */
28
29 #include "sync.h"
30 #define LOG_TAG "WifiHAL"
31 #include <utils/Log.h>
32 #include <time.h>
33 #include <errno.h>
34 #include <stdlib.h>
35 #include <unistd.h>
36 #include "wificonfigcommand.h"
37
38 /* Implementation of the API functions exposed in wifi_config.h */
wifi_extended_dtim_config_set(wifi_request_id id,wifi_interface_handle iface,int extended_dtim)39 wifi_error wifi_extended_dtim_config_set(wifi_request_id id,
40 wifi_interface_handle iface,
41 int extended_dtim)
42 {
43 wifi_error ret;
44 WiFiConfigCommand *wifiConfigCommand;
45 struct nlattr *nlData;
46 interface_info *ifaceInfo = getIfaceInfo(iface);
47 wifi_handle wifiHandle = getWifiHandle(iface);
48
49 ALOGV("%s: extended_dtim:%d", __FUNCTION__, extended_dtim);
50
51 wifiConfigCommand = new WiFiConfigCommand(
52 wifiHandle,
53 id,
54 OUI_QCA,
55 QCA_NL80211_VENDOR_SUBCMD_SET_WIFI_CONFIGURATION);
56
57 if (wifiConfigCommand == NULL) {
58 ALOGE("%s: Error wifiConfigCommand NULL", __FUNCTION__);
59 return WIFI_ERROR_UNKNOWN;
60 }
61
62 /* Create the NL message. */
63 ret = wifiConfigCommand->create();
64 if (ret != WIFI_SUCCESS) {
65 ALOGE("wifi_extended_dtim_config_set: failed to create NL msg. "
66 "Error:%d", ret);
67 goto cleanup;
68 }
69
70 /* Set the interface Id of the message. */
71 ret = wifiConfigCommand->set_iface_id(ifaceInfo->name);
72 if (ret != WIFI_SUCCESS) {
73 ALOGE("wifi_extended_dtim_config_set: failed to set iface id. "
74 "Error:%d", ret);
75 goto cleanup;
76 }
77
78 /* Add the vendor specific attributes for the NL command. */
79 nlData = wifiConfigCommand->attr_start(NL80211_ATTR_VENDOR_DATA);
80 if (!nlData) {
81 ALOGE("wifi_extended_dtim_config_set: failed attr_start for "
82 "VENDOR_DATA. Error:%d", ret);
83 goto cleanup;
84 }
85
86 ret = wifiConfigCommand->put_u32(
87 QCA_WLAN_VENDOR_ATTR_WIFI_CONFIG_DYNAMIC_DTIM, extended_dtim);
88 if (ret != WIFI_SUCCESS) {
89 ALOGE("wifi_extended_dtim_config_set(): failed to put vendor data. "
90 "Error:%d", ret);
91 goto cleanup;
92 }
93 wifiConfigCommand->attr_end(nlData);
94
95 /* Send the NL msg. */
96 wifiConfigCommand->waitForRsp(false);
97 ret = wifiConfigCommand->requestEvent();
98 if (ret != WIFI_SUCCESS) {
99 ALOGE("wifi_extended_dtim_config_set(): requestEvent Error:%d", ret);
100 goto cleanup;
101 }
102
103 cleanup:
104 delete wifiConfigCommand;
105 return ret;
106 }
107
108 /* Set the country code to driver. */
wifi_set_country_code(wifi_interface_handle iface,const char * country_code)109 wifi_error wifi_set_country_code(wifi_interface_handle iface,
110 const char* country_code)
111 {
112 int requestId;
113 wifi_error ret;
114 WiFiConfigCommand *wifiConfigCommand;
115 wifi_handle wifiHandle = getWifiHandle(iface);
116
117 ALOGV("%s: %s", __FUNCTION__, country_code);
118
119 /* No request id from caller, so generate one and pass it on to the driver.
120 * Generate it randomly.
121 */
122 requestId = get_requestid();
123
124 wifiConfigCommand = new WiFiConfigCommand(
125 wifiHandle,
126 requestId,
127 OUI_QCA,
128 QCA_NL80211_VENDOR_SUBCMD_SET_WIFI_CONFIGURATION);
129 if (wifiConfigCommand == NULL) {
130 ALOGE("%s: Error wifiConfigCommand NULL", __FUNCTION__);
131 return WIFI_ERROR_UNKNOWN;
132 }
133
134 /* Create the NL message with NL80211_CMD_REQ_SET_REG NL cmd. */
135 ret = wifiConfigCommand->create_generic(NL80211_CMD_REQ_SET_REG);
136 if (ret != WIFI_SUCCESS) {
137 ALOGE("wifi_set_country_code: failed to create NL msg. Error:%d", ret);
138 goto cleanup;
139 }
140
141 ret = wifiConfigCommand->put_string(NL80211_ATTR_REG_ALPHA2, country_code);
142 if (ret != WIFI_SUCCESS) {
143 ALOGE("wifi_set_country_code: put country code failed. Error:%d", ret);
144 goto cleanup;
145 }
146
147 /* Send the NL msg. */
148 wifiConfigCommand->waitForRsp(false);
149 ret = wifiConfigCommand->requestEvent();
150 if (ret != WIFI_SUCCESS) {
151 ALOGE("wifi_set_country_code(): requestEvent Error:%d", ret);
152 goto cleanup;
153 }
154 usleep(WAIT_TIME_FOR_SET_REG_DOMAIN);
155
156 cleanup:
157 delete wifiConfigCommand;
158 return ret;
159 }
160
wifi_set_beacon_wifi_iface_stats_averaging_factor(wifi_request_id id,wifi_interface_handle iface,u16 factor)161 wifi_error wifi_set_beacon_wifi_iface_stats_averaging_factor(
162 wifi_request_id id,
163 wifi_interface_handle iface,
164 u16 factor)
165 {
166 wifi_error ret;
167 WiFiConfigCommand *wifiConfigCommand;
168 struct nlattr *nlData;
169 interface_info *ifaceInfo = getIfaceInfo(iface);
170 wifi_handle wifiHandle = getWifiHandle(iface);
171
172 ALOGV("%s factor:%u", __FUNCTION__, factor);
173 wifiConfigCommand = new WiFiConfigCommand(
174 wifiHandle,
175 id,
176 OUI_QCA,
177 QCA_NL80211_VENDOR_SUBCMD_SET_WIFI_CONFIGURATION);
178 if (wifiConfigCommand == NULL) {
179 ALOGE("%s: Error wifiConfigCommand NULL", __FUNCTION__);
180 return WIFI_ERROR_UNKNOWN;
181 }
182
183 /* Create the NL message. */
184 ret = wifiConfigCommand->create();
185 if (ret != WIFI_SUCCESS) {
186 ALOGE("wifi_set_beacon_wifi_iface_stats_averaging_factor: failed to "
187 "create NL msg. Error:%d", ret);
188 goto cleanup;
189 }
190
191 /* Set the interface Id of the message. */
192 ret = wifiConfigCommand->set_iface_id(ifaceInfo->name);
193 if (ret != WIFI_SUCCESS) {
194 ALOGE("wifi_set_beacon_wifi_iface_stats_averaging_factor: failed to "
195 "set iface id. Error:%d", ret);
196 goto cleanup;
197 }
198
199 /* Add the vendor specific attributes for the NL command. */
200 nlData = wifiConfigCommand->attr_start(NL80211_ATTR_VENDOR_DATA);
201 if (!nlData) {
202 ALOGE("wifi_set_beacon_wifi_iface_stats_averaging_factor: failed "
203 "attr_start for VENDOR_DATA. Error:%d", ret);
204 goto cleanup;
205 }
206
207 if (wifiConfigCommand->put_u32(
208 QCA_WLAN_VENDOR_ATTR_WIFI_CONFIG_STATS_AVG_FACTOR, factor)) {
209 ALOGE("wifi_set_beacon_wifi_iface_stats_averaging_factor(): failed to "
210 "put vendor data. Error:%d", ret);
211 goto cleanup;
212 }
213 wifiConfigCommand->attr_end(nlData);
214
215 /* Send the NL msg. */
216 wifiConfigCommand->waitForRsp(false);
217 ret = wifiConfigCommand->requestEvent();
218 if (ret != WIFI_SUCCESS) {
219 ALOGE("wifi_set_beacon_wifi_iface_stats_averaging_factor(): "
220 "requestEvent Error:%d", ret);
221 goto cleanup;
222 }
223
224 cleanup:
225 delete wifiConfigCommand;
226 return ret;
227 }
228
wifi_set_guard_time(wifi_request_id id,wifi_interface_handle iface,u32 guard_time)229 wifi_error wifi_set_guard_time(wifi_request_id id,
230 wifi_interface_handle iface,
231 u32 guard_time)
232 {
233 wifi_error ret;
234 WiFiConfigCommand *wifiConfigCommand;
235 struct nlattr *nlData;
236 interface_info *ifaceInfo = getIfaceInfo(iface);
237 wifi_handle wifiHandle = getWifiHandle(iface);
238
239 ALOGV("%s : guard_time:%u", __FUNCTION__, guard_time);
240
241 wifiConfigCommand = new WiFiConfigCommand(
242 wifiHandle,
243 id,
244 OUI_QCA,
245 QCA_NL80211_VENDOR_SUBCMD_SET_WIFI_CONFIGURATION);
246 if (wifiConfigCommand == NULL) {
247 ALOGE("%s: Error wifiConfigCommand NULL", __FUNCTION__);
248 return WIFI_ERROR_UNKNOWN;
249 }
250
251 /* Create the NL message. */
252 ret = wifiConfigCommand->create();
253 if (ret != WIFI_SUCCESS) {
254 ALOGE("wifi_set_guard_time: failed to create NL msg. Error:%d", ret);
255 goto cleanup;
256 }
257
258 /* Set the interface Id of the message. */
259 ret = wifiConfigCommand->set_iface_id(ifaceInfo->name);
260 if (ret != WIFI_SUCCESS) {
261 ALOGE("wifi_set_guard_time: failed to set iface id. Error:%d", ret);
262 goto cleanup;
263 }
264
265 /* Add the vendor specific attributes for the NL command. */
266 nlData = wifiConfigCommand->attr_start(NL80211_ATTR_VENDOR_DATA);
267 if (!nlData) {
268 ALOGE("wifi_set_guard_time: failed attr_start for VENDOR_DATA. "
269 "Error:%d", ret);
270 goto cleanup;
271 }
272
273 if (wifiConfigCommand->put_u32(
274 QCA_WLAN_VENDOR_ATTR_WIFI_CONFIG_GUARD_TIME, guard_time)) {
275 ALOGE("wifi_set_guard_time: failed to add vendor data.");
276 goto cleanup;
277 }
278 wifiConfigCommand->attr_end(nlData);
279
280 /* Send the NL msg. */
281 wifiConfigCommand->waitForRsp(false);
282 ret = wifiConfigCommand->requestEvent();
283 if (ret != WIFI_SUCCESS) {
284 ALOGE("wifi_set_guard_time(): requestEvent Error:%d", ret);
285 goto cleanup;
286 }
287
288 cleanup:
289 delete wifiConfigCommand;
290 return ret;
291 }
292
wifi_select_tx_power_scenario(wifi_interface_handle handle,wifi_power_scenario scenario)293 wifi_error wifi_select_tx_power_scenario(wifi_interface_handle handle,
294 wifi_power_scenario scenario)
295 {
296 wifi_error ret;
297 WiFiConfigCommand *wifiConfigCommand;
298 struct nlattr *nlData;
299 interface_info *ifaceInfo = getIfaceInfo(handle);
300 wifi_handle wifiHandle = getWifiHandle(handle);
301 u32 bdf_file = 0;
302
303 ALOGV("%s : power scenario:%d", __FUNCTION__, scenario);
304
305 wifiConfigCommand = new WiFiConfigCommand(
306 wifiHandle,
307 1,
308 OUI_QCA,
309 QCA_NL80211_VENDOR_SUBCMD_SET_SAR_LIMITS);
310 if (wifiConfigCommand == NULL) {
311 ALOGE("%s: Error wifiConfigCommand NULL", __FUNCTION__);
312 return WIFI_ERROR_UNKNOWN;
313 }
314
315 /* Create the NL message. */
316 ret = wifiConfigCommand->create();
317 if (ret != WIFI_SUCCESS) {
318 ALOGE("wifi_select_tx_power_scenario: failed to create NL msg. Error:%d", ret);
319 goto cleanup;
320 }
321
322 /* Set the interface Id of the message. */
323 ret = wifiConfigCommand->set_iface_id(ifaceInfo->name);
324 if (ret != WIFI_SUCCESS) {
325 ALOGE("wifi_select_tx_power_scenario: failed to set iface id. Error:%d", ret);
326 goto cleanup;
327 }
328
329 /* Add the vendor specific attributes for the NL command. */
330 nlData = wifiConfigCommand->attr_start(NL80211_ATTR_VENDOR_DATA);
331 if (!nlData) {
332 ALOGE("wifi_select_tx_power_scenario: failed attr_start for VENDOR_DATA. "
333 "Error:%d", ret);
334 goto cleanup;
335 }
336
337 switch (scenario) {
338 case WIFI_POWER_SCENARIO_VOICE_CALL:
339 case WIFI_POWER_SCENARIO_ON_HEAD_CELL_OFF:
340 bdf_file = QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SELECT_BDF0;
341 break;
342
343 case WIFI_POWER_SCENARIO_ON_HEAD_CELL_ON:
344 bdf_file = QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SELECT_BDF1;
345 break;
346
347 case WIFI_POWER_SCENARIO_ON_BODY_CELL_OFF:
348 bdf_file = QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SELECT_BDF2;
349 break;
350
351 case WIFI_POWER_SCENARIO_ON_BODY_CELL_ON:
352 bdf_file = QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SELECT_BDF3;
353 break;
354
355 default:
356 ALOGE("wifi_select_tx_power_scenario: invalid scenario %d", scenario);
357 ret = WIFI_ERROR_INVALID_ARGS;
358 goto cleanup;
359 }
360
361 if (wifiConfigCommand->put_u32(
362 QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SAR_ENABLE,
363 bdf_file)) {
364 ALOGE("failed to put SAR_ENABLE");
365 goto cleanup;
366 }
367 wifiConfigCommand->attr_end(nlData);
368
369 ret = wifiConfigCommand->requestEvent();
370 if (ret != WIFI_SUCCESS) {
371 ALOGE("wifi_select_tx_power_scenario(): requestEvent Error:%d", ret);
372 goto cleanup;
373 }
374
375 cleanup:
376 delete wifiConfigCommand;
377 return ret;
378 }
379
wifi_reset_tx_power_scenario(wifi_interface_handle handle)380 wifi_error wifi_reset_tx_power_scenario(wifi_interface_handle handle)
381 {
382 wifi_error ret;
383 WiFiConfigCommand *wifiConfigCommand;
384 struct nlattr *nlData;
385 interface_info *ifaceInfo = getIfaceInfo(handle);
386 wifi_handle wifiHandle = getWifiHandle(handle);
387
388 wifiConfigCommand = new WiFiConfigCommand(
389 wifiHandle,
390 1,
391 OUI_QCA,
392 QCA_NL80211_VENDOR_SUBCMD_SET_SAR_LIMITS);
393 if (wifiConfigCommand == NULL) {
394 ALOGE("%s: Error wifiConfigCommand NULL", __FUNCTION__);
395 return WIFI_ERROR_UNKNOWN;
396 }
397
398 /* Create the NL message. */
399 ret = wifiConfigCommand->create();
400 if (ret != WIFI_SUCCESS) {
401 ALOGE("wifi_reset_tx_power_scenario: failed to create NL msg. Error:%d", ret);
402 goto cleanup;
403 }
404
405 /* Set the interface Id of the message. */
406 ret = wifiConfigCommand->set_iface_id(ifaceInfo->name);
407 if (ret != WIFI_SUCCESS) {
408 ALOGE("wifi_reset_tx_power_scenario: failed to set iface id. Error:%d", ret);
409 goto cleanup;
410 }
411
412 /* Add the vendor specific attributes for the NL command. */
413 nlData = wifiConfigCommand->attr_start(NL80211_ATTR_VENDOR_DATA);
414 if (!nlData) {
415 ALOGE("wifi_reset_tx_power_scenario: failed attr_start for VENDOR_DATA. "
416 "Error:%d", ret);
417 goto cleanup;
418 }
419
420 if (wifiConfigCommand->put_u32(QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SAR_ENABLE,
421 QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SELECT_NONE)) {
422 ALOGE("failed to put SAR_ENABLE or NUM_SPECS");
423 goto cleanup;
424 }
425 wifiConfigCommand->attr_end(nlData);
426
427 ret = wifiConfigCommand->requestEvent();
428 if (ret != WIFI_SUCCESS) {
429 ALOGE("wifi_reset_tx_power_scenario(): requestEvent Error:%d", ret);
430 goto cleanup;
431 }
432
433 cleanup:
434 delete wifiConfigCommand;
435 return ret;
436 }
437
WiFiConfigCommand(wifi_handle handle,int id,u32 vendor_id,u32 subcmd)438 WiFiConfigCommand::WiFiConfigCommand(wifi_handle handle,
439 int id, u32 vendor_id,
440 u32 subcmd)
441 : WifiVendorCommand(handle, id, vendor_id, subcmd)
442 {
443 /* Initialize the member data variables here */
444 mWaitforRsp = false;
445 mRequestId = id;
446 }
447
~WiFiConfigCommand()448 WiFiConfigCommand::~WiFiConfigCommand()
449 {
450 unregisterVendorHandler(mVendor_id, mSubcmd);
451 }
452
453 /* This function implements creation of Vendor command */
create()454 wifi_error WiFiConfigCommand::create()
455 {
456 wifi_error ret = mMsg.create(NL80211_CMD_VENDOR, 0, 0);
457 if (ret != WIFI_SUCCESS)
458 return ret;
459
460 /* Insert the oui in the msg */
461 ret = mMsg.put_u32(NL80211_ATTR_VENDOR_ID, mVendor_id);
462 if (ret != WIFI_SUCCESS)
463 return ret;
464 /* Insert the subcmd in the msg */
465 ret = mMsg.put_u32(NL80211_ATTR_VENDOR_SUBCMD, mSubcmd);
466
467 return ret;
468 }
469
470 /* This function implements creation of generic NL command */
create_generic(u8 cmdId)471 wifi_error WiFiConfigCommand::create_generic(u8 cmdId)
472 {
473 wifi_error ret = mMsg.create(cmdId, 0, 0);
474 return ret;
475 }
476
waitForRsp(bool wait)477 void WiFiConfigCommand::waitForRsp(bool wait)
478 {
479 mWaitforRsp = wait;
480 }
481
482 /* Callback handlers registered for nl message send */
error_handler_wifi_config(struct sockaddr_nl * nla,struct nlmsgerr * err,void * arg)483 static int error_handler_wifi_config(struct sockaddr_nl *nla,
484 struct nlmsgerr *err,
485 void *arg)
486 {
487 struct sockaddr_nl *tmp;
488 int *ret = (int *)arg;
489 tmp = nla;
490 *ret = err->error;
491 ALOGE("%s: Error code:%d (%s)", __FUNCTION__, *ret, strerror(-(*ret)));
492 return NL_STOP;
493 }
494
495 /* Callback handlers registered for nl message send */
ack_handler_wifi_config(struct nl_msg * msg,void * arg)496 static int ack_handler_wifi_config(struct nl_msg *msg, void *arg)
497 {
498 int *ret = (int *)arg;
499 struct nl_msg * a;
500
501 a = msg;
502 *ret = 0;
503 return NL_STOP;
504 }
505
506 /* Callback handlers registered for nl message send */
finish_handler_wifi_config(struct nl_msg * msg,void * arg)507 static int finish_handler_wifi_config(struct nl_msg *msg, void *arg)
508 {
509 int *ret = (int *)arg;
510 struct nl_msg * a;
511
512 a = msg;
513 *ret = 0;
514 return NL_SKIP;
515 }
516
517 /*
518 * Override base class requestEvent and implement little differently here.
519 * This will send the request message.
520 * We don't wait for any response back in case of wificonfig,
521 * thus no wait for condition.
522 */
requestEvent()523 wifi_error WiFiConfigCommand::requestEvent()
524 {
525 int status;
526 wifi_error res = WIFI_SUCCESS;
527 struct nl_cb *cb;
528
529 cb = nl_cb_alloc(NL_CB_DEFAULT);
530 if (!cb) {
531 ALOGE("%s: Callback allocation failed",__FUNCTION__);
532 res = WIFI_ERROR_OUT_OF_MEMORY;
533 goto out;
534 }
535
536 status = nl_send_auto_complete(mInfo->cmd_sock, mMsg.getMessage());
537 if (status < 0) {
538 res = mapKernelErrortoWifiHalError(status);
539 goto out;
540 }
541 status = 1;
542
543 nl_cb_err(cb, NL_CB_CUSTOM, error_handler_wifi_config, &status);
544 nl_cb_set(cb, NL_CB_FINISH, NL_CB_CUSTOM, finish_handler_wifi_config,
545 &status);
546 nl_cb_set(cb, NL_CB_ACK, NL_CB_CUSTOM, ack_handler_wifi_config, &status);
547
548 /* Err is populated as part of finish_handler. */
549 while (status > 0) {
550 nl_recvmsgs(mInfo->cmd_sock, cb);
551 }
552
553 if (status < 0) {
554 res = mapKernelErrortoWifiHalError(status);
555 goto out;
556 }
557
558 if (mWaitforRsp == true) {
559 struct timespec abstime;
560 abstime.tv_sec = 4;
561 abstime.tv_nsec = 0;
562 res = mCondition.wait(abstime);
563 if (res == WIFI_ERROR_TIMED_OUT)
564 ALOGE("%s: Time out happened.", __FUNCTION__);
565
566 ALOGV("%s: Command invoked return value:%d, mWaitForRsp=%d",
567 __FUNCTION__, res, mWaitforRsp);
568 }
569 out:
570 /* Cleanup the mMsg */
571 mMsg.destroy();
572 return res;
573 }
574
575