mac80211源码分析

具体概念性的内容,以及mac80211的框架,可以参看:
http://blog.csdn.net/zxygww/article/details/24874155

WLAN
linux下wlan总体架构如图所示:

这里写图片描述
mac80211架构,如图所示:

这里写图片描述
用户空间:
配置:wpa_supplicant和hostapd:
所有直接与用户交互的应用程序都在这里。 例如可以基于GUI / CLI。 在Ubuntu / Fedora发行版本的网络管理器是基于GUI的,但最核心的部分是基于命令行的。例如,使用wpa_supplicant用于控制STA的部分和使用hostapd用于控制AP的部分。
两者都是可通过其CLI版本(wpa_cli,hostapd_cli)配置的。

用户空间和内核空间之间的桥
如何在用户空间中的各种应用程序和内核中的核心实体之间进行沟通呢? 我们有不同的方法,但都是基于不同的套接字接口。

WExt ==> 通用无线扩展:IOCTL接口
NL80211==> Netlink套接字
HostAP ==> 原始数据包套接字
特定于芯片组:
              Atheros==> IOCTL 接口 
              Prism,IPW etc. 


内核空间
配置和UMAC
对于开源世界来说,内核的WLAN架构是mac80211,它分成2个内核模块。
cfg80211.ko:它处理所有的配置,和用户空间的交互。
mac80211.ko:协议:上层的MAC,和驱动程序的交互。

大部分功能和管理是由具有mac80211模块在底层的MAC协助下处理的。
低层的Mac驱动程序
低层的MAC驱动担任UMAC和芯片组(固件和硬件)之间的桥梁。 他们通过Linux内核提供的服​务来执行所有的设备初始化,注册到操作系统上,错误注册,中断注册等。

一个精心编写的驱动程序遵循这些约定:

保持一个操作系统无关层:方便移植到不同的操作系统。
保持一个UMAC无关层:方便移植到不同的UMAC上:专有,开源,第三方等。
总线抽象层:保持不同的物理总线之间,如PCI,PCIe,AHB,SDIO等的相容性


芯片组:固件和硬件
完整的802.11协议的功能在这里实现。
固件可能是运行在一个单独的处理器/微控制器上,它配置和控制硬件,同时还通过一个芯片组(控制路径)特定的通讯接口与主机(驱动程序)进行交互。
数据路径通常包括一个硬件上的DMA控制器,它负责产生中断给主处理器和从/至主机传输数据包到硬件队列。
mac80211:auth-assoc-deauth

这里写图片描述

This is copied from Documentation/networking/mac80211-auth-assoc-deauth.txt in the kernel sources

文件
查看源码,记住各个源码文件的作用:

ieee80211_i.h(主要数据结构)
main.c(主函数入口)
iface.c(虚拟接口处理)
key.c,key.h(密钥管理)
sta_info.c,sta_info.h(用户管理)
pm.c(功率管理)
rate.c,rate.h(速率控制函数)
rc80211*(速率控制算法)
rx.c(帧接收路径代码)
tx.c(帧发送路径代码)
scan.c(软件扫描代码)
mlme.c(station/managed模式MLME)
ibss.c(IBSSMLME)
cfg.c,cfg.h,wext.c(配置入口代码)
aes*,tkip*,wep*,michael*,wpa*(WPA/RSN/WEP代码)
wme.c,wme.h(QoS代码)
util.c(公共函数)

 

 

结构体
记住重要的结构体以及结构体的作用:

ieee80211_local/ieee80211_hw
每个数据结构代表一个无线设备(ieee80211_hw嵌入到ieee80211_local)
ieee80211_hw是ieee80211_local在驱动中的可见部分
包含无线设备的所有操作信息

struct ieee80211_hw {
    struct ieee80211_conf conf;
    struct wiphy *wiphy;
    const char *rate_control_algorithm;
    void *priv;
    u32 flags;
    unsigned int extra_tx_headroom;
    unsigned int extra_beacon_tailroom;
    int vif_data_size;
    int sta_data_size;
    int chanctx_data_size;
    u16 queues;
    u16 max_listen_interval;
    s8 max_signal;
    u8 max_rates;
    u8 max_report_rates;
    u8 max_rate_tries;
    u8 max_rx_aggregation_subframes;
    u8 max_tx_aggregation_subframes;
    u8 offchannel_tx_hw_queue;
    u8 radiotap_mcs_details;
    u16 radiotap_vht_details;
    netdev_features_t netdev_features;
    u8 uapsd_queues;
    u8 uapsd_max_sp_len;
    u8 n_cipher_schemes;
    const struct ieee80211_cipher_scheme *cipher_schemes;
};


sta_info/ieee80211_sta
代表每一个station
可能是mesh,IBSS,AP,WDS
ieee80211_sta是驱动可见部分

struct ieee80211_sta {
    u32 supp_rates[IEEE80211_NUM_BANDS];
    u8 addr[ETH_ALEN];
    u16 aid;
    struct ieee80211_sta_ht_cap ht_cap;
    struct ieee80211_sta_vht_cap vht_cap;
    bool wme;
    u8 uapsd_queues;
    u8 max_sp;
    u8 rx_nss;
    enum ieee80211_sta_rx_bandwidth bandwidth;
    enum ieee80211_smps_mode smps_mode;
    struct ieee80211_sta_rates __rcu *rates;
    bool tdls;

    /* must be last */
    u8 drv_priv[0] __aligned(sizeof(void *));
};


ieee80211_conf
硬件配置
当前信道是最重要的字段
硬件特殊参数

struct ieee80211_conf {
    u32 flags;
    int power_level, dynamic_ps_timeout;
    int max_sleep_period;

    u16 listen_interval;
    u8 ps_dtim_period;

    u8 long_frame_max_tx_count, short_frame_max_tx_count;

    struct cfg80211_chan_def chandef;
    bool radar_enabled;
    enum ieee80211_smps_mode smps_mode;
};


ieee80211_bss_conf
BSS配置
多BSSes类型(IBSS/AP/managed)
包含比如基础速率位图
perBSS parameters in case hardware supports creating/associating withmultiple BSSes

struct ieee80211_bss_conf {
    const u8 *bssid;
    /* association related data */
    bool assoc, ibss_joined;
    bool ibss_creator;
    u16 aid;
    /* erp related data */
    bool use_cts_prot;
    bool use_short_preamble;
    bool use_short_slot;
    bool enable_beacon;
    u8 dtim_period;
    u16 beacon_int;
    u16 assoc_capability;
    u64 sync_tsf;
    u32 sync_device_ts;
    u8 sync_dtim_count;
    u32 basic_rates;
    struct ieee80211_rate *beacon_rate;
    int mcast_rate[IEEE80211_NUM_BANDS];
    u16 ht_operation_mode;
    s32 cqm_rssi_thold;
    u32 cqm_rssi_hyst;
    struct cfg80211_chan_def chandef;
    __be32 arp_addr_list[IEEE80211_BSS_ARP_ADDR_LIST_LEN];
    int arp_addr_cnt;
    bool qos;
    bool idle;
    bool ps;
    u8 ssid[IEEE80211_MAX_SSID_LEN];
    size_t ssid_len;
    bool hidden_ssid;
    int txpower;
    struct ieee80211_p2p_noa_attr p2p_noa_attr;
};


ieee80211_key/ieee80211_key_conf
代表加密/解密密钥
ieee80211_key_conf提供给驱动用于硬件加速
ieee80211_key包含book-keeping和软件解密状态

