OpenWRT數據接收過程

轉自:http://blog.chinaunix.net/uid-26675482-id-4589249.html


1. ieee80211_tasklet_handler()

Linux內核是通過中斷來對接收到的數據進行響應的。當硬件檢測到有接收數據的時候,產生一箇中斷,中斷觸發下半部的tasklet機制,在802.11協議棧這裏會調用ieee80211_tasklet_handler()函數。我們來看一看函數體:(位於OpenWRT內核文件夾子目錄/net/mac80211,文件main.c中)


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:
                     ...
              default:
                     ...
              }
       }
}


2. ieee80211_rx()


系統收到數據時會開闢一個sk_buff緩存空間進行數據的存儲,ieee80211_tasklet_handler()觸發後對sk_buff中存儲的數據幀進行判斷,如果是接收來的數據(MPDU),則進入ieee80211_rx()函數:(位於OpenWRT內核文件夾子目錄/net/mac80211,文件rx.c中)。


void ieee80211_rx(struct ieee80211_hw *hw, struct sk_buff *skb)
{
       struct ieee80211_local *local = hw_to_local(hw);
       struct ieee80211_rate *rate = NULL;
       struct ieee80211_supported_band *sband;
       struct ieee80211_rx_status *status = IEEE80211_SKB_RXCB(skb);
       ...
       __ieee80211_rx_handle_packet(hw, skb);
       rcu_read_unlock();
       return;
 drop:
       kfree_skb(skb);
}
EXPORT_SYMBOL(ieee80211_rx);

3. __ieee80211_rx_handle_packet()



ieee80211_rx()函數再調用__ieee80211_rx_handle_packet()(位於OpenWRT內核文件夾子目錄/net/mac80211,文件rx.c中),__ieee80211_rx_handle_packet()是接收幀的處理函數,會對幀類型進行判斷,如果檢測出該幀是beacon幀(或sta主動掃描後從AP端返回的響應幀),則進入ieee80211_scan_rx()函數對幀信息進行掃描,如果是數據幀,則調用ieee80211_prepare_and_rx_handle()對幀進行處理,下面分析接收幀爲數據幀的情況。

void ieee80211_rx(struct ieee80211_hw *hw, struct sk_buff *skb)
{
       struct ieee80211_local *local = hw_to_local(hw);
       struct ieee80211_rate *rate = NULL;
       struct ieee80211_supported_band *sband;
       struct ieee80211_rx_status *status = IEEE80211_SKB_RXCB(skb);
       ...
       __ieee80211_rx_handle_packet(hw, skb);
       rcu_read_unlock();
       return;
 drop:
       kfree_skb(skb);
}
EXPORT_SYMBOL(ieee80211_rx);
print


4. ieee80211_prepare_and_rx_handle()

調用ieee80211_prepare_and_rx_handle()(位於OpenWRT內核文件夾子目錄/net/mac80211,文件rx.c中)。



/*
 * This function returns whether or not the SKB
 * was destined for RX processing or not, which,
 * if consume is true, is equivalent to whether
 * or not the skb was consumed.
 */
static bool ieee80211_prepare_and_rx_handle(struct ieee80211_rx_data *rx, struct sk_buff *skb, bool consume)
{
…
       ieee80211_invoke_rx_handlers(rx);
       return true;
}


5. ieee80211_invoke_rx_handlers()

調用ieee80211_invoke_rx_handlers()(位於OpenWRT內核文件夾子目錄/net/mac80211,文件rx.c中)。


static void ieee80211_invoke_rx_handlers(struct ieee80211_rx_data *rx)
{
…
       ieee80211_rx_reorder_ampdu(rx, &reorder_release);
       ieee80211_rx_handlers(rx, &reorder_release);
       return;
 rxh_next:
       ieee80211_rx_handlers_result(rx, res);
…
}

6. ieee80211_rx_handlers()


調用ieee80211_rx_handlers()(位於OpenWRT內核文件夾子目錄/net/mac80211,文件rx.c中)。


static void ieee80211_rx_handlers(struct ieee80211_rx_data *rx, struct sk_buff_head *frames)
{
       ieee80211_rx_result res = RX_DROP_MONITOR;
       struct sk_buff *skb;
#define CALL_RXH(rxh)                     \
       do {                            \
              res = rxh(rx);              \
              if (res != RX_CONTINUE)    \
                     goto rxh_next;  \
       } while (0);
       spin_lock_bh(&rx->local->rx_path_lock);
       while ((skb = __skb_dequeue(frames))) {
              /*
               * all the other fields are valid across frames
               * that belong to an aMPDU since they are on the
               * same TID from the same station
               */
              rx->skb = skb;
              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 CPTCFG_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)
              /* special treatment -- needs the queue */
              res = ieee80211_rx_h_ctrl(rx, frames);
              if (res != RX_CONTINUE)
                     goto rxh_next;
              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)
 rxh_next:
              ieee80211_rx_handlers_result(rx, res);
