802.11协议簇是国际电工电子工程学会(IEEE)为无线局域网络制定的标准。
cfg80211是Linux 802.11配置API。cfg80211用于代码wext(Wireless-Extensions),nl80211用于配置一个cfg80211设备,且用于kernel与userspace间的通信。wext现处理维护状态,没有新的功能被增加,只是修改bug。如果需要通过wext操作,则需要定义CONFIG_CFG80211_WEXT。
mac80211它是一个driver开发者可用于为SoftMAC无线设备写驱动的框架,mac80211为SoftMAC设备实现了cfg80211回调函数,且mac80211通过cfg80211实现了向网络子系统注册和配置。
mac80211和wifi设备驱动程序息息相关,但是它是内核抽象出来的设备无关层,每一个注册进来的wifi设备,它都帮助我们注册一个 net_device,并且使用统一的 ops 函数。那么网络协议上层传递过来的数据包首先经过mac80211层,然后才会传递到设备驱动层,再由硬件发送出去。
当 usb host 识别出 usb 设备为 ath9k 时,会调用到驱动程序中的 probe 函数。
static int ath9k_hif_usb_probe(struct usb_interface *interface,
const struct usb_device_id *id)
{
struct usb_device *udev = interface_to_usbdev(interface);
struct hif_device_usb *hif_dev;
int ret = 0;
hif_dev = kzalloc(sizeof(struct hif_device_usb), GFP_KERNEL);
usb_get_dev(udev);
hif_dev->udev = udev;
hif_dev->interface = interface;
hif_dev->usb_device_id = id;
//dev_set_drvdata(&intf->dev, data);
usb_set_intfdata(interface, hif_dev);
init_completion(&hif_dev->fw_done);
/* Find out which firmware to load */
if (IS_AR7010_DEVICE(id->driver_info))
hif_dev->fw_name = FIRMWARE_AR7010_1_1;
else
hif_dev->fw_name = FIRMWARE_AR9271;
//加载固件,成功后调用 ath9k_hif_usb_firmware_cb
ret = request_firmware_nowait(THIS_MODULE, true, hif_dev->fw_name,
&hif_dev->udev->dev, GFP_KERNEL,
hif_dev, ath9k_hif_usb_firmware_cb);
return 0;
}
static void ath9k_hif_usb_firmware_cb(const struct firmware *fw, void *context)
{
struct hif_device_usb *hif_dev = context;
int ret;
//hif_dev->htc_handle->target->hif = hif_usb
hif_dev->htc_handle = ath9k_htc_hw_alloc(hif_dev, &hif_usb, &hif_dev->udev->dev);
hif_dev->firmware = fw;
ret = ath9k_hif_usb_dev_init(hif_dev);
ret = ath9k_htc_hw_init(hif_dev->htc_handle,
&hif_dev->interface->dev,
hif_dev->usb_device_id->idProduct,
hif_dev->udev->product,
hif_dev->usb_device_id->driver_info);
complete(&hif_dev->fw_done);
return;
}
static int ath9k_hif_usb_dev_init(struct hif_device_usb *hif_dev)
{
struct usb_host_interface *alt = &hif_dev->interface->altsetting[0];
struct usb_endpoint_descriptor *endp;
int ret, idx;
ret = ath9k_hif_usb_download_fw(hif_dev);
ret = ath9k_hif_usb_alloc_urbs(hif_dev);
return 0;
}
static int ath9k_hif_usb_download_fw(struct hif_device_usb *hif_dev)
{
int transfer, err;
const void *data = hif_dev->firmware->data;
size_t len = hif_dev->firmware->size;
u32 addr = AR9271_FIRMWARE;
u8 *buf = kzalloc(4096, GFP_KERNEL);
u32 firm_offset;
if (!buf)
return -ENOMEM;
while (len) {
transfer = min_t(size_t, len, 4096);
memcpy(buf, data, transfer);
//通过控制传输将固件传递给usb设备
//FIRMWARE_DOWNLOAD并非标准的request,可能是厂家自己定义的
//我们构造urb时无需关心端点的最大包大小,虽然确实是由多次传输构成的
//比如主机请求一个长度16字节的描述符,设备收到请求之后会分两次发送给主机
err = usb_control_msg(hif_dev->udev,
usb_sndctrlpipe(hif_dev->udev, 0),
FIRMWARE_DOWNLOAD, 0x40 | USB_DIR_OUT,
addr >> 8, 0, buf, transfer, HZ);
len -= transfer;
data += transfer;
addr += transfer;
}
kfree(buf);
if (IS_AR7010_DEVICE(hif_dev->usb_device_id->driver_info))
firm_offset = AR7010_FIRMWARE_TEXT;
else
firm_offset = AR9271_FIRMWARE_TEXT;
//通知设备固件下载完成
err = usb_control_msg(hif_dev->udev, usb_sndctrlpipe(hif_dev->udev, 0),
FIRMWARE_DOWNLOAD_COMP,
0x40 | USB_DIR_OUT,
firm_offset >> 8, 0, NULL, 0, HZ);
return 0;
}
//数据传输4个同的端点
#define USB_WLAN_TX_PIPE 1
#define USB_WLAN_RX_PIPE 2
#define USB_REG_IN_PIPE 3
#define USB_REG_OUT_PIPE 4
static int ath9k_hif_usb_alloc_urbs(struct hif_device_usb *hif_dev)
{
init_usb_anchor(&hif_dev->regout_submitted);
//分配一个tx_buf绑定一个urb,把tx_buf挂入hif_dev->tx.tx_buf链表
ath9k_hif_usb_alloc_tx_urbs(hif_dev)
ath9k_hif_usb_alloc_rx_urbs(hif_dev)
ath9k_hif_usb_alloc_reg_in_urbs(hif_dev)
return 0;
}
数据urb,有数据到来调用 ath9k_hif_usb_rx_cb
static int ath9k_hif_usb_alloc_rx_urbs(struct hif_device_usb *hif_dev)
{
struct urb *urb = NULL;
struct sk_buff *skb = NULL;
int i, ret;
init_usb_anchor(&hif_dev->rx_submitted);
spin_lock_init(&hif_dev->rx_lock);
for (i = 0; i < MAX_RX_URB_NUM; i++) {
urb = usb_alloc_urb(0, GFP_KERNEL);
skb = alloc_skb(MAX_RX_BUF_SIZE, GFP_KERNEL);
//提交urb 有数据到来时调用 ath9k_hif_usb_rx_cb
usb_fill_bulk_urb(urb, hif_dev->udev,
usb_rcvbulkpipe(hif_dev->udev,
USB_WLAN_RX_PIPE),
skb->data, MAX_RX_BUF_SIZE,
ath9k_hif_usb_rx_cb, skb);
usb_anchor_urb(urb, &hif_dev->rx_submitted);
ret = usb_submit_urb(urb, GFP_KERNEL);
//当urb的引用计数为0时释放内存
usb_free_urb(urb);
}
return 0;
}
控制数据的urb ?,有控制数据到来调用 ath9k_hif_usb_reg_in_cb
static int ath9k_hif_usb_alloc_reg_in_urbs(struct hif_device_usb *hif_dev)
{
struct urb *urb = NULL;
struct sk_buff *skb = NULL;
int i, ret;
init_usb_anchor(&hif_dev->reg_in_submitted);
for (i = 0; i < MAX_REG_IN_URB_NUM; i++) {
urb = usb_alloc_urb(0, GFP_KERNEL);
skb = alloc_skb(MAX_REG_IN_BUF_SIZE, GFP_KERNEL);
usb_fill_bulk_urb(urb, hif_dev->udev,
usb_rcvbulkpipe(hif_dev->udev,
USB_REG_IN_PIPE),
skb->data, MAX_REG_IN_BUF_SIZE,
ath9k_hif_usb_reg_in_cb, skb);
usb_anchor_urb(urb, &hif_dev->reg_in_submitted);
ret = usb_submit_urb(urb, GFP_KERNEL);
usb_free_urb(urb);
}
return 0;
}
int ath9k_htc_hw_init(struct htc_target *target,
struct device *dev, u16 devid,
char *product, u32 drv_info)
{
if (ath9k_htc_probe_device(target, dev, devid, product, drv_info)) {
pr_err("Failed to initialize the device\n");
return -ENODEV;
}
return 0;
}
int ath9k_htc_probe_device(struct htc_target *htc_handle, struct device *dev,
u16 devid, char *product, u32 drv_info)
{
struct ieee80211_hw *hw;
struct ath9k_htc_priv *priv;
int ret;
//分配一个无线设备结构,绑定一个操作函数集
hw = ieee80211_alloc_hw(sizeof(struct ath9k_htc_priv), &ath9k_htc_ops);
priv = hw->priv;
priv->hw = hw;
priv->htc = htc_handle;
priv->dev = dev;
htc_handle->drv_priv = priv;
SET_IEEE80211_DEV(hw, priv->dev);
ret = ath9k_htc_wait_for_target(priv);
priv->wmi = ath9k_init_wmi(priv);
ret = ath9k_init_htc_services(priv, devid, drv_info);
ret = ath9k_init_device(priv, devid, product, drv_info);
return 0;
}
ath9k_htc_probe_device(target, dev, devid, product, drv_info))
//在内核中wiphy(包括local)来描述一个线设备,在这里构造一个,并指定操作函数
ieee80211_alloc_hw(sizeof(struct ath9k_htc_priv), &ath9k_htc_ops);
struct wiphy * wiphy = wiphy_new(&mac80211_config_ops, priv_size);
struct ieee80211_local *local = wiphy_priv(wiphy);
local->ops = ops;
tasklet_init(&local->tx_pending_tasklet, ieee80211_tx_pending,(unsigned long)local);
tasklet_init(&local->tasklet,ieee80211_tasklet_handler,(unsigned long) local);
ath9k_init_device(priv, devid, product, drv_info);
ath9k_init_priv(priv, devid, product, drv_info);
tasklet_init(&priv->rx_tasklet, ath9k_rx_tasklet, (unsigned long)priv);
ieee80211_register_hw(hw); //硬件无关,无线子系统帮我们构造了一个netdevice提供统一的ops:ieee80211_dataif_ops
wiphy_register(local->hw.wiphy);
ieee80211_if_add(local, "wlan%d", NULL,NL80211_IFTYPE_STATION, NULL);
alloc_netdev_mqs(sizeof(*sdata) + local->hw.vif_data_size,name, ieee80211_if_setup, txqs, 1);
ieee80211_setup_sdata(sdata, type);
dev->netdev_ops = &ieee80211_dataif_ops;
register_netdevice(ndev);
struct ieee80211_ops ath9k_htc_ops = {
.tx = ath9k_htc_tx,
.start = ath9k_htc_start,
.stop = ath9k_htc_stop,
.add_interface = ath9k_htc_add_interface,
.remove_interface = ath9k_htc_remove_interface,
.config = ath9k_htc_config,
.configure_filter = ath9k_htc_configure_filter,
.sta_add = ath9k_htc_sta_add,
.sta_remove = ath9k_htc_sta_remove,
.conf_tx = ath9k_htc_conf_tx,
.bss_info_changed = ath9k_htc_bss_info_changed,
.set_key = ath9k_htc_set_key,
.get_tsf = ath9k_htc_get_tsf,
.set_tsf = ath9k_htc_set_tsf,
.reset_tsf = ath9k_htc_reset_tsf,
.ampdu_action = ath9k_htc_ampdu_action,
.sw_scan_start = ath9k_htc_sw_scan_start,
.sw_scan_complete = ath9k_htc_sw_scan_complete,
.set_rts_threshold = ath9k_htc_set_rts_threshold,
.rfkill_poll = ath9k_htc_rfkill_poll_state,
.set_coverage_class = ath9k_htc_set_coverage_class,
.set_bitrate_mask = ath9k_htc_set_bitrate_mask,
.get_stats = ath9k_htc_get_stats,
};
协议上层发送数据包过来,首先是到无线子系统帮我们注册的netdevice这里,调用
static const struct net_device_ops ieee80211_dataif_ops = {
.ndo_open = ieee80211_open,
.ndo_stop = ieee80211_stop,
.ndo_uninit = ieee80211_teardown_sdata,
.ndo_start_xmit = ieee80211_subif_start_xmit,
.ndo_set_rx_mode = ieee80211_set_multicast_list,
.ndo_change_mtu = ieee80211_change_mtu,
.ndo_set_mac_address = ieee80211_change_mac,
.ndo_select_queue = ieee80211_netdev_select_queue,
};
中的 ieee80211_subif_start_xmit
//硬件无关
ieee80211_xmit
=> ieee80211_tx
=> __ieee80211_tx
=> ieee80211_tx_frags
=> drv_tx
local->ops->tx(&local->hw, skb); => ath9k_htc_tx
//硬件相关
static void ath9k_htc_tx(struct ieee80211_hw *hw, struct sk_buff *skb)
ret = ath9k_htc_tx_start(priv, skb, slot, false);
//判断是数据还是管理包,至于如何分辨的需要分析sk_buff,暂时保留
if (ieee80211_is_data(hdr->frame_control))
ath9k_htc_tx_data(priv, vif, skb, sta_idx, vif_idx, slot, is_cab);
else
ath9k_htc_tx_mgmt(priv, avp, skb, sta_idx, vif_idx, slot);
htc_send(priv->htc, skb);
htc_issue_send(target, skb, skb->len, 0, tx_ctl->epid);
hif_usb_send
//根据数据包的类型,发送给对应的端点
ret = hif_usb_send_tx(hif_dev, skb);
ret = hif_usb_send_regout(hif_dev, skb);
usb_fill_bulk_urb(urb, hif_dev->udev,
usb_sndbulkpipe(hif_dev->udev,
USB_REG_OUT_PIPE),
skb->data,
skb->len,
hif_usb_regout_cb,
cmd);
当一个数据包在空中被无线设备捕捉到后,硬件将会向内核发出一个中断(大部分 PCI 接口的设备这样做),或则通过轮询机制判断是否有数据到来(如,使用了 USB 接口)。
前者,中断将会引发中断处理程序的执行,后者促使特定的接收函数将被调用。
一般设备驱动层的回调函数不会做太多关于接收数据包的操作,仅仅做数据校验,为 mac80211 填充接收描述符,然后把数据包推给 mac80211,由 mac80211 来做之后的工作(直接或间接将数据包放入接收队列)。
数据进入 mac80211 后,将会调用 ieee80211_rx 或者其他变种接收函数。在这里数据路径和管理路径也将分开进行。
如果收到的帧是数据,它将被转换成 802.3 数据帧(通过 __ieee80211_data_to8023 实现),然后该数据帧将通过 netif_receive_skb 交付到网络协议栈。在协议栈中,各层网络协议将会对数据进行解析,识别协议首部。
如果接收到的是控制帧,数据将会由 ieee80211_sta_rx_queued_mgmt 处理。部分控制帧在 mac80211 层就终止,另外一些将会通过 cfg80211 发送到用户空间下的管理程序。
例如,身份认证控制帧被 cfg80211_rx_mlme_mgmt 处理,然后通过 nl80211_send_rx_auth 发送到用户空间下的 wpa_supplicant ; 相应的关联响应控制帧被 cfg80211_rx_assoc_resp 处理,并由 nl80211_send_rx_assoc 发送到用户空间。
//硬件相关
ath9k_hif_usb_alloc_urbs(hif_dev);
static int ath9k_hif_usb_alloc_reg_in_urbs(struct hif_device_usb *hif_dev)
usb_fill_bulk_urb(urb, hif_dev->udev, usb_rcvbulkpipe(hif_dev->udev, USB_REG_IN_PIPE), skb->data, MAX_REG_IN_BUF_SIZE,
ath9k_hif_usb_reg_in_cb, skb);
static void ath9k_hif_usb_reg_in_cb(struct urb *urb)
ath9k_htc_rx_msg(hif_dev->htc_handle, skb, skb->len, USB_REG_IN_PIPE);
endpoint->ep_callbacks.rx(endpoint->ep_callbacks.priv, skb, epid);
static inline int ath9k_htc_connect_svc(struct ath9k_htc_priv *priv,
req.ep_callbacks.rx = ath9k_htc_rxep;
void ath9k_htc_rxep(void *drv_priv, struct sk_buff *skb, enum htc_endpoint_id ep_id)
tasklet_schedule(&priv->rx_tasklet);
void ath9k_rx_tasklet(unsigned long data)
ieee80211_rx(priv->hw, skb);
//硬件无关
/*
* This is the receive path handler. It is called by a low level driver when an
* 802.11 MPDU is received from the hardware.
*/
void ieee80211_rx(struct ieee80211_hw *hw, struct sk_buff *skb)
static void __ieee80211_rx_handle_packet(struct ieee80211_hw *hw,struct sk_buff *skb)
static bool ieee80211_prepare_and_rx_handle(struct ieee80211_rx_data *rx,struct sk_buff *skb, bool consume)
static void ieee80211_invoke_rx_handlers(struct ieee80211_rx_data *rx)
static void ieee80211_rx_handlers(struct ieee80211_rx_data *rx)
static void ieee80211_rx_handlers(struct ieee80211_rx_data *rx)
{
CALL_RXH(ieee80211_rx_h_decrypt)
CALL_RXH(ieee80211_rx_h_check_more_data)
CALL_RXH(ieee80211_rx_h_uapsd_and_pspoll)
CALL_RXH(ieee80211_rx_h_sta_process)
CALL_RXH(ieee80211_rx_h_defragment)
CALL_RXH(ieee80211_rx_h_michael_mic_verify)
/* must be after MMIC verify so header is counted in MPDU mic */
#ifdef CONFIG_MAC80211_MESH
if (ieee80211_vif_is_mesh(&rx->sdata->vif))
CALL_RXH(ieee80211_rx_h_mesh_fwding);
#endif
CALL_RXH(ieee80211_rx_h_amsdu)
CALL_RXH(ieee80211_rx_h_data) //数据
CALL_RXH(ieee80211_rx_h_ctrl); //控制
CALL_RXH(ieee80211_rx_h_mgmt_check)
CALL_RXH(ieee80211_rx_h_action)
CALL_RXH(ieee80211_rx_h_userspace_mgmt)
CALL_RXH(ieee80211_rx_h_action_return)
CALL_RXH(ieee80211_rx_h_mgmt) //管理
}
以数据为例:
ieee80211_rx_h_data(struct ieee80211_rx_data *rx)
err = __ieee80211_data_to_8023(rx, &port_control);
ieee80211_deliver_skb(rx);
dev_queue_xmit(xmit_skb);
netif_receive_skb(skb);
理论上,我们可以像数据路径一样在用户空间下通过套接字发送控制帧。但是目前有很多开发得十分完善的用户层管理工具能完成这样的工作。
特别是 wpa_supplicant 和 host_apd 。wpa_supplicant 控制客户端 STA 模式下无线网络的连接,如扫描发现网络、身份认证、关联等。
而 host_apd 可以做 AP 。说白了前者就是用来连接热点,后者用来发射热点。这些用户层工具通过 netlink 套接字与内核通信。
内核中相关的回调接口是 cfg80211 中的 nl80211 。用户层的工具通过 netlink 提供的库(如, NL80211_CMD_TRIGGER_SCAN )将命令发送到内核。
在内核中,由 nl80211 接收应用层发出的命令。如下代码展示了对应绑定情况。
static int __init cfg80211_init(void)
{
err = register_pernet_device(&cfg80211_pernet_ops);
err = wiphy_sysfs_init();
err = register_netdevice_notifier(&cfg80211_netdev_notifier);
err = nl80211_init();
}
int nl80211_init(void)
{
err = genl_register_family_with_ops(&nl80211_fam, nl80211_ops, ARRAY_SIZE(nl80211_ops));
...
}
static struct genl_ops nl80211_ops[] = {
{
.cmd = NL80211_CMD_GET_WIPHY,
.doit = nl80211_get_wiphy,
.dumpit = nl80211_dump_wiphy,
.policy = nl80211_policy,
/* can be retrieved by unprivileged users */
.internal_flags = NL80211_FLAG_NEED_WIPHY,
},
{
.cmd = NL80211_CMD_TRIGGER_SCAN,
.doit = nl80211_trigger_scan,
.policy = nl80211_policy,
.flags = GENL_ADMIN_PERM,
.internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
NL80211_FLAG_NEED_RTNL,
},
...
}
static int nl80211_trigger_scan(struct sk_buff *skb, struct genl_info *info)
{
struct cfg80211_registered_device *rdev = info->user_ptr[0];
err = rdev->ops->scan(&rdev->wiphy, dev, request);
}
在 mac80211 中, ieee80211_scan 将会具体去实现扫描发现网络的具体细节,最终还是会调到驱动层的操作函数
local->ops->sw_scan_start(&local->hw);
Linux Wireless架构总结
浅谈 Linux 内核无线子系统