天天看點

mac80211解析三

ieee80211_register_hw

函數中,實作了無線網絡裝置的的注冊,網絡接口的注冊和建立主要由

ieee80211_if_add

函數來完成,即:

result = ieee80211_if_add(local, "wlan%d", NULL,
                  NL80211_IFTYPE_STATION, NULL); 
           

一般注冊生成wlan0,函數

ieee80211_if_add

的内容如下:

int ieee80211_if_add(struct ieee80211_local *local, const char *name,
             struct wireless_dev **new_wdev, enum nl80211_iftype type,
             struct vif_params *params)
{
    struct net_device *ndev = NULL;
    struct ieee80211_sub_if_data *sdata = NULL;
    int ret, i;
    int txqs = ;

    ASSERT_RTNL();

    if (type == NL80211_IFTYPE_P2P_DEVICE) {
        struct wireless_dev *wdev;

        sdata = kzalloc(sizeof(*sdata) + local->hw.vif_data_size,
                GFP_KERNEL);
        if (!sdata)
            return -ENOMEM;
        wdev = &sdata->wdev;

        sdata->dev = NULL;
        strlcpy(sdata->name, name, IFNAMSIZ);
        ieee80211_assign_perm_addr(local, wdev->address, type);
        memcpy(sdata->vif.addr, wdev->address, ETH_ALEN);
    } else {
        if (local->hw.queues >= IEEE80211_NUM_ACS)
            txqs = IEEE80211_NUM_ACS;

        ndev = alloc_netdev_mqs(sizeof(*sdata) +
                    local->hw.vif_data_size,
                    name, ieee80211_if_setup, txqs, 1);
        if (!ndev)
            return -ENOMEM;
        dev_net_set(ndev, wiphy_net(local->hw.wiphy));

        ndev->needed_headroom = local->tx_headroom +
                    4*6 /* four MAC addresses */
                    + 2 + 2 + 2 + 2 /* ctl, dur, seq, qos */
                    + 6 /* mesh */
                    + 8 /* rfc1042/bridge tunnel */
                    - ETH_HLEN /* ethernet hard_header_len */
                    + IEEE80211_ENCRYPT_HEADROOM;
        ndev->needed_tailroom = IEEE80211_ENCRYPT_TAILROOM;

        ret = dev_alloc_name(ndev, ndev->name);
        if (ret < 0) {
            free_netdev(ndev);
            return ret;
        }

        ieee80211_assign_perm_addr(local, ndev->perm_addr, type);
        memcpy(ndev->dev_addr, ndev->perm_addr, ETH_ALEN);
        SET_NETDEV_DEV(ndev, wiphy_dev(local->hw.wiphy));

        /* don't use IEEE80211_DEV_TO_SUB_IF -- it checks too much */
        sdata = netdev_priv(ndev);
        ndev->ieee80211_ptr = &sdata->wdev;
        memcpy(sdata->vif.addr, ndev->dev_addr, ETH_ALEN);
        memcpy(sdata->name, ndev->name, IFNAMSIZ);

        sdata->dev = ndev;
    }

    /* initialise type-independent data */
    sdata->wdev.wiphy = local->hw.wiphy;
    sdata->local = local;

    for (i = ; i < IEEE80211_FRAGMENT_MAX; i++)
        skb_queue_head_init(&sdata->fragments[i].skb_list);

    INIT_LIST_HEAD(&sdata->key_list);

    INIT_DELAYED_WORK(&sdata->dfs_cac_timer_work,
              ieee80211_dfs_cac_timer_work);
    INIT_DELAYED_WORK(&sdata->dec_tailroom_needed_wk,
              ieee80211_delayed_tailroom_dec);

