/*
 * Copyright (c) 2012-2018 The Linux Foundation. All rights reserved.
 *
 * Previously licensed under the ISC license by Qualcomm Atheros, Inc.
 *
 *
 * Permission to use, copy, modify, and/or distribute this software for
 * any purpose with or without fee is hereby granted, provided that the
 * above copyright notice and this permission notice appear in all
 * copies.
 *
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
 * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
 * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
 * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
 * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
 * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 * PERFORMANCE OF THIS SOFTWARE.
 */

/*
 * This file was originally distributed by Qualcomm Atheros, Inc.
 * under proprietary terms before Copyright ownership was assigned
 * to the Linux Foundation.
 */

/**========================================================================

  \file  wlan_hdd_scan.c

  \brief WLAN Host Device Driver implementation

  ========================================================================*/

/**=========================================================================

                       EDIT HISTORY FOR FILE


  This section contains comments describing changes made to the module.
  Notice that changes are listed in reverse chronological order.


  $Header:$   $DateTime: $ $Author: $


  when        who    what, where, why
  --------    ---    --------------------------------------------------------
  04/5/09     Shailender     Created module.

  ==========================================================================*/
      /* To extract the Scan results */

/* Add a stream event */

#include <wlan_qct_driver.h>
#include <wlan_hdd_includes.h>
#include <vos_api.h>
#include <palTypes.h>
#include <aniGlobal.h>
#include <dot11f.h>

#include <linux/wireless.h>
#include <net/cfg80211.h>
#include <vos_sched.h>


#define WEXT_CSCAN_HEADER               "CSCAN S\x01\x00\x00S\x00"
#define WEXT_CSCAN_HEADER_SIZE          12
#define WEXT_CSCAN_SSID_SECTION         'S'
#define WEXT_CSCAN_CHANNEL_SECTION      'C'
#define WEXT_CSCAN_NPROBE_SECTION       'N'
#define WEXT_CSCAN_ACTV_DWELL_SECTION   'A'
#define WEXT_CSCAN_PASV_DWELL_SECTION   'P'
#define WEXT_CSCAN_HOME_DWELL_SECTION   'H'
#define WEXT_CSCAN_TYPE_SECTION         'T'
#define WEXT_CSCAN_PENDING_SECTION      'O'
#define WEXT_CSCAN_TYPE_DEFAULT         0
#define WEXT_CSCAN_TYPE_PASSIVE         1
#define WEXT_CSCAN_PASV_DWELL_TIME      130
#define WEXT_CSCAN_PASV_DWELL_TIME_DEF  250
#define WEXT_CSCAN_PASV_DWELL_TIME_MAX  3000
#define WEXT_CSCAN_HOME_DWELL_TIME      130
#define MAX_RATES                       12

#define WEXT_CSCAN_SCAN_DONE_WAIT_TIME  2000

typedef struct hdd_scan_info{
    struct net_device *dev;
    struct iw_request_info *info;
    char *start;
    char *end;
} hdd_scan_info_t, *hdd_scan_info_tp;

static v_S31_t hdd_TranslateABGRateToMbpsRate(v_U8_t *pFcRate)
{

    /** Slightly more sophisticated processing has to take place here.
              Basic rates are rounded DOWN.  HT rates are rounded UP.*/
    return ( (( ((v_S31_t) *pFcRate) & 0x007f) * 1000000) / 2);
}


static eHalStatus hdd_AddIwStreamEvent(int cmd, int length, char* data, hdd_scan_info_t *pscanInfo, char **last_event, char **current_event )
{
    struct iw_event event;

    *last_event = *current_event;
    vos_mem_zero(&event, sizeof (struct iw_event));
    event.cmd = cmd;
    event.u.data.flags = 1;
    event.u.data.length = length;
    *current_event = iwe_stream_add_point (pscanInfo->info,*current_event, pscanInfo->end,  &event, data);

    if(*last_event == *current_event)
    {
            /* no space to add event */
        hddLog( LOGE, "%s: no space left to add event", __func__);
        return -E2BIG; /* Error code, may be E2BIG */
    }

    return 0;
}

/**---------------------------------------------------------------------------

  \brief hdd_GetWPARSNIEs() -

   This function extract the WPA/RSN IE from the Bss descriptor IEs fields

  \param  - ieFields - Pointer to the Bss Descriptor IEs.
              - ie_length - IE Length.
              - last_event -Points to the last event.
              - current_event - Points to the
  \return - 0 for success, non zero for failure

  --------------------------------------------------------------------------*/


/* Extract the WPA and/or RSN IEs */
static eHalStatus hdd_GetWPARSNIEs( v_U8_t *ieFields, v_U16_t ie_length, char **last_event, char **current_event, hdd_scan_info_t *pscanInfo )
{
    v_U8_t eid, elen, *element;
    v_U16_t tie_length=0;

    ENTER();

    element = ieFields;
    tie_length = ie_length;

    while( tie_length > 2 && element != NULL )
    {
        eid = element[0];
        elen = element[1];

        /*If element length is greater than total remaining ie length,
         *break the loop*/
        if ((elen+2) > tie_length)
           break;

        switch(eid)
        {
            case DOT11F_EID_WPA:
            case DOT11F_EID_RSN:
#ifdef FEATURE_WLAN_WAPI
            case DOT11F_EID_WAPI:
#endif
                if(hdd_AddIwStreamEvent( IWEVGENIE,  elen+2, (char*)element, pscanInfo, last_event, current_event ) < 0 )
                    return -E2BIG;
                break;

            default:
                break;
        }

        /* Next element */
        tie_length -= (2 + elen);
        element += 2 + elen;
    }

    return 0;
}

