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 -;
}
}
// 根据name进行匹配,并最后保存到wpa_supplicant->dirver中
wpa_s->driver = wpa_drivers[i];
wpa_s->global_drv_priv = global->drv_priv[i]; return ;
}

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 = -;
dl_list_init(&global->interfaces);
global->if_add_ifindex = -; 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) < )
goto err;
// 此global->ioctl_sock用作为ioctl命令的fd
global->ioctl_sock = socket(PF_INET, SOCK_DGRAM, );
if (global->ioctl_sock < ) {
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命令的fd
global->ioctl_sock = socket(PF_INET, SOCK_DGRAM, );

分析以上两句:

(a)netlink_init方法中创建了一个socket,并添加到eloop_run方法中的rfds中。用于从kernel态发送事件给用户态

netlink->sock = socket(PF_NETLINK, SOCK_RAW, NETLINK_ROUTE);
......
eloop_register_read_sock(netlink->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) < )
return -;
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态发送请求”。

这个流程的代码稍后贴出。

 /*
* With an ioctl, arg may well be a user mode pointer, but we don't know
* what to do with it - that's up to the protocol still.
*/ static long sock_ioctl(struct file *file, unsigned cmd, unsigned long arg)
{
struct socket *sock;
struct sock *sk;
void __user *argp = (void __user *)arg;
int pid, err;
struct net *net; sock = file->private_data;
sk = sock->sk;
net = sock_net(sk);
if (cmd >= SIOCDEVPRIVATE && cmd <= (SIOCDEVPRIVATE + )) {
err = dev_ioctl(net, cmd, argp);
} else
#ifdef CONFIG_WEXT_CORE
if (cmd >= SIOCIWFIRST && cmd <= SIOCIWLAST) {
err = dev_ioctl(net, cmd, argp);
} else
#endif
switch (cmd) {
case FIOSETOWN:
case SIOCSPGRP:
err = -EFAULT;
if (get_user(pid, (int __user *)argp))
break;
err = f_setown(sock->file, pid, );
break;
case FIOGETOWN:
case SIOCGPGRP:
err = put_user(f_getown(sock->file),
(int __user *)argp);
break;
case SIOCGIFBR:
case SIOCSIFBR:
case SIOCBRADDBR:
case SIOCBRDELBR:
err = -ENOPKG;
if (!br_ioctl_hook)
request_module("bridge"); mutex_lock(&br_ioctl_mutex);
if (br_ioctl_hook)
err = br_ioctl_hook(net, cmd, argp);
mutex_unlock(&br_ioctl_mutex);
break;
case SIOCGIFVLAN:
case SIOCSIFVLAN:
err = -ENOPKG;
if (!vlan_ioctl_hook)
request_module("8021q"); mutex_lock(&vlan_ioctl_mutex);
if (vlan_ioctl_hook)
err = vlan_ioctl_hook(net, argp);
mutex_unlock(&vlan_ioctl_mutex);
break;
case SIOCADDDLCI:
case SIOCDELDLCI:
err = -ENOPKG;
if (!dlci_ioctl_hook)
request_module("dlci"); mutex_lock(&dlci_ioctl_mutex);
if (dlci_ioctl_hook)
err = dlci_ioctl_hook(cmd, argp);
mutex_unlock(&dlci_ioctl_mutex);
break;
default:
err = sock_do_ioctl(net, sock, cmd, arg);
break;
}
return err;
}