struct ieee80211_key_conf {
    u32 cipher;
    u8 icv_len;
    u8 iv_len;
    u8 hw_key_idx;
    u8 flags;
    s8 keyidx;
    u8 keylen;
    u8 key[0];
};


ieee80211_tx_info
大部分复杂数据结构
skb内部控制缓冲区(cb)
经历三个阶段:1、由mac80211初始化;2、由驱动使用;3、由发送状态通告使用

struct ieee80211_tx_info {
    /* common information */
    u32 flags;
    u8 band;

    u8 hw_queue;

    u16 ack_frame_id;

    union {
        struct {
            union {
                /* rate control */
                struct {
                    struct ieee80211_tx_rate rates[
                        IEEE80211_TX_MAX_RATES];
                    s8 rts_cts_rate_idx;
                    u8 use_rts:1;
                    u8 use_cts_prot:1;
                    u8 short_preamble:1;
                    u8 skip_table:1;
                    /* 2 bytes free */
                };
                /* only needed before rate control */
                unsigned long jiffies;
            };
            /* NB: vif can be NULL for injected frames */
            struct ieee80211_vif *vif;
            struct ieee80211_key_conf *hw_key;
            u32 flags;
            /* 4 bytes free */
        } control;
        struct {
            struct ieee80211_tx_rate rates[IEEE80211_TX_MAX_RATES];
            s32 ack_signal;
            u8 ampdu_ack_len;
            u8 ampdu_len;
            u8 antenna;
            void *status_driver_data[21 / sizeof(void *)];
        } status;
        struct {
            struct ieee80211_tx_rate driver_rates[
                IEEE80211_TX_MAX_RATES];
            u8 pad[4];

            void *rate_driver_data[
                IEEE80211_TX_INFO_RATE_DRIVER_DATA_SIZE / sizeof(void *)];
        };
        void *driver_data[
            IEEE80211_TX_INFO_DRIVER_DATA_SIZE / sizeof(void *)];
    };
};


ieee80211_rx_status
包含接收帧状态
驱动通过接收帧传给mac80211

struct ieee80211_rx_status {
    u64 mactime;
    u32 device_timestamp;
    u32 ampdu_reference;
    u32 flag;
    u16 freq;
    u8 vht_flag;
    u8 rate_idx;
    u8 vht_nss;
    u8 rx_flags;
    u8 band;
    u8 antenna;
    s8 signal;
    u8 chains;
    s8 chain_signal[IEEE80211_MAX_CHAINS];
    u8 ampdu_delimiter_crc;
};


ieee80211_sub_if_data/ieee80211_vif
包含每个虚拟接口信息
ieee80211_vifis passed to driver for those virtual interfaces the driver knowsabout (no monitor,VLAN)
包含的sub-structures取决于模式

struct ieee80211_vif {
    enum nl80211_iftype type;
    struct ieee80211_bss_conf bss_conf;
    u8 addr[ETH_ALEN];
    bool p2p;
    bool csa_active;

    u8 cab_queue;
    u8 hw_queue[IEEE80211_NUM_ACS];

    struct ieee80211_chanctx_conf __rcu *chanctx_conf;

    u32 driver_flags;

#ifdef CPTCFG_MAC80211_DEBUGFS
    struct dentry *debugfs_dir;
#endif

    /* must be last */
    u8 drv_priv[0] __aligned(sizeof(void *));
};

这里写图片描述

这里写图片描述

 

main.c
mac80211的main.c,module入口文件

#include <net/mac80211.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/netdevice.h>
#include <linux/types.h>
#include <linux/slab.h>
#include <linux/skbuff.h>
#include <linux/etherdevice.h>
#include <linux/if_arp.h>
#include <linux/rtnetlink.h>
#include <linux/bitmap.h>
#include <linux/pm_qos.h>
#include <linux/inetdevice.h>
#include <net/net_namespace.h>
#include <net/cfg80211.h>
#include <net/addrconf.h>

#include "ieee80211_i.h"
#include "driver-ops.h"
#include "rate.h"
#include "mesh.h"
#include "wep.h"
#include "led.h"
#include "cfg.h"
#include "debugfs.h"

void ieee80211_configure_filter(struct ieee80211_local *local)
{
    u64 mc;
    unsigned int changed_flags;
    unsigned int new_flags = 0;

    if (atomic_read(&local->iff_promiscs))
        new_flags |= FIF_PROMISC_IN_BSS;

    if (atomic_read(&local->iff_allmultis))
        new_flags |= FIF_ALLMULTI;

    if (local->monitors || test_bit(SCAN_SW_SCANNING, &local->scanning) ||
        test_bit(SCAN_ONCHANNEL_SCANNING, &local->scanning))
        new_flags |= FIF_BCN_PRBRESP_PROMISC;

    if (local->fif_probe_req || local->probe_req_reg)
        new_flags |= FIF_PROBE_REQ;

    if (local->fif_fcsfail)
        new_flags |= FIF_FCSFAIL;

    if (local->fif_plcpfail)
        new_flags |= FIF_PLCPFAIL;

    if (local->fif_control)
        new_flags |= FIF_CONTROL;

    if (local->fif_other_bss)
        new_flags |= FIF_OTHER_BSS;

    if (local->fif_pspoll)
        new_flags |= FIF_PSPOLL;

    spin_lock_bh(&local->filter_lock);
    changed_flags = local->filter_flags ^ new_flags;

    mc = drv_prepare_multicast(local, &local->mc_list);
    spin_unlock_bh(&local->filter_lock);

    /* be a bit nasty */
    new_flags |= (1<<31);

    drv_configure_filter(local, changed_flags, &new_flags, mc);

    WARN_ON(new_flags & (1<<31));

    local->filter_flags = new_flags & ~(1<<31);
}

static void ieee80211_reconfig_filter(struct work_struct *work)
{
    struct ieee80211_local *local =
        container_of(work, struct ieee80211_local, reconfig_filter);

    ieee80211_configure_filter(local);
}

static u32 ieee80211_hw_conf_chan(struct ieee80211_local *local)
{
    struct ieee80211_sub_if_data *sdata;
    struct cfg80211_chan_def chandef = {};
    u32 changed = 0;
    int power;
    u32 offchannel_flag;

    offchannel_flag = local->hw.conf.flags & IEEE80211_CONF_OFFCHANNEL;

    if (local->scan_chandef.chan) {
        chandef = local->scan_chandef;
    } else if (local->tmp_channel) {
        chandef.chan = local->tmp_channel;
        chandef.width = NL80211_CHAN_WIDTH_20_NOHT;
        chandef.center_freq1 = chandef.chan->center_freq;
    } else
        chandef = local->_oper_chandef;

    WARN(!cfg80211_chandef_valid(&chandef),
         "control:%d MHz width:%d center: %d/%d MHz",
         chandef.chan->center_freq, chandef.width,
         chandef.center_freq1, chandef.center_freq2);

    if (!cfg80211_chandef_identical(&chandef, &local->_oper_chandef))
        local->hw.conf.flags |= IEEE80211_CONF_OFFCHANNEL;
    else
        local->hw.conf.flags &= ~IEEE80211_CONF_OFFCHANNEL;

    offchannel_flag ^= local->hw.conf.flags & IEEE80211_CONF_OFFCHANNEL;

    if (offchannel_flag ||
        !cfg80211_chandef_identical(&local->hw.conf.chandef,
                    &local->_oper_chandef)) {
        local->hw.conf.chandef = chandef;
        changed |= IEEE80211_CONF_CHANGE_CHANNEL;
    }

    if (!conf_is_ht(&local->hw.conf)) {
        /*
         * mac80211.h documents that this is only valid
         * when the channel is set to an HT type, and
         * that otherwise STATIC is used.
         */
        local->hw.conf.smps_mode = IEEE80211_SMPS_STATIC;
    } else if (local->hw.conf.smps_mode != local->smps_mode) {
        local->hw.conf.smps_mode = local->smps_mode;
        changed |= IEEE80211_CONF_CHANGE_SMPS;
    }

    power = ieee80211_chandef_max_power(&chandef);

    rcu_read_lock();
    list_for_each_entry_rcu(sdata, &local->interfaces, list) {
        if (!rcu_access_pointer(sdata->vif.chanctx_conf))
            continue;
        if (sdata->vif.type == NL80211_IFTYPE_AP_VLAN)
            continue;
        power = min(power, sdata->vif.bss_conf.txpower);
    }
    rcu_read_unlock();

    if (local->hw.conf.power_level != power) {
        changed |= IEEE80211_CONF_CHANGE_POWER;
        local->hw.conf.power_level = power;
    }

    return changed;
}