/**---------------------------------------------------------------------------

  \brief hdd_IndicateScanResult() -

   This function returns the scan results to the wpa_supplicant

  \param  - scanInfo - Pointer to the scan info structure.
              - descriptor - Pointer to the Bss Descriptor.

  \return - 0 for success, non zero for failure

  --------------------------------------------------------------------------*/
#define MAX_CUSTOM_LEN 64
static eHalStatus hdd_IndicateScanResult(hdd_scan_info_t *scanInfo, tCsrScanResultInfo *scan_result)
{
   hdd_adapter_t *pAdapter = WLAN_HDD_GET_PRIV_PTR(scanInfo->dev) ;
   tHalHandle hHal = WLAN_HDD_GET_HAL_CTX(pAdapter);
   tSirBssDescription *descriptor = &scan_result->BssDescriptor;
   struct iw_event event;
   char *current_event = scanInfo->start;
   char *end = scanInfo->end;
   char *last_event;
   char *current_pad;
   v_U16_t ie_length = 0;
   v_U16_t capabilityInfo;
   char *modestr;
   int error;
   char custom[MAX_CUSTOM_LEN];
   char *p;
   tANI_U32 status;

   hddLog( LOG1, "hdd_IndicateScanResult " MAC_ADDRESS_STR,
          MAC_ADDR_ARRAY(descriptor->bssId));

   error = 0;
   last_event = current_event;
   vos_mem_zero(&event, sizeof (event));

   /* BSSID */
   event.cmd = SIOCGIWAP;
   event.u.ap_addr.sa_family = ARPHRD_ETHER;
   vos_mem_copy (event.u.ap_addr.sa_data, descriptor->bssId,
                  sizeof (descriptor->bssId));
   current_event = iwe_stream_add_event(scanInfo->info,current_event, end,
                   &event, IW_EV_ADDR_LEN);

   if (last_event == current_event)
   {
      /* no space to add event */
      /* Error code may be E2BIG */
       hddLog( LOGE, "hdd_IndicateScanResult: no space for SIOCGIWAP ");
       return -E2BIG;
   }

   last_event = current_event;
   vos_mem_zero(&event, sizeof (struct iw_event));

 /* Protocol Name */
   event.cmd = SIOCGIWNAME;

   switch (descriptor->nwType)
   {
   case eSIR_11A_NW_TYPE:
       modestr = "a";
       break;
   case eSIR_11B_NW_TYPE:
       modestr = "b";
       break;
   case eSIR_11G_NW_TYPE:
       modestr = "g";
       break;
   case eSIR_11N_NW_TYPE:
       modestr = "n";
       break;
   default:
       hddLog( LOGW, "%s: Unknown network type [%d]",
              __func__, descriptor->nwType);
       modestr = "?";
       break;
   }
   snprintf(event.u.name, IFNAMSIZ, "IEEE 802.11%s", modestr);
   current_event = iwe_stream_add_event(scanInfo->info,current_event, end,
                   &event, IW_EV_CHAR_LEN);

   if (last_event == current_event)
   { /* no space to add event */
       hddLog( LOGE, "hdd_IndicateScanResult: no space for SIOCGIWNAME");
      /* Error code, may be E2BIG */
       return -E2BIG;
   }

   last_event = current_event;
   vos_mem_zero( &event, sizeof (struct iw_event));

   /*Freq*/
   event.cmd = SIOCGIWFREQ;

   event.u.freq.m = descriptor->channelId;
   event.u.freq.e = 0;
   event.u.freq.i = 0;
   current_event = iwe_stream_add_event(scanInfo->info,current_event, end,
                                        &event, IW_EV_FREQ_LEN);

   if (last_event == current_event)
   { /* no space to add event */
       hddLog( LOGE, "hdd_IndicateScanResult: no space for SIOCGIWFREQ");
       return -E2BIG;
   }

   last_event = current_event;
   vos_mem_zero( &event, sizeof (struct iw_event));

   /* BSS Mode */
   event.cmd = SIOCGIWMODE;

   capabilityInfo = descriptor->capabilityInfo;

   if (SIR_MAC_GET_ESS(capabilityInfo))
   {
       event.u.mode = IW_MODE_MASTER;
   }
   else if (SIR_MAC_GET_IBSS(capabilityInfo))
   {
       event.u.mode = IW_MODE_ADHOC;
   }
   else
   {
       /* neither ESS or IBSS */
       event.u.mode = IW_MODE_AUTO;
   }

   current_event = iwe_stream_add_event(scanInfo->info,current_event, end,
                                        &event, IW_EV_UINT_LEN);

   if (last_event == current_event)
   { /* no space to add event */
       hddLog( LOGE, "hdd_IndicateScanResult: no space for SIOCGIWMODE");
       return -E2BIG;
   }
   /* To extract SSID */
   ie_length = GET_IE_LEN_IN_BSS( descriptor->length );

   if (ie_length > 0)
   {
       /* dot11BeaconIEs is a large struct, so we make it static to
          avoid stack overflow.  This API is only invoked via ioctl,
          so it is serialized by the kernel rtnl_lock and hence does
          not need to be reentrant */
       static tDot11fBeaconIEs dot11BeaconIEs;
       tDot11fIESSID *pDot11SSID;
       tDot11fIESuppRates *pDot11SuppRates;
       tDot11fIEExtSuppRates *pDot11ExtSuppRates;
       tDot11fIEHTCaps *pDot11IEHTCaps;
       int numBasicRates = 0;
       int maxNumRates = 0;

       pDot11IEHTCaps = NULL;

       status = dot11fUnpackBeaconIEs ((tpAniSirGlobal)
           hHal, (tANI_U8 *) descriptor->ieFields, ie_length,  &dot11BeaconIEs);
       if (DOT11F_FAILED(status))
       {
           hddLog(LOGE,
           FL("unpack failed for Beacon IE status:(0x%08x)"),
              status);
           return eHAL_STATUS_FAILURE;
       }

       pDot11SSID = &dot11BeaconIEs.SSID;

       if (pDot11SSID->present ) {
          last_event = current_event;
          vos_mem_zero (&event, sizeof (struct iw_event));

          event.cmd = SIOCGIWESSID;
          event.u.data.flags = 1;
          event.u.data.length = scan_result->ssId.length;
          current_event = iwe_stream_add_point (scanInfo->info,current_event, end,
                  &event, (char *)scan_result->ssId.ssId);

          if(last_event == current_event)
          { /* no space to add event */
             hddLog( LOGE, "hdd_IndicateScanResult: no space for SIOCGIWESSID");
             return -E2BIG;
          }
       }

      if( hdd_GetWPARSNIEs( ( tANI_U8 *) descriptor->ieFields, ie_length, &last_event, &current_event, scanInfo )  < 0    )
      {
          hddLog( LOGE, "hdd_IndicateScanResult: no space for SIOCGIWESSID");
          return -E2BIG;
      }

      last_event = current_event;
      current_pad = current_event + IW_EV_LCP_LEN;
      vos_mem_zero( &event, sizeof (struct iw_event));

      /*Rates*/
      event.cmd = SIOCGIWRATE;


      pDot11SuppRates = &dot11BeaconIEs.SuppRates;

      if (pDot11SuppRates->present )
      {
          int i;

          numBasicRates = pDot11SuppRates->num_rates;
          for (i=0; i<pDot11SuppRates->num_rates; i++)
          {
              if (0 != (pDot11SuppRates->rates[i] & 0x7F))
              {
                  event.u.bitrate.value = hdd_TranslateABGRateToMbpsRate (
                      &pDot11SuppRates->rates[i]);

                  current_pad = iwe_stream_add_value (scanInfo->info,current_event,
                      current_pad, end, &event, IW_EV_PARAM_LEN);
              }
          }

      }

      pDot11ExtSuppRates = &dot11BeaconIEs.ExtSuppRates;

      if (pDot11ExtSuppRates->present )
      {
          int i,no_of_rates;
          maxNumRates = numBasicRates + pDot11ExtSuppRates->num_rates;

          /* Check to make sure the total number of rates
               doesn't exceed IW_MAX_BITRATES */

          maxNumRates = VOS_MIN(maxNumRates , IW_MAX_BITRATES);

          if((maxNumRates - numBasicRates) > MAX_RATES)
          {
             no_of_rates = MAX_RATES;
             hddLog( LOGW, "Accessing array out of bound that array is pDot11ExtSuppRates->rates ");
          }
          else
          {
            no_of_rates = maxNumRates - numBasicRates;
          }
          for ( i=0; i< no_of_rates ; i++ )
          {
              if (0 != (pDot11ExtSuppRates->rates[i] & 0x7F))
              {
                  event.u.bitrate.value = hdd_TranslateABGRateToMbpsRate (
                      &pDot11ExtSuppRates->rates[i]);

                  current_pad = iwe_stream_add_value (scanInfo->info,current_event,
                      current_pad, end, &event, IW_EV_PARAM_LEN);
              }
          }
      }


      if ((current_pad - current_event) >= IW_EV_LCP_LEN)
      {
          current_event = current_pad;
      }
      else
      {
          if (last_event == current_event)
          { /* no space to add event */
              hddLog( LOGE, "hdd_IndicateScanResult: no space for SIOCGIWRATE");
              return -E2BIG;
          }
      }

      last_event = current_event;
      vos_mem_zero (&event, sizeof (struct iw_event));


      event.cmd = SIOCGIWENCODE;

      if (SIR_MAC_GET_PRIVACY(capabilityInfo))
      {
         event.u.data.flags = IW_ENCODE_ENABLED | IW_ENCODE_NOKEY;
      }
      else
      {
         event.u.data.flags = IW_ENCODE_DISABLED;
      }
      event.u.data.length = 0;

      current_event = iwe_stream_add_point(scanInfo->info,current_event, end, &event, (char *)pDot11SSID->ssid);


      if(last_event == current_event)
      { /* no space to add event
               Error code, may be E2BIG */
          hddLog( LOGE, "hdd_IndicateScanResult: no space for SIOCGIWENCODE");
          return -E2BIG;
      }
   }

   last_event = current_event;
   vos_mem_zero( &event, sizeof (struct iw_event));

    /*RSSI*/
   event.cmd = IWEVQUAL;
   event.u.qual.qual = descriptor->rssi;
   event.u.qual.noise = descriptor->sinr;

   event.u.qual.level = VOS_MIN ((descriptor->rssi + descriptor->sinr), 0);

   event.u.qual.updated = IW_QUAL_ALL_UPDATED;

   current_event = iwe_stream_add_event(scanInfo->info,current_event,
       end, &event, IW_EV_QUAL_LEN);

   if(last_event == current_event)
   { /* no space to add event */
       hddLog( LOGE, "hdd_IndicateScanResult: no space for IWEVQUAL");
       return -E2BIG;
   }


   /* AGE */
   event.cmd = IWEVCUSTOM;
   p = custom;
   p += scnprintf(p, MAX_CUSTOM_LEN, " Age: %lu",
                 vos_timer_get_system_time() - descriptor->nReceivedTime);
   event.u.data.length = p - custom;
   current_event = iwe_stream_add_point (scanInfo->info,current_event, end,
                                         &event, custom);
   if(last_event == current_event)
   { /* no space to add event */
      hddLog( LOGE, "hdd_IndicateScanResult: no space for IWEVCUSTOM (age)");
      return -E2BIG;
   }

   scanInfo->start = current_event;

   return 0;
}

