好记性不如铅笔头

操作系统

linux802.11softmac代码流程简单解析

Linux中wifi驱动分为两种模式,FullMac模式和SoftMac模式,于此相对应,Linux也有两套对应的驱动和代码框架来实现,这里简单的笔记下SoftMac的代码框架。

注:
1 本文基于Linux2.6.32.65代码实现。
2 作者在Linux wireless开发方面是新手一枚,对框架的理解上可能不太深刻,欢迎大家批评指正。

在Linux2.6.32目录中,可以发现在/net下有两个目录,mac80211和wireless.这里分别对应了softmac模式和fullmac实现模式.这里简单的笔记下wireless即softmac模式下的功能实现.

不过在笔记softmac前,先笔记下fullmac的相关内容。参考链接【 http://116.62.110.235/blog/linux-80211-fullmac/ 】
SoftMAC的原理为Linux内核中实现了一部分802.11的上层协议,这样做的好处是减少了WIFI芯片的工作量。那么由于FullMAC已经实现了一套完整的程序框架,那么softmac就可以作为FullMac的一个调用子框架。通过阅读代码可以发现,SoftMAC也是注册了一个cfg80211_ops的函数回调指针结构体到FullMac中,这样,当外部nl80211进行调用时,会调用到SoftMac中,SoftMac进行一次802.11业务处理之后再转向WIFI芯片。

那么这里就笔记下SoftMAC的大概调用流程。

\linux-2.6.32.65\net\mac80211\cfg.c
我们可以看到,softmac中也定义了一个cfg80211_ops的函数回调结构体。我们先不关注该结构体是如何被注册到FullMac中,先分析回调函数里面的流程。
struct cfg80211_ops mac80211_config_ops = {
......
......
	.scan = ieee80211_scan,
......
......
};
......
......
static int ieee80211_scan(struct wiphy *wiphy,
			  struct net_device *dev,
			  struct cfg80211_scan_request *req)
{
......
......
	return ieee80211_request_scan(sdata, req);
}
......
......
\linux-2.6.32.65\net\mac80211\scan.c
我们跟着函数流程一级一级走下去
int ieee80211_request_scan(struct ieee80211_sub_if_data *sdata,
			   struct cfg80211_scan_request *req)
{
	int res;

	mutex_lock(&sdata->local->scan_mtx);
	res = __ieee80211_start_scan(sdata, req);
	mutex_unlock(&sdata->local->scan_mtx);

	return res;
}

static int __ieee80211_start_scan(struct ieee80211_sub_if_data *sdata,
				  struct cfg80211_scan_request *req)
{
	struct ieee80211_local *local = sdata->local;
	struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
	int rc;

	if (local->scan_req)
		return -EBUSY;

	if (req != local->int_scan_req &&
	    sdata->vif.type == NL80211_IFTYPE_STATION &&
	    !list_empty(&ifmgd->work_list)) {
		/* actually wait for the work it's doing to finish/time out */
		set_bit(IEEE80211_STA_REQ_SCAN, &ifmgd->request);
		local->scan_req = req;
		local->scan_sdata = sdata;
		return 0;
	}

	if (local->ops->hw_scan) {
		u8 *ies;
		int ielen;

		ies = kmalloc(2 + IEEE80211_MAX_SSID_LEN +
			      local->scan_ies_len + req->ie_len, GFP_KERNEL);
		if (!ies)
			return -ENOMEM;

		ielen = ieee80211_build_preq_ies(local, ies,
						 req->ie, req->ie_len);
		local->orig_ies = req->ie;
		local->orig_ies_len = req->ie_len;
		req->ie = ies;
		req->ie_len = ielen;
	}

	local->scan_req = req;
	local->scan_sdata = sdata;

	if (local->ops->hw_scan)
		__set_bit(SCAN_HW_SCANNING, &local->scanning);
	else
		__set_bit(SCAN_SW_SCANNING, &local->scanning);
	/*
	 * Kicking off the scan need not be protected,
	 * only the scan variable stuff, since now
	 * local->scan_req is assigned and other callers
	 * will abort their scan attempts.
	 *
	 * This avoids getting a scan_mtx -> iflist_mtx
	 * dependency, so that the scan completed calls
	 * have more locking freedom.
	 */

	ieee80211_recalc_idle(local);
	mutex_unlock(&local->scan_mtx);

	if (local->ops->hw_scan)
		rc = drv_hw_scan(local, local->scan_req);
	else
		rc = ieee80211_start_sw_scan(local);

	mutex_lock(&local->scan_mtx);

	if (rc) {
		if (local->ops->hw_scan)
			ieee80211_restore_scan_ies(local);
		local->scanning = 0;

		ieee80211_recalc_idle(local);

		local->scan_req = NULL;
		local->scan_sdata = NULL;
	}

	return rc;
}

\linux-2.6.32.65\net\mac80211\driver-ops.h
static inline int drv_hw_scan(struct ieee80211_local *local,
			      struct cfg80211_scan_request *req)
{
	int ret = local->ops->hw_scan(&local->hw, req);
	trace_drv_hw_scan(local, req, ret);
	return ret;
}

通过上面的两个函数可以发现,SoftMac在处理消息时,会先进行一部分协议上的处理,之后会调用ieee80211_local这一结构体中的ops函数指针进行WIFI芯片上的工作。
这里需要注意下ieee80211_local是SoftMAC的管理结构体。
分析到这里,我们在回头来看下cfg80211_ops指针是如何被注册到FullMAC中的。

\linux-2.6.32.65\net\mac80211\main.c
在该文件中导出了一个函数ieee80211_alloc_hw,这个函数和FullMac代码框架中的wiphy_new类似,也是注册一个底层函数进来,我们可以看到这里注册了一个ieee80211_ops结构体,同时创造了一个ieee80211_local结构体。

struct ieee80211_hw *ieee80211_alloc_hw(size_t priv_data_len,
					const struct ieee80211_ops *ops)
{
	struct ieee80211_local *local;
	int priv_size, i;
	struct wiphy *wiphy;

	/* Ensure 32-byte alignment of our private data and hw private data.
	 * We use the wiphy priv data for both our ieee80211_local and for
	 * the driver's private data
	 *
	 * In memory it'll be like this:
	 *
	 * +-------------------------+
	 * | struct wiphy	    |
	 * +-------------------------+
	 * | struct ieee80211_local  |
	 * +-------------------------+
	 * | driver's private data   |
	 * +-------------------------+
	 *
	 */
	priv_size = ALIGN(sizeof(*local), NETDEV_ALIGN) + priv_data_len;

	wiphy = wiphy_new(&mac80211_config_ops, priv_size);

	if (!wiphy)
		return NULL;

	wiphy->netnsok = true;
	wiphy->privid = mac80211_wiphy_privid;

	/* Yes, putting cfg80211_bss into ieee80211_bss is a hack */
	wiphy->bss_priv_size = sizeof(struct ieee80211_bss) -
			       sizeof(struct cfg80211_bss);

	local = wiphy_priv(wiphy);

	local->hw.wiphy = wiphy;

	local->hw.priv = (char *)local + ALIGN(sizeof(*local), NETDEV_ALIGN);

	BUG_ON(!ops->tx);
	BUG_ON(!ops->start);
	BUG_ON(!ops->stop);
	BUG_ON(!ops->config);
	BUG_ON(!ops->add_interface);
	BUG_ON(!ops->remove_interface);
	BUG_ON(!ops->configure_filter);
	local->ops = ops;

......
......

	return local_to_hw(local);
}

 

Leave a Reply

20 − 13 =

此站点使用Akismet来减少垃圾评论。了解我们如何处理您的评论数据