现在我们来看看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);(brcms_c_isr(wl->wlc)) {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){ …(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); {(n >= bound_limit)break;morepending = dma_rx(wlc_hw->di[fifo], &recv_frames);n++;} while (morepending);dma_rxfill(wlc_hw->di[fifo]);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()
* Return true if more frames need to be processed. false otherwise. * Param ‘bound’ indicates max. # frames to process before break out. brcms_c_recv(struct brcms_c_info *wlc, struct sk_buff *p){…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 voidbrcms_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);len_mpdu = p->len – D11_PHY_HDR_LEN – FCS_LEN;skb_pull(p, D11_PHY_HDR_LEN);__skb_trim(p, len_mpdu);(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()
其实,每个人都是幸福的。只是,你的幸福,常常在别人眼里。