高通QSDK 驱动roam过程分析

wpa的roam命令底层驱动处理过程

nl80211_connect ----------------------来自nl80211_ops
	cfg80211_connect
		rdev_connect = wlan_cfg80211_connect ---------------来自wlan_cfg80211_ops
                osif_cm_connect
                    ucfg_cm_start_connect
                        cm_connect_start_req
                                status = cm_sm_deliver_event(vdev, WLAN_CM_SM_EV_CONNECT_REQ, sizeof(*connect_req), connect_req); //发送事件到链接状态机
  • 在应用层调用wpa_cli -p /var/run/wpa_supplicant-ath0 -iath0 roam 00:0f:ff:01:40:12这样的漫游命令后,wpa应用程序会通过一些列调用到wpa_driver_nl80211_try_connect函数中(具体过程参见《todo》),发送命令为NL80211_CMD_CONNECT的nl消息到底层驱动;

  • 底层驱动收到消息后,会调用qca/src/linux-4.4/net/wireless/nl80211.c/static const struct genl_ops nl80211_ops[] 中定义的对应处理函数nl80211_connect

下面我们具体分析下qca/src/linux-4.4/net/wireless/nl80211.c/nl80211_connect函数

nl80211_connect函数分析

static int nl80211_connect(struct sk_buff *skb, struct genl_info *info)
{
	struct cfg80211_connect_params connect;
	struct wiphy *wiphy;
	struct cfg80211_cached_keys *connkeys = NULL;
	int err;
   // 构建链接参数,把nl消息里面的应用层传过来内容赋值到connect 里面
	memset(&connect, 0, sizeof(connect));
    // 这里只是演示了部分赋值代码,具体请看源码
    if (info->attrs[NL80211_ATTR_IE]) {
		connect.ie = nla_data(info->attrs[NL80211_ATTR_IE]);
		connect.ie_len = nla_len(info->attrs[NL80211_ATTR_IE]);
	}
    if (info->attrs[NL80211_ATTR_MAC])
		connect.bssid = nla_data(info->attrs[NL80211_ATTR_MAC]);
	else if (info->attrs[NL80211_ATTR_MAC_HINT])
		connect.bssid_hint =
			nla_data(info->attrs[NL80211_ATTR_MAC_HINT]);
	connect.ssid = nla_data(info->attrs[NL80211_ATTR_SSID]);
	connect.ssid_len = nla_len(info->attrs[NL80211_ATTR_SSID]);
    
    // 赋值完后,调用qca/src/linux-4.4/net/wireless/sme.c/cfg80211_connect,
   err = cfg80211_connect(rdev, dev, &connect, connkeys, NULL);
    
    return err;
}

cfg80211_connect函数分析

qca/src/linux-4.4/net/wireless/sme.c/cfg80211_connect函数原型如下:

/*
 * API calls for nl80211/wext compatibility code
 */
int cfg80211_connect(struct cfg80211_registered_device *rdev,
		     struct net_device *dev,
		     struct cfg80211_connect_params *connect,
		     struct cfg80211_cached_keys *connkeys,
		     const u8 *prev_bssid)
{
	struct wireless_dev *wdev = dev->ieee80211_ptr;
	int err;
   // 此处省略无用代码
	memcpy(wdev->ssid, connect->ssid, connect->ssid_len);
	wdev->ssid_len = connect->ssid_len;
	if (!rdev->ops->connect){
		err = cfg80211_sme_connect(wdev, connect, prev_bssid);
	}else{
		// 这里会走这个分支,我们下面分析该函数qca/src/linux-4.4/net/wireless/rdev-ops.h/rdev_connect 函数
		err = rdev_connect(rdev, dev, connect);
	}
 

	return 0;
}

rdev_connect函数分析