sock_ioctl

 /**
* dev_ioctl - network device ioctl
* @net: the applicable net namespace
* @cmd: command to issue
* @arg: pointer to a struct ifreq in user space
*
* Issue ioctl functions to devices. This is normally called by the
* user space syscall interfaces but can sometimes be useful for
* other purposes. The return value is the return from the syscall if
* positive or a negative errno code on error.
*/ int dev_ioctl(struct net *net, unsigned int cmd, void __user *arg)
{
struct ifreq ifr;
int ret;
char *colon; /* One special case: SIOCGIFCONF takes ifconf argument
and requires shared lock, because it sleeps writing
to user space.
*/ if (cmd == SIOCGIFCONF) {
rtnl_lock();
ret = dev_ifconf(net, (char __user *) arg);
rtnl_unlock();
return ret;
}
if (cmd == SIOCGIFNAME)
return dev_ifname(net, (struct ifreq __user *)arg); if (copy_from_user(&ifr, arg, sizeof(struct ifreq)))
return -EFAULT; ifr.ifr_name[IFNAMSIZ-] = ; colon = strchr(ifr.ifr_name, ':');
if (colon)
*colon = ; /*
* See which interface the caller is talking about.
*/ switch (cmd) {
/*
* These ioctl calls:
* - can be done by all.
* - atomic and do not require locking.
* - return a value
*/
case SIOCGIFFLAGS:
case SIOCGIFMETRIC:
case SIOCGIFMTU:
case SIOCGIFHWADDR:
case SIOCGIFSLAVE:
case SIOCGIFMAP:
case SIOCGIFINDEX:
case SIOCGIFTXQLEN:
dev_load(net, ifr.ifr_name);
rcu_read_lock();
ret = dev_ifsioc_locked(net, &ifr, cmd);
rcu_read_unlock();
if (!ret) {
if (colon)
*colon = ':';
if (copy_to_user(arg, &ifr,
sizeof(struct ifreq)))
ret = -EFAULT;
}
return ret; case SIOCETHTOOL:
dev_load(net, ifr.ifr_name);
rtnl_lock();
ret = dev_ethtool(net, &ifr);
rtnl_unlock();
if (!ret) {
if (colon)
*colon = ':';
if (copy_to_user(arg, &ifr,
sizeof(struct ifreq)))
ret = -EFAULT;
}
return ret; /*
* These ioctl calls:
* - require superuser power.
* - require strict serialization.
* - return a value
*/
case SIOCGMIIPHY:
case SIOCGMIIREG:
case SIOCSIFNAME:
if (!capable(CAP_NET_ADMIN))
return -EPERM;
dev_load(net, ifr.ifr_name);
rtnl_lock();
ret = dev_ifsioc(net, &ifr, cmd);
rtnl_unlock();
if (!ret) {
if (colon)
*colon = ':';
if (copy_to_user(arg, &ifr,
sizeof(struct ifreq)))
ret = -EFAULT;
}
return ret; /*
* These ioctl calls:
* - require superuser power.
* - require strict serialization.
* - do not return a value
*/
case SIOCSIFFLAGS:
case SIOCSIFMETRIC:
case SIOCSIFMTU:
case SIOCSIFMAP:
case SIOCSIFHWADDR:
case SIOCSIFSLAVE:
case SIOCADDMULTI:
case SIOCDELMULTI:
case SIOCSIFHWBROADCAST:
case SIOCSIFTXQLEN:
case SIOCSMIIREG:
case SIOCBONDENSLAVE:
case SIOCBONDRELEASE:
case SIOCBONDSETHWADDR:
case SIOCBONDCHANGEACTIVE:
case SIOCBRADDIF:
case SIOCBRDELIF:
case SIOCSHWTSTAMP:
if (!capable(CAP_NET_ADMIN))
return -EPERM;
/* fall through */
case SIOCBONDSLAVEINFOQUERY:
case SIOCBONDINFOQUERY:
dev_load(net, ifr.ifr_name);
rtnl_lock();
ret = dev_ifsioc(net, &ifr, cmd);
rtnl_unlock();
return ret; case SIOCGIFMEM:
/* Get the per device memory space. We can add this but
* currently do not support it */
case SIOCSIFMEM:
/* Set the per device memory buffer space.
* Not applicable in our case */
case SIOCSIFLINK:
return -ENOTTY; /*
* Unknown or private ioctl.
*/
default:
if (cmd == SIOCWANDEV ||
(cmd >= SIOCDEVPRIVATE &&
cmd <= SIOCDEVPRIVATE + )) {
dev_load(net, ifr.ifr_name);
rtnl_lock();
ret = dev_ifsioc(net, &ifr, cmd);
rtnl_unlock();
if (!ret && copy_to_user(arg, &ifr,
sizeof(struct ifreq)))
ret = -EFAULT;
return ret;
}
/* Take care of Wireless Extensions */
if (cmd >= SIOCIWFIRST && cmd <= SIOCIWLAST)
return wext_handle_ioctl(net, &ifr, cmd, arg); //执行wext_handle_ioctl方法
return -ENOTTY;
}
}

dev_ioctl