    for (i = ; i < IEEE80211_NUM_BANDS; i++) {
        struct ieee80211_supported_band *sband;
        sband = local->hw.wiphy->bands[i];
        sdata->rc_rateidx_mask[i] =
            sband ? ( << sband->n_bitrates) -  : ;
        if (sband)
            memcpy(sdata->rc_rateidx_mcs_mask[i],
                   sband->ht_cap.mcs.rx_mask,
                   sizeof(sdata->rc_rateidx_mcs_mask[i]));
        else
            memset(sdata->rc_rateidx_mcs_mask[i], ,
                   sizeof(sdata->rc_rateidx_mcs_mask[i]));
    }

    ieee80211_set_default_queues(sdata);

    sdata->ap_power_level = IEEE80211_UNSET_POWER_LEVEL;
    sdata->user_power_level = local->user_power_level;

    sdata->encrypt_headroom = IEEE80211_ENCRYPT_HEADROOM;

    /* setup type-dependent data */
    ieee80211_setup_sdata(sdata, type);

    if (ndev) {
        if (params) {
            ndev->ieee80211_ptr->use_4addr = params->use_4addr;
            if (type == NL80211_IFTYPE_STATION)
                sdata->u.mgd.use_4addr = params->use_4addr;
        }

        ndev->features |= local->hw.netdev_features;

        ret = register_netdevice(ndev);
        if (ret) {
            free_netdev(ndev);
            return ret;
        }
    }

    mutex_lock(&local->iflist_mtx);
    list_add_tail_rcu(&sdata->list, &local->interfaces);
    mutex_unlock(&local->iflist_mtx);

    if (new_wdev)
        *new_wdev = &sdata->wdev;

    return ;
}
           

其中使用

alloc_netdev_mqs

配置設定了網絡裝置的私有資料,并用setup函數建立網絡接口,即

ieee80211_if_setup

函數,該函數内容如下:

static void ieee80211_if_setup(struct net_device *dev)
{
    ether_setup(dev);
    dev->priv_flags &= ~IFF_TX_SKB_SHARING;
    dev->netdev_ops = &ieee80211_dataif_ops;
    dev->destructor = ieee80211_if_free;
} 
           

可以看到,這裡配置設定的

net_device

的網絡接口操作結構體

netdev_ops

=

&ieee80211_dataif_ops

, 其中

ieee80211_dataif_ops

定義如下:

static const struct net_device_ops ieee80211_dataif_ops = {
    .ndo_open       = ieee80211_open,
    .ndo_stop       = ieee80211_stop,
    .ndo_uninit     = ieee80211_uninit,
    .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,
    .ndo_get_stats64    = ieee80211_get_stats64,
};
           

然後

ieee80211_if_add

函數中繼續初始化網絡裝置和sdata,使用

ieee80211_setup_sdata

函數建立網絡接口依賴的data,内容如下:

/*
 * Helper function to initialise an interface to a specific type.
 */
static void ieee80211_setup_sdata(struct ieee80211_sub_if_data *sdata,
                  enum nl80211_iftype type)
{
    /* clear type-dependent union */
    memset(&sdata->u, , sizeof(sdata->u));

    /* and set some type-dependent values */
    sdata->vif.type = type;
    sdata->vif.p2p = false;
    sdata->wdev.iftype = type;

    sdata->control_port_protocol = cpu_to_be16(ETH_P_PAE);
    sdata->control_port_no_encrypt = false;
    sdata->encrypt_headroom = IEEE80211_ENCRYPT_HEADROOM;

    sdata->noack_map = ;

    /* only monitor/p2p-device differ */
    if (sdata->dev) {
        sdata->dev->netdev_ops = &ieee80211_dataif_ops;
        sdata->dev->type = ARPHRD_ETHER;
    }

    skb_queue_head_init(&sdata->skb_queue);
    INIT_WORK(&sdata->work, ieee80211_iface_work);
    INIT_WORK(&sdata->recalc_smps, ieee80211_recalc_smps_work);
    INIT_WORK(&sdata->csa_finalize_work, ieee80211_csa_finalize_work);
    INIT_LIST_HEAD(&sdata->assigned_chanctx_list);
    INIT_LIST_HEAD(&sdata->reserved_chanctx_list);