int ieee80211_hw_config(struct ieee80211_local *local, u32 changed)
{
    int ret = 0;

    might_sleep();

    if (!local->use_chanctx)
        changed |= ieee80211_hw_conf_chan(local);
    else
        changed &= ~(IEEE80211_CONF_CHANGE_CHANNEL |
                 IEEE80211_CONF_CHANGE_POWER);

    if (changed && local->open_count) {
        ret = drv_config(local, changed);
        /*
         * Goal:
         * HW reconfiguration should never fail, the driver has told
         * us what it can support so it should live up to that promise.
         *
         * Current status:
         * rfkill is not integrated with mac80211 and a
         * configuration command can thus fail if hardware rfkill
         * is enabled
         *
         * FIXME: integrate rfkill with mac80211 and then add this
         * WARN_ON() back
         *
         */
        /* WARN_ON(ret); */
    }

    return ret;
}

void ieee80211_bss_info_change_notify(struct ieee80211_sub_if_data *sdata,
                      u32 changed)
{
    struct ieee80211_local *local = sdata->local;

    if (!changed || sdata->vif.type == NL80211_IFTYPE_AP_VLAN)
        return;

    drv_bss_info_changed(local, sdata, &sdata->vif.bss_conf, changed);
}

u32 ieee80211_reset_erp_info(struct ieee80211_sub_if_data *sdata)
{
    sdata->vif.bss_conf.use_cts_prot = false;
    sdata->vif.bss_conf.use_short_preamble = false;
    sdata->vif.bss_conf.use_short_slot = false;
    return BSS_CHANGED_ERP_CTS_PROT |
           BSS_CHANGED_ERP_PREAMBLE |
           BSS_CHANGED_ERP_SLOT;
}

static void ieee80211_tasklet_handler(unsigned long data)
{
    struct ieee80211_local *local = (struct ieee80211_local *) data;
    struct sk_buff *skb;

    while ((skb = skb_dequeue(&local->skb_queue)) ||
           (skb = skb_dequeue(&local->skb_queue_unreliable))) {
        switch (skb->pkt_type) {
        case IEEE80211_RX_MSG:
            /* Clear skb->pkt_type in order to not confuse kernel
             * netstack. */
            skb->pkt_type = 0;
            ieee80211_rx(&local->hw, skb);
            break;
        case IEEE80211_TX_STATUS_MSG:
            skb->pkt_type = 0;
            ieee80211_tx_status(&local->hw, skb);
            break;
        default:
            WARN(1, "mac80211: Packet is of unknown type %d\n",
                 skb->pkt_type);
            dev_kfree_skb(skb);
            break;
        }
    }
}

static void ieee80211_restart_work(struct work_struct *work)
{
    struct ieee80211_local *local =
        container_of(work, struct ieee80211_local, restart_work);

    /* wait for scan work complete */
    flush_workqueue(local->workqueue);

    WARN(test_bit(SCAN_HW_SCANNING, &local->scanning),
         "%s called with hardware scan in progress\n", __func__);

    rtnl_lock();
    ieee80211_scan_cancel(local);
    ieee80211_reconfig(local);
    rtnl_unlock();
}

void ieee80211_restart_hw(struct ieee80211_hw *hw)
{
    struct ieee80211_local *local = hw_to_local(hw);

    trace_api_restart_hw(local);

    wiphy_info(hw->wiphy,
           "Hardware restart was requested\n");

    /* use this reason, ieee80211_reconfig will unblock it */
    ieee80211_stop_queues_by_reason(hw, IEEE80211_MAX_QUEUE_MAP,
                    IEEE80211_QUEUE_STOP_REASON_SUSPEND);

    /*
     * Stop all Rx during the reconfig. We don't want state changes
     * or driver callbacks while this is in progress.
     */
    local->in_reconfig = true;
    barrier();

    schedule_work(&local->restart_work);
}
EXPORT_SYMBOL(ieee80211_restart_hw);

#ifdef CONFIG_INET
static int ieee80211_ifa_changed(struct notifier_block *nb,
                 unsigned long data, void *arg)
{
    struct in_ifaddr *ifa = arg;
    struct ieee80211_local *local =
        container_of(nb, struct ieee80211_local,
                 ifa_notifier);
    struct net_device *ndev = ifa->ifa_dev->dev;
    struct wireless_dev *wdev = ndev->ieee80211_ptr;
    struct in_device *idev;
    struct ieee80211_sub_if_data *sdata;
    struct ieee80211_bss_conf *bss_conf;
    struct ieee80211_if_managed *ifmgd;
    int c = 0;

    /* Make sure it's our interface that got changed */
    if (!wdev)
        return NOTIFY_DONE;

    if (wdev->wiphy != local->hw.wiphy)
        return NOTIFY_DONE;

    sdata = IEEE80211_DEV_TO_SUB_IF(ndev);
    bss_conf = &sdata->vif.bss_conf;

    /* ARP filtering is only supported in managed mode */
    if (sdata->vif.type != NL80211_IFTYPE_STATION)
        return NOTIFY_DONE;

    idev = __in_dev_get_rtnl(sdata->dev);
    if (!idev)
        return NOTIFY_DONE;

    ifmgd = &sdata->u.mgd;
    sdata_lock(sdata);

    /* Copy the addresses to the bss_conf list */
    ifa = idev->ifa_list;
    while (ifa) {
        if (c < IEEE80211_BSS_ARP_ADDR_LIST_LEN)
            bss_conf->arp_addr_list[c] = ifa->ifa_address;
        ifa = ifa->ifa_next;
        c++;
    }

    bss_conf->arp_addr_cnt = c;

    /* Configure driver only if associated (which also implies it is up) */
    if (ifmgd->associated)
        ieee80211_bss_info_change_notify(sdata,
                         BSS_CHANGED_ARP_FILTER);

    sdata_unlock(sdata);

    return NOTIFY_OK;
}
#endif

#if IS_ENABLED(CONFIG_IPV6)
static int ieee80211_ifa6_changed(struct notifier_block *nb,
                  unsigned long data, void *arg)
{
    struct inet6_ifaddr *ifa = (struct inet6_ifaddr *)arg;
    struct inet6_dev *idev = ifa->idev;
    struct net_device *ndev = ifa->idev->dev;
    struct ieee80211_local *local =
        container_of(nb, struct ieee80211_local, ifa6_notifier);
    struct wireless_dev *wdev = ndev->ieee80211_ptr;
    struct ieee80211_sub_if_data *sdata;

    /* Make sure it's our interface that got changed */
    if (!wdev || wdev->wiphy != local->hw.wiphy)
        return NOTIFY_DONE;

    sdata = IEEE80211_DEV_TO_SUB_IF(ndev);

    /*
     * For now only support station mode. This is mostly because
     * doing AP would have to handle AP_VLAN in some way ...
     */
    if (sdata->vif.type != NL80211_IFTYPE_STATION)
        return NOTIFY_DONE;

    drv_ipv6_addr_change(local, sdata, idev);

    return NOTIFY_OK;
}
#endif