/**---------------------------------------------------------------------------

  \brief hdd_ScanRequestCallback() -

   The sme module calls this callback function once it finish the scan request
   and this function notifies the scan complete event to the wpa_supplicant.

  \param  - halHandle - Pointer to the Hal Handle.
              - pContext - Pointer to the data context.
              - sessionId - Session identifier
              - scanId - Scan ID.
              - status - CSR Status.
  \return - 0 for success, non zero for failure

  --------------------------------------------------------------------------*/

static eHalStatus hdd_ScanRequestCallback(tHalHandle halHandle, void *pContext,
                                          tANI_U8 sessionId, tANI_U32 scanId,
                                          eCsrScanStatus status)
{
    struct net_device *dev = (struct net_device *) pContext;
    hdd_adapter_t *pAdapter = WLAN_HDD_GET_PRIV_PTR(dev) ;
    union iwreq_data wrqu;
    int we_event;
    char *msg;

    ENTER();

    hddLog(LOGW,"%s called with halHandle = %pK, pContext = %pK, scanID = %d,"
           " returned status = %d", __func__, halHandle, pContext,
           (int) scanId, (int) status);

    /* if there is a scan request pending when the wlan driver is unloaded
       we may be invoked as SME flushes its pending queue.  If that is the
       case, the underlying net_device may have already been destroyed, so
       do some quick sanity before proceeding */
    if (pAdapter->dev != dev)
    {
       hddLog(LOGW, "%s: device mismatch %pK vs %pK",
               __func__, pAdapter->dev, dev);
        return eHAL_STATUS_SUCCESS;
    }

    /* Check the scanId */
    if (pAdapter->scan_info.scanId != scanId)
    {
        hddLog(LOGW, "%s called with mismatched scanId pHddCtx->scan_info.scanId = %d "
               "scanId = %d ", __func__, (int) pAdapter->scan_info.scanId,
                (int) scanId);
    }

    /* Scan is no longer pending */
    pAdapter->scan_info.mScanPending = VOS_FALSE;

    // notify any applications that may be interested
    memset(&wrqu, '\0', sizeof(wrqu));
    we_event = SIOCGIWSCAN;
    msg = NULL;
    wireless_send_event(dev, we_event, &wrqu, msg);

    EXIT();

    VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO, "%s: exit !!!",__func__);

    return eHAL_STATUS_SUCCESS;
}