    switch (type) {
    case NL80211_IFTYPE_P2P_GO:
        type = NL80211_IFTYPE_AP;
        sdata->vif.type = type;
        sdata->vif.p2p = true;
        /* fall through */
    case NL80211_IFTYPE_AP:
        skb_queue_head_init(&sdata->u.ap.ps.bc_buf);
        INIT_LIST_HEAD(&sdata->u.ap.vlans);
        INIT_WORK(&sdata->u.ap.request_smps_work,
              ieee80211_request_smps_ap_work);
        sdata->vif.bss_conf.bssid = sdata->vif.addr;
        sdata->u.ap.req_smps = IEEE80211_SMPS_OFF;
        break;
    case NL80211_IFTYPE_P2P_CLIENT:
        type = NL80211_IFTYPE_STATION;
        sdata->vif.type = type;
        sdata->vif.p2p = true;
        /* fall through */
    case NL80211_IFTYPE_STATION:
        sdata->vif.bss_conf.bssid = sdata->u.mgd.bssid;
        ieee80211_sta_setup_sdata(sdata);
        break;
    case NL80211_IFTYPE_ADHOC:
        sdata->vif.bss_conf.bssid = sdata->u.ibss.bssid;
        ieee80211_ibss_setup_sdata(sdata);
        break;
    case NL80211_IFTYPE_MESH_POINT:
        if (ieee80211_vif_is_mesh(&sdata->vif))
            ieee80211_mesh_init_sdata(sdata);
        break;
    case NL80211_IFTYPE_MONITOR:
        sdata->dev->type = ARPHRD_IEEE80211_RADIOTAP;
        sdata->dev->netdev_ops = &ieee80211_monitorif_ops;
        sdata->u.mntr_flags = MONITOR_FLAG_CONTROL |
                      MONITOR_FLAG_OTHER_BSS;
        break;
    case NL80211_IFTYPE_WDS:
        sdata->vif.bss_conf.bssid = NULL;
        break;
    case NL80211_IFTYPE_AP_VLAN:
    case NL80211_IFTYPE_P2P_DEVICE:
        sdata->vif.bss_conf.bssid = sdata->vif.addr;
        break;
    case NL80211_IFTYPE_UNSPECIFIED:
    case NUM_NL80211_IFTYPES:
        BUG();
        break;
    }

    ieee80211_debugfs_add_netdev(sdata);
}
           

經過一些列配置設定和初始化之後,調用

register_netdevice

函數将網絡裝置接口進行注冊。

最後通過:

list_add_tail_rcu(&sdata->list, &local->interfaces);
           

把sdata對象加入

local->interfaces

,完成接口建立。

删除接口由使用者空間通過nl80211發起,調用

cfg80211_ops

中的

del_virtual_intf

, 即

ieee80211_del_iface

函數,

static int ieee80211_del_iface(struct wiphy *wiphy, struct wireless_dev *wdev)
{
    ieee80211_if_remove(IEEE80211_WDEV_TO_SUB_IF(wdev));
    return 0;
}
           

ieee80211_if_remove

函數:

void ieee80211_if_remove(struct ieee80211_sub_if_data *sdata)
{
    ASSERT_RTNL();

    mutex_lock(&sdata->local->iflist_mtx);
    list_del_rcu(&sdata->list);
    mutex_unlock(&sdata->local->iflist_mtx);

    synchronize_rcu();

    if (sdata->dev) {
        unregister_netdevice(sdata->dev);
    } else {
        cfg80211_unregister_wdev(&sdata->wdev);
        ieee80211_teardown_sdata(sdata);
        kfree(sdata);
    }
}
           

把sdata對象從

local->interfaces

移除 ,然後注冊網絡接口,實作删除。

繼續閱讀