wpa_supplicant与kernel交互的操作,一般需要先明确驱动接口,以及用户态和kernel态的接口函数,以此来进行调用操作。这里分为4个步骤讨论。
1.首先需要明确指定的驱动接口。因为有较多的驱动接口可以使用,如wext、nl80211等。指定了之后,才能调用相应接口的方法。
2.保存驱动接口
3.接口函数的实现(分为用户态和kernel态)。系统已经定义了,我们只需找到定义的地方,了解有哪些函数。
4.交互
(a)用户态向kernel态发送请求(通过ioctl)
(b)kernel态向用户态发送事件通知(通过netlink)
1.首先需要明确指定的驱动接口
(1)查看init.XX.rc中指定的driver的命令参数;
(2)根据命令参数,在wpa_driver_ops *wpa_drivers[] 中查找对应接口。
wpa_drivers[]的定义是在[-->external/wpa_supplicant_8/src/drivers/drivers.c]
2.保存驱动接口
在wpa_supplicant初始化过程中,在wpa_supplicant_init_iface方法中会调用wpa_supplicant_set_driver方法。该方法中又会调用select_driver方法。
static int select_driver(struct wpa_supplicant *wpa_s, int i)
{ struct wpa_global *global = wpa_s->global;if (wpa_drivers[i]->global_init && global->drv_priv[i] == NULL) {
//调用global_init方法,这与driver选择wext调用的流程不同了
global->drv_priv[i] = wpa_drivers[i]->global_init(); if (global->drv_priv[i] == NULL) {
wpa_printf(MSG_ERROR, "Failed to initialize driver "
"'%s'", wpa_drivers[i]->name);
return -1;
}
}
// 根据name进行匹配,并最后保存到wpa_supplicant->dirver中
wpa_s->driver = wpa_drivers[i];
wpa_s->global_drv_priv = global->drv_priv[i]; return 0;
}
3.接口操作函数实现
3.1用户态
代码:/external/wpa_supplicant_8/wpa_supplicant/src/drivers/driver_nl80211.c
const struct wpa_driver_ops wpa_driver_nl80211_ops = {
.name = "nl80211",
.desc = "Linux nl80211/cfg80211",
.get_bssid = wpa_driver_nl80211_get_bssid,
.get_ssid = wpa_driver_nl80211_get_ssid,
.set_key = wpa_driver_nl80211_set_key,
.scan2 = wpa_driver_nl80211_scan,
.sched_scan = wpa_driver_nl80211_sched_scan,
.stop_sched_scan = wpa_driver_nl80211_stop_sched_scan,
.get_scan_results2 = wpa_driver_nl80211_get_scan_results,
.deauthenticate = wpa_driver_nl80211_deauthenticate,
.disassociate = wpa_driver_nl80211_disassociate,
.authenticate = wpa_driver_nl80211_authenticate,
.associate = wpa_driver_nl80211_associate,
.global_init = nl80211_global_init,
.global_deinit = nl80211_global_deinit,
.init2 = wpa_driver_nl80211_init,
.deinit = wpa_driver_nl80211_deinit,
.get_capa = wpa_driver_nl80211_get_capa,
.set_operstate = wpa_driver_nl80211_set_operstate,
.set_supp_port = wpa_driver_nl80211_set_supp_port,
.set_country = wpa_driver_nl80211_set_country,
.set_ap = wpa_driver_nl80211_set_ap,
.if_add = wpa_driver_nl80211_if_add,
.if_remove = wpa_driver_nl80211_if_remove,
.send_mlme = wpa_driver_nl80211_send_mlme,
.get_hw_feature_data = wpa_driver_nl80211_get_hw_feature_data,
.sta_add = wpa_driver_nl80211_sta_add,
.sta_remove = wpa_driver_nl80211_sta_remove,
.hapd_send_eapol = wpa_driver_nl80211_hapd_send_eapol,
#ifdef ANDROID_QCOM_PATCH
.hapd_set_countermeasures = wpa_driver_nl80211_set_countermeasures,#endif
.sta_set_flags = wpa_driver_nl80211_sta_set_flags,
#ifdef HOSTAPD
.hapd_init = i802_init,
.hapd_deinit = i802_deinit,
.set_wds_sta = i802_set_wds_sta,#endif /* HOSTAPD */#if defined(HOSTAPD) || defined(CONFIG_AP)
.get_seqnum = i802_get_seqnum,
.flush = i802_flush,
.read_sta_data = i802_read_sta_data,
.get_inact_sec = i802_get_inact_sec,
.sta_clear_stats = i802_sta_clear_stats,
.set_rts = i802_set_rts,
.set_frag = i802_set_frag,
.set_tx_queue_params = i802_set_tx_queue_params,
.set_sta_vlan = i802_set_sta_vlan,
.sta_deauth = i802_sta_deauth,
.sta_disassoc = i802_sta_disassoc,#endif /* HOSTAPD || CONFIG_AP */
.set_freq = i802_set_freq,
.send_action = wpa_driver_nl80211_send_action,
.send_action_cancel_wait = wpa_driver_nl80211_send_action_cancel_wait,
.remain_on_channel = wpa_driver_nl80211_remain_on_channel,
.cancel_remain_on_channel =
wpa_driver_nl80211_cancel_remain_on_channel,
.probe_req_report = wpa_driver_nl80211_probe_req_report,
.deinit_ap = wpa_driver_nl80211_deinit_ap,
.resume = wpa_driver_nl80211_resume,
.send_ft_action = nl80211_send_ft_action,
.signal_monitor = nl80211_signal_monitor,
.signal_poll = nl80211_signal_poll,
.send_frame = nl80211_send_frame,
.shared_freq = wpa_driver_nl80211_shared_freq,
.set_param = nl80211_set_param,
.get_radio_name = nl80211_get_radio_name,
.add_pmkid = nl80211_add_pmkid,
.remove_pmkid = nl80211_remove_pmkid,
.flush_pmkid = nl80211_flush_pmkid,
.set_rekey_info = nl80211_set_rekey_info,
.poll_client = nl80211_poll_client,
.set_p2p_powersave = nl80211_set_p2p_powersave,
#ifdef CONFIG_TDLS
.send_tdls_mgmt = nl80211_send_tdls_mgmt,
.tdls_oper = nl80211_tdls_oper,#endif /* CONFIG_TDLS */#ifdef ANDROID_P2P
.set_noa = wpa_driver_set_p2p_noa,#endif#ifdef ANDROID
.driver_cmd = wpa_driver_nl80211_driver_cmd, //处理DRIVER开头的命令#endif};
ps:driver_cmd用于处理DRIVER的命令,调用流程如下:
wpa_supplicant_ctrl_iface_process-> (根据命令字符串调用对应的函数)
wpa_supplicant_driver_cmd->wpa_drv_driver_cmd->wpa_s->driver->driver_cmd->wpa_driver_nl80211_driver_cmd -> (User)
...
cfg80211...
3.2 kernel态实现
Kernel态实现的操作函数,实现代码见:net/wireless/wext-compat.c
static const iw_handler cfg80211_handlers[] = {
[IW_IOCTL_IDX(SIOCGIWNAME)] = (iw_handler) cfg80211_wext_giwname,
[IW_IOCTL_IDX(SIOCSIWFREQ)] = (iw_handler) cfg80211_wext_siwfreq,
[IW_IOCTL_IDX(SIOCGIWFREQ)] = (iw_handler) cfg80211_wext_giwfreq,
[IW_IOCTL_IDX(SIOCSIWMODE)] = (iw_handler) cfg80211_wext_siwmode,
[IW_IOCTL_IDX(SIOCGIWMODE)] = (iw_handler) cfg80211_wext_giwmode,
[IW_IOCTL_IDX(SIOCGIWRANGE)] = (iw_handler) cfg80211_wext_giwrange,
[IW_IOCTL_IDX(SIOCSIWAP)] = (iw_handler) cfg80211_wext_siwap,
[IW_IOCTL_IDX(SIOCGIWAP)] = (iw_handler) cfg80211_wext_giwap,
[IW_IOCTL_IDX(SIOCSIWMLME)] = (iw_handler) cfg80211_wext_siwmlme,
[IW_IOCTL_IDX(SIOCSIWSCAN)] = (iw_handler) cfg80211_wext_siwscan,
[IW_IOCTL_IDX(SIOCGIWSCAN)] = (iw_handler) cfg80211_wext_giwscan,
[IW_IOCTL_IDX(SIOCSIWESSID)] = (iw_handler) cfg80211_wext_siwessid,
[IW_IOCTL_IDX(SIOCGIWESSID)] = (iw_handler) cfg80211_wext_giwessid,
[IW_IOCTL_IDX(SIOCSIWRATE)] = (iw_handler) cfg80211_wext_siwrate,
[IW_IOCTL_IDX(SIOCGIWRATE)] = (iw_handler) cfg80211_wext_giwrate,
[IW_IOCTL_IDX(SIOCSIWRTS)] = (iw_handler) cfg80211_wext_siwrts,
[IW_IOCTL_IDX(SIOCGIWRTS)] = (iw_handler) cfg80211_wext_giwrts,
[IW_IOCTL_IDX(SIOCSIWFRAG)] = (iw_handler) cfg80211_wext_siwfrag,
[IW_IOCTL_IDX(SIOCGIWFRAG)] = (iw_handler) cfg80211_wext_giwfrag,
[IW_IOCTL_IDX(SIOCSIWTXPOW)] = (iw_handler) cfg80211_wext_siwtxpower,
[IW_IOCTL_IDX(SIOCGIWTXPOW)] = (iw_handler) cfg80211_wext_giwtxpower,
[IW_IOCTL_IDX(SIOCSIWRETRY)] = (iw_handler) cfg80211_wext_siwretry,
[IW_IOCTL_IDX(SIOCGIWRETRY)] = (iw_handler) cfg80211_wext_giwretry,
[IW_IOCTL_IDX(SIOCSIWENCODE)] = (iw_handler) cfg80211_wext_siwencode,
[IW_IOCTL_IDX(SIOCGIWENCODE)] = (iw_handler) cfg80211_wext_giwencode,
[IW_IOCTL_IDX(SIOCSIWPOWER)] = (iw_handler) cfg80211_wext_siwpower,
[IW_IOCTL_IDX(SIOCGIWPOWER)] = (iw_handler) cfg80211_wext_giwpower,
[IW_IOCTL_IDX(SIOCSIWGENIE)] = (iw_handler) cfg80211_wext_siwgenie,
[IW_IOCTL_IDX(SIOCSIWAUTH)] = (iw_handler) cfg80211_wext_siwauth,
[IW_IOCTL_IDX(SIOCGIWAUTH)] = (iw_handler) cfg80211_wext_giwauth,
[IW_IOCTL_IDX(SIOCSIWENCODEEXT)]= (iw_handler) cfg80211_wext_siwencodeext,
[IW_IOCTL_IDX(SIOCSIWPMKSA)] = (iw_handler) cfg80211_wext_siwpmksa,
};const struct iw_handler_def cfg80211_wext_handler = {
.num_standard = ARRAY_SIZE(cfg80211_handlers),
.standard = cfg80211_handlers,
.get_wireless_stats = cfg80211_wireless_stats,
};
4.用户态和kernel态交互
4.1初始化
首先说明下用户态和kernel态交互的方式,如下所述:
a.用户态向kernel态发送请求时,通过ioctl来实现
b.kernel态向用户态发送事件通知,通过netlink实现
交互的初始化有两部分组成:nl80211_global_init和wpa_driver_nl80211_init方法。以上a/b两点中ioctl和netlink是在nl80211_global_init方法中创建。
(1) nl80211_global_init方法
因为在”2.保存驱动接口”,select_driver方法中调用了global_init方法(会根据用户态的结构体wpa_driver_nl80211_ops中查找对应方法,即nl80211_global_init)。
static void * nl80211_global_init(void)
{ struct nl80211_global *global; struct netlink_config *cfg; global = os_zalloc(sizeof(*global)); if (global == NULL) return NULL; global->ioctl_sock = -1;
dl_list_init(&global->interfaces); global->if_add_ifindex = -1;
cfg = os_zalloc(sizeof(*cfg));
if (cfg == NULL) goto err;
cfg->ctx = global;
cfg->newlink_cb = wpa_driver_nl80211_event_rtm_newlink;
cfg->dellink_cb = wpa_driver_nl80211_event_rtm_dellink; global->netlink = netlink_init(cfg); //初始化netlink,并注册事件接收函数
if (global->netlink == NULL) {
os_free(cfg); goto err;
}
if (wpa_driver_nl80211_init_nl_global(global) < 0)
goto err;// 此global->ioctl_sock用作为ioctl命令的fd
global->ioctl_sock = socket(PF_INET, SOCK_DGRAM, 0); if (global->ioctl_sock < 0) {
perror("socket(PF_INET,SOCK_DGRAM)"); goto err;
}
return global;
err:
nl80211_global_deinit(global); return NULL;
}
在nl80211_global_init方法中,有两条关键语句:
(a)// 初始化netlink,并注册事件接收函数 global->netlink = netlink_init(cfg);
(b)// 此global->ioctl_sock用作为ioctl命令的fdglobal->ioctl_sock = socket(PF_INET, SOCK_DGRAM, 0);
分析以上两句:
(a)netlink_init方法中创建了一个socket,并添加到eloop_run方法中的rfds中。用于从kernel态发送事件给用户态
netlink->sock =->sock, netlink_receive, netlink,NULL);
(b)该socket用于从用户态发送请求给kernel态
(2)wpa_driver_nl80211_init方法
在wpa_supplicant_init_iface方法中有语句:
if (wpa_supplicant_set_driver(wpa_s, driver) < 0)
return -1;
wpa_s->drv_priv = wpa_drv_init(wpa_s, wpa_s->ifname);
在设置完驱动后,会调用wpa_drv_init方法,其方法体中会调用init2方法,即wpa_driver_nl80211_init。该方法用来Initialize nl80211 driver interface.
4.2 用户态和kernel态交互之ioctl实现
在用户态可简单执行一个ioctl(fd,cmd,...)命令即可。
先看下socket.c文件
/*
* Socket files have a set of 'special' operations as well as the generic file ones. These don't appear in the operation structures but are done directly via the socketcall() multiplexor. */static const struct file_operations socket_file_ops = {
.owner = THIS_MODULE,
.llseek = no_llseek,
.aio_read = sock_aio_read,
.aio_write = sock_aio_write,
.poll = sock_poll,
.unlocked_ioctl = sock_ioctl, // 这个就是被执行的ioctl #ifdef CONFIG_COMPAT
.compat_ioctl = compat_sock_ioctl,#endif
.mmap = sock_mmap,
.open = sock_no_open, /* special open code to disallow open via /proc */
.release = sock_close,
.fasync = sock_fasync,
.sendpage = sock_sendpage,
.splice_write = generic_splice_sendpage,
.splice_read = sock_splice_read,
};
从用户态调用sock_ioctl到kernel态调用iw_handler的执行流程如下:
sock_ioctl-> (kernel/net/socket.c)
dev_ioctl-> (kernel/net/core/dev.c)
下面的方法都在/net/wireless/wext-core.c中
wext_handle_ioctl-> (把执行结果从kernel态copy到用户态)
wext_ioctl_dispatch->(参数包括cmd/ioctl_standard_call/ioctl_private_call)
wireless_process_ioctl->
get_handler-> (根据cmd来判断调用standard或是private,即ioctl_standard_call或是ioctl_private_call方法)
ioctl_standard_call (执行cmd指定的iw_handler<cfg80211_handlers中定义的>,并返回结果)
这样就完成了”通过ioctl,用户态向kernel态发送请求”。
这个流程的代码稍后贴出。
sock_ioctl
dev_ioctl
wext_handle_ioctl
wext_ioctl_dispatch
wireless_process_ioctl
ioctl_standard_call
来源:oschina
链接:https://my.oschina.net/u/994235/blog/295845