/**
 * __iw_set_scan() - set scan request
 * @dev: Pointer to the net device.
 * @info: Pointer to the iw_request_info.
 * @wrqu: Pointer to the iwreq_data.
 * @extra: Pointer to the data.
 *
 * This function process the scan request from the wpa_supplicant
 * and set the scan request to the SME
 *
 * Return: 0 on success, error number otherwise
 */
static int __iw_set_scan(struct net_device *dev, struct iw_request_info *info,
			 union iwreq_data *wrqu, char *extra)
{
   hdd_adapter_t *pAdapter = WLAN_HDD_GET_PRIV_PTR(dev) ;
   hdd_context_t *pHddCtx = WLAN_HDD_GET_CTX(pAdapter);
   hdd_wext_state_t *pwextBuf = WLAN_HDD_GET_WEXT_STATE_PTR(pAdapter);
   tCsrScanRequest scanRequest;
   v_U32_t scanId = 0;
   eHalStatus status = eHAL_STATUS_SUCCESS;
   struct iw_scan_req *scanReq = (struct iw_scan_req *)extra;
   hdd_adapter_t *con_sap_adapter;
   uint16_t con_dfs_ch;
   int ret;

   ENTER();

   ret = wlan_hdd_validate_context(pHddCtx);
   if (0 != ret)
       return ret;

   /* Block All Scan during DFS operation and send null scan result */
   con_sap_adapter = hdd_get_con_sap_adapter(pAdapter, true);
   if (con_sap_adapter) {
       con_dfs_ch = con_sap_adapter->sessionCtx.ap.operatingChannel;

       if (VOS_IS_DFS_CH(con_dfs_ch)) {
           hddLog(LOGW, "%s:##In DFS Master mode. Scan aborted", __func__);
           return -EOPNOTSUPP;
       }
   }


