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 */
38wifi_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
101cleanup:
102    delete wifiConfigCommand;
103    return (wifi_error)ret;
104}
105
106/* Set the country code to driver. */
107wifi_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
152cleanup:
153    delete wifiConfigCommand;
154    return (wifi_error)ret;
155}
156
157wifi_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
220cleanup:
221    delete wifiConfigCommand;
222    return (wifi_error)ret;
223}
224
225wifi_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
284cleanup:
285    delete wifiConfigCommand;
286    return (wifi_error)ret;
287}
288
289WiFiConfigCommand::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
299WiFiConfigCommand::~WiFiConfigCommand()
300{
301    unregisterVendorHandler(mVendor_id, mSubcmd);
302}
303
304/* This function implements creation of Vendor command */
305int 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;
319out:
320    return ret;
321}
322
323/* This function implements creation of generic NL command */
324int WiFiConfigCommand::create_generic(u8 cmdId) {
325    int ret = mMsg.create(cmdId, 0, 0);
326    return ret;
327}
328
329void WiFiConfigCommand::waitForRsp(bool wait)
330{
331    mWaitforRsp = wait;
332}
333
334/* Callback handlers registered for nl message send */
335static 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 */
348static 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 */
359static 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 */
375int 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    }
415out:
416    /* Cleanup the mMsg */
417    mMsg.destroy();
418    return res;
419}
420
421