Broadcom softmac WLAN 驱动解析(2)
现在我们来看看driver是如何从WLAN chipset那里接收数据的
在上一篇文章中提到,数据过来时会产生中断,而在brcms_attach()函数体中,注册的interrupt handler是brcms_isr(),所以数据过来触发的第一个函数就是brcms_isr()。
1. 触发brcms_isr()
static irqreturn_t brcms_isr(int irq, void *dev_id) { struct brcms_info *wl; irqreturn_t ret = IRQ_NONE; wl = (struct brcms_info *) dev_id; spin_lock(&wl->isr_lock); /* call common first level interrupt handler */ if (brcms_c_isr(wl->wlc)) { /* schedule second level handler */ tasklet_schedule(&wl->tasklet); ret = IRQ_HANDLED; } spin_unlock(&wl->isr_lock); return ret; }
这里通过tasklet_schedule()来运行tasklet。在brcms_attach()中,已经用tasklet_init()指定了底半部的handler是brcms_dpc.
2. 触发brcms_dpc()
void brcms_dpc(unsigned long data) { .../* call the common second level interrupt handler */ if (wl->pub->up) { if (wl->resched) { unsigned long flags; spin_lock_irqsave(&wl->isr_lock, flags); brcms_c_intrsupd(wl->wlc); spin_unlock_irqrestore(&wl->isr_lock, flags); } wl->resched = brcms_c_dpc(wl->wlc, true); } ... }
3. 调用brcms_c_dpc()
/* second-level interrupt processing * Return true if another dpc needs to be re-scheduled. false otherwise. * Param 'bounded' indicates if applicable loops should be bounded. */ bool brcms_c_dpc(struct brcms_c_info *wlc, bool bounded) { ... /* * received data or control frame, MI_DMAINT is * indication of RX_FIFO interrupt */ if (macintstatus & MI_DMAINT) if (brcms_b_recv(wlc_hw, RX_FIFO, bounded)) wlc->macintstatus |= MI_DMAINT; ... }
4. 调用brcms_b_recv()
brcms_b_recv(struct brcms_hardware *wlc_hw, uint fifo, bool bound) { ... skb_queue_head_init(&recv_frames); /* gather received frames */ do { /* !give others some time to run! */ if (n >= bound_limit) break; morepending = dma_rx(wlc_hw->di[fifo], &recv_frames); n++; } while (morepending); /* post more rbufs */ dma_rxfill(wlc_hw->di[fifo]); /* process each frame */ skb_queue_walk_safe(&recv_frames, p, next) { struct d11rxhdr_le *rxh_le; struct d11rxhdr *rxh; skb_unlink(p, &recv_frames); rxh_le = (struct d11rxhdr_le *)p->data; rxh = (struct d11rxhdr *)p->data; ... brcms_c_recv(wlc_hw->wlc, p); } return morepending; }
5. 调用brcms_c_recv()
/* Process received frames */ /* * Return true if more frames need to be processed. false otherwise. * Param 'bound' indicates max. # frames to process before break out. */ static void brcms_c_recv(struct brcms_c_info *wlc, struct sk_buff *p) { ... /* not supporting A-MSDU */ is_amsdu = rxh->RxStatus2 & RXS_AMSDU_MASK; if (is_amsdu) goto toss; brcms_c_recvctl(wlc, rxh, p); return; toss: brcmu_pkt_buf_free_skb(p); }
6. 调用brcms_c_recvctl()
static void brcms_c_recvctl(struct brcms_c_info *wlc, struct d11rxhdr *rxh, struct sk_buff *p) { int len_mpdu; struct ieee80211_rx_status rx_status; struct ieee80211_hdr *hdr; memset(&rx_status, 0, sizeof(rx_status)); prep_mac80211_status(wlc, rxh, p, &rx_status); /* mac header+body length, exclude CRC and plcp header */ len_mpdu = p->len - D11_PHY_HDR_LEN - FCS_LEN; skb_pull(p, D11_PHY_HDR_LEN); __skb_trim(p, len_mpdu); /* unmute transmit */ if (wlc->hw->suspended_fifos) { hdr = (struct ieee80211_hdr *)p->data; if (ieee80211_is_beacon(hdr->frame_control)) brcms_b_mute(wlc->hw, false); } memcpy(IEEE80211_SKB_RXCB(p), &rx_status, sizeof(rx_status)); ieee80211_rx_irqsafe(wlc->pub->ieee_hw, p); }
这里我们暂时只需要关注最后一行,也就是ieee80211_rx_irqsafe()
7. 调用ieee80211_rx_irqsafe()
/* This is a version of the rx handler that can be called from hard irq * context. Post the skb on the queue and schedule the tasklet */ void ieee80211_rx_irqsafe(struct ieee80211_hw *hw, struct sk_buff *skb) { struct ieee80211_local *local = hw_to_local(hw); BUILD_BUG_ON(sizeof(struct ieee80211_rx_status) > sizeof(skb->cb)); skb->pkt_type = IEEE80211_RX_MSG; skb_queue_tail(&local->skb_queue, skb); tasklet_schedule(&local->tasklet); }
关键的一行代码是tasklet_schedule(), 回忆一下我们在分析ieee80211_alloc_hw()时提到的这句代码:
tasklet_init(&local->tasklet, ieee80211_tasklet_handler, (unsigned long) local);
这里终于派上了用场,ieee80211_tasklet_handler被触发了。
8. 调用ieee80211_tasklet_handler()
static void ieee80211_tasklet_handler(unsigned long data) { struct ieee80211_local *local = (struct ieee80211_local *) data; struct sta_info *sta, *tmp; struct skb_eosp_msg_data *eosp_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; ... } } }
这里我们只关心IEEE80211_RX_MSG类型的处理。
9. 调用ieee80211_rx(), 终于走到了rx.c
/* * 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) { ... ieee80211_tpt_led_trig_rx(local, ((struct ieee80211_hdr *)skb->data)->frame_control, skb->len); __ieee80211_rx_handle_packet(hw, skb); rcu_read_unlock(); return; drop: kfree_skb(skb); }
这里只列出了关键的代码,也就是调用__ieee80211_rx_handle_packet()
10. 调用__ieee80211_rx_handle_packet()
static void __ieee80211_rx_handle_packet(struct ieee80211_hw *hw, struct sk_buff *skb) { ... if (unlikely(ieee80211_is_probe_resp(hdr->frame_control) || ieee80211_is_beacon(hdr->frame_control))) ieee80211_scan_rx(local, skb); if (ieee80211_is_data(fc)) { prev_sta = NULL; for_each_sta_info(local, hdr->addr2, sta, tmp) { if (!prev_sta) { prev_sta = sta; continue; } rx.sta = prev_sta; rx.sdata = prev_sta->sdata; ieee80211_prepare_and_rx_handle(&rx, skb, false); prev_sta = sta; } if (prev_sta) { rx.sta = prev_sta; rx.sdata = prev_sta->sdata; if (ieee80211_prepare_and_rx_handle(&rx, skb, true)) return; goto out; } } ... }
可以看到如果是probe response或者beacon这样的frame, 会有另外的处理。我们这里只分析是data的情况。
11. 调用ieee80211_prepare_and_rx_handle()
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; }
12. 调用ieee80211_invoke_rx_handlers()
static void ieee80211_invoke_rx_handlers(struct ieee80211_rx_data *rx) { ... ieee80211_rx_handlers(rx, &reorder_release); return; ... }
13. 调用ieee80211_rx_handlers()
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 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) /* 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); }
还是只看是data的情况,会继续调用ieee80211_rx_h_data()
14. 调用ieee80211_rx_h_data()
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; }
15. 调用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); } } ... }
这里最核心的代码就是netif_receive_skb(skb)了,至此,数据已经从WLAN chipset接收到并发送至内核的网络子系统去处理。网上关于Linux的网络子系统的文章很多,个人觉得这一篇写的很不错:http://bbs.chinaunix.net/thread-2141004-1-1.html