#undef CALL_RXH
       }
       spin_unlock_bh(&rx->local->rx_path_lock);
}


7. ieee80211_rx_h_data()


只看是數據幀的情況,會繼續調用ieee80211_rx_h_data()(位於OpenWRT內核文件夾子目錄/net/mac80211,文件rx.c中)。


static ieee80211_rx_result debug_noinline ieee80211_rx_h_data(struct ieee80211_rx_data *rx)
{
…
       rx->skb->dev = dev;
       dev->stats.rx_packets++;
       dev->stats.rx_bytes += rx->skb->len;
       if (local->ps_sdata && local->hw.conf.dynamic_ps_timeout > 0 &&
           !is_multicast_ether_addr(
                  ((struct ethhdr *)rx->skb->data)->h_dest) &&
           (!local->scanning &&
            !test_bit(SDATA_STATE_OFFCHANNEL, &sdata->state))) {
                     mod_timer(&local->dynamic_ps_timer, jiffies +
                      msecs_to_jiffies(local->hw.conf.dynamic_ps_timeout));
       }
       ieee80211_deliver_skb(rx);
       return RX_QUEUED;
}


8. ieee80211_deliver_skb()


/*
 * requires that rx->skb is a frame with ethernet header
 */
static void
ieee80211_deliver_skb(struct ieee80211_rx_data *rx)
{
       …
       skb = rx->skb;
       …
       if (skb) {
              int align __maybe_unused;
#ifndef CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS
              /*
               * 'align' will only take the values 0 or 2 here
               * since all frames are required to be aligned
               * to 2-byte boundaries when being passed to
               * mac80211; the code here works just as well if
               * that isn't true, but mac80211 assumes it can
               * access fields as 2-byte aligned (e.g. for
               * compare_ether_addr)
               */
              align = ((unsigned long)(skb->data + sizeof(struct ethhdr))) & 3;
              if (align) {
                     if (WARN_ON(skb_headroom(skb) < 3)) {
                            dev_kfree_skb(skb);
                            skb = NULL;
                     } else {
                            u8 *data = skb->data;
                            size_t len = skb_headlen(skb);
                            skb->data -= align;
                            memmove(skb->data, data, len);
                            skb_set_tail_pointer(skb, len);
                     }
              }
#endif
              if (skb) {
                     /* deliver to local stack */
                     skb->protocol = eth_type_trans(skb, dev);
                     memset(skb->cb, 0, sizeof(skb->cb));
                     netif_receive_skb(skb);
              }
       }
       …
}
調用ieee80211_deliver_skb()(位於OpenWRT內核文件夾子目錄/net/mac80211,文件rx.c中)。


/*
 * requires that rx->skb is a frame with ethernet header
 */
static void
ieee80211_deliver_skb(struct ieee80211_rx_data *rx)
{
       …
       skb = rx->skb;
       …
       if (skb) {
              int align __maybe_unused;
#ifndef CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS
              /*
               * 'align' will only take the values 0 or 2 here
               * since all frames are required to be aligned
               * to 2-byte boundaries when being passed to
               * mac80211; the code here works just as well if
               * that isn't true, but mac80211 assumes it can
               * access fields as 2-byte aligned (e.g. for
               * compare_ether_addr)
               */
              align = ((unsigned long)(skb->data + sizeof(struct ethhdr))) & 3;
              if (align) {
                     if (WARN_ON(skb_headroom(skb) < 3)) {
                            dev_kfree_skb(skb);
                            skb = NULL;
                     } else {
                            u8 *data = skb->data;
                            size_t len = skb_headlen(skb);
                            skb->data -= align;
                            memmove(skb->data, data, len);
                            skb_set_tail_pointer(skb, len);
                     }
              }
#endif
              if (skb) {
                     /* deliver to local stack */
                     skb->protocol = eth_type_trans(skb, dev);
                     memset(skb->cb, 0, sizeof(skb->cb));
                     netif_receive_skb(skb);
              }
       }
       …
}


這裏最核心的代碼就是netif_receive_skb(skb)了,至此,數據已經接收到併發送至內核的網絡子系統去處理。netif_receive_skb定義於內核文件夾linux-3.3.8的子目錄/net/core的文件dev.c中。


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章