1. 程式人生 > >qcacld-2.0的wlan分析之二

qcacld-2.0的wlan分析之二

在裝置進行pci匯流排註冊之後,進入pci驅動的probe函式,該函式中最為重要的就是開啟wlan的主機裝置驅動,即hdd_wlan_startup()函式:

/**---------------------------------------------------------------------------
  \brief hdd_wlan_startup() - HDD init function
  This is the driver startup code executed once a WLAN device has been detected
  \param  - dev - Pointer to the underlying device
  \return
- 0 for success, < 0 for failure --------------------------------------------------------------------------*/ int hdd_wlan_startup(struct device *dev, v_VOID_t *hif_sc) { VOS_STATUS status; hdd_adapter_t *pAdapter = NULL; #ifdef WLAN_OPEN_P2P_INTERFACE hdd_adapter_t *pP2pAdapter = NULL; #endif
hdd_context_t *pHddCtx = NULL; v_CONTEXT_t pVosContext= NULL; #ifdef WLAN_BTAMP_FEATURE VOS_STATUS vStatus = VOS_STATUS_SUCCESS; WLANBAP_ConfigType btAmpConfig; hdd_config_t *pConfig; #endif eHalStatus hal_status; int ret; int i; struct wiphy *wiphy; unsigned long rc; tSmeThermalParams thermalParam; tSirTxPowerLimit *hddtxlimit
; #ifdef FEATURE_WLAN_CH_AVOID int unsafeChannelIndex; #endif ENTER(); ....... pAdapter = hdd_open_adapter( pHddCtx, WLAN_HDD_INFRA_STATION, "wlan%d", wlan_hdd_get_intf_addr(pHddCtx), FALSE ); ...... success: EXIT(); return 0; }

該函式初始化內容繁多,主要集中的有兩部分,一個是vos_open 開啟開虛擬介面,另外是hdd_open_adapter 函式,提供了網路介面並建立了無線網路介面卡。

hdd_wlan_startup 中通過wlan_hdd_cfg80211_wiphy_alloc 為無線裝置分配一個wiphy結構體,再通過hdd_open_adapter 函式中hdd_register_interface 實現無線網路設備註冊,

/*
 * FUNCTION: wlan_hdd_cfg80211_wiphy_alloc
 * This function is called by hdd_wlan_startup()
 * during initialization.
 * This function is used to allocate wiphy structure.
 */
struct wiphy *wlan_hdd_cfg80211_wiphy_alloc(int priv_size)
{
    struct wiphy *wiphy;
    ENTER();

    /*
     *   Create wiphy device
     */
    wiphy = wiphy_new(&wlan_hdd_cfg80211_ops, priv_size);

    if (!wiphy)
    {
        /* Print error and jump into err label and free the memory */
        hddLog(VOS_TRACE_LEVEL_ERROR, "%s: wiphy init failed", __func__);
        return NULL;
    }

    return wiphy;
}

在分配新的wiphy時,關聯了wlan_hdd_cfg80211_ops 結構體,該結構體是cfg80211提供的操作結構體,即struct cfg80211_ops


hdd_adapter_t* hdd_open_adapter( hdd_context_t *pHddCtx, tANI_U8 session_type,
                                 const char *iface_name, tSirMacAddr macAddr,
                                 tANI_U8 rtnl_held )
{
   hdd_adapter_t *pAdapter = NULL;
   hdd_adapter_list_node_t *pHddAdapterNode = NULL;
   VOS_STATUS status = VOS_STATUS_E_FAILURE;
   VOS_STATUS exitbmpsStatus = VOS_STATUS_E_FAILURE;
   hdd_cfg80211_state_t *cfgState;
   int ret;

   hddLog(VOS_TRACE_LEVEL_INFO_HIGH, "%s: iface =%s type = %d\n", __func__,
                                      iface_name, session_type);

   if (pHddCtx->current_intf_count >= pHddCtx->max_intf_count){
        /* Max limit reached on the number of vdevs configured by the host.
         *  Return error
         */
        VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
                 "%s: Unable to add virtual intf: currentVdevCnt=%d,hostConfiguredVdevCnt=%d",
                 __func__,pHddCtx->current_intf_count, pHddCtx->max_intf_count);
        return NULL;
   }

   if(macAddr == NULL)
   {
         /* Not received valid macAddr */
         VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
                 "%s:Unable to add virtual intf: Not able to get"
                             "valid mac address",__func__);
         return NULL;
   }

   status = hdd_check_for_existing_macaddr(pHddCtx, macAddr);
   if (VOS_STATUS_E_FAILURE == status) {
         VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
                   "%s: Duplicate MAC addr: "MAC_ADDRESS_STR" already exists",
                   __func__, MAC_ADDR_ARRAY(macAddr));
         return NULL;
   }

   /*
    * If Powersave Offload is enabled
    * Fw will take care incase of concurrency
    */
   if(!pHddCtx->cfg_ini->enablePowersaveOffload)
   {
      //Disable BMPS incase of Concurrency
      exitbmpsStatus = hdd_disable_bmps_imps(pHddCtx, session_type);

      if(VOS_STATUS_E_FAILURE == exitbmpsStatus)
      {
         //Fail to Exit BMPS
         hddLog(VOS_TRACE_LEVEL_ERROR, FL("Fail to Exit BMPS"));
         VOS_ASSERT(0);
         return NULL;
      }
   }

   switch(session_type)
   {
      case WLAN_HDD_INFRA_STATION:
         /* Reset locally administered bit if the device mode is STA */
         WLAN_HDD_RESET_LOCALLY_ADMINISTERED_BIT(macAddr);
         /* fall through */
      case WLAN_HDD_P2P_CLIENT:
      case WLAN_HDD_P2P_DEVICE:
      {
         pAdapter = hdd_alloc_station_adapter( pHddCtx, macAddr, iface_name );

         if( NULL == pAdapter )
         {
            hddLog(VOS_TRACE_LEVEL_FATAL,
                   FL("failed to allocate adapter for session %d"), session_type);
            return NULL;
          }

         if (session_type == WLAN_HDD_INFRA_STATION)
            pAdapter->wdev.iftype = NL80211_IFTYPE_STATION;
         else if (session_type == WLAN_HDD_P2P_DEVICE)
            pAdapter->wdev.iftype = NL80211_IFTYPE_P2P_DEVICE;
         else
            pAdapter->wdev.iftype = NL80211_IFTYPE_P2P_CLIENT;

         pAdapter->device_mode = session_type;

         status = hdd_init_station_mode( pAdapter );
         if( VOS_STATUS_SUCCESS != status )
            goto err_free_netdev;

         status = hdd_register_interface( pAdapter, rtnl_held );
         if( VOS_STATUS_SUCCESS != status )
         {
            hdd_deinit_adapter(pHddCtx, pAdapter, rtnl_held);
            goto err_free_netdev;
         }
         // Workqueue which gets scheduled in IPv4 notification callback
#ifdef CONFIG_CNSS
         cnss_init_work(&pAdapter->ipv4NotifierWorkQueue,
                        hdd_ipv4_notifier_work_queue);
#else
         INIT_WORK(&pAdapter->ipv4NotifierWorkQueue,
                   hdd_ipv4_notifier_work_queue);
#endif

#ifdef WLAN_NS_OFFLOAD
         // Workqueue which gets scheduled in IPv6 notification callback.
#ifdef CONFIG_CNSS
         cnss_init_work(&pAdapter->ipv6NotifierWorkQueue,
                        hdd_ipv6_notifier_work_queue);
#else
         INIT_WORK(&pAdapter->ipv6NotifierWorkQueue,
                   hdd_ipv6_notifier_work_queue);
#endif
#endif
         //Stop the Interface TX queue.
         netif_tx_disable(pAdapter->dev);
         //netif_tx_disable(pWlanDev);
         netif_carrier_off(pAdapter->dev);

#ifdef QCA_LL_TX_FLOW_CT
         /* SAT mode default TX Flow control instance
          * This instance will be used for
          * STA mode, IBSS mode and TDLS mode */
         if (pAdapter->tx_flow_timer_initialized == VOS_FALSE) {
            vos_timer_init(&pAdapter->tx_flow_control_timer,
                           VOS_TIMER_TYPE_SW,
                           hdd_tx_resume_timer_expired_handler,
                           pAdapter);
            pAdapter->tx_flow_timer_initialized = VOS_TRUE;
         }
         WLANTL_RegisterTXFlowControl(pHddCtx->pvosContext,
                     hdd_tx_resume_cb,
                     pAdapter->sessionId,
                     (void *)pAdapter);
#endif /* QCA_LL_TX_FLOW_CT */

         break;
      }

      case WLAN_HDD_P2P_GO:
      case WLAN_HDD_SOFTAP:
      {
         pAdapter = hdd_wlan_create_ap_dev( pHddCtx, macAddr, (tANI_U8 *)iface_name );
         if( NULL == pAdapter )
         {
            hddLog(VOS_TRACE_LEVEL_FATAL,
                   FL("failed to allocate adapter for session %d"), session_type);
            return NULL;
         }

         pAdapter->wdev.iftype = (session_type == WLAN_HDD_SOFTAP) ?
                                  NL80211_IFTYPE_AP:
                                  NL80211_IFTYPE_P2P_GO;
         pAdapter->device_mode = session_type;

         status = hdd_init_ap_mode(pAdapter);
         if( VOS_STATUS_SUCCESS != status )
            goto err_free_netdev;

         status = hdd_register_hostapd( pAdapter, rtnl_held );
         if( VOS_STATUS_SUCCESS != status )
         {
            hdd_deinit_adapter(pHddCtx, pAdapter, rtnl_held);
            goto err_free_netdev;
         }

         netif_tx_disable(pAdapter->dev);
         netif_carrier_off(pAdapter->dev);

         hdd_set_conparam( 1 );

         break;
      }
      case WLAN_HDD_MONITOR:
      {
         pAdapter = hdd_alloc_station_adapter( pHddCtx, macAddr, iface_name );
         if( NULL == pAdapter )
         {
            hddLog(VOS_TRACE_LEVEL_FATAL,
                   FL("failed to allocate adapter for session %d"), session_type);
            return NULL;
         }

         pAdapter->wdev.iftype = NL80211_IFTYPE_MONITOR;
         pAdapter->device_mode = session_type;
         status = hdd_register_interface( pAdapter, rtnl_held );
         pAdapter->dev->netdev_ops = &wlan_mon_drv_ops;
         hdd_init_tx_rx( pAdapter );
         set_bit(INIT_TX_RX_SUCCESS, &pAdapter->event_flags);
         //Set adapter to be used for data tx. It will use either GO or softap.
         pAdapter->sessionCtx.monitor.pAdapterForTx =
                           hdd_get_adapter(pAdapter->pHddCtx, WLAN_HDD_SOFTAP);
         if (NULL == pAdapter->sessionCtx.monitor.pAdapterForTx)
         {
            pAdapter->sessionCtx.monitor.pAdapterForTx =
                           hdd_get_adapter(pAdapter->pHddCtx, WLAN_HDD_P2P_GO);
         }
         /* This work queue will be used to transmit management packet over
          * monitor interface. */
         if (NULL == pAdapter->sessionCtx.monitor.pAdapterForTx) {
             hddLog(VOS_TRACE_LEVEL_ERROR,"%s:Failed:hdd_get_adapter",__func__);
             return NULL;
         }

#ifdef CONFIG_CNSS
         cnss_init_work(&pAdapter->sessionCtx.monitor.pAdapterForTx->
                        monTxWorkQueue, hdd_mon_tx_work_queue);
#else
         INIT_WORK(&pAdapter->sessionCtx.monitor.pAdapterForTx->monTxWorkQueue,
                   hdd_mon_tx_work_queue);
#endif
      }
         break;
      case WLAN_HDD_FTM:
      {
         pAdapter = hdd_alloc_station_adapter( pHddCtx, macAddr, iface_name );

         if( NULL == pAdapter )
         {
            hddLog(VOS_TRACE_LEVEL_FATAL,
                   FL("failed to allocate adapter for session %d"), session_type);
            return NULL;
         }

         /* Assign NL80211_IFTYPE_STATION as interface type to resolve Kernel Warning
          * message while loading driver in FTM mode. */
         pAdapter->wdev.iftype = NL80211_IFTYPE_STATION;
         pAdapter->device_mode = session_type;
         status = hdd_register_interface( pAdapter, rtnl_held );

         hdd_init_tx_rx( pAdapter );

         //Stop the Interface TX queue.
         netif_tx_disable(pAdapter->dev);
         netif_carrier_off(pAdapter->dev);
      }
         break;
      default:
      {
         hddLog(VOS_TRACE_LEVEL_FATAL,"%s Invalid session type %d",
                __func__, session_type);
         VOS_ASSERT(0);
         return NULL;
      }
   }

