轉自: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: ... } } }
系統收到數據時會開闢一個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);
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);
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); … }
調用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); }
只看是數據幀的情況,會繼續調用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; }
/* * 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中。