/* There isn't a lot of sense in it, but you can transmit anything you like */
static const struct ieee80211_txrx_stypes
ieee80211_default_mgmt_stypes[NUM_NL80211_IFTYPES] = {
    [NL80211_IFTYPE_ADHOC] = {
        .tx = 0xffff,
        .rx = BIT(IEEE80211_STYPE_ACTION >> 4) |
            BIT(IEEE80211_STYPE_AUTH >> 4) |
            BIT(IEEE80211_STYPE_DEAUTH >> 4) |
            BIT(IEEE80211_STYPE_PROBE_REQ >> 4),
    },
    [NL80211_IFTYPE_STATION] = {
        .tx = 0xffff,
        .rx = BIT(IEEE80211_STYPE_ACTION >> 4) |
            BIT(IEEE80211_STYPE_PROBE_REQ >> 4),
    },
    [NL80211_IFTYPE_AP] = {
        .tx = 0xffff,
        .rx = BIT(IEEE80211_STYPE_ASSOC_REQ >> 4) |
            BIT(IEEE80211_STYPE_REASSOC_REQ >> 4) |
            BIT(IEEE80211_STYPE_PROBE_REQ >> 4) |
            BIT(IEEE80211_STYPE_DISASSOC >> 4) |
            BIT(IEEE80211_STYPE_AUTH >> 4) |
            BIT(IEEE80211_STYPE_DEAUTH >> 4) |
            BIT(IEEE80211_STYPE_ACTION >> 4),
    },
    [NL80211_IFTYPE_AP_VLAN] = {
        /* copy AP */
        .tx = 0xffff,
        .rx = BIT(IEEE80211_STYPE_ASSOC_REQ >> 4) |
            BIT(IEEE80211_STYPE_REASSOC_REQ >> 4) |
            BIT(IEEE80211_STYPE_PROBE_REQ >> 4) |
            BIT(IEEE80211_STYPE_DISASSOC >> 4) |
            BIT(IEEE80211_STYPE_AUTH >> 4) |
            BIT(IEEE80211_STYPE_DEAUTH >> 4) |
            BIT(IEEE80211_STYPE_ACTION >> 4),
    },
    [NL80211_IFTYPE_P2P_CLIENT] = {
        .tx = 0xffff,
        .rx = BIT(IEEE80211_STYPE_ACTION >> 4) |
            BIT(IEEE80211_STYPE_PROBE_REQ >> 4),
    },
    [NL80211_IFTYPE_P2P_GO] = {
        .tx = 0xffff,
        .rx = BIT(IEEE80211_STYPE_ASSOC_REQ >> 4) |
            BIT(IEEE80211_STYPE_REASSOC_REQ >> 4) |
            BIT(IEEE80211_STYPE_PROBE_REQ >> 4) |
            BIT(IEEE80211_STYPE_DISASSOC >> 4) |
            BIT(IEEE80211_STYPE_AUTH >> 4) |
            BIT(IEEE80211_STYPE_DEAUTH >> 4) |
            BIT(IEEE80211_STYPE_ACTION >> 4),
    },
    [NL80211_IFTYPE_MESH_POINT] = {
        .tx = 0xffff,
        .rx = BIT(IEEE80211_STYPE_ACTION >> 4) |
            BIT(IEEE80211_STYPE_AUTH >> 4) |
            BIT(IEEE80211_STYPE_DEAUTH >> 4),
    },
    [NL80211_IFTYPE_P2P_DEVICE] = {
        .tx = 0xffff,
        .rx = BIT(IEEE80211_STYPE_ACTION >> 4) |
            BIT(IEEE80211_STYPE_PROBE_REQ >> 4),
    },
};

static const struct ieee80211_ht_cap mac80211_ht_capa_mod_mask = {
    .ampdu_params_info = IEEE80211_HT_AMPDU_PARM_FACTOR |
                 IEEE80211_HT_AMPDU_PARM_DENSITY,

    .cap_info = cpu_to_le16(IEEE80211_HT_CAP_SUP_WIDTH_20_40 |
                IEEE80211_HT_CAP_MAX_AMSDU |
                IEEE80211_HT_CAP_SGI_20 |
                IEEE80211_HT_CAP_SGI_40 |
                IEEE80211_HT_CAP_LDPC_CODING |
                IEEE80211_HT_CAP_40MHZ_INTOLERANT),
    .mcs = {
        .rx_mask = { 0xff, 0xff, 0xff, 0xff, 0xff,
                 0xff, 0xff, 0xff, 0xff, 0xff, },
    },
};

static const struct ieee80211_vht_cap mac80211_vht_capa_mod_mask = {
    .vht_cap_info =
        cpu_to_le32(IEEE80211_VHT_CAP_RXLDPC |
                IEEE80211_VHT_CAP_SHORT_GI_80 |
                IEEE80211_VHT_CAP_SHORT_GI_160 |
                IEEE80211_VHT_CAP_RXSTBC_1 |
                IEEE80211_VHT_CAP_RXSTBC_2 |
                IEEE80211_VHT_CAP_RXSTBC_3 |
                IEEE80211_VHT_CAP_RXSTBC_4 |
                IEEE80211_VHT_CAP_TXSTBC |
                IEEE80211_VHT_CAP_SU_BEAMFORMER_CAPABLE |
                IEEE80211_VHT_CAP_SU_BEAMFORMEE_CAPABLE |
                IEEE80211_VHT_CAP_TX_ANTENNA_PATTERN |
                IEEE80211_VHT_CAP_RX_ANTENNA_PATTERN |
                IEEE80211_VHT_CAP_MAX_A_MPDU_LENGTH_EXPONENT_MASK),
    .supp_mcs = {
        .rx_mcs_map = cpu_to_le16(~0),
        .tx_mcs_map = cpu_to_le16(~0),
    },
};

