1 /* Copyright (c) 2015, 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 int ret = 0;
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 < 0) {
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 < 0) {
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 if (wifiConfigCommand->put_u32(
87 QCA_WLAN_VENDOR_ATTR_WIFI_CONFIG_DYNAMIC_DTIM, extended_dtim)) {
88 ALOGE("wifi_extended_dtim_config_set(): failed to put vendor data. "
89 "Error:%d", ret);
90 goto cleanup;
91 }
92 wifiConfigCommand->attr_end(nlData);
93
94 /* Send the NL msg. */
95 wifiConfigCommand->waitForRsp(false);
96 ret = wifiConfigCommand->requestEvent();
97 if (ret != 0) {
98 ALOGE("wifi_extended_dtim_config_set(): requestEvent Error:%d", ret);
99 goto cleanup;
100 }
101
102 cleanup:
103 delete wifiConfigCommand;
104 return mapErrorKernelToWifiHAL(ret);
105 }
106
107 /* Set the country code to driver. */
wifi_set_country_code(wifi_interface_handle iface,const char * country_code)108 wifi_error wifi_set_country_code(wifi_interface_handle iface,
109 const char* country_code)
110 {
111 int requestId, ret = 0;
112 WiFiConfigCommand *wifiConfigCommand;
113 wifi_handle wifiHandle = getWifiHandle(iface);
114
115 ALOGV("%s: %s", __FUNCTION__, country_code);
116
117 /* No request id from caller, so generate one and pass it on to the driver.
118 * Generate it randomly.
119 */
120 requestId = get_requestid();
121
122 wifiConfigCommand = new WiFiConfigCommand(
123 wifiHandle,
124 requestId,
125 OUI_QCA,
126 QCA_NL80211_VENDOR_SUBCMD_SET_WIFI_CONFIGURATION);
127 if (wifiConfigCommand == NULL) {
128 ALOGE("%s: Error wifiConfigCommand NULL", __FUNCTION__);
129 return WIFI_ERROR_UNKNOWN;
130 }
131
132 /* Create the NL message with NL80211_CMD_REQ_SET_REG NL cmd. */
133 ret = wifiConfigCommand->create_generic(NL80211_CMD_REQ_SET_REG);
134 if (ret < 0) {
135 ALOGE("wifi_set_country_code: failed to create NL msg. Error:%d", ret);
136 goto cleanup;
137 }
138
139 if (wifiConfigCommand->put_string(NL80211_ATTR_REG_ALPHA2, country_code)) {
140 ALOGE("wifi_set_country_code: put country code failed. Error:%d", ret);
141 goto cleanup;
142 }
143
144 /* Send the NL msg. */
145 wifiConfigCommand->waitForRsp(false);
146 ret = wifiConfigCommand->requestEvent();
147 if (ret != 0) {
148 ALOGE("wifi_set_country_code(): requestEvent Error:%d", ret);
149 goto cleanup;
150 }
151 usleep(WAIT_TIME_FOR_SET_REG_DOMAIN);
152
153 cleanup:
154 delete wifiConfigCommand;
155 return mapErrorKernelToWifiHAL(ret);
156 }
157
wifi_set_beacon_wifi_iface_stats_averaging_factor(wifi_request_id id,wifi_interface_handle iface,u16 factor)158 wifi_error wifi_set_beacon_wifi_iface_stats_averaging_factor(
159 wifi_request_id id,
160 wifi_interface_handle iface,
161 u16 factor)
162 {
163 int ret = 0;
164 WiFiConfigCommand *wifiConfigCommand;
165 struct nlattr *nlData;
166 interface_info *ifaceInfo = getIfaceInfo(iface);
167 wifi_handle wifiHandle = getWifiHandle(iface);
168
169 ALOGV("%s factor:%u", __FUNCTION__, factor);
170 wifiConfigCommand = new WiFiConfigCommand(
171 wifiHandle,
172 id,
173 OUI_QCA,
174 QCA_NL80211_VENDOR_SUBCMD_SET_WIFI_CONFIGURATION);
175 if (wifiConfigCommand == NULL) {
176 ALOGE("%s: Error wifiConfigCommand NULL", __FUNCTION__);
177 return WIFI_ERROR_UNKNOWN;
178 }
179
180 /* Create the NL message. */
181 ret = wifiConfigCommand->create();
182 if (ret < 0) {
183 ALOGE("wifi_set_beacon_wifi_iface_stats_averaging_factor: failed to "
184 "create NL msg. Error:%d", ret);
185 goto cleanup;
186 }
187
188 /* Set the interface Id of the message. */
189 ret = wifiConfigCommand->set_iface_id(ifaceInfo->name);
190 if (ret < 0) {
191 ALOGE("wifi_set_beacon_wifi_iface_stats_averaging_factor: failed to "
192 "set iface id. Error:%d", ret);
193 goto cleanup;
194 }
195
196 /* Add the vendor specific attributes for the NL command. */
197 nlData = wifiConfigCommand->attr_start(NL80211_ATTR_VENDOR_DATA);
198 if (!nlData) {
199 ALOGE("wifi_set_beacon_wifi_iface_stats_averaging_factor: failed "
200 "attr_start for VENDOR_DATA. Error:%d", ret);
201 goto cleanup;
202 }
203
204 if (wifiConfigCommand->put_u32(
205 QCA_WLAN_VENDOR_ATTR_WIFI_CONFIG_STATS_AVG_FACTOR, factor)) {
206 ALOGE("wifi_set_beacon_wifi_iface_stats_averaging_factor(): failed to "
207 "put vendor data. Error:%d", ret);
208 goto cleanup;
209 }
210 wifiConfigCommand->attr_end(nlData);
211
212 /* Send the NL msg. */
213 wifiConfigCommand->waitForRsp(false);
214 ret = wifiConfigCommand->requestEvent();
215 if (ret != 0) {
216 ALOGE("wifi_set_beacon_wifi_iface_stats_averaging_factor(): "
217 "requestEvent Error:%d", ret);
218 goto cleanup;
219 }
220
221 cleanup:
222 delete wifiConfigCommand;
223 return mapErrorKernelToWifiHAL(ret);
224 }
225
wifi_set_guard_time(wifi_request_id id,wifi_interface_handle iface,u32 guard_time)226 wifi_error wifi_set_guard_time(wifi_request_id id,
227 wifi_interface_handle iface,
228 u32 guard_time)
229 {
230 int ret = 0;
231 WiFiConfigCommand *wifiConfigCommand;
232 struct nlattr *nlData;
233 interface_info *ifaceInfo = getIfaceInfo(iface);
234 wifi_handle wifiHandle = getWifiHandle(iface);
235
236 ALOGV("%s : guard_time:%u", __FUNCTION__, guard_time);
237
238 wifiConfigCommand = new WiFiConfigCommand(
239 wifiHandle,
240 id,
241 OUI_QCA,
242 QCA_NL80211_VENDOR_SUBCMD_SET_WIFI_CONFIGURATION);
243 if (wifiConfigCommand == NULL) {
244 ALOGE("%s: Error wifiConfigCommand NULL", __FUNCTION__);
245 return WIFI_ERROR_UNKNOWN;
246 }
247
248 /* Create the NL message. */
249 ret = wifiConfigCommand->create();
250 if (ret < 0) {
251 ALOGE("wifi_set_guard_time: failed to create NL msg. Error:%d", ret);
252 goto cleanup;
253 }
254
255 /* Set the interface Id of the message. */
256 ret = wifiConfigCommand->set_iface_id(ifaceInfo->name);
257 if (ret < 0) {
258 ALOGE("wifi_set_guard_time: failed to set iface id. Error:%d", ret);
259 goto cleanup;
260 }
261
262 /* Add the vendor specific attributes for the NL command. */
263 nlData = wifiConfigCommand->attr_start(NL80211_ATTR_VENDOR_DATA);
264 if (!nlData) {
265 ALOGE("wifi_set_guard_time: failed attr_start for VENDOR_DATA. "
266 "Error:%d", ret);
267 goto cleanup;
268 }
269
270 if (wifiConfigCommand->put_u32(
271 QCA_WLAN_VENDOR_ATTR_WIFI_CONFIG_GUARD_TIME, guard_time)) {
272 ALOGE("wifi_set_guard_time: failed to add vendor data.");
273 goto cleanup;
274 }
275 wifiConfigCommand->attr_end(nlData);
276
277 /* Send the NL msg. */
278 wifiConfigCommand->waitForRsp(false);
279 ret = wifiConfigCommand->requestEvent();
280 if (ret != 0) {
281 ALOGE("wifi_set_guard_time(): requestEvent Error:%d", ret);
282 goto cleanup;
283 }
284
285 cleanup:
286 delete wifiConfigCommand;
287 return mapErrorKernelToWifiHAL(ret);
288 }
289
wifi_select_tx_power_scenario(wifi_interface_handle handle,wifi_power_scenario scenario)290 wifi_error wifi_select_tx_power_scenario(wifi_interface_handle handle,
291 wifi_power_scenario scenario)
292 {
293 int ret = 0;
294 WiFiConfigCommand *wifiConfigCommand;
295 struct nlattr *nlData;
296 interface_info *ifaceInfo = getIfaceInfo(handle);
297 wifi_handle wifiHandle = getWifiHandle(handle);
298 u32 bdf_file = 0;
299
300 ALOGV("%s : power scenario:%d", __FUNCTION__, scenario);
301
302 wifiConfigCommand = new WiFiConfigCommand(
303 wifiHandle,
304 1,
305 OUI_QCA,
306 QCA_NL80211_VENDOR_SUBCMD_SET_SAR_LIMITS);
307 if (wifiConfigCommand == NULL) {
308 ALOGE("%s: Error wifiConfigCommand NULL", __FUNCTION__);
309 return WIFI_ERROR_UNKNOWN;
310 }
311
312 /* Create the NL message. */
313 ret = wifiConfigCommand->create();
314 if (ret < 0) {
315 ALOGE("wifi_select_tx_power_scenario: failed to create NL msg. Error:%d", ret);
316 goto cleanup;
317 }
318
319 /* Set the interface Id of the message. */
320 ret = wifiConfigCommand->set_iface_id(ifaceInfo->name);
321 if (ret < 0) {
322 ALOGE("wifi_select_tx_power_scenario: failed to set iface id. Error:%d", ret);
323 goto cleanup;
324 }
325
326 /* Add the vendor specific attributes for the NL command. */
327 nlData = wifiConfigCommand->attr_start(NL80211_ATTR_VENDOR_DATA);
328 if (!nlData) {
329 ALOGE("wifi_select_tx_power_scenario: failed attr_start for VENDOR_DATA. "
330 "Error:%d", ret);
331 goto cleanup;
332 }
333
334 if (scenario == WIFI_POWER_SCENARIO_VOICE_CALL) {
335 bdf_file = QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SELECT_BDF0;
336 } else {
337 ALOGE("wifi_select_tx_power_scenario: invalid scenario %d", scenario);
338 ret = WIFI_ERROR_INVALID_ARGS;
339 goto cleanup;
340 }
341 if (wifiConfigCommand->put_u32(
342 QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SAR_ENABLE,
343 bdf_file)) {
344 ALOGE("failed to put SAR_ENABLE");
345 goto cleanup;
346 }
347 wifiConfigCommand->attr_end(nlData);
348
349 ret = wifiConfigCommand->requestEvent();
350 if (ret != 0) {
351 ALOGE("wifi_select_tx_power_scenario(): requestEvent Error:%d", ret);
352 goto cleanup;
353 }
354
355 cleanup:
356 delete wifiConfigCommand;
357 return mapErrorKernelToWifiHAL(ret);
358 }
359
wifi_reset_tx_power_scenario(wifi_interface_handle handle)360 wifi_error wifi_reset_tx_power_scenario(wifi_interface_handle handle)
361 {
362 int ret = 0;
363 WiFiConfigCommand *wifiConfigCommand;
364 struct nlattr *nlData;
365 interface_info *ifaceInfo = getIfaceInfo(handle);
366 wifi_handle wifiHandle = getWifiHandle(handle);
367
368 wifiConfigCommand = new WiFiConfigCommand(
369 wifiHandle,
370 1,
371 OUI_QCA,
372 QCA_NL80211_VENDOR_SUBCMD_SET_SAR_LIMITS);
373 if (wifiConfigCommand == NULL) {
374 ALOGE("%s: Error wifiConfigCommand NULL", __FUNCTION__);
375 return WIFI_ERROR_UNKNOWN;
376 }
377
378 /* Create the NL message. */
379 ret = wifiConfigCommand->create();
380 if (ret < 0) {
381 ALOGE("wifi_reset_tx_power_scenario: failed to create NL msg. Error:%d", ret);
382 goto cleanup;
383 }
384
385 /* Set the interface Id of the message. */
386 ret = wifiConfigCommand->set_iface_id(ifaceInfo->name);
387 if (ret < 0) {
388 ALOGE("wifi_reset_tx_power_scenario: failed to set iface id. Error:%d", ret);
389 goto cleanup;
390 }
391
392 /* Add the vendor specific attributes for the NL command. */
393 nlData = wifiConfigCommand->attr_start(NL80211_ATTR_VENDOR_DATA);
394 if (!nlData) {
395 ALOGE("wifi_reset_tx_power_scenario: failed attr_start for VENDOR_DATA. "
396 "Error:%d", ret);
397 goto cleanup;
398 }
399
400 if (wifiConfigCommand->put_u32(QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SAR_ENABLE,
401 QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SELECT_NONE)) {
402 ALOGE("failed to put SAR_ENABLE or NUM_SPECS");
403 goto cleanup;
404 }
405 wifiConfigCommand->attr_end(nlData);
406
407 ret = wifiConfigCommand->requestEvent();
408 if (ret != 0) {
409 ALOGE("wifi_reset_tx_power_scenario(): requestEvent Error:%d", ret);
410 goto cleanup;
411 }
412
413 cleanup:
414 delete wifiConfigCommand;
415 return mapErrorKernelToWifiHAL(ret);
416 }
417
WiFiConfigCommand(wifi_handle handle,int id,u32 vendor_id,u32 subcmd)418 WiFiConfigCommand::WiFiConfigCommand(wifi_handle handle,
419 int id, u32 vendor_id,
420 u32 subcmd)
421 : WifiVendorCommand(handle, id, vendor_id, subcmd)
422 {
423 /* Initialize the member data variables here */
424 mWaitforRsp = false;
425 mRequestId = id;
426 }
427
~WiFiConfigCommand()428 WiFiConfigCommand::~WiFiConfigCommand()
429 {
430 unregisterVendorHandler(mVendor_id, mSubcmd);
431 }
432
433 /* This function implements creation of Vendor command */
create()434 int WiFiConfigCommand::create() {
435 int ret = mMsg.create(NL80211_CMD_VENDOR, 0, 0);
436 if (ret < 0) {
437 return ret;
438 }
439
440 /* Insert the oui in the msg */
441 ret = mMsg.put_u32(NL80211_ATTR_VENDOR_ID, mVendor_id);
442 if (ret < 0)
443 goto out;
444 /* Insert the subcmd in the msg */
445 ret = mMsg.put_u32(NL80211_ATTR_VENDOR_SUBCMD, mSubcmd);
446 if (ret < 0)
447 goto out;
448 out:
449 return ret;
450 }
451
452 /* This function implements creation of generic NL command */
create_generic(u8 cmdId)453 int WiFiConfigCommand::create_generic(u8 cmdId) {
454 int ret = mMsg.create(cmdId, 0, 0);
455 return ret;
456 }
457
waitForRsp(bool wait)458 void WiFiConfigCommand::waitForRsp(bool wait)
459 {
460 mWaitforRsp = wait;
461 }
462
463 /* Callback handlers registered for nl message send */
error_handler_wifi_config(struct sockaddr_nl * nla,struct nlmsgerr * err,void * arg)464 static int error_handler_wifi_config(struct sockaddr_nl *nla,
465 struct nlmsgerr *err,
466 void *arg)
467 {
468 struct sockaddr_nl *tmp;
469 int *ret = (int *)arg;
470 tmp = nla;
471 *ret = err->error;
472 ALOGE("%s: Error code:%d (%s)", __FUNCTION__, *ret, strerror(-(*ret)));
473 return NL_STOP;
474 }
475
476 /* Callback handlers registered for nl message send */
ack_handler_wifi_config(struct nl_msg * msg,void * arg)477 static int ack_handler_wifi_config(struct nl_msg *msg, void *arg)
478 {
479 int *ret = (int *)arg;
480 struct nl_msg * a;
481
482 a = msg;
483 *ret = 0;
484 return NL_STOP;
485 }
486
487 /* Callback handlers registered for nl message send */
finish_handler_wifi_config(struct nl_msg * msg,void * arg)488 static int finish_handler_wifi_config(struct nl_msg *msg, void *arg)
489 {
490 int *ret = (int *)arg;
491 struct nl_msg * a;
492
493 a = msg;
494 *ret = 0;
495 return NL_SKIP;
496 }
497
498 /*
499 * Override base class requestEvent and implement little differently here.
500 * This will send the request message.
501 * We don't wait for any response back in case of wificonfig,
502 * thus no wait for condition.
503 */
requestEvent()504 int WiFiConfigCommand::requestEvent()
505 {
506 int res = -1;
507 struct nl_cb *cb;
508
509 cb = nl_cb_alloc(NL_CB_DEFAULT);
510 if (!cb) {
511 ALOGE("%s: Callback allocation failed",__FUNCTION__);
512 res = -1;
513 goto out;
514 }
515
516 res = nl_send_auto_complete(mInfo->cmd_sock, mMsg.getMessage());
517 if (res < 0)
518 goto out;
519 res = 1;
520
521 nl_cb_err(cb, NL_CB_CUSTOM, error_handler_wifi_config, &res);
522 nl_cb_set(cb, NL_CB_FINISH, NL_CB_CUSTOM, finish_handler_wifi_config,
523 &res);
524 nl_cb_set(cb, NL_CB_ACK, NL_CB_CUSTOM, ack_handler_wifi_config, &res);
525
526 /* Err is populated as part of finish_handler. */
527 while (res > 0){
528 nl_recvmsgs(mInfo->cmd_sock, cb);
529 }
530
531 /* Only wait for the asynchronous event if HDD returns success, res=0 */
532 if (!res && (mWaitforRsp == true)) {
533 struct timespec abstime;
534 abstime.tv_sec = 4;
535 abstime.tv_nsec = 0;
536 res = mCondition.wait(abstime);
537 if (res == ETIMEDOUT)
538 {
539 ALOGE("%s: Time out happened.", __FUNCTION__);
540 }
541 ALOGV("%s: Command invoked return value:%d, mWaitForRsp=%d",
542 __FUNCTION__, res, mWaitforRsp);
543 }
544 out:
545 /* Cleanup the mMsg */
546 mMsg.destroy();
547 return res;
548 }
549
550