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 "wificonfigcommand.h"
36
37 /* 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)38 wifi_error wifi_extended_dtim_config_set(wifi_request_id id,
39 wifi_interface_handle iface,
40 int extended_dtim)
41 {
42 int ret = 0;
43 WiFiConfigCommand *wifiConfigCommand;
44 struct nlattr *nlData;
45 interface_info *ifaceInfo = getIfaceInfo(iface);
46 wifi_handle wifiHandle = getWifiHandle(iface);
47
48 ALOGV("%s: extended_dtim:%d", __FUNCTION__, extended_dtim);
49
50 wifiConfigCommand = new WiFiConfigCommand(
51 wifiHandle,
52 id,
53 OUI_QCA,
54 QCA_NL80211_VENDOR_SUBCMD_SET_WIFI_CONFIGURATION);
55
56 if (wifiConfigCommand == NULL) {
57 ALOGE("%s: Error wifiConfigCommand NULL", __FUNCTION__);
58 return WIFI_ERROR_UNKNOWN;
59 }
60
61 /* Create the NL message. */
62 ret = wifiConfigCommand->create();
63 if (ret < 0) {
64 ALOGE("wifi_extended_dtim_config_set: failed to create NL msg. "
65 "Error:%d", ret);
66 goto cleanup;
67 }
68
69 /* Set the interface Id of the message. */
70 ret = wifiConfigCommand->set_iface_id(ifaceInfo->name);
71 if (ret < 0) {
72 ALOGE("wifi_extended_dtim_config_set: failed to set iface id. "
73 "Error:%d", ret);
74 goto cleanup;
75 }
76
77 /* Add the vendor specific attributes for the NL command. */
78 nlData = wifiConfigCommand->attr_start(NL80211_ATTR_VENDOR_DATA);
79 if (!nlData) {
80 ALOGE("wifi_extended_dtim_config_set: failed attr_start for "
81 "VENDOR_DATA. Error:%d", ret);
82 goto cleanup;
83 }
84
85 if (wifiConfigCommand->put_u32(
86 QCA_WLAN_VENDOR_ATTR_WIFI_CONFIG_DYNAMIC_DTIM, extended_dtim)) {
87 ALOGE("wifi_extended_dtim_config_set(): failed to put vendor data. "
88 "Error:%d", ret);
89 goto cleanup;
90 }
91 wifiConfigCommand->attr_end(nlData);
92
93 /* Send the NL msg. */
94 wifiConfigCommand->waitForRsp(false);
95 ret = wifiConfigCommand->requestEvent();
96 if (ret != 0) {
97 ALOGE("wifi_extended_dtim_config_set(): requestEvent Error:%d", ret);
98 goto cleanup;
99 }
100
101 cleanup:
102 delete wifiConfigCommand;
103 return (wifi_error)ret;
104 }
105
106 /* Set the country code to driver. */
wifi_set_country_code(wifi_interface_handle iface,const char * country_code)107 wifi_error wifi_set_country_code(wifi_interface_handle iface,
108 const char* country_code)
109 {
110 int requestId, ret = 0;
111 WiFiConfigCommand *wifiConfigCommand;
112 wifi_handle wifiHandle = getWifiHandle(iface);
113
114 ALOGV("%s: %s", __FUNCTION__, country_code);
115
116 /* No request id from caller, so generate one and pass it on to the driver.
117 * Generate it randomly.
118 */
119 requestId = get_requestid();
120
121 wifiConfigCommand = new WiFiConfigCommand(
122 wifiHandle,
123 requestId,
124 OUI_QCA,
125 QCA_NL80211_VENDOR_SUBCMD_SET_WIFI_CONFIGURATION);
126 if (wifiConfigCommand == NULL) {
127 ALOGE("%s: Error wifiConfigCommand NULL", __FUNCTION__);
128 return WIFI_ERROR_UNKNOWN;
129 }
130
131 /* Create the NL message with NL80211_CMD_REQ_SET_REG NL cmd. */
132 ret = wifiConfigCommand->create_generic(NL80211_CMD_REQ_SET_REG);
133 if (ret < 0) {
134 ALOGE("wifi_set_country_code: failed to create NL msg. Error:%d", ret);
135 goto cleanup;
136 }
137
138 if (wifiConfigCommand->put_string(NL80211_ATTR_REG_ALPHA2, country_code)) {
139 ALOGE("wifi_set_country_code: put country code failed. Error:%d", ret);
140 goto cleanup;
141 }
142
143 /* Send the NL msg. */
144 wifiConfigCommand->waitForRsp(false);
145 ret = wifiConfigCommand->requestEvent();
146 if (ret != 0) {
147 ALOGE("wifi_set_country_code(): requestEvent Error:%d", ret);
148 goto cleanup;
149 }
150 usleep(WAIT_TIME_FOR_SET_REG_DOMAIN);
151
152 cleanup:
153 delete wifiConfigCommand;
154 return (wifi_error)ret;
155 }
156
wifi_set_beacon_wifi_iface_stats_averaging_factor(wifi_request_id id,wifi_interface_handle iface,u16 factor)157 wifi_error wifi_set_beacon_wifi_iface_stats_averaging_factor(
158 wifi_request_id id,
159 wifi_interface_handle iface,
160 u16 factor)
161 {
162 int ret = 0;
163 WiFiConfigCommand *wifiConfigCommand;
164 struct nlattr *nlData;
165 interface_info *ifaceInfo = getIfaceInfo(iface);
166 wifi_handle wifiHandle = getWifiHandle(iface);
167
168 ALOGV("%s factor:%u", __FUNCTION__, factor);
169 wifiConfigCommand = new WiFiConfigCommand(
170 wifiHandle,
171 id,
172 OUI_QCA,
173 QCA_NL80211_VENDOR_SUBCMD_SET_WIFI_CONFIGURATION);
174 if (wifiConfigCommand == NULL) {
175 ALOGE("%s: Error wifiConfigCommand NULL", __FUNCTION__);
176 return WIFI_ERROR_UNKNOWN;
177 }
178
179 /* Create the NL message. */
180 ret = wifiConfigCommand->create();
181 if (ret < 0) {
182 ALOGE("wifi_set_beacon_wifi_iface_stats_averaging_factor: failed to "
183 "create NL msg. Error:%d", ret);
184 goto cleanup;
185 }
186
187 /* Set the interface Id of the message. */
188 ret = wifiConfigCommand->set_iface_id(ifaceInfo->name);
189 if (ret < 0) {
190 ALOGE("wifi_set_beacon_wifi_iface_stats_averaging_factor: failed to "
191 "set iface id. Error:%d", ret);
192 goto cleanup;
193 }
194
195 /* Add the vendor specific attributes for the NL command. */
196 nlData = wifiConfigCommand->attr_start(NL80211_ATTR_VENDOR_DATA);
197 if (!nlData) {
198 ALOGE("wifi_set_beacon_wifi_iface_stats_averaging_factor: failed "
199 "attr_start for VENDOR_DATA. Error:%d", ret);
200 goto cleanup;
201 }
202
203 if (wifiConfigCommand->put_u32(
204 QCA_WLAN_VENDOR_ATTR_WIFI_CONFIG_STATS_AVG_FACTOR, factor)) {
205 ALOGE("wifi_set_beacon_wifi_iface_stats_averaging_factor(): failed to "
206 "put vendor data. Error:%d", ret);
207 goto cleanup;
208 }
209 wifiConfigCommand->attr_end(nlData);
210
211 /* Send the NL msg. */
212 wifiConfigCommand->waitForRsp(false);
213 ret = wifiConfigCommand->requestEvent();
214 if (ret != 0) {
215 ALOGE("wifi_set_beacon_wifi_iface_stats_averaging_factor(): "
216 "requestEvent Error:%d", ret);
217 goto cleanup;
218 }
219
220 cleanup:
221 delete wifiConfigCommand;
222 return (wifi_error)ret;
223 }
224
wifi_set_guard_time(wifi_request_id id,wifi_interface_handle iface,u32 guard_time)225 wifi_error wifi_set_guard_time(wifi_request_id id,
226 wifi_interface_handle iface,
227 u32 guard_time)
228 {
229 int ret = 0;
230 WiFiConfigCommand *wifiConfigCommand;
231 struct nlattr *nlData;
232 interface_info *ifaceInfo = getIfaceInfo(iface);
233 wifi_handle wifiHandle = getWifiHandle(iface);
234
235 ALOGV("%s : guard_time:%u", __FUNCTION__, guard_time);
236
237 wifiConfigCommand = new WiFiConfigCommand(
238 wifiHandle,
239 id,
240 OUI_QCA,
241 QCA_NL80211_VENDOR_SUBCMD_SET_WIFI_CONFIGURATION);
242 if (wifiConfigCommand == NULL) {
243 ALOGE("%s: Error wifiConfigCommand NULL", __FUNCTION__);
244 return WIFI_ERROR_UNKNOWN;
245 }
246
247 /* Create the NL message. */
248 ret = wifiConfigCommand->create();
249 if (ret < 0) {
250 ALOGE("wifi_set_guard_time: failed to create NL msg. Error:%d", ret);
251 goto cleanup;
252 }
253
254 /* Set the interface Id of the message. */
255 ret = wifiConfigCommand->set_iface_id(ifaceInfo->name);
256 if (ret < 0) {
257 ALOGE("wifi_set_guard_time: failed to set iface id. Error:%d", ret);
258 goto cleanup;
259 }
260
261 /* Add the vendor specific attributes for the NL command. */
262 nlData = wifiConfigCommand->attr_start(NL80211_ATTR_VENDOR_DATA);
263 if (!nlData) {
264 ALOGE("wifi_set_guard_time: failed attr_start for VENDOR_DATA. "
265 "Error:%d", ret);
266 goto cleanup;
267 }
268
269 if (wifiConfigCommand->put_u32(
270 QCA_WLAN_VENDOR_ATTR_WIFI_CONFIG_GUARD_TIME, guard_time)) {
271 ALOGE("wifi_set_guard_time: failed to add vendor data.");
272 goto cleanup;
273 }
274 wifiConfigCommand->attr_end(nlData);
275
276 /* Send the NL msg. */
277 wifiConfigCommand->waitForRsp(false);
278 ret = wifiConfigCommand->requestEvent();
279 if (ret != 0) {
280 ALOGE("wifi_set_guard_time(): requestEvent Error:%d", ret);
281 goto cleanup;
282 }
283
284 cleanup:
285 delete wifiConfigCommand;
286 return (wifi_error)ret;
287 }
288
WiFiConfigCommand(wifi_handle handle,int id,u32 vendor_id,u32 subcmd)289 WiFiConfigCommand::WiFiConfigCommand(wifi_handle handle,
290 int id, u32 vendor_id,
291 u32 subcmd)
292 : WifiVendorCommand(handle, id, vendor_id, subcmd)
293 {
294 /* Initialize the member data variables here */
295 mWaitforRsp = false;
296 mRequestId = id;
297 }
298
~WiFiConfigCommand()299 WiFiConfigCommand::~WiFiConfigCommand()
300 {
301 unregisterVendorHandler(mVendor_id, mSubcmd);
302 }
303
304 /* This function implements creation of Vendor command */
create()305 int WiFiConfigCommand::create() {
306 int ret = mMsg.create(NL80211_CMD_VENDOR, 0, 0);
307 if (ret < 0) {
308 return ret;
309 }
310
311 /* Insert the oui in the msg */
312 ret = mMsg.put_u32(NL80211_ATTR_VENDOR_ID, mVendor_id);
313 if (ret < 0)
314 goto out;
315 /* Insert the subcmd in the msg */
316 ret = mMsg.put_u32(NL80211_ATTR_VENDOR_SUBCMD, mSubcmd);
317 if (ret < 0)
318 goto out;
319 out:
320 return ret;
321 }
322
323 /* This function implements creation of generic NL command */
create_generic(u8 cmdId)324 int WiFiConfigCommand::create_generic(u8 cmdId) {
325 int ret = mMsg.create(cmdId, 0, 0);
326 return ret;
327 }
328
waitForRsp(bool wait)329 void WiFiConfigCommand::waitForRsp(bool wait)
330 {
331 mWaitforRsp = wait;
332 }
333
334 /* Callback handlers registered for nl message send */
error_handler_wifi_config(struct sockaddr_nl * nla,struct nlmsgerr * err,void * arg)335 static int error_handler_wifi_config(struct sockaddr_nl *nla,
336 struct nlmsgerr *err,
337 void *arg)
338 {
339 struct sockaddr_nl *tmp;
340 int *ret = (int *)arg;
341 tmp = nla;
342 *ret = err->error;
343 ALOGE("%s: Error code:%d (%s)", __FUNCTION__, *ret, strerror(-(*ret)));
344 return NL_STOP;
345 }
346
347 /* Callback handlers registered for nl message send */
ack_handler_wifi_config(struct nl_msg * msg,void * arg)348 static int ack_handler_wifi_config(struct nl_msg *msg, void *arg)
349 {
350 int *ret = (int *)arg;
351 struct nl_msg * a;
352
353 a = msg;
354 *ret = 0;
355 return NL_STOP;
356 }
357
358 /* Callback handlers registered for nl message send */
finish_handler_wifi_config(struct nl_msg * msg,void * arg)359 static int finish_handler_wifi_config(struct nl_msg *msg, void *arg)
360 {
361 int *ret = (int *)arg;
362 struct nl_msg * a;
363
364 a = msg;
365 *ret = 0;
366 return NL_SKIP;
367 }
368
369 /*
370 * Override base class requestEvent and implement little differently here.
371 * This will send the request message.
372 * We don't wait for any response back in case of wificonfig,
373 * thus no wait for condition.
374 */
requestEvent()375 int WiFiConfigCommand::requestEvent()
376 {
377 int res = -1;
378 struct nl_cb *cb;
379
380 cb = nl_cb_alloc(NL_CB_DEFAULT);
381 if (!cb) {
382 ALOGE("%s: Callback allocation failed",__FUNCTION__);
383 res = -1;
384 goto out;
385 }
386
387 res = nl_send_auto_complete(mInfo->cmd_sock, mMsg.getMessage());
388 if (res < 0)
389 goto out;
390 res = 1;
391
392 nl_cb_err(cb, NL_CB_CUSTOM, error_handler_wifi_config, &res);
393 nl_cb_set(cb, NL_CB_FINISH, NL_CB_CUSTOM, finish_handler_wifi_config,
394 &res);
395 nl_cb_set(cb, NL_CB_ACK, NL_CB_CUSTOM, ack_handler_wifi_config, &res);
396
397 /* Err is populated as part of finish_handler. */
398 while (res > 0){
399 nl_recvmsgs(mInfo->cmd_sock, cb);
400 }
401
402 /* Only wait for the asynchronous event if HDD returns success, res=0 */
403 if (!res && (mWaitforRsp == true)) {
404 struct timespec abstime;
405 abstime.tv_sec = 4;
406 abstime.tv_nsec = 0;
407 res = mCondition.wait(abstime);
408 if (res == ETIMEDOUT)
409 {
410 ALOGE("%s: Time out happened.", __FUNCTION__);
411 }
412 ALOGV("%s: Command invoked return value:%d, mWaitForRsp=%d",
413 __FUNCTION__, res, mWaitforRsp);
414 }
415 out:
416 /* Cleanup the mMsg */
417 mMsg.destroy();
418 return res;
419 }
420
421