static const u8 extended_capabilities[] = {
    0, 0, 0, 0, 0, 0, 0,
    WLAN_EXT_CAPA8_OPMODE_NOTIF,
};
/*   ieee80211_alloc_hw()
* 分配wiphy对象空间(保证私有数据和硬件私有数据32字节对齐,wiphy包含ieee80211_local和驱动私有数据)
* 初始化wiphy对象(包括重传次数,RTS门限等等)
* 初始化ieee80211_local(包括重传次数,工作队列,接口链表等等)
* 初始化sta_pending_list链表
* 初始化sta_list链表
*
*/


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;
    bool use_chanctx;

    if (WARN_ON(!ops->tx || !ops->start || !ops->stop || !ops->config ||
            !ops->add_interface || !ops->remove_interface ||
            !ops->configure_filter))
        return NULL;

    if (WARN_ON(ops->sta_state && (ops->sta_add || ops->sta_remove)))
        return NULL;

    /* check all or no channel context operations exist */
    i = !!ops->add_chanctx + !!ops->remove_chanctx +
        !!ops->change_chanctx + !!ops->assign_vif_chanctx +
        !!ops->unassign_vif_chanctx;
    if (WARN_ON(i != 0 && i != 5))
        return NULL;
    use_chanctx = i == 5;

    /* 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->mgmt_stypes = ieee80211_default_mgmt_stypes;

    wiphy->privid = mac80211_wiphy_privid;

    wiphy->flags |= WIPHY_FLAG_NETNS_OK |
            WIPHY_FLAG_4ADDR_AP |
            WIPHY_FLAG_4ADDR_STATION |
            WIPHY_FLAG_REPORTS_OBSS |
            WIPHY_FLAG_OFFCHAN_TX;

    wiphy->extended_capabilities = extended_capabilities;
    wiphy->extended_capabilities_mask = extended_capabilities;
    wiphy->extended_capabilities_len = ARRAY_SIZE(extended_capabilities);

    if (ops->remain_on_channel)
        wiphy->flags |= WIPHY_FLAG_HAS_REMAIN_ON_CHANNEL;

    wiphy->features |= NL80211_FEATURE_SK_TX_STATUS |
               NL80211_FEATURE_SAE |
               NL80211_FEATURE_HT_IBSS |
               NL80211_FEATURE_VIF_TXPOWER |
               NL80211_FEATURE_USERSPACE_MPM;

    if (!ops->hw_scan)
        wiphy->features |= NL80211_FEATURE_LOW_PRIORITY_SCAN |
                   NL80211_FEATURE_AP_SCAN;


    if (!ops->set_key)
        wiphy->flags |= WIPHY_FLAG_IBSS_RSN;

    wiphy->bss_priv_size = sizeof(struct ieee80211_bss);

    local = wiphy_priv(wiphy);

    local->hw.wiphy = wiphy;

    local->hw.priv = (char *)local + ALIGN(sizeof(*local), NETDEV_ALIGN);

    local->ops = ops;
    local->use_chanctx = use_chanctx;

    /* set up some defaults */
    local->hw.queues = 1;
    local->hw.max_rates = 1;
    local->hw.max_report_rates = 0;
    local->hw.max_rx_aggregation_subframes = IEEE80211_MAX_AMPDU_BUF;
    local->hw.max_tx_aggregation_subframes = IEEE80211_MAX_AMPDU_BUF;
    local->hw.offchannel_tx_hw_queue = IEEE80211_INVAL_HW_QUEUE;
    local->hw.conf.long_frame_max_tx_count = wiphy->retry_long;
    local->hw.conf.short_frame_max_tx_count = wiphy->retry_short;
    local->hw.radiotap_mcs_details = IEEE80211_RADIOTAP_MCS_HAVE_MCS |
                     IEEE80211_RADIOTAP_MCS_HAVE_GI |
                     IEEE80211_RADIOTAP_MCS_HAVE_BW;
    local->hw.radiotap_vht_details = IEEE80211_RADIOTAP_VHT_KNOWN_GI |
                     IEEE80211_RADIOTAP_VHT_KNOWN_BANDWIDTH;
    local->hw.uapsd_queues = IEEE80211_DEFAULT_UAPSD_QUEUES;
    local->hw.uapsd_max_sp_len = IEEE80211_DEFAULT_MAX_SP_LEN;
    local->user_power_level = IEEE80211_UNSET_POWER_LEVEL;
    wiphy->ht_capa_mod_mask = &mac80211_ht_capa_mod_mask;
    wiphy->vht_capa_mod_mask = &mac80211_vht_capa_mod_mask;

    INIT_LIST_HEAD(&local->interfaces);

    __hw_addr_init(&local->mc_list);

    mutex_init(&local->iflist_mtx);
    mutex_init(&local->mtx);

    mutex_init(&local->key_mtx);
    spin_lock_init(&local->filter_lock);
    spin_lock_init(&local->rx_path_lock);
    spin_lock_init(&local->queue_stop_reason_lock);

    INIT_LIST_HEAD(&local->chanctx_list);
    mutex_init(&local->chanctx_mtx);

    INIT_DELAYED_WORK(&local->scan_work, ieee80211_scan_work);

    INIT_WORK(&local->restart_work, ieee80211_restart_work);

    INIT_WORK(&local->radar_detected_work,
          ieee80211_dfs_radar_detected_work);

    INIT_WORK(&local->reconfig_filter, ieee80211_reconfig_filter);
    local->smps_mode = IEEE80211_SMPS_OFF;

    INIT_WORK(&local->dynamic_ps_enable_work,
          ieee80211_dynamic_ps_enable_work);
    INIT_WORK(&local->dynamic_ps_disable_work,
          ieee80211_dynamic_ps_disable_work);
    setup_timer(&local->dynamic_ps_timer,
            ieee80211_dynamic_ps_timer, (unsigned long) local);

    INIT_WORK(&local->sched_scan_stopped_work,
          ieee80211_sched_scan_stopped_work);

    spin_lock_init(&local->ack_status_lock);
    idr_init(&local->ack_status_frames);

    sta_info_init(local);

    for (i = 0; i < IEEE80211_MAX_QUEUES; i++) {
        skb_queue_head_init(&local->pending[i]);
        atomic_set(&local->agg_queue_stop[i], 0);
    }
    tasklet_init(&local->tx_pending_tasklet, ieee80211_tx_pending,
             (unsigned long)local);

    tasklet_init(&local->tasklet,
             ieee80211_tasklet_handler,
             (unsigned long) local);

    skb_queue_head_init(&local->skb_queue);
    skb_queue_head_init(&local->skb_queue_unreliable);

    ieee80211_led_names(local);

    ieee80211_roc_setup(local);

    return &local->hw;
}
EXPORT_SYMBOL(ieee80211_alloc_hw);

static int ieee80211_init_cipher_suites(struct ieee80211_local *local)
{
    bool have_wep = !(IS_ERR(local->wep_tx_tfm) ||
              IS_ERR(local->wep_rx_tfm));
    bool have_mfp = local->hw.flags & IEEE80211_HW_MFP_CAPABLE;
    const struct ieee80211_cipher_scheme *cs = local->hw.cipher_schemes;
    int n_suites = 0, r = 0, w = 0;
    u32 *suites;
    static const u32 cipher_suites[] = {
        /* keep WEP first, it may be removed below */
        WLAN_CIPHER_SUITE_WEP40,
        WLAN_CIPHER_SUITE_WEP104,
        WLAN_CIPHER_SUITE_TKIP,
        WLAN_CIPHER_SUITE_CCMP,

        /* keep last -- depends on hw flags! */
        WLAN_CIPHER_SUITE_AES_CMAC
    };

    /* Driver specifies the ciphers, we have nothing to do... */
    if (local->hw.wiphy->cipher_suites && have_wep)
        return 0;

    /* Set up cipher suites if driver relies on mac80211 cipher defs */
    if (!local->hw.wiphy->cipher_suites && !cs) {
        local->hw.wiphy->cipher_suites = cipher_suites;
        local->hw.wiphy->n_cipher_suites = ARRAY_SIZE(cipher_suites);

        if (!have_mfp)
            local->hw.wiphy->n_cipher_suites--;

        if (!have_wep) {
            local->hw.wiphy->cipher_suites += 2;
            local->hw.wiphy->n_cipher_suites -= 2;
        }

        return 0;
    }

    if (!local->hw.wiphy->cipher_suites) {
        /*
         * Driver specifies cipher schemes only
         * We start counting ciphers defined by schemes, TKIP and CCMP
         */
        n_suites = local->hw.n_cipher_schemes + 2;

        /* check if we have WEP40 and WEP104 */
        if (have_wep)
            n_suites += 2;

        /* check if we have AES_CMAC */
        if (have_mfp)
            n_suites++;

        suites = kmalloc(sizeof(u32) * n_suites, GFP_KERNEL);
        if (!suites)
            return -ENOMEM;

        suites[w++] = WLAN_CIPHER_SUITE_CCMP;
        suites[w++] = WLAN_CIPHER_SUITE_TKIP;

        if (have_wep) {
            suites[w++] = WLAN_CIPHER_SUITE_WEP40;
            suites[w++] = WLAN_CIPHER_SUITE_WEP104;
        }

        if (have_mfp)
            suites[w++] = WLAN_CIPHER_SUITE_AES_CMAC;

        for (r = 0; r < local->hw.n_cipher_schemes; r++)
            suites[w++] = cs[r].cipher;
    } else {
        /* Driver provides cipher suites, but we need to exclude WEP */
        suites = kmemdup(local->hw.wiphy->cipher_suites,
                 sizeof(u32) * local->hw.wiphy->n_cipher_suites,
                 GFP_KERNEL);
        if (!suites)
            return -ENOMEM;

        for (r = 0; r < local->hw.wiphy->n_cipher_suites; r++) {
            u32 suite = local->hw.wiphy->cipher_suites[r];

            if (suite == WLAN_CIPHER_SUITE_WEP40 ||
                suite == WLAN_CIPHER_SUITE_WEP104)
                continue;
            suites[w++] = suite;
        }
    }

    local->hw.wiphy->cipher_suites = suites;
    local->hw.wiphy->n_cipher_suites = w;
    local->wiphy_ciphers_allocated = true;

    return 0;
}
/*
*  ieee80211_register_hw()
* 分配int_scan_req数据结构
* 初始化支持接口类型(包括MONITOR接口)
* 注册wiphy
* 初始化WEP
* 初始化速率控制算法
* 注册STA接口(默认wlan
*
*/