static inline int rdev_connect(struct cfg80211_registered_device *rdev,
			       struct net_device *dev,
			       struct cfg80211_connect_params *sme)
{
	int ret;
	trace_rdev_connect(&rdev->wiphy, dev, sme);
	// 这里有个回调connect,下面分析如何注册,和里面的内容为什么内容,这里 rdev->ops->connect == wlan_cfg80211_connect 后面分析
	ret = rdev->ops->connect(&rdev->wiphy, dev, sme);
	trace_rdev_return_int(&rdev->wiphy, ret);
	return ret;
}
  1. 注册rdev->ops地方qca-wifi-g431c69b42e38-dirty/os/linux/src/ieee80211_cfg80211.c/ieee80211_cfg80211_radio_attach函数

    __ol_ath_attach
    {   
        #if UMAC_SUPPORT_CFG80211
                if (ic->ic_cfg80211_config) {
                    ic->ic_cfg80211_radio_attach(osdev->device, pdev_netdev, ic); 
                    // 这里的 ic_cfg80211_radio_attach == ieee80211_cfg80211_radio_attach
                }
        #endif
    }
    
    ieee80211_cfg80211_radio_attach
    	    wiphy = wlan_cfg80211_wiphy_alloc(&wlan_cfg80211_ops, sizeof(struct cfg80211_context));
    				 wiphy = wiphy_new(ops, priv_size);
    							return wiphy_new_nm(ops, sizeof_priv, NULL);
    
    • qca-wifi-g431c69b42e38-dirty-unified-profile/qca-wifi-g431c69b42e38-dirty/os/linux/src/ieee80211_cfg80211.c/wlan_cfg80211_ops内容如下:

      
      /**
       * struct cfg80211_ops - cfg80211_ops
       *
       * @add_virtual_intf: Add virtual interface
       * @del_virtual_intf: Delete virtual interface
       * @change_virtual_intf: Change virtual interface
       * @change_station: Change station
       * @add_beacon: Add beacon in sap mode
       * @del_beacon: Delete beacon in sap mode
       * @set_beacon: Set beacon in sap mode
       * @start_ap: Start ap
       * @change_beacon: Change beacon
       * @stop_ap: Stop ap
       * @change_bss: Change bss
       * @add_key: Add key
       * @get_key: Get key
       * @del_key: Delete key
       * @set_default_key: Set default key
       * @set_channel: Set channel
       * @scan: Scan
       * @connect: Connect
       * @disconnect: Disconnect
       * @join_ibss = Join ibss
       * @leave_ibss = Leave ibss
       * @set_wiphy_params = Set wiphy params
       * @set_tx_power = Set tx power
       * @get_tx_power = get tx power
       * @remain_on_channel = Remain on channel
       * @cancel_remain_on_channel = Cancel remain on channel
       * @mgmt_tx = Tx management frame
       * @mgmt_tx_cancel_wait = Cancel management tx wait
       * @set_default_mgmt_key = Set default management key
       * @set_txq_params = Set tx queue parameters
       * @get_station = Get station
       * @set_power_mgmt = Set power management
       * @del_station = Delete station
       * @add_station = Add station
       * @set_pmksa = Set pmksa
       * @del_pmksa = Delete pmksa
       * @flush_pmksa = Flush pmksa
       * @update_ft_ies = Update FT IEs
       * @tdls_mgmt = Tdls management
       * @tdls_oper = Tdls operation
       * @set_rekey_data = Set rekey data
       * @sched_scan_start = Scheduled scan start
       * @sched_scan_stop = Scheduled scan stop
       * @resume = Resume wlan
       * @suspend = Suspend wlan
       * @set_mac_acl = Set mac acl
       * @testmode_cmd = Test mode command
       * @set_ap_chanwidth = Set AP channel bandwidth
       * @dump_survey = Dump survey
       * @key_mgmt_set_pmk = Set pmk key management
       * @get_channel = get Channel info
       */
      static struct cfg80211_ops wlan_cfg80211_ops = {
          .add_virtual_intf = wlan_cfg80211_add_virtual_intf,
          .del_virtual_intf = wlan_cfg80211_del_virtual_intf,
      #if (LINUX_VERSION_CODE < KERNEL_VERSION(3, 4, 0))
          .set_beacon = NULL,
      #else
          .start_ap = wlan_cfg80211_start_ap,
          .change_beacon = wlan_cfg80211_change_beacon,
          .stop_ap = wlan_cfg80211_stop_ap,
      #endif
          .change_bss = wlan_cfg80211_change_bss,
          .add_key = wlan_cfg80211_add_key,
          .get_key = wlan_cfg80211_get_key,
          .del_key = wlan_cfg80211_del_key,
          .set_default_key = wlan_cfg80211_set_default_key,
          .scan = wlan_cfg80211_scan_start,
      #if (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 5, 0))
          .abort_scan = wlan_cfg80211_scan_abort,
      #endif
          .connect = wlan_cfg80211_connect,  // 连接函数
          .disconnect = wlan_cfg80211_disconnect,
          .join_ibss = NULL,
          .leave_ibss = NULL,
          .set_wiphy_params = wlan_cfg80211_set_wiphy_params,
          .set_tx_power = wlan_cfg80211_set_txpower,
          .get_tx_power = wlan_cfg80211_get_txpower,
          .set_default_mgmt_key = wlan_cfg80211_set_default_mgmt_key,
          .set_default_beacon_key = wlan_cfg80211_set_default_beacon_key,
          .get_station = wlan_cfg80211_get_station,
          .set_power_mgmt = wlan_cfg80211_set_power_mgmt,
          .set_pmksa = NULL,
          .del_pmksa = NULL,
          .flush_pmksa = NULL,
          .update_ft_ies = wlan_cfg80211_update_ft_ies,
          .change_virtual_intf = wlan_cfg80211_change_virtual_intf,
          .probe_client = wlan_cfg80211_probe_client,
          .add_station = wlan_cfg80211_add_station,
          .del_station = wlan_cfg80211_del_station,
          .change_station = wlan_cfg80211_change_station,
          .set_rekey_data = wlan_cfg80211_set_rekey_dataata,
          .sched_scan_start = wlan_cfg80211_sched_scan_start,
          .sched_scan_stop = wlan_cfg80211_sched_scan_stop,
          .set_mac_acl = wlan_cfg80211_set_mac_acl,
      #ifdef CONFIG_NL80211_TESTMODE
          /* Callback funtion to receive UTF commands from FTM daemon */
          .testmode_cmd = wlan_cfg80211_testmode,
      #endif
          .set_antenna = wlan_cfg80211_set_antenna,
          .get_antenna = wlan_cfg80211_get_antenna,
          .mgmt_tx_cancel_wait = wlan_cfg80211_mgmt_tx_cancel_wait,
          .mgmt_tx = wlan_cfg80211_mgmt_tx,
          .set_txq_params = wlan_cfg80211_set_txq_params,
          .tdls_mgmt = NULL,
          .remain_on_channel = wlan_cfg80211_remain_on_channel,
          .tdls_oper = NULL,
          .cancel_remain_on_channel = wlan_cfg80211_cancel_remain_on_channel,
          .dump_survey = wlan_hdd_cfg80211_dump_survey,
          .channel_switch = wlan_cfg80211_channel_switch,
          .set_ap_chanwidth = wlan_cfg80211_set_ap_chanwidth,
          .get_channel = wlan_cfg80211_get_channel,
      #if ATH_SUPPORT_HS20
          .set_qos_map = wlan_cfg80211_set_qos_map,
      #endif
      #if UMAC_SUPPORT_WPA3_STA
          .external_auth = wlan_cfg80211_external_auth,
      #endif
      #ifdef WLAN_SUPPORT_FILS
          .set_fils_aad = wlan_cfg80211_set_fils_aad,
      #endif /* WLAN_SUPPORT_FILS */
      };
      
      

      由上面分析得到 .connect = wlan_cfg80211_connect 进而确定了 rdev->ops->connect==wlan_cfg80211_connect

    • 下面重点分析wlan_cfg80211_connect函数

