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); }
发表评论