   if(pAdapter->scan_info.mScanPending == TRUE)
   {
       VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_FATAL, "%s:mScanPending is TRUE !!!",__func__);
       return eHAL_STATUS_SUCCESS;
   }

   vos_mem_zero( &scanRequest, sizeof(scanRequest));

   if (NULL != wrqu->data.pointer)
   {
       /* set scanType, active or passive */
       if ((IW_SCAN_TYPE_ACTIVE ==  scanReq->scan_type) ||
           (eSIR_ACTIVE_SCAN == pHddCtx->ioctl_scan_mode))
       {
           scanRequest.scanType = eSIR_ACTIVE_SCAN;
       }
       else
       {
           scanRequest.scanType = eSIR_PASSIVE_SCAN;
       }

       /* set bssid using sockaddr from iw_scan_req */
       vos_mem_copy(scanRequest.bssid,
                       &scanReq->bssid.sa_data, sizeof(scanRequest.bssid) );

      if (wrqu->data.flags & IW_SCAN_THIS_ESSID)  {

          if(scanReq->essid_len &&
              (scanReq->essid_len <= SIR_MAC_MAX_SSID_LENGTH)) {
              scanRequest.SSIDs.numOfSSIDs = 1;
              scanRequest.SSIDs.SSIDList =( tCsrSSIDInfo *)vos_mem_malloc(sizeof(tCsrSSIDInfo));
              if(scanRequest.SSIDs.SSIDList) {
                 scanRequest.SSIDs.SSIDList->SSID.length =             scanReq->essid_len;
                 vos_mem_copy(scanRequest.SSIDs.SSIDList->  SSID.ssId,scanReq->essid,scanReq->essid_len);
              }
              else
              {
                scanRequest.SSIDs.numOfSSIDs = 0;
                VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR, "%s: Unable to allocate memory",__func__);
                VOS_ASSERT(0);
              }
          }
          else
          {
            hddLog(LOGE, FL("Invalid essid length : %d"), scanReq->essid_len);
          }
      }

       /* set min and max channel time */
       scanRequest.minChnTime = scanReq->min_channel_time;
       scanRequest.maxChnTime = scanReq->max_channel_time;

   }
   else
   {
       if (pHddCtx->ioctl_scan_mode == eSIR_ACTIVE_SCAN) {
           /* set the scan type to active */
           scanRequest.scanType = eSIR_ACTIVE_SCAN;
       } else {
           scanRequest.scanType = eSIR_PASSIVE_SCAN;
       }

       vos_mem_set( scanRequest.bssid, sizeof( tCsrBssid ), 0xff );

       /* set min and max channel time to zero */
       scanRequest.minChnTime = 0;
       scanRequest.maxChnTime = 0;
   }

   /* set BSSType to default type */
   scanRequest.BSSType = eCSR_BSS_TYPE_ANY;

   /*Scan all the channels */
   scanRequest.ChannelInfo.numOfChannels = 0;

   scanRequest.ChannelInfo.ChannelList = NULL;

   /* set requestType to full scan */
   scanRequest.requestType = eCSR_SCAN_REQUEST_FULL_SCAN;

   /* if previous genIE is not NULL, update ScanIE */
   if (0 != pwextBuf->genIE.length)
   {
       memset( &pAdapter->scan_info.scanAddIE, 0, sizeof(pAdapter->scan_info.scanAddIE) );
       memcpy( pAdapter->scan_info.scanAddIE.addIEdata, pwextBuf->genIE.addIEdata,
           pwextBuf->genIE.length );
       pAdapter->scan_info.scanAddIE.length = pwextBuf->genIE.length;

       pwextBuf->roamProfile.pAddIEScan = pAdapter->scan_info.scanAddIE.addIEdata;
       pwextBuf->roamProfile.nAddIEScanLength = pAdapter->scan_info.scanAddIE.length;

       /* clear previous genIE after use it */
       memset( &pwextBuf->genIE, 0, sizeof(pwextBuf->genIE) );
   }

   /* push addIEScan in scanRequset if exist */
   if (pAdapter->scan_info.scanAddIE.length &&
       (pAdapter->scan_info.scanAddIE.length <=
        sizeof(pAdapter->scan_info.scanAddIE.addIEdata)))
   {
       scanRequest.uIEFieldLen = pAdapter->scan_info.scanAddIE.length;
       scanRequest.pIEField = pAdapter->scan_info.scanAddIE.addIEdata;
   }

   status = sme_ScanRequest((WLAN_HDD_GET_CTX(pAdapter))->hHal,
                             pAdapter->sessionId, &scanRequest, &scanId,
                             &hdd_ScanRequestCallback, dev);
   if (!HAL_STATUS_SUCCESS(status))
   {
       VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_FATAL, "%s:sme_ScanRequest  fail %d!!!",__func__, status);
       goto error;
   }

   pAdapter->scan_info.mScanPending = TRUE;

   pAdapter->scan_info.scanId = scanId;

error:
   if ((wrqu->data.flags & IW_SCAN_THIS_ESSID) && (scanReq->essid_len))
       vos_mem_free(scanRequest.SSIDs.SSIDList);

   EXIT();
   return status;
}

/**
 * iw_set_scan() - SSR wrapper for __iw_set_scan
 * @dev: Pointer to the net device.
 * @info: Pointer to the iw_request_info.
 * @wrqu: Pointer to the iwreq_data.
 * @extra: Pointer to the data.
 *
 * Return: 0 on success, error number otherwise
 */
int iw_set_scan(struct net_device *dev, struct iw_request_info *info,
		 union iwreq_data *wrqu, char *extra)
{
	int ret;

