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

posted on 2013-04-10 22:10  hellolwl  阅读(1419)  评论(0编辑  收藏  举报

导航