int ieee80211_register_hw(struct ieee80211_hw *hw)
{
    struct ieee80211_local *local = hw_to_local(hw);
    int result, i;
    enum ieee80211_band band;
    int channels, max_bitrates;
    bool supp_ht, supp_vht;
    netdev_features_t feature_whitelist;
    struct cfg80211_chan_def dflt_chandef = {};

    if (hw->flags & IEEE80211_HW_QUEUE_CONTROL &&
        (local->hw.offchannel_tx_hw_queue == IEEE80211_INVAL_HW_QUEUE ||
         local->hw.offchannel_tx_hw_queue >= local->hw.queues))
        return -EINVAL;

#ifdef CONFIG_PM
    if (hw->wiphy->wowlan && (!local->ops->suspend || !local->ops->resume))
        return -EINVAL;
#endif

    if (!local->use_chanctx) {
        for (i = 0; i < local->hw.wiphy->n_iface_combinations; i++) {
            const struct ieee80211_iface_combination *comb;

            comb = &local->hw.wiphy->iface_combinations[i];

            if (comb->num_different_channels > 1)
                return -EINVAL;
        }
    } else {
        /*
         * WDS is currently prohibited when channel contexts are used
         * because there's no clear definition of which channel WDS
         * type interfaces use
         */
        if (local->hw.wiphy->interface_modes & BIT(NL80211_IFTYPE_WDS))
            return -EINVAL;

        /* DFS currently not supported with channel context drivers */
        for (i = 0; i < local->hw.wiphy->n_iface_combinations; i++) {
            const struct ieee80211_iface_combination *comb;

            comb = &local->hw.wiphy->iface_combinations[i];

            if (comb->radar_detect_widths)
                return -EINVAL;
        }
    }

    /* Only HW csum features are currently compatible with mac80211 */
    feature_whitelist = NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM |
                NETIF_F_HW_CSUM;
    if (WARN_ON(hw->netdev_features & ~feature_whitelist))
        return -EINVAL;

    if (hw->max_report_rates == 0)
        hw->max_report_rates = hw->max_rates;

    local->rx_chains = 1;

    /*
     * generic code guarantees at least one band,
     * set this very early because much code assumes
     * that hw.conf.channel is assigned
     */
    channels = 0;
    max_bitrates = 0;
    supp_ht = false;
    supp_vht = false;
    for (band = 0; band < IEEE80211_NUM_BANDS; band++) {
        struct ieee80211_supported_band *sband;

        sband = local->hw.wiphy->bands[band];
        if (!sband)
            continue;

        if (!dflt_chandef.chan) {
            cfg80211_chandef_create(&dflt_chandef,
                        &sband->channels[0],
                        NL80211_CHAN_NO_HT);
            /* init channel we're on */
            if (!local->use_chanctx && !local->_oper_chandef.chan) {
                local->hw.conf.chandef = dflt_chandef;
                local->_oper_chandef = dflt_chandef;
            }
            local->monitor_chandef = dflt_chandef;
        }

        channels += sband->n_channels;

        if (max_bitrates < sband->n_bitrates)
            max_bitrates = sband->n_bitrates;
        supp_ht = supp_ht || sband->ht_cap.ht_supported;
        supp_vht = supp_vht || sband->vht_cap.vht_supported;

        if (sband->ht_cap.ht_supported)
            local->rx_chains =
                max(ieee80211_mcs_to_chains(&sband->ht_cap.mcs),
                    local->rx_chains);

        /* TODO: consider VHT for RX chains, hopefully it's the same */
    }

    /* if low-level driver supports AP, we also support VLAN */
    if (local->hw.wiphy->interface_modes & BIT(NL80211_IFTYPE_AP)) {
        hw->wiphy->interface_modes |= BIT(NL80211_IFTYPE_AP_VLAN);
        hw->wiphy->software_iftypes |= BIT(NL80211_IFTYPE_AP_VLAN);
    }

    /* mac80211 always supports monitor */
    hw->wiphy->interface_modes |= BIT(NL80211_IFTYPE_MONITOR);
    hw->wiphy->software_iftypes |= BIT(NL80211_IFTYPE_MONITOR);

    /* mac80211 doesn't support more than one IBSS interface right now */
    for (i = 0; i < hw->wiphy->n_iface_combinations; i++) {
        const struct ieee80211_iface_combination *c;
        int j;

        c = &hw->wiphy->iface_combinations[i];

        for (j = 0; j < c->n_limits; j++)
            if ((c->limits[j].types & BIT(NL80211_IFTYPE_ADHOC)) &&
                c->limits[j].max > 1)
                return -EINVAL;
    }

    local->int_scan_req = kzalloc(sizeof(*local->int_scan_req) +
                      sizeof(void *) * channels, GFP_KERNEL);
    if (!local->int_scan_req)
        return -ENOMEM;

    for (band = 0; band < IEEE80211_NUM_BANDS; band++) {
        if (!local->hw.wiphy->bands[band])
            continue;
        local->int_scan_req->rates[band] = (u32) -1;
    }

#ifndef CPTCFG_MAC80211_MESH
    /* mesh depends on Kconfig, but drivers should set it if they want */
    local->hw.wiphy->interface_modes &= ~BIT(NL80211_IFTYPE_MESH_POINT);
#endif

    /* if the underlying driver supports mesh, mac80211 will (at least)
     * provide routing of mesh authentication frames to userspace */
    if (local->hw.wiphy->interface_modes & BIT(NL80211_IFTYPE_MESH_POINT))
        local->hw.wiphy->flags |= WIPHY_FLAG_MESH_AUTH;

    /* mac80211 supports control port protocol changing */
    local->hw.wiphy->flags |= WIPHY_FLAG_CONTROL_PORT_PROTOCOL;

    if (local->hw.flags & IEEE80211_HW_SIGNAL_DBM) {
        local->hw.wiphy->signal_type = CFG80211_SIGNAL_TYPE_MBM;
    } else if (local->hw.flags & IEEE80211_HW_SIGNAL_UNSPEC) {
        local->hw.wiphy->signal_type = CFG80211_SIGNAL_TYPE_UNSPEC;
        if (hw->max_signal <= 0) {
            result = -EINVAL;
            goto fail_wiphy_register;
        }
    }

    WARN((local->hw.flags & IEEE80211_HW_SUPPORTS_UAPSD)
         && (local->hw.flags & IEEE80211_HW_PS_NULLFUNC_STACK),
         "U-APSD not supported with HW_PS_NULLFUNC_STACK\n");

    /*
     * Calculate scan IE length -- we need this to alloc
     * memory and to subtract from the driver limit. It
     * includes the DS Params, (extended) supported rates, and HT
     * information -- SSID is the driver's responsibility.
     */
    local->scan_ies_len = 4 + max_bitrates /* (ext) supp rates */ +
        3 /* DS Params */;
    if (supp_ht)
        local->scan_ies_len += 2 + sizeof(struct ieee80211_ht_cap);

    if (supp_vht)
        local->scan_ies_len +=
            2 + sizeof(struct ieee80211_vht_cap);

    if (!local->ops->hw_scan) {
        /* For hw_scan, driver needs to set these up. */
        local->hw.wiphy->max_scan_ssids = 4;
        local->hw.wiphy->max_scan_ie_len = IEEE80211_MAX_DATA_LEN;
    }

    /*
     * If the driver supports any scan IEs, then assume the
     * limit includes the IEs mac80211 will add, otherwise
     * leave it at zero and let the driver sort it out; we
     * still pass our IEs to the driver but userspace will
     * not be allowed to in that case.
     */
    if (local->hw.wiphy->max_scan_ie_len)
        local->hw.wiphy->max_scan_ie_len -= local->scan_ies_len;

    WARN_ON(!ieee80211_cs_list_valid(local->hw.cipher_schemes,
                     local->hw.n_cipher_schemes));

    result = ieee80211_init_cipher_suites(local);
    if (result < 0)
        goto fail_wiphy_register;

    if (!local->ops->remain_on_channel)
        local->hw.wiphy->max_remain_on_channel_duration = 5000;

    /* mac80211 based drivers don't support internal TDLS setup */
    if (local->hw.wiphy->flags & WIPHY_FLAG_SUPPORTS_TDLS)
        local->hw.wiphy->flags |= WIPHY_FLAG_TDLS_EXTERNAL_SETUP;

    local->hw.wiphy->max_num_csa_counters = IEEE80211_MAX_CSA_COUNTERS_NUM;

    result = wiphy_register(local->hw.wiphy);
    if (result < 0)
        goto fail_wiphy_register;

    /*
     * We use the number of queues for feature tests (QoS, HT) internally
     * so restrict them appropriately.
     */
    if (hw->queues > IEEE80211_MAX_QUEUES)
        hw->queues = IEEE80211_MAX_QUEUES;

    local->workqueue =
        alloc_ordered_workqueue("%s", 0, wiphy_name(local->hw.wiphy));
    if (!local->workqueue) {
        result = -ENOMEM;
        goto fail_workqueue;
    }

    /*
     * The hardware needs headroom for sending the frame,
     * and we need some headroom for passing the frame to monitor
     * interfaces, but never both at the same time.
     */
    local->tx_headroom = max_t(unsigned int , local->hw.extra_tx_headroom,
                   IEEE80211_TX_STATUS_HEADROOM);

    debugfs_hw_add(local);

    /*
     * if the driver doesn't specify a max listen interval we
     * use 5 which should be a safe default
     */
    if (local->hw.max_listen_interval == 0)
        local->hw.max_listen_interval = 5;

    local->hw.conf.listen_interval = local->hw.max_listen_interval;

    local->dynamic_ps_forced_timeout = -1;

    result = ieee80211_wep_init(local);
    if (result < 0)
        wiphy_debug(local->hw.wiphy, "Failed to initialize wep: %d\n",
                result);

    local->hw.conf.flags = IEEE80211_CONF_IDLE;

    ieee80211_led_init(local);

    rtnl_lock();

    result = ieee80211_init_rate_ctrl_alg(local,
                          hw->rate_control_algorithm);
    if (result < 0) {
        wiphy_debug(local->hw.wiphy,
                "Failed to initialize rate control algorithm\n");
        goto fail_rate;
    }

    /* add one default STA interface if supported */
    if (local->hw.wiphy->interface_modes & BIT(NL80211_IFTYPE_STATION)) {
        result = ieee80211_if_add(local, "wlan%d", NULL,
                      NL80211_IFTYPE_STATION, NULL);
        if (result)
            wiphy_warn(local->hw.wiphy,
                   "Failed to add default virtual iface\n");
    }

    rtnl_unlock();

    local->network_latency_notifier.notifier_call =
        ieee80211_max_network_latency;
    result = pm_qos_add_notifier(PM_QOS_NETWORK_LATENCY,
                     &local->network_latency_notifier);
    if (result) {
        rtnl_lock();
        goto fail_pm_qos;
    }

#ifdef CONFIG_INET
    local->ifa_notifier.notifier_call = ieee80211_ifa_changed;
    result = register_inetaddr_notifier(&local->ifa_notifier);
    if (result)
        goto fail_ifa;
#endif

#if IS_ENABLED(CONFIG_IPV6)
    local->ifa6_notifier.notifier_call = ieee80211_ifa6_changed;
    result = register_inet6addr_notifier(&local->ifa6_notifier);
    if (result)
        goto fail_ifa6;
#endif

    return 0;

#if IS_ENABLED(CONFIG_IPV6)
 fail_ifa6:
#ifdef CONFIG_INET
    unregister_inetaddr_notifier(&local->ifa_notifier);
#endif
#endif
#if defined(CONFIG_INET) || defined(CONFIG_IPV6)
 fail_ifa:
    pm_qos_remove_notifier(PM_QOS_NETWORK_LATENCY,
                   &local->network_latency_notifier);
    rtnl_lock();
#endif
 fail_pm_qos:
    ieee80211_led_exit(local);
    ieee80211_remove_interfaces(local);
 fail_rate:
    rtnl_unlock();
    ieee80211_wep_free(local);
    sta_info_stop(local);
    destroy_workqueue(local->workqueue);
 fail_workqueue:
    wiphy_unregister(local->hw.wiphy);
 fail_wiphy_register:
    if (local->wiphy_ciphers_allocated)
        kfree(local->hw.wiphy->cipher_suites);
    kfree(local->int_scan_req);
    return result;
}
EXPORT_SYMBOL(ieee80211_register_hw);

