目录
1,什么是cfg80211?wiphy又是什么?
cfg80211:用于对无线设备进行配置管理。cfg80211&nl80211基于消息机制,使用netlink接口。
wiphy:无线网络设备驱动使用cfg80211需要硬件设备在cfg80211中实现注册。实现注册就要定义一系列的硬件功能描述的结构体。每个设备的基础性结构体是wiphy,设备连接到系统是,都要使用。每个wiphy有0个,1个或者许多个虚拟接口相关联。
2,cfg80211工作流程是什么?
static int __init cfg80211_init(void)
{
//注册网络命名空间
register_pernet_device(&cfg80211_pernet_ops);
//注册ieee80211_class类
wiphy_sysfs_init();
//将class注册到内核中,同时会在/sys/class/下创建class对应的节点
--->class_register(&ieee80211_class);
//注册网络通知,以接收网络事件,以通知链的方式
register_netdevice_notifier(&cfg80211_netdev_notifier);
//注册netlink“nl80211”,其操作为nl80211_ops
nl80211_init();
--->genl_register_family(&nl80211_fam);
//在netlink_register链上注册一个回调函数,notifier机制是子系统之间通信手段,
//netlink_register链上的子系统会检测用户态socket的连接状况
//如果发生改变则通知运行回调函数
--->netlink_register_notifier(&nl80211_netlink_notifier);
//创建sys/class/ieee80211目录
ieee80211_debugfs_dir = debugfs_create_dir("ieee80211", NULL);
regulatory_init();
//创建名为“cfg80211”的内核线程
cfg80211_wq = alloc_ordered_workqueue("cfg80211", WQ_MEM_RECLAIM);
}
3,cfg80211如何通过netlink与iw进行交互?
nl80211_init()之后,通过nl80211_ops结构体实现与CMD关联,如iw发送消息NL80211_CMD_GET_POWER_SAVE,cfg80211就执行nl80211_get_power_save()来进行下一步操作。
//关键结构体
static __genl_const struct genl_ops nl80211_ops[] = {
{
.cmd = NL80211_CMD_GET_WIPHY,
.doit = nl80211_get_wiphy,
.dumpit = nl80211_dump_wiphy,
.done = nl80211_dump_wiphy_done,
.policy = nl80211_policy,
/* can be retrieved by unprivileged users */
.internal_flags = NL80211_FLAG_NEED_WIPHY |
NL80211_FLAG_NEED_RTNL,
},
...
...
{
.cmd = NL80211_CMD_ABORT_SCAN,
.doit = nl80211_abort_scan,
.policy = nl80211_policy,
.flags = GENL_UNS_ADMIN_PERM,
.internal_flags = NL80211_FLAG_NEED_WDEV_UP |
NL80211_FLAG_NEED_RTNL,
},
...
{
.cmd = NL80211_CMD_GET_POWER_SAVE,
.doit = nl80211_get_power_save,
.policy = nl80211_policy,
/* can be retrieved by unprivileged users */
.internal_flags = NL80211_FLAG_NEED_NETDEV |
NL80211_FLAG_NEED_RTNL,
},
...
};
/*疑问:
1,nl80211_ops结构体是怎样与nl80211_init()关联的??
*
nl80211_get_power_save()函数实现如下,构造msg,填充msg,返回给iw。
//nl80211_get_power_save()函数实现
static int nl80211_get_power_save(struct sk_buff *skb, struct genl_info *info)
{
struct wireless_dev *wdev;
struct net_device *dev = info->user_ptr[1];
struct sk_buff *msg;
wdev = dev->ieee80211_ptr;
//调用genlmsg_new()函数申请skb套接字缓存空间
msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
//构造返回msg
hdr = nl80211hdr_put(msg, genl_info_snd_portid(info), info->snd_seq,
0, NL80211_CMD_GET_POWER_SAVE);
//获取ps_state值
if (wdev->ps)
ps_state = NL80211_PS_ENABLED;
else
ps_state = NL80211_PS_DISABLED;
//传ps_state值到msg
nla_put_u32(msg, NL80211_ATTR_PS_STATE, ps_state)
//调用genlmsg_end()更新消息头重的长度字段
genlmsg_end(msg, hdr);
//启动回发流程,为genlmsg_unicast()的一个封装
return genlmsg_reply(msg, info);
}
/*疑问:
1,struct genl_info *info结构体的值,什么时候赋值的??
注:需要理解Generic Netlink,打算另写篇文件深入介绍。
*/
4,cfg80211怎么与mac80211进行交互?
4.1,以NL80211_CMD_TRIGGER_SCAN指令为例,cfg80211接收iw的NL80211_CMD_TRIGGER_SCAN消息后,运行nl80211_trigger_scan()函数,nl80211_trigger_scan()的分析如下
//nl80211_trigger_scan()关键分析
static int nl80211_trigger_scan(struct sk_buff *skb, struct genl_info *info)
{
struct cfg80211_registered_device *rdev = info->user_ptr[0];
struct wireless_dev *wdev = info->user_ptr[1];
struct wiphy *wiphy;
wiphy = &rdev->wiphy;
//调用mac80211注册的cfg80211_ops对应的函数.scan
rdev->scan_req = request;
err = rdev_scan(rdev, request);
---->ret = rdev->ops->scan(&rdev->wiphy, request);
//构造netlink消息,回复给iw
nl80211_send_scan_start(rdev, wdev);
---->msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
---->nl80211_prep_scan_msg(msg, rdev, wdev, 0, 0, 0,NL80211_CMD_TRIGGER_SCAN) < 0)
-------->nl80211hdr_put(msg, portid, seq, flags, cmd);
-------->nla_put_u32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx)
-------->nl80211_add_scan_req(msg, rdev);
-------->genlmsg_end(msg, hdr);
---->genlmsg_multicast_netns(&nl80211_fam, wiphy_net(&rdev->wiphy), msg, 0,
NL80211_MCGRP_SCAN, GFP_KERNEL);
return err;
}
/*疑问:
1,cfg80211是如何关联到mac80211的cfg80211_ops,使其可以调用ops对应函数?
*/
4.2,mac80211向cfg80211注册的cfg80211_ops 如下
const struct cfg80211_ops mac80211_config_ops = {
.add_virtual_intf = ieee80211_add_iface,
.del_virtual_intf = ieee80211_del_iface,
.change_virtual_intf = ieee80211_change_iface,
.start_p2p_device = ieee80211_start_p2p_device,
.stop_p2p_device = ieee80211_stop_p2p_device,
.add_key = ieee80211_add_key,
.del_key = ieee80211_del_key,
.get_key = ieee80211_get_key,
.set_default_key = ieee80211_config_default_key,
.set_default_mgmt_key = ieee80211_config_default_mgmt_key,
.start_ap = ieee80211_start_ap,
.change_beacon = ieee80211_change_beacon,
.stop_ap = ieee80211_stop_ap,
.add_station = ieee80211_add_station,
.del_station = ieee80211_del_station,
.change_station = ieee80211_change_station,
.get_station = ieee80211_get_station,
.dump_station = ieee80211_dump_station,
.dump_survey = ieee80211_dump_survey,
#ifdef CPTCFG_MAC80211_MESH
.add_mpath = ieee80211_add_mpath,
.del_mpath = ieee80211_del_mpath,
.change_mpath = ieee80211_change_mpath,
.get_mpath = ieee80211_get_mpath,
.dump_mpath = ieee80211_dump_mpath,
.get_mpp = ieee80211_get_mpp,
.dump_mpp = ieee80211_dump_mpp,
.update_mesh_config = ieee80211_update_mesh_config,
.get_mesh_config = ieee80211_get_mesh_config,
.join_mesh = ieee80211_join_mesh,
.leave_mesh = ieee80211_leave_mesh,
#endif
.join_ocb = ieee80211_join_ocb,
.leave_ocb = ieee80211_leave_ocb,
.change_bss = ieee80211_change_bss,
.set_txq_params = ieee80211_set_txq_params,
.set_monitor_channel = ieee80211_set_monitor_channel,
.suspend = ieee80211_suspend,
.resume = ieee80211_resume,
.scan = ieee80211_scan,
.abort_scan = ieee80211_abort_scan,
.sched_scan_start = ieee80211_sched_scan_start,
.sched_scan_stop = ieee80211_sched_scan_stop,
.auth = ieee80211_auth,
.assoc = ieee80211_assoc,
.deauth = ieee80211_deauth,
.disassoc = ieee80211_disassoc,
.join_ibss = ieee80211_join_ibss,
.leave_ibss = ieee80211_leave_ibss,
.set_mcast_rate = ieee80211_set_mcast_rate,
.set_wiphy_params = ieee80211_set_wiphy_params,
.set_tx_power = ieee80211_set_tx_power,
.get_tx_power = ieee80211_get_tx_power,
.set_antenna_gain = ieee80211_set_antenna_gain,
.set_wds_peer = ieee80211_set_wds_peer,
.rfkill_poll = ieee80211_rfkill_poll,
CFG80211_TESTMODE_CMD(ieee80211_testmode_cmd)
CFG80211_TESTMODE_DUMP(ieee80211_testmode_dump)
.set_power_mgmt = ieee80211_set_power_mgmt,
.set_bitrate_mask = ieee80211_set_bitrate_mask,
.remain_on_channel = ieee80211_remain_on_channel,
.cancel_remain_on_channel = ieee80211_cancel_remain_on_channel,
.mgmt_tx = ieee80211_mgmt_tx,
.mgmt_tx_cancel_wait = ieee80211_mgmt_tx_cancel_wait,
.set_cqm_rssi_config = ieee80211_set_cqm_rssi_config,
.set_cqm_rssi_range_config = ieee80211_set_cqm_rssi_range_config,
.mgmt_frame_register = ieee80211_mgmt_frame_register,
.set_antenna = ieee80211_set_antenna,
.get_antenna = ieee80211_get_antenna,
.set_rekey_data = ieee80211_set_rekey_data,
.tdls_oper = ieee80211_tdls_oper,
.tdls_mgmt = ieee80211_tdls_mgmt,
.tdls_channel_switch = ieee80211_tdls_channel_switch,
.tdls_cancel_channel_switch = ieee80211_tdls_cancel_channel_switch,
.probe_client = ieee80211_probe_client,
.set_noack_map = ieee80211_set_noack_map,
#ifdef CONFIG_PM
.set_wakeup = ieee80211_set_wakeup,
#endif
.get_channel = ieee80211_cfg_get_channel,
.start_radar_detection = ieee80211_start_radar_detection,
.channel_switch = ieee80211_channel_switch,
.set_qos_map = ieee80211_set_qos_map,
.set_ap_chanwidth = ieee80211_set_ap_chanwidth,
.add_tx_ts = ieee80211_add_tx_ts,
.del_tx_ts = ieee80211_del_tx_ts,
.start_nan = ieee80211_start_nan,
.stop_nan = ieee80211_stop_nan,
.nan_change_conf = ieee80211_nan_change_conf,
.add_nan_func = ieee80211_add_nan_func,
.del_nan_func = ieee80211_del_nan_func,
.set_multicast_to_unicast = ieee80211_set_multicast_to_unicast,
};
5,总结
指令流程:iw<--netlink-->cfg80211<--cfg80211_ops-->mac80211
文章的细节与疑问,后面会继续更新!
欢迎订阅公众号【从零开始学无线】,一起学习交流!