wlan_cfg80211_connect 函数分析

qca-wifi-g431c69b42e38-dirty/os/linux/src/ieee80211_cfg80211.c/wlan_cfg80211_connect函数内容如下:

int wlan_cfg80211_connect(struct wiphy *wiphy,
        struct net_device *ndev,
        struct cfg80211_connect_params *req)
{
      // 这里省略了一些函数内容,我们重点关注过程,具体内容请看函数原型
        if (vap->iv_roam.iv_roaming) {
            // 因为是roam命令 故而这里 走这个分支
            WIM_LOG_DEBUG("wlan_cfg80211_connect"); 
            wlan_vdev_get_bss_peer_mac(vap->vdev_obj, &conn_param.prev_bssid);
            osif_vap_roam(ndev);
            // 终于到osif层了
            osif_cm_connect(ndev, vap->vdev_obj, req, &conn_param);
        } else {
            osif_vap_pre_init(ndev, 0);
            osif_cm_connect(ndev, vap->vdev_obj, req, &conn_param);
        }
        /* Restore bssid */
        req->bssid = old_bssid;
        return 0 ;
}
    osif_cm_connect
        ucfg_cm_start_connect
            cm_connect_start_req
            		status = cm_sm_deliver_event(vdev, WLAN_CM_SM_EV_CONNECT_REQ, sizeof(*connect_req), connect_req); //发送事件到链接状态机
				   在往下就是状态机了