void ieee80211_napi_add(struct ieee80211_hw *hw, struct napi_struct *napi,
            struct net_device *napi_dev,
            int (*poll)(struct napi_struct *, int),
            int weight)
{
    struct ieee80211_local *local = hw_to_local(hw);

    netif_napi_add(napi_dev, napi, poll, weight);
    local->napi = napi;
}
EXPORT_SYMBOL_GPL(ieee80211_napi_add);

void ieee80211_unregister_hw(struct ieee80211_hw *hw)
{
    struct ieee80211_local *local = hw_to_local(hw);

    tasklet_kill(&local->tx_pending_tasklet);
    tasklet_kill(&local->tasklet);

    pm_qos_remove_notifier(PM_QOS_NETWORK_LATENCY,
                   &local->network_latency_notifier);
#ifdef CONFIG_INET
    unregister_inetaddr_notifier(&local->ifa_notifier);
#endif
#if IS_ENABLED(CONFIG_IPV6)
    unregister_inet6addr_notifier(&local->ifa6_notifier);
#endif

    rtnl_lock();

    /*
     * At this point, interface list manipulations are fine
     * because the driver cannot be handing us frames any
     * more and the tasklet is killed.
     */
    ieee80211_remove_interfaces(local);

    rtnl_unlock();

    cancel_work_sync(&local->restart_work);
    cancel_work_sync(&local->reconfig_filter);
    flush_work(&local->sched_scan_stopped_work);

    ieee80211_clear_tx_pending(local);
    rate_control_deinitialize(local);

    if (skb_queue_len(&local->skb_queue) ||
        skb_queue_len(&local->skb_queue_unreliable))
        wiphy_warn(local->hw.wiphy, "skb_queue not empty\n");
    skb_queue_purge(&local->skb_queue);
    skb_queue_purge(&local->skb_queue_unreliable);

    destroy_workqueue(local->workqueue);
    wiphy_unregister(local->hw.wiphy);
    sta_info_stop(local);
    ieee80211_wep_free(local);
    ieee80211_led_exit(local);
    kfree(local->int_scan_req);
}
EXPORT_SYMBOL(ieee80211_unregister_hw);

