1 /* Copyright (c) 2014, 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 copyright
9  *    notice, this list of conditions and the following disclaimer in
10  *    the documentation and/or other materials provided with the
11  *    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 
35 #include "ifaceeventhandler.h"
36 #include "common.h"
37 
38 /* Used to handle NL command events from driver/firmware. */
39 IfaceEventHandlerCommand *mwifiEventHandler = NULL;
40 
41 /* Set the interface event monitor handler*/
wifi_set_iface_event_handler(wifi_request_id id,wifi_interface_handle iface,wifi_event_handler eh)42 wifi_error wifi_set_iface_event_handler(wifi_request_id id,
43                                         wifi_interface_handle iface,
44                                         wifi_event_handler eh)
45 {
46     wifi_handle wifiHandle = getWifiHandle(iface);
47 
48     /* Check if a similar request to set iface event handler was made earlier.
49      * Right now we don't differentiate between the case where (i) the new
50      * Request Id is different from the current one vs (ii) both new and
51      * Request Ids are the same.
52      */
53     if (mwifiEventHandler)
54     {
55         if (id == mwifiEventHandler->get_request_id()) {
56             ALOGE("%s: Iface Event Handler Set for request Id %d is still"
57                 "running. Exit", __func__, id);
58             return WIFI_ERROR_TOO_MANY_REQUESTS;
59         } else {
60             ALOGE("%s: Iface Event Handler Set for a different Request "
61                 "Id:%d is requested. Not supported. Exit", __func__, id);
62             return WIFI_ERROR_NOT_SUPPORTED;
63         }
64     }
65 
66     mwifiEventHandler = new IfaceEventHandlerCommand(
67                     wifiHandle,
68                     id,
69                     NL80211_CMD_REG_CHANGE);
70     if (mwifiEventHandler == NULL) {
71         ALOGE("%s: Error mwifiEventHandler NULL", __func__);
72         return WIFI_ERROR_UNKNOWN;
73     }
74     mwifiEventHandler->setCallbackHandler(eh);
75 
76     return WIFI_SUCCESS;
77 }
78 
79 /* Reset monitoring for the NL event*/
wifi_reset_iface_event_handler(wifi_request_id id,wifi_interface_handle iface)80 wifi_error wifi_reset_iface_event_handler(wifi_request_id id,
81                                           wifi_interface_handle iface)
82 {
83     if (mwifiEventHandler)
84     {
85         if (id == mwifiEventHandler->get_request_id()) {
86             ALOGV("Delete Object mwifiEventHandler for id = %d", id);
87             delete mwifiEventHandler;
88             mwifiEventHandler = NULL;
89         } else {
90             ALOGE("%s: Iface Event Handler Set for a different Request "
91                 "Id:%d is requested. Not supported. Exit", __func__, id);
92             return WIFI_ERROR_NOT_SUPPORTED;
93         }
94     } else {
95         ALOGV("Object mwifiEventHandler for id = %d already Deleted", id);
96     }
97 
98     return WIFI_SUCCESS;
99 }
100 
101 /* This function will be the main handler for the registered incoming
102  * (from driver) Commads. Calls the appropriate callback handler after
103  * parsing the vendor data.
104  */
handleEvent(WifiEvent & event)105 int IfaceEventHandlerCommand::handleEvent(WifiEvent &event)
106 {
107     wifiEventHandler::handleEvent(event);
108 
109     switch(mSubcmd)
110     {
111         case NL80211_CMD_REG_CHANGE:
112         {
113             char code[2];
114             memset(&code[0], 0, 2);
115             if(tb[NL80211_ATTR_REG_ALPHA2])
116             {
117                 memcpy(&code[0], (char *) nla_data(tb[NL80211_ATTR_REG_ALPHA2]), 2);
118             } else {
119                 ALOGE("%s: NL80211_ATTR_REG_ALPHA2 not found", __func__);
120             }
121             ALOGV("Country : %c%c", code[0], code[1]);
122             if(mHandler.on_country_code_changed)
123             {
124                 mHandler.on_country_code_changed(code);
125             }
126         }
127         break;
128         default:
129             ALOGV("NL Event : %d Not supported", mSubcmd);
130     }
131 
132     return NL_SKIP;
133 }
134 
IfaceEventHandlerCommand(wifi_handle handle,int id,u32 subcmd)135 IfaceEventHandlerCommand::IfaceEventHandlerCommand(wifi_handle handle, int id, u32 subcmd)
136         : wifiEventHandler(handle, id, subcmd)
137 {
138     ALOGV("wifiEventHandler %p constructed", this);
139     registerHandler(mSubcmd);
140     memset(&mHandler, 0, sizeof(wifi_event_handler));
141     mEventData = NULL;
142     mDataLen = 0;
143 }
144 
~IfaceEventHandlerCommand()145 IfaceEventHandlerCommand::~IfaceEventHandlerCommand()
146 {
147     ALOGV("IfaceEventHandlerCommand %p destructor", this);
148     unregisterHandler(mSubcmd);
149 }
150 
setCallbackHandler(wifi_event_handler nHandler)151 void IfaceEventHandlerCommand::setCallbackHandler(wifi_event_handler nHandler)
152 {
153     mHandler = nHandler;
154 }
155 
get_request_id()156 int wifiEventHandler::get_request_id()
157 {
158     return mRequestId;
159 }
160 
get_request_id()161 int IfaceEventHandlerCommand::get_request_id()
162 {
163     return wifiEventHandler::get_request_id();
164 }
165 
wifiEventHandler(wifi_handle handle,int id,u32 subcmd)166 wifiEventHandler::wifiEventHandler(wifi_handle handle, int id, u32 subcmd)
167         : WifiCommand(handle, id)
168 {
169     mRequestId = id;
170     mSubcmd = subcmd;
171     registerHandler(mSubcmd);
172     ALOGV("wifiEventHandler %p constructed", this);
173 }
174 
~wifiEventHandler()175 wifiEventHandler::~wifiEventHandler()
176 {
177     ALOGV("wifiEventHandler %p destructor", this);
178     unregisterHandler(mSubcmd);
179 }
180 
handleEvent(WifiEvent & event)181 int wifiEventHandler::handleEvent(WifiEvent &event)
182 {
183     struct genlmsghdr *gnlh = event.header();
184     mSubcmd = gnlh->cmd;
185     nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
186             genlmsg_attrlen(gnlh, 0), NULL);
187     ALOGV("Got NL Event : %d from the Driver.", gnlh->cmd);
188 
189     return NL_SKIP;
190 }
191 
WifihalGeneric(wifi_handle handle,int id,u32 vendor_id,u32 subcmd)192 WifihalGeneric::WifihalGeneric(wifi_handle handle, int id, u32 vendor_id,
193                                   u32 subcmd)
194         : WifiVendorCommand(handle, id, vendor_id, subcmd)
195 {
196     hal_info *info = getHalInfo(handle);
197 
198     /* Initialize the member data variables here */
199     mSet = 0;
200     mSetSizeMax = 0;
201     mSetSizePtr = NULL;
202     mConcurrencySet = 0;
203     filterVersion = 0;
204     filterLength = 0;
205     firmware_bus_max_size = 0;
206     mCapa = &(info->capa);
207     mfilter_packet_read_buffer = NULL;
208     mfilter_packet_length = 0;
209     res_size = 0;
210     channel_buff = NULL;
211     memset(&mDriverFeatures, 0, sizeof(mDriverFeatures));
212     memset(&mRadarResultParams, 0, sizeof(RadarHistoryResultsParams));
213 }
214 
~WifihalGeneric()215 WifihalGeneric::~WifihalGeneric()
216 {
217     mCapa = NULL;
218     if (mDriverFeatures.flags != NULL) {
219         free(mDriverFeatures.flags);
220         mDriverFeatures.flags = NULL;
221     }
222 }
223 
requestResponse()224 wifi_error WifihalGeneric::requestResponse()
225 {
226     return WifiCommand::requestResponse(mMsg);
227 }
228 
get_wifi_iftype_masks(u32 in_mask)229 static u32 get_wifi_iftype_masks(u32 in_mask)
230 {
231     u32 op_mask = 0;
232 
233     if (in_mask & BIT(NL80211_IFTYPE_STATION)) {
234         op_mask |= BIT(WIFI_INTERFACE_STA);
235         op_mask |= BIT(WIFI_INTERFACE_TDLS);
236     }
237     if (in_mask & BIT(NL80211_IFTYPE_AP))
238         op_mask |= BIT(WIFI_INTERFACE_SOFTAP);
239     if (in_mask & BIT(NL80211_IFTYPE_P2P_CLIENT))
240         op_mask |= BIT(WIFI_INTERFACE_P2P_CLIENT);
241     if (in_mask & BIT(NL80211_IFTYPE_P2P_GO))
242         op_mask |= BIT(WIFI_INTERFACE_P2P_GO);
243     if (in_mask & BIT(NL80211_IFTYPE_NAN))
244         op_mask |= BIT(WIFI_INTERFACE_NAN);
245 
246     return op_mask;
247 }
248 
get_channel_width(u32 nl_width)249 static wifi_channel_width get_channel_width(u32 nl_width)
250 {
251     switch(nl_width) {
252     case NL80211_CHAN_WIDTH_20:
253          return WIFI_CHAN_WIDTH_20;
254     case NL80211_CHAN_WIDTH_40:
255          return WIFI_CHAN_WIDTH_40;
256     case NL80211_CHAN_WIDTH_80:
257          return WIFI_CHAN_WIDTH_80;
258     case NL80211_CHAN_WIDTH_160:
259          return WIFI_CHAN_WIDTH_160;
260     case NL80211_CHAN_WIDTH_80P80:
261          return WIFI_CHAN_WIDTH_80P80;
262     case NL80211_CHAN_WIDTH_5:
263          return WIFI_CHAN_WIDTH_5;
264     case NL80211_CHAN_WIDTH_10:
265          return WIFI_CHAN_WIDTH_10;
266     default:
267          return WIFI_CHAN_WIDTH_INVALID;
268     }
269 }
270 
handle_response_usable_channels(struct nlattr * VendorData,u32 mDataLen)271 int WifihalGeneric::handle_response_usable_channels(struct nlattr *VendorData,
272                                                     u32 mDataLen)
273 {
274     struct nlattr *tb[QCA_WLAN_VENDOR_ATTR_USABLE_CHANNELS_MAX + 1];
275     struct nlattr *curr_attr;
276     wifi_usable_channel *chan_info = NULL;
277     int rem;
278     u32 currSize = 0;
279 
280     if (nla_parse(tb, QCA_WLAN_VENDOR_ATTR_USABLE_CHANNELS_MAX,
281                   (struct nlattr *)mVendorData, mDataLen, NULL)) {
282          ALOGE("Failed to parse NL channels list");
283          return WIFI_ERROR_INVALID_ARGS;
284     }
285 
286     if (!tb[QCA_WLAN_VENDOR_ATTR_USABLE_CHANNELS_CHAN_INFO]) {
287          ALOGE("%s: USABLE_CHANNELS_CHAN_INFO not found", __FUNCTION__);
288          return WIFI_ERROR_INVALID_ARGS;
289     }
290 
291     for_each_nested_attribute(curr_attr,
292                      tb[QCA_WLAN_VENDOR_ATTR_USABLE_CHANNELS_CHAN_INFO], rem) {
293          struct nlattr *ch_info[QCA_WLAN_VENDOR_ATTR_CHAN_INFO_MAX + 1];
294 
295          if (currSize >= mSetSizeMax) {
296               ALOGE("Got max channels %d completed", mSetSizeMax);
297               break;
298          }
299 
300          if (nla_parse_nested(ch_info, QCA_WLAN_VENDOR_ATTR_CHAN_INFO_MAX,
301                               curr_attr, NULL)) {
302               ALOGE("Failed to get usable channel info");
303               return NL_SKIP;
304          }
305 
306          chan_info = &channel_buff[currSize];
307          if (!ch_info[QCA_WLAN_VENDOR_ATTR_CHAN_INFO_PRIMARY_FREQ]) {
308               ALOGE("%s: CHAN_INFO_PRIMARY_FREQ not found",
309                     __FUNCTION__);
310               return NL_SKIP;
311          }
312 
313          chan_info->freq = nla_get_u32(ch_info[
314                                   QCA_WLAN_VENDOR_ATTR_CHAN_INFO_PRIMARY_FREQ]);
315          if (!ch_info[QCA_WLAN_VENDOR_ATTR_CHAN_INFO_BANDWIDTH]) {
316               ALOGE("%s: CHAN_INFO_BANDWIDTH not found",
317                     __FUNCTION__);
318               return NL_SKIP;
319          }
320 
321          chan_info->width = get_channel_width(nla_get_u32(
322                             ch_info[QCA_WLAN_VENDOR_ATTR_CHAN_INFO_BANDWIDTH]));
323          if (!ch_info[QCA_WLAN_VENDOR_ATTR_CHAN_INFO_IFACE_MODE_MASK]) {
324               ALOGE("%s: CHAN_INFO_IFACE_MODE_MASK not found",
325                     __FUNCTION__);
326               return NL_SKIP;
327          }
328 
329          chan_info->iface_mode_mask = get_wifi_iftype_masks(nla_get_u32(
330                       ch_info[QCA_WLAN_VENDOR_ATTR_CHAN_INFO_IFACE_MODE_MASK]));
331          ALOGV("Primary freq %d BW %d iface mask %d", chan_info->freq,
332                chan_info->width, chan_info->iface_mode_mask);
333          currSize++;
334     }
335 
336     res_size = currSize;
337     ALOGV("%s: Result size %d", __FUNCTION__, res_size);
338 
339     return NL_SKIP;
340 }
341 
handleResponse(WifiEvent & reply)342 int WifihalGeneric::handleResponse(WifiEvent &reply)
343 {
344     ALOGV("Got a Wi-Fi HAL module message from Driver");
345     int i = 0;
346     WifiVendorCommand::handleResponse(reply);
347 
348     // Parse the vendordata and get the attribute
349     switch(mSubcmd)
350     {
351         case QCA_NL80211_VENDOR_SUBCMD_GET_SUPPORTED_FEATURES:
352             {
353                 struct nlattr *tb_vendor[QCA_WLAN_VENDOR_ATTR_FEATURE_SET_MAX + 1];
354                 nla_parse(tb_vendor, QCA_WLAN_VENDOR_ATTR_FEATURE_SET_MAX,
355                         (struct nlattr *)mVendorData,
356                         mDataLen, NULL);
357 
358                 if (!tb_vendor[QCA_WLAN_VENDOR_ATTR_FEATURE_SET])
359                 {
360                     ALOGE("%s: QCA_WLAN_VENDOR_ATTR_FEATURE_SET not found", __func__);
361                     return -EINVAL;
362                 }
363                 mSet = nla_get_u32(tb_vendor[QCA_WLAN_VENDOR_ATTR_FEATURE_SET]);
364                 ALOGV("Supported feature set : %" PRIx64, mSet);
365 
366                 break;
367             }
368         case QCA_NL80211_VENDOR_SUBCMD_GET_FEATURES:
369             {
370                 struct nlattr *attr;
371                 struct nlattr *tb_vendor[QCA_WLAN_VENDOR_ATTR_MAX + 1];
372                 nla_parse(tb_vendor, QCA_WLAN_VENDOR_ATTR_MAX,
373                           (struct nlattr *)mVendorData, mDataLen, NULL);
374                 attr = tb_vendor[QCA_WLAN_VENDOR_ATTR_FEATURE_FLAGS];
375                 if (attr) {
376                     int len = nla_len(attr);
377                     mDriverFeatures.flags = (u8 *)malloc(len);
378                     if (mDriverFeatures.flags != NULL) {
379                         memcpy(mDriverFeatures.flags, nla_data(attr), len);
380                         mDriverFeatures.flags_len = len;
381                     }
382                  }
383                  break;
384             }
385         case QCA_NL80211_VENDOR_SUBCMD_GET_CONCURRENCY_MATRIX:
386             {
387                 struct nlattr *tb_vendor[
388                     QCA_WLAN_VENDOR_ATTR_GET_CONCURRENCY_MATRIX_MAX + 1];
389                 nla_parse(tb_vendor,
390                     QCA_WLAN_VENDOR_ATTR_GET_CONCURRENCY_MATRIX_MAX,
391                     (struct nlattr *)mVendorData,mDataLen, NULL);
392 
393                 if (tb_vendor[
394                     QCA_WLAN_VENDOR_ATTR_GET_CONCURRENCY_MATRIX_RESULTS_SET_SIZE]) {
395                     u32 val;
396                     val = nla_get_u32(
397                         tb_vendor[
398                     QCA_WLAN_VENDOR_ATTR_GET_CONCURRENCY_MATRIX_RESULTS_SET_SIZE]);
399 
400                     ALOGV("%s: Num of concurrency combinations: %d",
401                         __func__, val);
402                     val = val > (unsigned int)mSetSizeMax ?
403                           (unsigned int)mSetSizeMax : val;
404                     *mSetSizePtr = val;
405 
406                     /* Extract the list of channels. */
407                     if (*mSetSizePtr > 0 &&
408                         tb_vendor[
409                         QCA_WLAN_VENDOR_ATTR_GET_CONCURRENCY_MATRIX_RESULTS_SET]) {
410                         nla_memcpy(mConcurrencySet,
411                             tb_vendor[
412                         QCA_WLAN_VENDOR_ATTR_GET_CONCURRENCY_MATRIX_RESULTS_SET],
413                             sizeof(feature_set) * (*mSetSizePtr));
414                     }
415 
416                     ALOGV("%s: Get concurrency matrix response received.",
417                         __func__);
418                     ALOGV("%s: Num of concurrency combinations : %d",
419                         __func__, *mSetSizePtr);
420                     ALOGV("%s: List of valid concurrency combinations is: ",
421                         __func__);
422                     for(i = 0; i < *mSetSizePtr; i++)
423                     {
424                         ALOGV("%" PRIx64, *(mConcurrencySet + i));
425                     }
426                 }
427             }
428             break;
429         case QCA_NL80211_VENDOR_SUBCMD_PACKET_FILTER:
430             {
431                 int subCmd;
432                 struct nlattr *tb_vendor[
433                         QCA_WLAN_VENDOR_ATTR_PACKET_FILTER_MAX + 1];
434                 nla_parse(tb_vendor, QCA_WLAN_VENDOR_ATTR_PACKET_FILTER_MAX,
435                         (struct nlattr *)mVendorData,
436                         mDataLen, NULL);
437 
438                 if (tb_vendor[QCA_WLAN_VENDOR_ATTR_PACKET_FILTER_SUB_CMD])
439                 {
440                     subCmd = nla_get_u32(
441                            tb_vendor[QCA_WLAN_VENDOR_ATTR_PACKET_FILTER_SUB_CMD]);
442                 } else {
443                     /*
444                      * The older drivers may not send PACKET_FILTER_SUB_CMD as
445                      * they support QCA_WLAN_GET_PACKET_FILTER only.
446                      */
447                     subCmd = QCA_WLAN_GET_PACKET_FILTER;
448                 }
449                 if (subCmd == QCA_WLAN_GET_PACKET_FILTER) {
450                     if (!tb_vendor[QCA_WLAN_VENDOR_ATTR_PACKET_FILTER_VERSION])
451                     {
452                         ALOGE("%s: QCA_WLAN_VENDOR_ATTR_PACKET_FILTER_VERSION"
453                               " not found", __FUNCTION__);
454                         return -EINVAL;
455                     }
456                     filterVersion = nla_get_u32(
457                            tb_vendor[QCA_WLAN_VENDOR_ATTR_PACKET_FILTER_VERSION]);
458                     ALOGV("Current version : %u", filterVersion);
459 
460                     if (!tb_vendor[QCA_WLAN_VENDOR_ATTR_PACKET_FILTER_SIZE])
461                     {
462                         ALOGE("%s: QCA_WLAN_VENDOR_ATTR_PACKET_FILTER_SIZE"
463                               " not found", __FUNCTION__);
464                         return -EINVAL;
465                     }
466                     filterLength = nla_get_u32(
467                         tb_vendor[QCA_WLAN_VENDOR_ATTR_PACKET_FILTER_SIZE]);
468                     ALOGV("Max filter length Supported : %u", filterLength);
469                 } else if (subCmd == QCA_WLAN_READ_PACKET_FILTER) {
470 
471                    if (!tb_vendor[QCA_WLAN_VENDOR_ATTR_PACKET_FILTER_PROGRAM])
472                    {
473                        ALOGE("%s: QCA_WLAN_VENDOR_ATTR_PACKET_FILTER_PROGRAM"
474                              " not found", __FUNCTION__);
475                        return -EINVAL;
476                    }
477                    if (nla_len(tb_vendor[QCA_WLAN_VENDOR_ATTR_PACKET_FILTER_PROGRAM])
478                            < mfilter_packet_length)
479                    {
480                        ALOGE("%s: Expected packet filter length :%d but received only: %d bytes",
481                              __FUNCTION__, mfilter_packet_length,
482                              nla_len(tb_vendor[QCA_WLAN_VENDOR_ATTR_PACKET_FILTER_PROGRAM]));
483                        return -EINVAL;
484                    }
485                    memcpy(mfilter_packet_read_buffer,
486                       nla_data(tb_vendor[QCA_WLAN_VENDOR_ATTR_PACKET_FILTER_PROGRAM]),
487                       mfilter_packet_length);
488                    ALOGV("Filter Program length : %u", mfilter_packet_length);
489                 } else {
490                        ALOGE("%s: Unknown APF sub command received",
491                              __FUNCTION__);
492                        return -EINVAL;
493                 }
494 
495             }
496             break;
497         case QCA_NL80211_VENDOR_SUBCMD_GET_BUS_SIZE:
498             {
499                 struct nlattr *tb_vendor[
500                         QCA_WLAN_VENDOR_ATTR_DRV_INFO_MAX + 1];
501                 nla_parse(tb_vendor, QCA_WLAN_VENDOR_ATTR_DRV_INFO_MAX,
502                           (struct nlattr *)mVendorData, mDataLen, NULL);
503 
504                 if (!tb_vendor[QCA_WLAN_VENDOR_ATTR_DRV_INFO_BUS_SIZE])
505                 {
506                     ALOGE("%s: QCA_WLAN_VENDOR_ATTR_DRV_INFO_BUS_SIZE"
507                           " not found", __FUNCTION__);
508                     return -EINVAL;
509                 }
510                 firmware_bus_max_size = nla_get_u32(
511                        tb_vendor[QCA_WLAN_VENDOR_ATTR_DRV_INFO_BUS_SIZE]);
512                 ALOGV("Max BUS size Supported: %d", firmware_bus_max_size);
513             }
514             break;
515         case QCA_NL80211_VENDOR_SUBCMD_GSCAN_GET_CAPABILITIES:
516             {
517                 struct nlattr *tbVendor[QCA_WLAN_VENDOR_ATTR_GSCAN_RESULTS_MAX + 1];
518                 nla_parse(tbVendor, QCA_WLAN_VENDOR_ATTR_GSCAN_RESULTS_MAX,
519                           (struct nlattr *)mVendorData,mDataLen, NULL);
520 
521                 if (wifiParseCapabilities(tbVendor) == WIFI_SUCCESS) {
522                     ALOGV("%s: GSCAN Capabilities:\n"
523                           "     max_ap_cache_per_scan:%d\n"
524                           "     max_bssid_history_entries:%d\n"
525                           "     max_hotlist_bssids:%d\n"
526                           "     max_hotlist_ssids:%d\n"
527                           "     max_rssi_sample_size:%d\n"
528                           "     max_scan_buckets:%d\n"
529                           "     max_scan_cache_size:%d\n"
530                           "     max_scan_reporting_threshold:%d\n"
531                           "     max_significant_wifi_change_aps:%d\n"
532                           "     max_number_epno_networks:%d\n"
533                           "     max_number_epno_networks_by_ssid:%d\n"
534                           "     max_number_of_white_listed_ssid:%d.",
535                           __FUNCTION__, mCapa->gscan_capa.max_ap_cache_per_scan,
536                           mCapa->gscan_capa.max_bssid_history_entries,
537                           mCapa->gscan_capa.max_hotlist_bssids,
538                           mCapa->gscan_capa.max_hotlist_ssids,
539                           mCapa->gscan_capa.max_rssi_sample_size,
540                           mCapa->gscan_capa.max_scan_buckets,
541                           mCapa->gscan_capa.max_scan_cache_size,
542                           mCapa->gscan_capa.max_scan_reporting_threshold,
543                           mCapa->gscan_capa.max_significant_wifi_change_aps,
544                           mCapa->gscan_capa.max_number_epno_networks,
545                           mCapa->gscan_capa.max_number_epno_networks_by_ssid,
546                           mCapa->gscan_capa.max_number_of_white_listed_ssid);
547 
548                     ALOGV("%s: Roaming Capabilities:\n"
549                           "    max_blacklist_size: %d\n"
550                           "    max_whitelist_size: %d\n",
551                           __FUNCTION__, mCapa->roaming_capa.max_blacklist_size,
552                           mCapa->roaming_capa.max_whitelist_size);
553                 }
554             }
555             break;
556         case QCA_NL80211_VENDOR_SUBCMD_USABLE_CHANNELS:
557             return handle_response_usable_channels((struct nlattr *)mVendorData,
558                                                    mDataLen);
559         case QCA_NL80211_VENDOR_SUBCMD_GET_RADAR_HISTORY:
560             {
561                 wifiParseRadarHistory();
562             }
563             break;
564         case QCA_NL80211_VENDOR_SUBCMD_GET_SAR_CAPABILITY:
565             {
566                 struct nlattr *tb_vendor[
567                         QCA_WLAN_VENDOR_ATTR_SAR_CAPABILITY_MAX + 1];
568                 nla_parse(tb_vendor, QCA_WLAN_VENDOR_ATTR_SAR_CAPABILITY_MAX,
569                           (struct nlattr *)mVendorData,mDataLen, NULL);
570 
571                 if (tb_vendor[QCA_WLAN_VENDOR_ATTR_SAR_CAPABILITY_VERSION])
572                 {
573                     mInfo->sar_version = (qca_wlan_vendor_sar_version) nla_get_u32(tb_vendor[
574                                                QCA_WLAN_VENDOR_ATTR_SAR_CAPABILITY_VERSION]);
575                 }
576                 ALOGV("%s: sar_version return %d", __func__, mInfo->sar_version);
577             }
578             break;
579         default :
580             ALOGE("%s: Wrong Wi-Fi HAL event received %d", __func__, mSubcmd);
581     }
582     return NL_SKIP;
583 }
584 
585 /* Parses and extract capabilities results. */
wifiParseCapabilities(struct nlattr ** tbVendor)586 wifi_error WifihalGeneric::wifiParseCapabilities(struct nlattr **tbVendor)
587 {
588     if (!tbVendor[QCA_WLAN_VENDOR_ATTR_GSCAN_RESULTS_CAPABILITIES_MAX_SCAN_CACHE_SIZE]) {
589         ALOGE("%s: QCA_WLAN_VENDOR_ATTR_GSCAN_RESULTS_CAPABILITIES_MAX_SCAN_CACHE_SIZE not found",
590               __FUNCTION__);
591         return WIFI_ERROR_INVALID_ARGS;
592     }
593     mCapa->gscan_capa.max_scan_cache_size = nla_get_u32(tbVendor[
594                               QCA_WLAN_VENDOR_ATTR_GSCAN_RESULTS_CAPABILITIES_MAX_SCAN_CACHE_SIZE]);
595 
596     if (!tbVendor[QCA_WLAN_VENDOR_ATTR_GSCAN_RESULTS_CAPABILITIES_MAX_SCAN_BUCKETS]) {
597         ALOGE("%s: QCA_WLAN_VENDOR_ATTR_GSCAN_RESULTS_CAPABILITIES_MAX_SCAN_BUCKETS not found",
598               __FUNCTION__);
599         return WIFI_ERROR_INVALID_ARGS;
600     }
601     mCapa->gscan_capa.max_scan_buckets = nla_get_u32(tbVendor[
602                                  QCA_WLAN_VENDOR_ATTR_GSCAN_RESULTS_CAPABILITIES_MAX_SCAN_BUCKETS]);
603 
604     if (!tbVendor[QCA_WLAN_VENDOR_ATTR_GSCAN_RESULTS_CAPABILITIES_MAX_AP_CACHE_PER_SCAN]) {
605         ALOGE("%s: QCA_WLAN_VENDOR_ATTR_GSCAN_RESULTS_CAPABILITIES_MAX_AP_CACHE_PER_SCAN not found",
606               __FUNCTION__);
607         return WIFI_ERROR_INVALID_ARGS;
608     }
609     mCapa->gscan_capa.max_ap_cache_per_scan = nla_get_u32(tbVendor[
610                             QCA_WLAN_VENDOR_ATTR_GSCAN_RESULTS_CAPABILITIES_MAX_AP_CACHE_PER_SCAN]);
611 
612     if (!tbVendor[QCA_WLAN_VENDOR_ATTR_GSCAN_RESULTS_CAPABILITIES_MAX_RSSI_SAMPLE_SIZE]) {
613         ALOGE("%s: QCA_WLAN_VENDOR_ATTR_GSCAN_RESULTS_CAPABILITIES_MAX_RSSI_SAMPLE_SIZE not found",
614               __FUNCTION__);
615         return WIFI_ERROR_INVALID_ARGS;
616     }
617     mCapa->gscan_capa.max_rssi_sample_size = nla_get_u32(tbVendor[
618                              QCA_WLAN_VENDOR_ATTR_GSCAN_RESULTS_CAPABILITIES_MAX_RSSI_SAMPLE_SIZE]);
619 
620     if (!tbVendor[QCA_WLAN_VENDOR_ATTR_GSCAN_RESULTS_CAPABILITIES_MAX_SCAN_REPORTING_THRESHOLD]) {
621         ALOGE("%s: QCA_WLAN_VENDOR_ATTR_GSCAN_RESULTS_CAPABILITIES_MAX_SCAN_REPORTING_THRESHOLD not"
622               " found", __FUNCTION__);
623         return WIFI_ERROR_INVALID_ARGS;
624     }
625     mCapa->gscan_capa.max_scan_reporting_threshold = nla_get_u32(tbVendor[
626                      QCA_WLAN_VENDOR_ATTR_GSCAN_RESULTS_CAPABILITIES_MAX_SCAN_REPORTING_THRESHOLD]);
627 
628     if (!tbVendor[QCA_WLAN_VENDOR_ATTR_GSCAN_RESULTS_CAPABILITIES_MAX_HOTLIST_BSSIDS]) {
629         ALOGE("%s: QCA_WLAN_VENDOR_ATTR_GSCAN_RESULTS_CAPABILITIES_MAX_HOTLIST_BSSIDS not found",
630               __FUNCTION__);
631         return WIFI_ERROR_INVALID_ARGS;
632     }
633     mCapa->gscan_capa.max_hotlist_bssids = nla_get_u32(tbVendor[
634                                QCA_WLAN_VENDOR_ATTR_GSCAN_RESULTS_CAPABILITIES_MAX_HOTLIST_BSSIDS]);
635 
636     if (!tbVendor[QCA_WLAN_VENDOR_ATTR_GSCAN_RESULTS_CAPABILITIES_MAX_SIGNIFICANT_WIFI_CHANGE_APS]
637        ) {
638         ALOGE("%s: QCA_WLAN_VENDOR_ATTR_GSCAN_RESULTS_CAPABILITIES_MAX_SIGNIFICANT_WIFI_CHANGE_APS "
639               "not found", __FUNCTION__);
640         return WIFI_ERROR_INVALID_ARGS;
641     }
642     mCapa->gscan_capa.max_significant_wifi_change_aps = nla_get_u32(tbVendor[
643                   QCA_WLAN_VENDOR_ATTR_GSCAN_RESULTS_CAPABILITIES_MAX_SIGNIFICANT_WIFI_CHANGE_APS]);
644 
645     if (!tbVendor[QCA_WLAN_VENDOR_ATTR_GSCAN_RESULTS_CAPABILITIES_MAX_BSSID_HISTORY_ENTRIES]) {
646         ALOGE("%s: QCA_WLAN_VENDOR_ATTR_GSCAN_RESULTS_CAPABILITIES_MAX_BSSID_HISTORY_ENTRIES not "
647               "found", __FUNCTION__);
648         return WIFI_ERROR_INVALID_ARGS;
649     }
650     mCapa->gscan_capa.max_bssid_history_entries = nla_get_u32(tbVendor[
651                         QCA_WLAN_VENDOR_ATTR_GSCAN_RESULTS_CAPABILITIES_MAX_BSSID_HISTORY_ENTRIES]);
652 
653     if (!tbVendor[QCA_WLAN_VENDOR_ATTR_GSCAN_RESULTS_CAPABILITIES_MAX_HOTLIST_SSIDS]) {
654         ALOGE("%s: QCA_WLAN_VENDOR_ATTR_GSCAN_RESULTS_CAPABILITIES_MAX_HOTLIST_SSIDS not found. Set"
655               " to 0.", __FUNCTION__);
656         mCapa->gscan_capa.max_hotlist_ssids = 0;
657     } else {
658         mCapa->gscan_capa.max_hotlist_ssids = nla_get_u32(tbVendor[
659                                 QCA_WLAN_VENDOR_ATTR_GSCAN_RESULTS_CAPABILITIES_MAX_HOTLIST_SSIDS]);
660     }
661 
662     if (!tbVendor[QCA_WLAN_VENDOR_ATTR_GSCAN_RESULTS_CAPABILITIES_MAX_NUM_EPNO_NETS]) {
663         ALOGE("%s: QCA_WLAN_VENDOR_ATTR_GSCAN_RESULTS_CAPABILITIES_MAX_NUM_EPNO_NETS not found. Set"
664               " to 0.", __FUNCTION__);
665         mCapa->gscan_capa.max_number_epno_networks = 0;
666     } else {
667         mCapa->gscan_capa.max_number_epno_networks
668             = nla_get_u32(tbVendor[QCA_WLAN_VENDOR_ATTR_GSCAN_RESULTS_CAPABILITIES_MAX_NUM_EPNO_NETS
669                                   ]);
670     }
671 
672     if (!tbVendor[QCA_WLAN_VENDOR_ATTR_GSCAN_RESULTS_CAPABILITIES_MAX_NUM_EPNO_NETS_BY_SSID]) {
673         ALOGE("%s: QCA_WLAN_VENDOR_ATTR_GSCAN_RESULTS_CAPABILITIES_MAX_NUM_EPNO_NETS_BY_SSID not "
674               "found. Set to 0.", __FUNCTION__);
675         mCapa->gscan_capa.max_number_epno_networks_by_ssid = 0;
676     } else {
677         mCapa->gscan_capa.max_number_epno_networks_by_ssid = nla_get_u32(tbVendor[
678                         QCA_WLAN_VENDOR_ATTR_GSCAN_RESULTS_CAPABILITIES_MAX_NUM_EPNO_NETS_BY_SSID]);
679     }
680 
681     if (!tbVendor[QCA_WLAN_VENDOR_ATTR_GSCAN_RESULTS_CAPABILITIES_MAX_NUM_WHITELISTED_SSID]) {
682         ALOGE("%s: QCA_WLAN_VENDOR_ATTR_GSCAN_RESULTS_CAPABILITIES_MAX_NUM_WHITELISTED_SSID not "
683               "found. Set to 0.", __FUNCTION__);
684         mCapa->gscan_capa.max_number_of_white_listed_ssid = 0;
685         mCapa->roaming_capa.max_whitelist_size = 0;
686     } else {
687         mCapa->gscan_capa.max_number_of_white_listed_ssid = nla_get_u32(tbVendor[
688                          QCA_WLAN_VENDOR_ATTR_GSCAN_RESULTS_CAPABILITIES_MAX_NUM_WHITELISTED_SSID]);
689         mCapa->roaming_capa.max_whitelist_size = mCapa->gscan_capa.max_number_of_white_listed_ssid;
690     }
691 
692     if (!tbVendor[QCA_WLAN_VENDOR_ATTR_GSCAN_MAX_NUM_BLACKLISTED_BSSID]) {
693         ALOGE("%s: QCA_WLAN_VENDOR_ATTR_GSCAN_RESULTS_CAPABILITIES_MAX"
694             "_NUM_BLACKLIST_BSSID not found. Set to 0.", __FUNCTION__);
695         mCapa->roaming_capa.max_blacklist_size = 0;
696     } else {
697         mCapa->roaming_capa.max_blacklist_size = nla_get_u32(tbVendor[
698                                       QCA_WLAN_VENDOR_ATTR_GSCAN_MAX_NUM_BLACKLISTED_BSSID]);
699     }
700     return WIFI_SUCCESS;
701 }
702 
wifiParseRadarHistory()703 wifi_error WifihalGeneric::wifiParseRadarHistory() {
704 {
705     // tbVendor
706     struct nlattr *tbVendor[QCA_WLAN_VENDOR_ATTR_RADAR_HISTORY_MAX + 1];
707     int rem = 0, num_dfs_entries = 0;
708 
709     if (nla_parse(tbVendor, QCA_WLAN_VENDOR_ATTR_RADAR_HISTORY_MAX,
710           (struct nlattr *)mVendorData,mDataLen, NULL)) {
711         ALOGE("%s: nla_parse fail", __FUNCTION__);
712         return WIFI_ERROR_INVALID_ARGS;
713     }
714     if (!tbVendor[QCA_WLAN_VENDOR_ATTR_RADAR_HISTORY_ENTRIES]) {
715         ALOGE("%s: radar attr entries not present", __FUNCTION__);
716         return WIFI_ERROR_INVALID_ARGS;
717     }
718 
719     // nested radar history
720     struct nlattr *tb[QCA_WLAN_VENDOR_ATTR_RADAR_HISTORY_MAX + 1];
721     struct nlattr *attr;
722     static struct nla_policy
723       policy[QCA_WLAN_VENDOR_ATTR_RADAR_HISTORY_MAX + 1] = {
724             [QCA_WLAN_VENDOR_ATTR_RADAR_HISTORY_FREQ] = { .type = NLA_U32 },
725             [QCA_WLAN_VENDOR_ATTR_RADAR_HISTORY_TIMESTAMP] = { .type = NLA_U64 },
726             [QCA_WLAN_VENDOR_ATTR_RADAR_HISTORY_DETECTED] = { .type = NLA_FLAG },
727     };
728     radar_history_result *newEntry;
729     radar_history_result *temp;
730     u32 totalEntrySize = 0;
731     u32 newEntrySize = sizeof(radar_history_result);
732 
733     nla_for_each_nested(attr,
734             tbVendor[QCA_WLAN_VENDOR_ATTR_RADAR_HISTORY_ENTRIES],
735             rem) {
736         if ((num_dfs_entries ++) > MAX_NUM_RADAR_HISTORY) {
737             ALOGE("%s: exceeded max entries, drop others", __FUNCTION__);
738             break;
739         }
740         if (nla_parse_nested(tb, QCA_WLAN_VENDOR_ATTR_RADAR_HISTORY_MAX,
741                 attr, policy)) {
742             ALOGI("%s: nla_parse_nested fail", __FUNCTION__);
743             continue;
744         }
745         if (!tb[QCA_WLAN_VENDOR_ATTR_RADAR_HISTORY_FREQ]) {
746             ALOGI("%s: radar attr freq not present", __FUNCTION__);
747             continue;
748         }
749         if (!tb[QCA_WLAN_VENDOR_ATTR_RADAR_HISTORY_TIMESTAMP]) {
750             ALOGI("%s: radar attr timestamp not present", __FUNCTION__);
751             continue;
752         }
753 
754         // realloc buffer for new entry
755         temp = (radar_history_result *) realloc(
756                 mRadarResultParams.entries, totalEntrySize + newEntrySize);
757         if (temp == NULL) {
758             ALOGE("%s: failed to realloc memory", __FUNCTION__);
759             free(mRadarResultParams.entries);
760             mRadarResultParams.entries = NULL;
761             return WIFI_ERROR_OUT_OF_MEMORY;
762         }
763         mRadarResultParams.entries = temp;
764 
765         newEntry = (radar_history_result *)(
766                 (u8 *) mRadarResultParams.entries + totalEntrySize);
767         memset(newEntry, 0, newEntrySize);
768         totalEntrySize += newEntrySize;
769 
770         // save to current radar entry
771         newEntry->freq = nla_get_u32(
772                 tb[QCA_WLAN_VENDOR_ATTR_RADAR_HISTORY_FREQ]);
773         newEntry->clock_boottime = nla_get_u64(
774                 tb[QCA_WLAN_VENDOR_ATTR_RADAR_HISTORY_TIMESTAMP]);
775         newEntry->radar_detected = false;
776         if (tb[QCA_WLAN_VENDOR_ATTR_RADAR_HISTORY_DETECTED]) {
777              newEntry->radar_detected = nla_get_flag(
778                      tb[QCA_WLAN_VENDOR_ATTR_RADAR_HISTORY_DETECTED]);
779         }
780         mRadarResultParams.num_entries ++;
781 
782         ALOGI("Radar history: freq:%d boottime: %" PRId64 " detected:%d",
783                 newEntry->freq,
784                 newEntry->clock_boottime,
785                 newEntry->radar_detected);
786         }
787     }
788 
789     return WIFI_SUCCESS;
790 }
791 
792 
getResponseparams(feature_set * pset)793 void WifihalGeneric::getResponseparams(feature_set *pset)
794 {
795     *pset = mSet;
796 }
797 
getDriverFeatures(features_info * pfeatures)798 void WifihalGeneric::getDriverFeatures(features_info *pfeatures)
799 {
800     if (!pfeatures)
801         return;
802 
803     if (mDriverFeatures.flags != NULL) {
804         pfeatures->flags = (u8 *)malloc(mDriverFeatures.flags_len);
805         if (pfeatures->flags) {
806             memcpy(pfeatures->flags, mDriverFeatures.flags,
807                    mDriverFeatures.flags_len);
808             pfeatures->flags_len = mDriverFeatures.flags_len;
809             return;
810         }
811     }
812 
813     pfeatures->flags_len = 0;
814     pfeatures->flags = NULL;
815 }
816 
setMaxSetSize(int set_size_max)817 void WifihalGeneric::setMaxSetSize(int set_size_max) {
818     mSetSizeMax = set_size_max;
819 }
820 
setConcurrencySet(feature_set set[])821 void WifihalGeneric::setConcurrencySet(feature_set set[]) {
822     mConcurrencySet = set;
823 }
824 
setSizePtr(int * set_size)825 void WifihalGeneric::setSizePtr(int *set_size) {
826     mSetSizePtr = set_size;
827 }
828 
getFilterVersion()829 int WifihalGeneric::getFilterVersion() {
830     return filterVersion;
831 }
832 
getFilterLength()833 int WifihalGeneric::getFilterLength() {
834     return filterLength;
835 }
setPacketBufferParams(u8 * host_packet_buffer,int packet_length)836 void WifihalGeneric::setPacketBufferParams(u8 *host_packet_buffer, int packet_length) {
837     mfilter_packet_read_buffer = host_packet_buffer;
838     mfilter_packet_length = packet_length;
839 }
840 
getBusSize()841 int WifihalGeneric::getBusSize() {
842     return firmware_bus_max_size;
843 }
844 
set_channels_buff(wifi_usable_channel * channels)845 void WifihalGeneric::set_channels_buff(wifi_usable_channel* channels)
846 {
847     channel_buff = channels;
848     memset(channel_buff, 0, sizeof(wifi_usable_channel) * mSetSizeMax);
849 }
850 
get_results_size(void)851 u32 WifihalGeneric::get_results_size(void)
852 {
853     return res_size;
854 }
855 
wifiGetCapabilities(wifi_interface_handle handle)856 wifi_error WifihalGeneric::wifiGetCapabilities(wifi_interface_handle handle)
857 {
858     wifi_error ret;
859     struct nlattr *nlData;
860     interface_info *ifaceInfo = getIfaceInfo(handle);
861 
862     /* Create the NL message. */
863     ret = create();
864     if (ret != WIFI_SUCCESS) {
865         ALOGE("%s: Failed to create NL message,  Error:%d", __FUNCTION__, ret);
866         return ret;
867     }
868 
869     /* Set the interface Id of the message. */
870     ret = set_iface_id(ifaceInfo->name);
871     if (ret != WIFI_SUCCESS) {
872         ALOGE("%s: Failed to set interface Id of message, Error:%d", __FUNCTION__, ret);
873         return ret;
874     }
875 
876     /* Add the vendor specific attributes for the NL command. */
877     nlData = attr_start(NL80211_ATTR_VENDOR_DATA);
878     if (!nlData)
879         return WIFI_ERROR_OUT_OF_MEMORY;
880 
881     ret = put_u32(QCA_WLAN_VENDOR_ATTR_GSCAN_SUBCMD_CONFIG_PARAM_REQUEST_ID, mId);
882     if (ret != WIFI_SUCCESS) {
883         ALOGE("%s: Failed to add request_ID to NL command, Error:%d", __FUNCTION__, ret);
884         return ret;
885     }
886 
887     attr_end(nlData);
888 
889     ret = requestResponse();
890     if (ret != WIFI_SUCCESS)
891         ALOGE("%s: Failed to send request, Error:%d", __FUNCTION__, ret);
892 
893     return ret;
894 }
895 
copyCachedRadarHistory(radar_history_result * resultBuf,int resultBufSize,int * numResults)896 wifi_error WifihalGeneric::copyCachedRadarHistory(
897         radar_history_result *resultBuf, int resultBufSize, int *numResults) {
898     *numResults = 0;
899 
900     if (mRadarResultParams.entries) {
901         radar_history_result *sEntry = NULL;
902         radar_history_result *tEntry = NULL;
903         u32 offset = 0;
904         int i;
905 
906         for (i = 0; i < mRadarResultParams.num_entries; i ++) {
907             if (resultBufSize < (offset + sizeof(radar_history_result))) {
908                 break;
909             }
910 
911             sEntry = (radar_history_result *)(
912                            (u8 *) mRadarResultParams.entries + offset);
913             tEntry = (radar_history_result *)(
914                            (u8 *) resultBuf + offset);
915             memcpy(tEntry, sEntry, sizeof(radar_history_result));
916             (*numResults) += 1;
917             offset += sizeof(radar_history_result);
918         }
919     }
920 
921     return WIFI_SUCCESS;
922 }
923 
freeCachedRadarHistory()924 void WifihalGeneric::freeCachedRadarHistory() {
925     if (mRadarResultParams.entries) {
926         free(mRadarResultParams.entries);
927         mRadarResultParams.entries = NULL;
928         mRadarResultParams.num_entries = 0;
929     }
930 }
931 
getSarVersion(wifi_interface_handle handle)932 wifi_error WifihalGeneric::getSarVersion(wifi_interface_handle handle)
933 {
934     wifi_error ret;
935     interface_info *ifaceInfo = getIfaceInfo(handle);
936 
937 
938     /* Create the NL message. */
939     ret = create();
940     if (ret != WIFI_SUCCESS) {
941         ALOGE("%s: Failed to create NL message,  Error:%d", __FUNCTION__, ret);
942         return ret;
943     }
944 
945     /* Set the interface Id of the message. */
946     ret = set_iface_id(ifaceInfo->name);
947     if (ret != WIFI_SUCCESS) {
948         ALOGE("%s: Failed to set interface Id of message, Error:%d", __FUNCTION__, ret);
949         return ret;
950     }
951 
952     ret = requestResponse();
953     if (ret != WIFI_SUCCESS)
954         ALOGE("%s: Failed to send request, Error:%d", __FUNCTION__, ret);
955 
956     return ret;
957 }
958 
959