qcacld-2.0的wlan分析之二
阿新 • • 發佈:2019-01-08
在裝置進行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
函式實現資料傳輸