int wext_handle_ioctl(struct net *net, struct ifreq *ifr, unsigned int cmd,
void __user *arg)
{
struct iw_request_info info = { .cmd = cmd, .flags = };
int ret; ret = wext_ioctl_dispatch(net, ifr, cmd, &info,
ioctl_standard_call,
ioctl_private_call); //调用wext_ioctl_dispatch方法
if (ret >= &&
IW_IS_GET(cmd) &&
copy_to_user(arg, ifr, sizeof(struct iwreq))) //将执行结果从kernel态copy到用户态
return -EFAULT; return ret;
}

wext_handle_ioctl

 /* entry point from dev ioctl */
static int wext_ioctl_dispatch(struct net *net, struct ifreq *ifr,
unsigned int cmd, struct iw_request_info *info,
wext_ioctl_func standard,
wext_ioctl_func private)
{
int ret = wext_permission_check(cmd); if (ret)
return ret; dev_load(net, ifr->ifr_name);
rtnl_lock();
ret = wireless_process_ioctl(net, ifr, cmd, info, standard, private); //调用wireless_process_ioctl方法
rtnl_unlock(); return ret;
}

wext_ioctl_dispatch

 /*
* Main IOCTl dispatcher.
* Check the type of IOCTL and call the appropriate wrapper...
*/
static int wireless_process_ioctl(struct net *net, struct ifreq *ifr,
unsigned int cmd,
struct iw_request_info *info,
wext_ioctl_func standard,
wext_ioctl_func private)
{
struct iwreq *iwr = (struct iwreq *) ifr;
struct net_device *dev;
iw_handler handler; /* Permissions are already checked in dev_ioctl() before calling us.
* The copy_to/from_user() of ifr is also dealt with in there */ /* Make sure the device exist */
if ((dev = __dev_get_by_name(net, ifr->ifr_name)) == NULL)
return -ENODEV; /* A bunch of special cases, then the generic case...
* Note that 'cmd' is already filtered in dev_ioctl() with
* (cmd >= SIOCIWFIRST && cmd <= SIOCIWLAST) */
if (cmd == SIOCGIWSTATS)
return standard(dev, iwr, cmd, info,
&iw_handler_get_iwstats); #ifdef CONFIG_WEXT_PRIV
if (cmd == SIOCGIWPRIV && dev->wireless_handlers)
return standard(dev, iwr, cmd, info,
iw_handler_get_private);
#endif /* Basic check */
if (!netif_device_present(dev))
return -ENODEV; /* New driver API : try to find the handler */
handler = get_handler(dev, cmd); //调用get_handler
if (handler) {
/* Standard and private are not the same */
if (cmd < SIOCIWFIRSTPRIV)
return standard(dev, iwr, cmd, info, handler);
else if (private)
return private(dev, iwr, cmd, info, handler);
}
/* Old driver API : call driver ioctl handler */
if (dev->netdev_ops->ndo_do_ioctl)
return dev->netdev_ops->ndo_do_ioctl(dev, ifr, cmd);
return -EOPNOTSUPP;
}

wireless_process_ioctl

 /*
* Wrapper to call a standard Wireless Extension handler.
* We do various checks and also take care of moving data between
* user space and kernel space.
*/
static int ioctl_standard_call(struct net_device * dev,
struct iwreq *iwr,
unsigned int cmd,
struct iw_request_info *info,
iw_handler handler)
{
const struct iw_ioctl_description * descr;
int ret = -EINVAL; /* Get the description of the IOCTL */
if (IW_IOCTL_IDX(cmd) >= standard_ioctl_num)
return -EOPNOTSUPP;
descr = &(standard_ioctl[IW_IOCTL_IDX(cmd)]); /* Check if we have a pointer to user space data or not */
if (descr->header_type != IW_HEADER_TYPE_POINT) { /* No extra arguments. Trivial to handle */
ret = handler(dev, info, &(iwr->u), NULL); /* Generate an event to notify listeners of the change */
if ((descr->flags & IW_DESCR_FLAG_EVENT) &&
((ret == ) || (ret == -EIWCOMMIT)))
wireless_send_event(dev, cmd, &(iwr->u), NULL);
} else {
ret = ioctl_standard_iw_point(&iwr->u.data, cmd, descr,
handler, dev, info);
} /* Call commit handler if needed and defined */
if (ret == -EIWCOMMIT)
ret = call_commit_handler(dev); /* Here, we will generate the appropriate event if needed */ return ret;
}

