高通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;
}
注册
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); //发送事件到链接状态机
在往下就是状态机了