该函数为实际上只有struct ath_node *an参数是需要传递的,
ieee80211_ioctl_giwrate输入的参数为net_device *dev,因此,需要将该输入参数转换得到struct ath_node *an参数:
因此,我们需要关注三个结构体:
struct net_device *dev结构体包含了ieee80211vap *vap结构体:
- ieee80211vap *vap = dev->priv;
- an = ATH_NODE(ni);
- struct ath_softc {
- struct ieee80211com sc_ic; /* NB: must be first */
- struct net_device *sc_dev;
- struct semaphore sc_lock; /* dev-level lock */
- struct ath_node {
- struct ieee80211_node an_node; /* base class */
- u_int16_t an_decomp_index; /* decompression mask index */
- u_int32_t an_avgrssi; /* average rssi over all rx frames */
- u_int8_t an_prevdatarix; /* rate ix of last data frame */
- u_int16_t an_minffrate; /* mimum rate in kbps for ff to aggragate */
- HAL_NODE_STATS an_halstats; /* rssi statistics used by hal */
- struct ath_buf *an_tx_ffbuf[WME_NUM_AC]; /* ff staging area */
- ath_bufhead an_uapsd_q; /* U-APSD delivery queue */
- int an_uapsd_qdepth; /* U-APSD delivery queue depth */
- ath_bufhead an_uapsd_overflowq; /* U-APSD overflow queue (for > MaxSp frames) */
- int an_uapsd_overflowqdepth; /* U-APSD overflow queue depth */
- spinlock_t an_uapsd_lock; /* U-APSD deleivery queue lock */
- /* variable-length rate control state follows */
- };
- struct ieee80211_node {
- struct ieee80211vap *ni_vap;
- struct ieee80211com *ni_ic;
- struct ieee80211_node_table *ni_table;
- …………..
- struct ieee80211vap {
- struct net_device *iv_dev; /* associated device */
- struct net_device_stats iv_devstats; /* interface statistics */
- struct ifmedia iv_media; /* interface media config */
- #ifdef CONFIG_NET_WIRELESS
- struct iw_statistics iv_iwstats; /* wireless statistics block */
- #endif
- …………
- struct ieee80211com *iv_ic; /* back ptr to common state */
- ……………
复制代码 下面三个结构体的包含关系为:
ath_node-> ieee80211_node-> ieee80211vap
并且由于首地址是一样的,因此,只要获得任意一个结构体,可以使用指针强制转换为另外两个结构体,例如已知ieee80211_node结构体,可以获得ath_node,在if_ath.c 的ath_tx_start函数中,使用
- an = ATH_NODE(ni)命令获得,
- #define ATH_NODE(_n) ((struct ath_node *)(_n))
- struct ath_softc *sc = dev->priv;
- struct ieee80211com *an = dev->priv;
- ath_rate_findrate(sc, an, shortPreamble, skb->len, &rix, &try0, &txrate);
复制代码 调试中发现,始终不能正确的传递参数,经过调试,发现ieee80211.c和if_ath.c代码的struct net_device变量是不一样的,两者的参数名字分别为ath0和wifi0,也就是两个网络设备的名字,因此,没有办法传递参数。
下面还是分析iwconfig中的参数是如何传递的,以ieee80211_ioctl_siwpower函数为例,设置发送功率。
- ieee80211_ioctl_siwpower(struct net_device *dev, struct iw_request_info *info,
- struct iw_param *wrq, char *extra)
- {
- struct ieee80211vap *vap = dev->priv;
- struct ieee80211com *ic = vap->iv_ic;
- ……………..
- return IS_UP(ic->ic_dev) ? ic->ic_reset(ic->ic_dev) : 0; 如果设备已经UP,那么以新的参数对设备设置。
复制代码 这里面调用了一个函数指针,ic->ic_reset,经过检查,只有 if_ath.c文件调用了,
- if_ath.c(692): ic->ic_reset = ath_reset;
- ath_attach(u_int16_t devid, struct net_device *dev)
- {
- struct ath_softc *sc = dev->priv;
- struct ieee80211com *ic = &sc->sc_ic;
- struct ath_hal *ah;
- ………….
- ic->ic_reset = ath_reset;
- ath_reset(struct net_device *dev)
- {
- struct ath_softc *sc = dev->priv;
- struct ieee80211com *ic = &sc->sc_ic;
- struct ath_hal *ah = sc->sc_ah;
- struct ieee80211_channel *c;
- HAL_STATUS status;
复制代码 iwconfig命令会最终调用ieee80211_wireless.c中的各种参数设置和控制命令,查看代码,发现ieee80211_wireless中本身定义了很多的参数变量,如果需要设置,再调用底层的函数将参数写入网卡芯片,如果读参数,就直接读自己已经保存好的参数,而并不是读真正的物理层的参数!
ieee80211_ioctl_siwpower代码最终会调用ath_reset,这两个函数的输入参数都是struct net_device *dev,他是如何进行参数转换的呢?将ath0变为wifi0?
Iwconfig中,首先由输入参数ath0(struct net_device *dev)获得vap
struct ieee80211vap *vap = dev->priv;
该结构体定义为:
- struct ieee80211vap {
- struct net_device *iv_dev; /* associated device */
- struct ieee80211com *iv_ic; /* back ptr to common state */
- ……………
复制代码 然后由vap获得ic,
- struct ieee80211com *ic = vap->iv_ic;
复制代码 而该结构体定义为:
- struct ieee80211com {
- struct net_device *ic_dev; /* associated device */
复制代码 又关联了一个net_device设备,这个设备正是wifi0,因此,调用ic_reset的时候,并不是直接将
ieee80211_ioctl_siwpower的输出参数dev放进去,而是转了两个弯,
ic->ic_reset(ic->ic_dev)。
再次回到获取速率的函数,调用下面的函数获得速率,
- ath_rate_findrate(sc, an, shortPreamble, skb->len, &rix, &try0, &txrate);
- 这里,sc以前直接定义为:ieee80211_ioctl_siwpower输入参数的dev,
- struct ath_softc *sc = dev->priv;
- 需要将其定义为wifi0,因此,需要定义为:
- struct ieee80211vap *vap = dev->priv; 由ath0 device获得vap
- struct ieee80211com *ic = vap->iv_ic;
- struct net_device *wifi= ic->ic_dev; 再次获得wifi device
- struct ath_softc *sc = wifi ->priv; 获得sc
复制代码 如果调用findrate函数,第二个参数ath_node需要传递过来,发现这比较困难,因此,暂时不使用这个方法,而是自己在ieee80211com结构体中定义一个变量,利用该变量传递参数。
- ath_rate_findrate(struct ath_softc *sc, struct ath_node *an,
- int shortPreamble, size_t frameLen,
- u_int8_t *rix, int *try0, u_int8_t *txrate)
复制代码 9、madwifi程序阅读(二)
前面的阅读已经发现,madwifi驱动里面有两套net_device设备,一套是if_ath_xx.c程序使用的,名字为“wifi0”,另一套是ieee802.11_xx程序使用的,为ath0。
- ath_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id)
- \if_ath_pci.c(179): dev = alloc_netdev(sizeof(struct ath_pci_softc), "wifi%d", ether_setup);
- int ath_attach(u_int16_t devid, struct net_device *dev)
- \if_ath.c(897): error = ieee80211_create_vap(ic, "ath%d", dev,
- autocreatemode, IEEE80211_CLONE_BSSID);
复制代码 相应的,两个设备都定义了自己的发送函数
这是ath0的发送函数,
- int ieee80211_vap_setup(struct ieee80211com *ic, struct net_device *dev,
- const char *name, int unit, int opmode, int flags)
- \ieee80211.c(395): dev->hard_start_xmit = ieee80211_hardstart;
- 这是wifi的发送函数,
- int ath_attach(u_int16_t devid, struct net_device *dev)
- if_ath.c(670): dev->hard_start_xmit = ath_hardstart;
复制代码 ieee80211_hardstart函数调用ieee80211_parent_queue_xmit,ieee80211_parent_queue_xmit 继续调用(void) dev_queue_xmit(skb),dev_queue_xmit函数是在linux内核中定义的
\net\core\dev.c(994):int dev_queue_xmit(struct sk_buff *skb),该函数会调用 dev->hard_start_xmit,这个函数估计就是ath_hardstart。
ath_hardstart调用ath_tx_start函数,ath_tx_start继续调用ath_hal_filltxdesc等与硬件相关的函数,完成最终的发送任务。
这样就出来一个问题了,由谁来调用ieee80211_hardstart?
经过查找,两个hard_start_xmit函数都没有在madfiwi程序中调用,看来都是在 linux内核中调用的。
也就说,madwifi同时注册了两个网络设备,一个是ath0,一个是wifi0,应用程序发送数据的流程是这样的:应用程序->内核->ath0设备->内核->wifi0设备。
要搞清楚为什么会这样,就必须了解madwifi中的VAP(虚拟AP的概念),可以在一个实际的无线设备上创建多个逻辑设备,要使用设备,必须使用下面的命令创建ath设备
wlanconfig athX create wlandev wifiX wlanmode
例如:
wlanconfig ath0 create wlandev wifi0 wlanmode ap
wlanconfig ath1 create wlandev wifi0 wlanmode ap
wlanconfig ath2 create wlandev wifi0 wlanmode ap
iwconfig ath0 essid "lmn"
iwconfig ath1 essid "XYZ"
iwconfig ath2 essid "abc"
因此,可以认为ath是逻辑设备,wifi是物理设备。
(上面部分参考了madwifi.pdf文档,一个ppt,上面对madwifi进行了粗略的介绍,包括存在的问题)
The OpenHAL ported to MADWiFi at
因此,我们在发送数据的时候,已经指明了使用ath0虚拟网卡,自然就会调用ieee80211_hardstart,问题就变成了两次调用内核操作是如何实现的?
还是先看看几个结构体是如何关联的,
- ath_attach(u_int16_t devid, struct net_device *dev)
- {
- struct ath_softc *sc = dev->priv; 由wifi设备获得sc
- struct ieee80211com *ic = &sc->sc_ic; 由wifi设备获得com设备ic,
- ……….
- ic->ic_dev = dev; ic的设备指向wifi
- ic->ic_mgtstart = ath_mgtstart;
- ic->ic_init = ath_init;
- ic->ic_reset = ath_reset;
- ic->ic_newassoc = ath_newassoc;
- ic->ic_updateslot = ath_updateslot;
- ………
- dev->open = ath_init;
- dev->stop = ath_stop;
- dev->hard_start_xmit = ath_hardstart;
- dev->tx_timeout = ath_tx_timeout;
复制代码 ieee80211_ifattach(ic); 加载ieee802设备
创建VAP,得到ath0设备。
- error = ieee80211_create_vap(ic, "ath%d", dev,
- autocreatemode, IEEE80211_CLONE_BSSID);
复制代码