ioctl_standard_call

4.3 用户态和kernel态交互之netlink实现

首先看netlink_init方法

struct netlink_data * netlink_init(struct netlink_config *cfg)
{
struct netlink_data *netlink;
struct sockaddr_nl local; netlink = os_zalloc(sizeof(*netlink));
if (netlink == NULL)
return NULL; netlink->cfg = cfg; netlink->sock = socket(PF_NETLINK, SOCK_RAW, NETLINK_ROUTE);
if (netlink->sock < ) {
wpa_printf(MSG_ERROR, "netlink: Failed to open netlink "
"socket: %s", strerror(errno));
netlink_deinit(netlink);
return NULL;
} os_memset(&local, , sizeof(local));
local.nl_family = AF_NETLINK;
local.nl_groups = RTMGRP_LINK;
if (bind(netlink->sock, (struct sockaddr *) &local, sizeof(local)) < )
{
wpa_printf(MSG_ERROR, "netlink: Failed to bind netlink "
"socket: %s", strerror(errno));
netlink_deinit(netlink);
return NULL;
} eloop_register_read_sock(netlink->sock, netlink_receive, netlink,
NULL); return netlink;
}

执行完netlink_init方法后,会通过eloop_register_read_sock方法将其中创建的socket以及callback方法注册到eloop_run方法中的rfds中,循环监听。一旦该socket有消息或事件变化,就执行netlink_receive方法。

static void netlink_receive(int sock, void *eloop_ctx, void *sock_ctx)
{
struct netlink_data *netlink = eloop_ctx;
char buf[];
int left;
struct sockaddr_nl from;
socklen_t fromlen;
struct nlmsghdr *h;
int max_events = ; try_again:
fromlen = sizeof(from);
left = recvfrom(sock, buf, sizeof(buf), MSG_DONTWAIT,
(struct sockaddr *) &from, &fromlen); //从netlink读取事件
if (left < ) {
if (errno != EINTR && errno != EAGAIN)
wpa_printf(MSG_INFO, "netlink: recvfrom failed: %s",
strerror(errno));
return;
} h = (struct nlmsghdr *) buf;
while (NLMSG_OK(h, left)) {
switch (h->nlmsg_type) {
case RTM_NEWLINK:
netlink_receive_link(netlink, netlink->cfg->newlink_cb,
h); //a
break;
case RTM_DELLINK:
netlink_receive_link(netlink, netlink->cfg->dellink_cb,
h); //b
break;
} h = NLMSG_NEXT(h, left);
} if (left > ) {
wpa_printf(MSG_DEBUG, "netlink: %d extra bytes in the end of "
"netlink message", left);
} if (--max_events > ) {
/*
* Try to receive all events in one eloop call in order to
* limit race condition on cases where AssocInfo event, Assoc
* event, and EAPOL frames are received more or less at the
* same time. We want to process the event messages first
* before starting EAPOL processing.
*/
goto try_again;
}
}

a/b中的方法调用,是在driver_nl80211.c中注册的,如下所示。

cfg->ctx = global;
cfg->newlink_cb = wpa_driver_nl80211_event_rtm_newlink;
cfg->dellink_cb = wpa_driver_nl80211_event_rtm_dellink;

这两个方法都会调用wpa_supplicant_event方法来处理。wpa_supplicant_event方法用来Report a driver event for wpa_supplicant。

所以这就完成了kernel向wpa_supplicant上传事件通知的过程了。

因此,kernel态向用户态发送事件通知(通过netlink)也已经分析完毕了。