	vos_ssr_protect(__func__);
	ret = __iw_set_scan(dev, info, wrqu, extra);
	vos_ssr_unprotect(__func__);

	return ret;
}

/**
 * __iw_get_scan() - get scan results
 * @dev: Pointer to the net device.
 * @info: Pointer to the iw_request_info.
 * @wrqu: Pointer to the iwreq_data.
 * @extra: Pointer to the data.
 *
 * This function returns the scan results to the wpa_supplicant
 *
 * Return: 0 on success, error number otherwise
 */
static int __iw_get_scan(struct net_device *dev,
			 struct iw_request_info *info,
			 union iwreq_data *wrqu, char *extra)
{
   hdd_adapter_t *pAdapter = WLAN_HDD_GET_PRIV_PTR(dev) ;
   tHalHandle hHal = WLAN_HDD_GET_HAL_CTX(pAdapter);
   tCsrScanResultInfo *pScanResult;
   eHalStatus status = eHAL_STATUS_SUCCESS;
   hdd_scan_info_t scanInfo;
   tScanResultHandle pResult;
   int i = 0;
   hdd_context_t *hdd_ctx;
   int ret;

   ENTER();

   hdd_ctx = WLAN_HDD_GET_CTX(pAdapter);
   ret = wlan_hdd_validate_context(hdd_ctx);
   if (0 != ret)
       return ret;

   VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO, "%s: enter buffer length %d!!!",
       __func__, (wrqu->data.length)?wrqu->data.length:IW_SCAN_MAX_DATA);

   if (TRUE == pAdapter->scan_info.mScanPending)
   {
       VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_FATAL, "%s:mScanPending is TRUE !!!",__func__);
       return -EAGAIN;
   }

   scanInfo.dev = dev;
   scanInfo.start = extra;
   scanInfo.info = info;

   if (0 == wrqu->data.length)
   {
       scanInfo.end = extra + IW_SCAN_MAX_DATA;
   }
   else
   {
       scanInfo.end = extra + wrqu->data.length;
   }

   status = sme_ScanGetResult(hHal,pAdapter->sessionId,NULL,&pResult);

   if (NULL == pResult)
   {
       // no scan results
       hddLog(LOG1,"iw_get_scan: NULL Scan Result ");
       return 0;
   }

   pScanResult = sme_ScanResultGetFirst(hHal, pResult);

   while (pScanResult)
   {
       status = hdd_IndicateScanResult(&scanInfo, pScanResult);
       if (0 != status)
       {
           break;
       }
       i++;
       pScanResult = sme_ScanResultGetNext(hHal, pResult);
   }

   sme_ScanResultPurge(hHal, pResult);

   VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO, "%s: exit total %d BSS reported !!!",__func__, i);
   EXIT();
   return status;
}

/**
 * iw_get_scan() - SSR wrapper function for __iw_get_scan
 * @dev: Pointer to the net device.
 * @info: Pointer to the iw_request_info.
 * @wrqu: Pointer to the iwreq_data.
 * @extra: Pointer to the data.
 *
 * Return: 0 on success, error number otherwise
 */
int iw_get_scan(struct net_device *dev,
			 struct iw_request_info *info,
			 union iwreq_data *wrqu, char *extra)
{
	int ret;

	vos_ssr_protect(__func__);
	ret = __iw_get_scan(dev, info, wrqu, extra);
	vos_ssr_unprotect(__func__);

	return ret;
}

#if 0
static eHalStatus hdd_CscanRequestCallback(tHalHandle halHandle, void *pContext,
                         tANI_U32 scanId, eCsrScanStatus status)
{
    struct net_device *dev = (struct net_device *) pContext;
    hdd_adapter_t *pAdapter = WLAN_HDD_GET_PRIV_PTR(dev) ;
    hdd_wext_state_t *pwextBuf = WLAN_HDD_GET_WEXT_STATE_PTR(pAdapter);
    union iwreq_data wrqu;
    int we_event;
    char *msg;
    VOS_STATUS vos_status = VOS_STATUS_SUCCESS;
    ENTER();

    hddLog(LOG1,"%s called with halHandle = %pK, pContext = %pK, scanID = %d,"
           " returned status = %d", __func__, halHandle, pContext,
            (int) scanId, (int) status);

    /* Check the scanId */
    if (pwextBuf->scanId != scanId)
    {
        hddLog(LOGW, "%s called with mismatched scanId pWextState->scanId = %d "
               "scanId = %d ", __func__, (int) pwextBuf->scanId,
                (int) scanId);
    }

    /* Scan is no longer pending */
    pwextBuf->mScanPending = VOS_FALSE;

    // notify any applications that may be interested
    memset(&wrqu, '\0', sizeof(wrqu));
    we_event = SIOCGIWSCAN;
    msg = NULL;
    wireless_send_event(dev, we_event, &wrqu, msg);

    vos_status = vos_event_set(&pwextBuf->vosevent);

    if (!VOS_IS_STATUS_SUCCESS(vos_status))
    {
       VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR, ("ERROR: HDD vos_event_set failed!!"));
       return VOS_STATUS_E_FAILURE;
    }

    EXIT();
    VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO, "%s: exit !!!",__func__);

    return eHAL_STATUS_SUCCESS;
}
#endif