......

   return NULL;
}

hdd_register_interface 函式中實現網路設備註冊:


VOS_STATUS hdd_register_interface( hdd_adapter_t *pAdapter, tANI_U8 rtnl_lock_held )
{
   struct net_device *pWlanDev = pAdapter->dev;
   //hdd_station_ctx_t *pHddStaCtx = &pAdapter->sessionCtx.station;
   //hdd_context_t *pHddCtx = WLAN_HDD_GET_CTX( pAdapter );
   //eHalStatus halStatus = eHAL_STATUS_SUCCESS;

   if( rtnl_lock_held )
   {
     if (strnchr(pWlanDev->name, strlen(pWlanDev->name), '%')) {
         if( dev_alloc_name(pWlanDev, pWlanDev->name) < 0 )
         {
            hddLog(VOS_TRACE_LEVEL_ERROR,"%s:Failed:dev_alloc_name",__func__);
            return VOS_STATUS_E_FAILURE;
         }
      }
      if (register_netdevice(pWlanDev))
      {
         hddLog(VOS_TRACE_LEVEL_ERROR,"%s:Failed:register_netdev",__func__);
         return VOS_STATUS_E_FAILURE;
      }
   }
   else
   {
      if(register_netdev(pWlanDev))
      {
         hddLog(VOS_TRACE_LEVEL_ERROR,"%s: Failed:register_netdev",__func__);
         return VOS_STATUS_E_FAILURE;
      }
   }
   set_bit(NET_DEVICE_REGISTERED, &pAdapter->event_flags);

   return VOS_STATUS_SUCCESS;
}