wpa_supplicant与kernel交互的更多相关文章

  1. 上层应用与wpa_supplicant,wpa_supplicant与kernel 相关socket创建交互分析

    单独拿出来,分析以下上层应用与wpa_supplicant   wpa_supplicant与kernel 的socket交互. 关联上层应用与wpa_supplicant的socket的创建.连接流 ...

  2. wpa_supplicant下行接口浅析

    wpa_supplicant通过socket通信机制实现下行接口,与内核进行通信,获取信息或下发命令. 以下摘自http://blog.csdn.net/fxfzz/article/details/6 ...

  3. 如何处理VirtualBox启动错误消息:The vboxdrv kernel module is not loaded

    我在启动minikube时,遇到如下错误消息: Starting local Kubernetes v1.10.0 cluster... Starting VM... E1010 03:27:37.9 ...

  4. shell使用攻略

    shell 是什么 ~ $ ls /bin/*sh /bin/bash /bin/csh /bin/ksh /bin/sh /bin/tcsh /bin/zsh 是什么 kernel shell 命令 ...

  5. centos5.6部署gcc4.7编译的程序导致问题

    因为用了c++0x的一些新特性,必须使用4.6及以上的版本编译,所以使用了4.7编译,运行时提示错误 libstdc++.so.6(GLIBCXX_3.4.14) 错误 这个时候下了个glibc2.7 ...

  6. linux驱动系列之arm汇编

    在arm平台学习linux时,会遇到arm汇编指令,arm汇编指令与8086汇编指令很多地方都不同,在此记下来以免后面忘了,同时在学习了汇编指令之后分析一些汇编指令编写的代码. 一.相对跳转指令b.b ...

  7. Android热插拔事件处理详解

    一.Android热插拔事件处理流程图 Android热插拔事件处理流程如下图所示: 二.组成 1. NetlinkManager:        全称是NetlinkManager.cpp位于And ...

  8. 关于Android开发的一些个人总结(2016.04)

    引言 本文主要目标是整理下我在2016年第一季度针对Android学习的内容,不会深入细节,只是简单聊聊框架以及学习的方法. 我的Android之路 因为之前对Android有一个简单的接触,研究生期 ...

  9. linux系统——ld-linux.so.X查找和加载共享动态库的顺序

    ld-linux.so查找共享库的顺序: Glibc安装的库中有一个为ld-linux.so.X,其中X为一个数字,在不同的平台上名字也会不同.可以用ldd查看: #ldd /bin/cat linu ...

随机推荐

  1. 阿里云Docker镜像仓库(Docker Registry)

    镜像仓库申请地址: https://cr.console.aliyun.com/cn-shanghai/instances/repositories   一.创建命名空间 例如daniel-hub   ...

  2. redhat系统升级openssh到7.5

    注意,注意,注意重要的事情说三遍,关于ssh的升级不能完全按照别人的教程进行升级,因为每台生产机器都是不一样的,有可能别人能升级成功但是另外一个就可能会失败,因为每台机器上面跑的应用是不一样的,涉及到 ...

  3. 大数据学习--day05(嵌套循环、方法、递归)

    嵌套循环.方法.递归 图形打印 public static void main(String[]arg) { /** * * * * * * */ // 3 2 1 0 // 1 3 5 for(in ...

  4. notepad无法对linux中的文件进行修改???

    .问题描述: 用notepad++的sftp服务连接LInux,连接成功 想修改图中的程序,来调试udp客户端,但是修改后保存失败(很早之前是成功的) .. 试着解决: 参考博客:https://bl ...

  5. Spark运行模式_spark自带cluster manager的standalone cluster模式(集群)

    这种运行模式和"Spark自带Cluster Manager的Standalone Client模式(集群)"还是有很大的区别的.使用如下命令执行应用程序(前提是已经启动了spar ...

  6. linux3.4.2之块设备驱动完整程序

    /*参考drivers/block/xd.c *以及drivers/block/z2ram.c */ #include <linux/module.h> #include <linu ...

  7. consonant_爆破音

    consonant_爆破音_[p]和[b].[t]和[d].[k]和[g] 声带震动:发音的重要作用. 爆破音:发音在一瞬间,不会延长气流. [p]:声带不震动,嘴唇咬在一起,有明显的气流.map.p ...

  8. Linux字符设备驱动--No.1

    平台:tiny210SOC:s5pv210内核:Linux 3.0.8字符驱动:按键中断驱动源码: /************************************************* ...

  9. C语言中字符串赋值的几个理解

    在C语言中,字符串的赋值主要有两种方法,第一种是通过指针的方式直接赋值,第二种是通过数组直接赋值. 一.首先,我们来看第一种赋值方法:指针式赋值 我们知道,上面的示例是显然可以正常执行的,也是很容易理 ...

  10. linux signal函数遇到的问题

    1.关于signal函数的定义 signal最开始的原型是这: void (*signal(int signo, void (*func)(int)))(int);看过下面两行,了解到上面这一行是这个 ...