ath9k usb wifi 网卡驱动浅析

ieee80211

  802.11协议簇是国际电工电子工程学会(IEEE)为无线局域网络制定的标准。

概述

  • nl80211: 用于对无线设备进行配置管理,它是一个基本Netlink的用户态协议(User态)
  • cfg80211: 用于对无线设备进行配置管理。与FullMAC, mac80211和nl80211一起工作。(Kernel态)
  • mac80211: 是一个driver开发者可用于为SoftMAC无线设备写驱动的框架 (Kernel态)。
  • WNIC : Wireless Network Interface Controller, 它总是指望硬件执行协议(如IEEE802.11)描述的功能。
  • MLME: 即MAC(Media Access Control ) Layer Management Entity,它管理物理层MAC状态机。
  • SoftMAC: 其MLME由软件实现,mac80211为SoftMAC实现提供了一个driver API。 即:SoftMAC设备允许对硬件执行更好地控制,允许用软件实现对802.11的帧管理,包括解析和产生802.11无线帧。目前大多数802.11设备为SoftMAC,而FullMAC设备较少。
  • FullMAC: 其MLME由硬件管理,当写FullMAC无线驱动时,不需要使用mac80211。
  • wpa_supplicant: 是用户空间一个应用程序,主要发起MLME命令,然后处理相关结果。

关联

  cfg80211是Linux 802.11配置API。cfg80211用于代码wext(Wireless-Extensions),nl80211用于配置一个cfg80211设备,且用于kernel与userspace间的通信。wext现处理维护状态,没有新的功能被增加,只是修改bug。如果需要通过wext操作,则需要定义CONFIG_CFG80211_WEXT。

  • cfg80211 and nl80211: 基于消息机制,使用netlink接口
  • wext: 基于ioctl机制
  • struct ieee80211_hw: 表示硬件信息和状态
  • ieee80211_alloc_hw:每个driver调用ieee80211_alloc_hw分配ieee80211_hw,且以ieee80211_ops为参数
  • ieee80211_register_hw:每个driver调用ieee80211_register_hw创建wlan0和 wmaster0,并进行各种初始化。
  • struct ieee80211_ops:每个driver实现它的成员函数,且它的成员函数都以struct ieee80211_hw做为第一个参数。在struct ieee80211_ops中定义了24个方法,以下7个方法必须实现:
    tx,start,stop,add_interface,remove_interface,config和configure_filter。

mac80211它是一个driver开发者可用于为SoftMAC无线设备写驱动的框架,mac80211为SoftMAC设备实现了cfg80211回调函数,且mac80211通过cfg80211实现了向网络子系统注册和配置。

数据传递过程

  mac80211和wifi设备驱动程序息息相关,但是它是内核抽象出来的设备无关层,每一个注册进来的wifi设备,它都帮助我们注册一个 net_device,并且使用统一的 ops 函数。那么网络协议上层传递过来的数据包首先经过mac80211层,然后才会传递到设备驱动层,再由硬件发送出去。
  这里写图片描述

ath9k向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 内核无线子系统

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值