hdd_wlan_startup 中的hdd_alloc_station_adapter 函式為網路裝置分配了一個網路介面卡,具體實現通過hdd_set_station_ops 函式:

void hdd_set_station_ops( struct net_device *pWlanDev )
{
      pWlanDev->netdev_ops = &wlan_drv_ops;
}

wlan_drv_ops 就是網路裝置的操作結構體,定義如下:


static struct net_device_ops wlan_drv_ops = {
      .ndo_open = hdd_open,
      .ndo_stop = hdd_stop,
      .ndo_uninit = hdd_uninit,
      .ndo_start_xmit = hdd_hard_start_xmit,
      .ndo_tx_timeout = hdd_tx_timeout,
      .ndo_get_stats = hdd_stats,
      .ndo_do_ioctl = hdd_ioctl,
      .ndo_set_mac_address = hdd_set_mac_address,
      .ndo_select_queue    = hdd_select_queue,
#ifdef WLAN_FEATURE_PACKET_FILTERING
#if (LINUX_VERSION_CODE > KERNEL_VERSION(3,1,0))
      .ndo_set_rx_mode = hdd_set_multicast_list,
#else
      .ndo_set_multicast_list = hdd_set_multicast_list,
#endif //LINUX_VERSION_CODE
#endif
 };

其中hdd_hard_start_xmit 函式實現資料傳輸