发包的时候, skb 在调用 dev_queue_xmit 时被传入。在跟踪具体调用关系后,最终是这样调用的: ops->ndo_start_xmit(skb, dev) 。
注意,刚才的这个函数需要注册才能生效。
对于 WiFi 设备而言,通常我们使用 mac80211 (代替了相应的设备驱动),那是因为 mac80211 已经帮我们注册了。
从 net/mac80211/iface.c 可以看到:
因此 mac80211 也就可以看作是一个 net_device ,当一个数据包通过 WiFi 传输时,相关的传输函数 ieee80211_subif_start_xmit 将被调用。
我们进入 mac80211 内部 ieee80211_subif_start_xmit 实现可以看到这样一个调用子序列:ieee80211_xmit => ieee80211_tx => ieee80211_tx_frags => drv_tx
目前我们处在 mac80211 和 WiFi 驱动的边界, drv_tx 仅仅调用了一个在 WiFi 驱动层实现的并已注册的回调函数。
到这里, mac80211 就结束了,并且设备驱动相关也暂时告一段落了。
正如之前提到的一样,通过 mac80211 中的 local->ops->tx ,注册到设备驱动中的回调函数将会被调用。尽管每个驱动对相应回调函数的实现不尽相同。
下面利用之前模块间接口的例子。结构体成员 tx 对应的函数 rt2x00max_tx 首先需要填充准备发送描述符(一般包含帧长度、ACK 策略、 RTS/CTS、重传时限、分片标志以及 MCS 等)
部分信息由 mac80211 传下来(结构体 ieee80211_tx_info 中就有一些信息将会被使用到),然后驱动程序还要将数据转换成底层硬件可识别的形式。
一旦发送描述符就位,驱动程序还会调整帧数据(如,调整字节对齐等),然后将数据帧放入发送队列,最后将要发送的帧的描述符发给硬件。
由于我们以一个基于 rt73usb 的 USB WiFi 适配器为例,所以数据帧最后是通过 USB 接口发送给无线设备。
然后数据将被插入 PHY 首部以及其他信息,最后数据包被发送到了空中。驱动同时也需要反馈发送状态给 mac80211 , 通常状态信息存放在 struct ieee80211_tx_info 中。
经过 ieee80211_tx_status 一系列的调用,或者某些变种函数反馈给上层。
说到这里,关于数据包的发送也暂时告一段落了。
理论上,我们可以像数据路径一样在用户空间下通过套接字发送控制帧。但是目前有很多开发得十分完善的用户层管理工具能完成这样的工作。
特别是 wpa_supplicant 和 host_apd 。wpa_supplicant 控制客户端 STA 模式下无线网络的连接,如扫描发现网络、身份认证、关联等。
而 host_apd 可以做 AP 。说白了前者就是用来连接热点,后者用来发射热点。这些用户层工具通过 netlink 套接字与内核通信。
内核中相关的回调接口是 cfg80211 中的 nl80211 。用户层的工具通过 netlink 提供的库(如, NL80211_CMD_TRIGGER_SCAN )将命令发送到内核。
在内核中,由 nl80211 接收应用层发出的命令。如下代码展示了对应绑定情况。