int iw_set_cscan(struct net_device *dev, struct iw_request_info *info,
                 union iwreq_data *wrqu, char *extra)
{
    hdd_adapter_t *pAdapter = WLAN_HDD_GET_PRIV_PTR(dev) ;
    hdd_wext_state_t *pwextBuf = WLAN_HDD_GET_WEXT_STATE_PTR(pAdapter);
    tCsrScanRequest scanRequest;
    v_U32_t scanId = 0;
    eHalStatus status = eHAL_STATUS_SUCCESS;
    v_U8_t channelIdx;

    ENTER();
    VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO, "%s: enter !!!",__func__);


    if ((WLAN_HDD_GET_CTX(pAdapter))->isLogpInProgress)
    {
        VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_FATAL, "%s:LOGP in Progress. Ignore!!!",__func__);
        return eHAL_STATUS_SUCCESS;
    }

    vos_mem_zero( &scanRequest, sizeof(scanRequest));
    if (NULL != wrqu->data.pointer)
    {
        char *str_ptr = NULL;
        tCsrSSIDInfo *SsidInfo = NULL;
        int num_ssid = 0;
        int i, j, ssid_start;
        hdd_scan_pending_option_e scanPendingOption = WEXT_SCAN_PENDING_GIVEUP;

        str_ptr = extra;

        i = WEXT_CSCAN_HEADER_SIZE;

        if( WEXT_CSCAN_PENDING_SECTION == str_ptr[i] )
        {
            scanPendingOption = (hdd_scan_pending_option_e)str_ptr[++i];
            ++i;
        }
        pAdapter->scan_info.scan_pending_option = scanPendingOption;

        if(pAdapter->scan_info.mScanPending == TRUE)
        {
            hddLog(LOG1,"%s: mScanPending is TRUE",__func__);
            /* If any scan is pending, just giveup this scan request */
            if(WEXT_SCAN_PENDING_GIVEUP == scanPendingOption)
            {
                pAdapter->scan_info.waitScanResult = FALSE;
                return eHAL_STATUS_SUCCESS;
            }
            /* If any scan pending, wait till finish current scan,
               and try this scan request when previous scan finish */
            else if(WEXT_SCAN_PENDING_DELAY == scanPendingOption)
            {
                pAdapter->scan_info.waitScanResult = TRUE;
                vos_event_reset(&pAdapter->scan_info.scan_finished_event);
                if(vos_wait_single_event(&pAdapter->scan_info.scan_finished_event,
                                          WEXT_CSCAN_SCAN_DONE_WAIT_TIME))
                {
                    hddLog(LOG1,"%s: Previous SCAN does not finished on time",__func__);
                    return eHAL_STATUS_SUCCESS;
                }
            }
            /* Piggyback previous scan result */
            else if(WEXT_SCAN_PENDING_PIGGYBACK == scanPendingOption)
            {
                pAdapter->scan_info.waitScanResult = TRUE;
                return eHAL_STATUS_SUCCESS;
            }
        }
        pAdapter->scan_info.waitScanResult = FALSE;

        /* Check for scan IE */
        while( WEXT_CSCAN_SSID_SECTION == str_ptr[i] )
        {
            /* ssid_len */
            if(str_ptr[++i] != WEXT_CSCAN_CHANNEL_SECTION)
            {
                /* total number of ssid's */
                num_ssid++;
                /* increment length filed */
                i += str_ptr[i] + 1;
            }
            /* i should be saved and it will be pointing to 'C' */
        }

        VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO, "%s: numSsid %d !!!",__func__, num_ssid);
        if( num_ssid )
        {
            /* To be fixed in SME and PE: override the number of ssid with 1,
            * as SME and PE does not handle multiple SSID in scan request
            * */
          scanRequest.SSIDs.numOfSSIDs = num_ssid;
          /* Allocate num_ssid tCsrSSIDInfo structure */
          SsidInfo = scanRequest.SSIDs.SSIDList =( tCsrSSIDInfo *)vos_mem_malloc(num_ssid*sizeof(tCsrSSIDInfo));
          if(NULL == scanRequest.SSIDs.SSIDList) {
             hddLog(VOS_TRACE_LEVEL_ERROR, "memory alloc failed SSIDInfo buffer");
             return -ENOMEM;
          }

          /* copy all the ssid's and their length */
          ssid_start = WEXT_CSCAN_HEADER_SIZE + 1;/* skipping 'S' */
          for(j = 0; j < num_ssid; j++) {
             if( SIR_MAC_MAX_SSID_LENGTH < str_ptr[ssid_start]){
                scanRequest.SSIDs.numOfSSIDs -= 1;
             } else{
                /* get the ssid length */
                SsidInfo->SSID.length = str_ptr[ssid_start++];
                vos_mem_copy(SsidInfo->SSID.ssId, &str_ptr[ssid_start], SsidInfo->SSID.length);
                hddLog(VOS_TRACE_LEVEL_INFO_HIGH, "SSID number %d:  %s", j, SsidInfo->SSID.ssId);
             }
                /* skipping length */
             ssid_start += str_ptr[ssid_start - 1] + 1;
             /* Store next ssid info */
             SsidInfo++;
          }
       }

        /* Check for Channel IE */
        if ( WEXT_CSCAN_CHANNEL_SECTION == str_ptr[i])
        {
            if( str_ptr[++i] == 0 )
            {
                scanRequest.ChannelInfo.numOfChannels = 0;
                scanRequest.ChannelInfo.ChannelList = NULL;
                i++;
            }
            else {

                /* increment the counter */
                scanRequest.ChannelInfo.numOfChannels = str_ptr[i++];
                /* store temp channel list */
                /* SME expects 1 byte channel content */
                scanRequest.ChannelInfo.ChannelList = vos_mem_malloc(scanRequest.ChannelInfo.numOfChannels * sizeof(v_U8_t));
                if(NULL == scanRequest.ChannelInfo.ChannelList)
                {
                    hddLog(LOGE, "memory alloc failed for channel list creation");
                    status = -ENOMEM;
                    goto exit_point;
                }

                for(channelIdx = 0; channelIdx < scanRequest.ChannelInfo.numOfChannels; channelIdx++)
                {
                   /* SCAN request from upper layer has 2 bytes channel */
                   scanRequest.ChannelInfo.ChannelList[channelIdx] = (v_U8_t)str_ptr[i];
                   i += sizeof(v_U16_t);
                }
            }
        }

        /* Set default */
        scanRequest.scanType = eSIR_ACTIVE_SCAN;
        scanRequest.minChnTime = 0;
        scanRequest.maxChnTime = 0;

        /* Now i is pointing to passive dwell dwell time */
        /* 'P',min dwell time, max dwell time */
        /* next two offsets contain min and max channel time */
        if( WEXT_CSCAN_PASV_DWELL_SECTION == (str_ptr[i]) )
        {
            /* No SSID specified, num_ssid == 0, then start passive scan */
            if (!num_ssid || (eSIR_PASSIVE_SCAN == pAdapter->scan_info.scan_mode))
            {
                scanRequest.scanType = eSIR_PASSIVE_SCAN;
                scanRequest.minChnTime = (v_U8_t)str_ptr[++i];//scanReq->min_channel_time;
                scanRequest.maxChnTime = (v_U8_t)str_ptr[++i];//scanReq->max_channel_time;
                i++;
            }
            else
            {
                i += 3;
            }
        }

        /* H indicates active channel time */
        if( WEXT_CSCAN_HOME_DWELL_SECTION == (str_ptr[i]) )
        {
            if (num_ssid || (eSIR_ACTIVE_SCAN == pAdapter->scan_info.scan_mode))
            {
                scanRequest.scanType = eSIR_ACTIVE_SCAN;
                scanRequest.minChnTime = str_ptr[++i];//scanReq->min_channel_time;
                scanRequest.maxChnTime = str_ptr[++i];//scanReq->max_channel_time;
                i++;
            }
            else
            {
                i +=3;
            }
        }
        scanRequest.BSSType = eCSR_BSS_TYPE_ANY;
        /* set requestType to full scan */
        scanRequest.requestType = eCSR_SCAN_REQUEST_FULL_SCAN;
        pAdapter->scan_info.mScanPending = TRUE;

        /* if previous genIE is not NULL, update ScanIE */
        if(0 != pwextBuf->genIE.length)
        {
            memset( &pAdapter->scan_info.scanAddIE, 0, sizeof(pAdapter->scan_info.scanAddIE) );
            memcpy( pAdapter->scan_info.scanAddIE.addIEdata, pwextBuf->genIE.addIEdata,
                pwextBuf->genIE.length );
            pAdapter->scan_info.scanAddIE.length = pwextBuf->genIE.length;

            pwextBuf->roamProfile.pAddIEScan = pAdapter->scan_info.scanAddIE.addIEdata;
            pwextBuf->roamProfile.nAddIEScanLength = pAdapter->scan_info.scanAddIE.length;

            /* clear previous genIE after use it */
            memset( &pwextBuf->genIE, 0, sizeof(pwextBuf->genIE) );
        }

        /* push addIEScan in scanRequset if exist */
        if (pAdapter->scan_info.scanAddIE.length &&
            (pAdapter->scan_info.scanAddIE.length <=
             sizeof(pAdapter->scan_info.scanAddIE.addIEdata)))
        {
            scanRequest.uIEFieldLen = pAdapter->scan_info.scanAddIE.length;
            scanRequest.pIEField = pAdapter->scan_info.scanAddIE.addIEdata;
        }

        status = sme_ScanRequest((WLAN_HDD_GET_CTX(pAdapter))->hHal,
                                 pAdapter->sessionId, &scanRequest, &scanId,
                                 &hdd_ScanRequestCallback, dev);
        if( !HAL_STATUS_SUCCESS(status) )
        {
            VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_FATAL, "%s: SME scan fail status %d !!!",__func__, status);
            pAdapter->scan_info.mScanPending = FALSE;
            status = -EINVAL;
            goto exit_point;
        }

        pAdapter->scan_info.scanId = scanId;

    } //end of data->pointer
    else {
        status = -1;
    }

exit_point:

    /* free ssidlist */
    if (scanRequest.SSIDs.SSIDList)
    {
        vos_mem_free(scanRequest.SSIDs.SSIDList);
    }
    /* free the channel list */
    if(scanRequest.ChannelInfo.ChannelList)
    {
        vos_mem_free((void*)scanRequest.ChannelInfo.ChannelList);
    }

    EXIT();
    VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO, "%s: exit !!!",__func__);
    return status;
}

/* Abort any MAC scan if in progress */
void hdd_abort_mac_scan(hdd_context_t* pHddCtx, tANI_U8 sessionId,
                        eCsrAbortReason reason)
{
    sme_AbortMacScan(pHddCtx->hHal, sessionId, reason);
}