static int ieee80211_free_ack_frame(int id, void *p, void *data)
{
    WARN_ONCE(1, "Have pending ack frames!\n");
    kfree_skb(p);
    return 0;
}

void ieee80211_free_hw(struct ieee80211_hw *hw)
{
    struct ieee80211_local *local = hw_to_local(hw);

    mutex_destroy(&local->iflist_mtx);
    mutex_destroy(&local->mtx);

    if (local->wiphy_ciphers_allocated)
        kfree(local->hw.wiphy->cipher_suites);

    idr_for_each(&local->ack_status_frames,
             ieee80211_free_ack_frame, NULL);
    idr_destroy(&local->ack_status_frames);

    kfree(rcu_access_pointer(local->tx_latency));

    wiphy_free(local->hw.wiphy);
}
EXPORT_SYMBOL(ieee80211_free_hw);

static int __init ieee80211_init(void)
{
    struct sk_buff *skb;
    int ret;

    BUILD_BUG_ON(sizeof(struct ieee80211_tx_info) > sizeof(skb->cb));
    BUILD_BUG_ON(offsetof(struct ieee80211_tx_info, driver_data) +
             IEEE80211_TX_INFO_DRIVER_DATA_SIZE > sizeof(skb->cb));

    ret = rc80211_minstrel_init();
    if (ret)
        return ret;

    ret = rc80211_minstrel_ht_init();
    if (ret)
        goto err_minstrel;

    ret = rc80211_pid_init();
    if (ret)
        goto err_pid;

    ret = ieee80211_iface_init();
    if (ret)
        goto err_netdev;

    return 0;
 err_netdev:
    rc80211_pid_exit();
 err_pid:
    rc80211_minstrel_ht_exit();
 err_minstrel:
    rc80211_minstrel_exit();

    return ret;
}

static void __exit ieee80211_exit(void)
{
    rc80211_pid_exit();
    rc80211_minstrel_ht_exit();
    rc80211_minstrel_exit();

    ieee80211s_stop();

    ieee80211_iface_exit();

    rcu_barrier();
}


subsys_initcall(ieee80211_init);
module_exit(ieee80211_exit);

MODULE_DESCRIPTION("IEEE 802.11 subsystem");
MODULE_LICENSE("GPL");


MAC层是802.11的主要功能部分。上层应用通过调用MAC层提供的接口原语调用MAC层的功能。
MAC一共向上提供了2大类接口原语,共30种。数据(1)和管理(29)。数据部分就是提供普通数据包的收发接口,管理部分是主要功能部分,例如发起 认证、连接、信道扫描等其它所有管理功能,如下表所示:(并非所有的原语都是可调用的,一部分是indication形式的向上通知。有request的 是可以调用的())

| 数据部分 |__|
| 数据 | MA-UNITDATA
管理部分
电源管理 MLME-POWERMGT
信道扫描 MLME-SCAN
时间同步 MLME-JOIN
认证 MLME-AUTHENTICATE
断开认证 MLME-DEAUTHENTICATE
建立连接 MLME-ASSOCIATE
重新连接 MLME-REASSOCIATE
断开连接 MLME-DISASSOCIATE
复位 MLME-RESET
网络开始 MLME-START
测量 MLME-MREQUEST
信道测量 MLME-MEASURE
测量报告 MLME-MREPORT
信道切换 MLME-CHANNELSWITCH
发送功率通知 MLME-TPCADAPT
设置密钥 MLME-SETKEYS
删除密钥 MLME-DELETEKEYS
迈克尔失败事件 MLME-MICHAELMICFAILURE
可扩展局域网认证协议帧 MLME-EAPOL
点对点连接请求 MLME-PeerKeySTART
设置发送或接收的安全保护 MLME-SETPROTECTION
帧密钥错误丢弃通知 MLME-PROTECTEDFRAMEDROPPED
交通流(TS)管理接口 MLME-ADDTS
MLME-DELTS
直接连接管理 MLME-DLS
MLME-DLSTearDown
高层同步支持 MLME-HL-SYNC
合并ACK帧管理 MLME-ADDBA
MLME-DELBA
Qos调度变更通知 MLME-SCHEDULE
发行商特有 MLME-VSPECIFIC
MIB管理 MLME-SET
MLME-GET
以上的所有的原语构成了MAC对外提供的可操作接口。
在内部,MAC由除了函数还有数据,叫MIB,存储MAC的各种参数。还有个专业术语叫SME的,其实是一个单独的模块,用来跟接口函数功能互动,完成各函数之间的关联操作和配合响应。属于配合接口正常运作的角色,对外不提供接口。
以上的接口原语,按照功能模块,可以归纳出MAC主要包括如下功能:
1、信道管理。包括:信道扫描(MLME-SCAN)、信道测量(MLME-MREQUEST、MLME-MEASURE、MLME-MREPORT)、信道切换(MLME-CHANNELSWITCH)
2、连接管理。包括:认证(MLME-AUTHENTICATE)、断开认证(MLME-DEAUTHENTICATE)、建立连接(MLME- ASSOCIATE)、重新连接(MLME-REASSOCIATE)、断开连接(MLME-DEASSOCIATE)、开始网络(MLME- START)、点对点连接请求(MLME-PeerKeySTART)、直接连接管理(MLME-DLS、MLME-DLSTearDown)、
3、服务质量(Qos):交通流(TS)管理接口(MLME-ADDTS、MLME-DELTS)、Qos调度变更通知(MLME-SCHEDULE)、
4、功率控制。包括:电源管理(MLME-POWERMGT)、发送功率通知(MLME-TPCADAPT)
5、安全。包括:密钥管理(MLME-SETKEYS、MLME-DELETEKEYS)、迈克尔失败事件(MLME- MICHAELMICFAILURE)、EAPOL(MLME-EAPOL)、帧密钥错误丢弃通知(MLME- PROTECTEDFRAMEDROPPED)
6、时间同步。包括:时间同步(MLME-JOIN)、高层同步支持(MLME-HL-SYNC)、
7、特性。包括:合并ACK帧管理(MLME-ADDBA、MLME-DELBA)、发行商特有(MLME-VSPECIFIC)、MIB管理(MLME-SET、MLME-GET)
————————————————
版权声明:本文为CSDN博主「viewsky11」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/viewsky11/article/details/53152070

  • 1
    点赞
  • 39
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值