Linux中wifi驱动分为两种模式,FullMac模式和SoftMac模式,于此相对应,Linux也有两套对应的驱动和代码框架来实现,这里简单的笔记下FullMac的代码框架。
注:
1 本文基于Linux2.6.32.65代码实现。
2 作者在Linux wireless开发方面是新手一枚,对框架的理解上可能不太深刻,欢迎大家批评指正。
在Linux2.6.32目录中,可以发现在/net下有两个目录,mac80211和wireless.这里分别对应了softmac模式和fullmac实现模式.这里简单的笔记下wireless即fullmac模式下的功能实现.
cfg80211是fullmac下的linux的对应驱动,由于fullmac下大多数工作都放在了wifi芯片上,因此cfg80211没有做太多的wifi业务上的工作,而是作为一个框架用于和外部程序间(比如hostapd,wpa_supplicant等)进行沟通.
由于cfg80211等驱动是工作在Linux内核模式下,hostapd等程序是工作在Linux用户模式下,因此Linux用户态和内核态的交互方式是实现关键.最新的Linux是基于netlink方式来实现的,在Linux内核中这部分功能集中在nl80211中.这里以linux2.6.32源码为例子,简单的笔记下主要的流程.
\linux-2.6.32.65\net\wireless\nl80211.c
注册驱动侧genetlink的回调响应函数.
static struct genl_ops nl80211_ops[] = {
{
.cmd = NL80211_CMD_ASSOCIATE,
.doit = nl80211_associate,
.policy = nl80211_policy,
.flags = GENL_ADMIN_PERM,
},
......
......
};
/* nl80211初始化时注册genetlink */
int nl80211_init(void)
{
int err;
err = genl_register_family_with_ops(&nl80211_fam,
nl80211_ops, ARRAY_SIZE(nl80211_ops));
if (err)
return err;
......
......
}
这里以连接流程为例,简单的笔记下函数调用流程.
当外部使用genetlink连接上之后,发送NL80211_CMD_ASSOCIATE命令,就会路由到nl80211_associate函数,我们跟着函数流程一级级看下去,最终可以看到调用了注册到cf80211内部的回调函数。
/* 连接回调函数 */
static int nl80211_associate(struct sk_buff *skb, struct genl_info *info)
{
struct cfg80211_registered_device *rdev; /* cfg80211_registered_device可以理解为是cfg802.11的管理结构体 */
struct net_device *dev;/* 网络设备 */
......
......
/* 获取对应的管理结构体指针 */
err = get_rdev_dev_by_info_ifindex(info, &rdev, &dev);
if (err)
goto unlock_rtnl;
/* 注册到cfg80211的回调函数必须定义 */
if (!rdev->ops->assoc) {
err = -EOPNOTSUPP;
goto out;
}
......
......
err = cfg80211_mlme_assoc(rdev, dev, chan, bssid, prev_bssid,
ssid, ssid_len, ie, ie_len, use_mfp,
&crypto);
......
......
}
\linux-2.6.32.65\net\wireless\mlme.c
int cfg80211_mlme_assoc(struct cfg80211_registered_device *rdev,
struct net_device *dev,
struct ieee80211_channel *chan,
const u8 *bssid, const u8 *prev_bssid,
const u8 *ssid, int ssid_len,
const u8 *ie, int ie_len, bool use_mfp,
struct cfg80211_crypto_settings *crypt)
{
struct wireless_dev *wdev = dev->ieee80211_ptr;
int err;
wdev_lock(wdev);
err = __cfg80211_mlme_assoc(rdev, dev, chan, bssid, prev_bssid,
ssid, ssid_len, ie, ie_len, use_mfp, crypt);
wdev_unlock(wdev);
return err;
}
int __cfg80211_mlme_assoc(struct cfg80211_registered_device *rdev,
struct net_device *dev,
struct ieee80211_channel *chan,
const u8 *bssid, const u8 *prev_bssid,
const u8 *ssid, int ssid_len,
const u8 *ie, int ie_len, bool use_mfp,
struct cfg80211_crypto_settings *crypt)
{
......
......
err = rdev->ops->assoc(&rdev->wiphy, dev, &req);
......
......
}
那么cfg80211中的回调函数是如何注册的呢?
在\linux-2.6.32.65\net\wireless\core.c中,可以看到有个函数为,该函数便是外部驱动注册回调函数结构体的位置,注意这里所谓的注册是将回调函数结构体包装为一个cfg80211_registered_device结构体,在整个802.11流程中使用.
struct wiphy *wiphy_new(const struct cfg80211_ops *ops, int sizeof_priv)
{
static int wiphy_counter;
/* 注册到linux内核中的802.11指针 */
struct cfg80211_registered_device *rdev;
int alloc_size;
/* 传入函数指针校验 */
WARN_ON(ops->add_key && (!ops->del_key || !ops->set_default_key));
WARN_ON(ops->auth && (!ops->assoc || !ops->deauth || !ops->disassoc));
WARN_ON(ops->connect && !ops->disconnect);
WARN_ON(ops->join_ibss && !ops->leave_ibss);
WARN_ON(ops->add_virtual_intf && !ops->del_virtual_intf);
WARN_ON(ops->add_station && !ops->del_station);
WARN_ON(ops->add_mpath && !ops->del_mpath);
/* 计算malloc的大小 */
alloc_size = sizeof(*rdev) + sizeof_priv;
rdev = kzalloc(alloc_size, GFP_KERNEL);
if (!rdev)
return NULL;
rdev->ops = ops;/* 保存ops指针 */
mutex_lock(&cfg80211_mutex);
rdev->wiphy_idx = wiphy_counter++;/* 可能有多个cfg80211被注册,这里计数硬件 */
if (unlikely(!wiphy_idx_valid(rdev->wiphy_idx))) {
wiphy_counter--;
mutex_unlock(&cfg80211_mutex);
/* ugh, wrapped! */
kfree(rdev);
return NULL;
}
mutex_unlock(&cfg80211_mutex);
/* give it a proper name */
dev_set_name(&rdev->wiphy.dev, PHY_NAME "%d", rdev->wiphy_idx);
mutex_init(&rdev->mtx);
mutex_init(&rdev->devlist_mtx);
INIT_LIST_HEAD(&rdev->netdev_list);
spin_lock_init(&rdev->bss_lock);
INIT_LIST_HEAD(&rdev->bss_list);
INIT_WORK(&rdev->scan_done_wk, __cfg80211_scan_done);
device_initialize(&rdev->wiphy.dev);
rdev->wiphy.dev.class = &ieee80211_class;
rdev->wiphy.dev.platform_data = rdev;
rdev->wiphy.ps_default = CONFIG_CFG80211_DEFAULT_PS_VALUE;
wiphy_net_set(&rdev->wiphy, &init_net);
rdev->rfkill_ops.set_block = cfg80211_rfkill_set_block;
rdev->rfkill = rfkill_alloc(dev_name(&rdev->wiphy.dev),
&rdev->wiphy.dev, RFKILL_TYPE_WLAN,
&rdev->rfkill_ops, rdev);
if (!rdev->rfkill) {
kfree(rdev);
return NULL;
}
INIT_WORK(&rdev->rfkill_sync, cfg80211_rfkill_sync_work);
INIT_WORK(&rdev->conn_work, cfg80211_conn_work);
INIT_WORK(&rdev->event_work, cfg80211_event_work);
init_waitqueue_head(&rdev->dev_wait);
/*
* Initialize wiphy parameters to IEEE 802.11 MIB default values.
* Fragmentation and RTS threshold are disabled by default with the
* special -1 value.
*/
rdev->wiphy.retry_short = 7;
rdev->wiphy.retry_long = 4;
rdev->wiphy.frag_threshold = (u32) -1;
rdev->wiphy.rts_threshold = (u32) -1;
return &rdev->wiphy;
}
发表评论