Broadcom softmac WLAN 驱动解析(3)

Linux kernel发送数据的接口函数是packet_sendmsg,本质上对应了users pace的sendmsg实现。
比如在wpa_supplicant中,wpa_driver_nl80211_send_frame()就是用sendmsg发送数据的:

static int wpa_driver_nl80211_send_frame(struct wpa_driver_nl80211_data *drv,
                     const void *data, size_t len,
                     int encrypt)
{
    ...
    struct msghdr msg = {
        .msg_name = NULL,
        .msg_namelen = 0,
        .msg_iov = iov,
        .msg_iovlen = 2,
        .msg_control = NULL,
        .msg_controllen = 0,
        .msg_flags = 0,
    };
    ...
    res = sendmsg(drv->monitor_sock, &msg, 0);
    if (res < 0) {
        wpa_printf(MSG_INFO, "nl80211: sendmsg: %s", strerror(errno));
        return -1;
    }
    return 0;
}

 1. 首先来看packet_sendmsg()的实现

static int packet_sendmsg(struct kiocb *iocb, struct socket *sock,
        struct msghdr *msg, size_t len)
{
    struct sock *sk = sock->sk;
    struct packet_sock *po = pkt_sk(sk);
    if (po->tx_ring.pg_vec)
        return tpacket_snd(po, msg);
    else
        return packet_snd(sock, msg, len);
}

2. 调用packet_snd()

static int packet_snd(struct socket *sock,
              struct msghdr *msg, size_t len)
{
    ...
        // 首先把数据从user space拷贝到kernel space
        err = memcpy_fromiovec((void *)&vnet_hdr, msg->msg_iov,
                       vnet_hdr_len);
    ...
    /*
     *    Now send it
     */
    // 然后用dev_queue_xmit()来发送skb.
    err = dev_queue_xmit(skb);
    if (err > 0 && (err = net_xmit_errno(err)) != 0)
        goto out_unlock;
    ...
}

  3. 调用dev_queue_xmit()

/**
 *    dev_queue_xmit - transmit a buffer
 *    @skb: buffer to transmit
 *
 *    Queue a buffer for transmission to a network device. The caller must
 *    have set the device and priority and built the buffer before calling
 *    this function. The function can be called from an interrupt.
 *
 *    A negative errno code is returned on a failure. A success does not
 *    guarantee the frame will be transmitted as it may be dropped due
 *    to congestion or traffic shaping.
 */
int dev_queue_xmit(struct sk_buff *skb)
{
    struct net_device *dev = skb->dev;
    struct netdev_queue *txq;
    struct Qdisc *q;
    int rc = -ENOMEM;

    skb_reset_mac_header(skb);

    /* Disable soft irqs for various locks below. Also
     * stops preemption for RCU.
     */
    rcu_read_lock_bh();

    skb_update_prio(skb);

    txq = netdev_pick_tx(dev, skb);
    q = rcu_dereference_bh(txq->qdisc);

#ifdef CONFIG_NET_CLS_ACT
    skb->tc_verd = SET_TC_AT(skb->tc_verd, AT_EGRESS);
#endif
    trace_net_dev_queue(skb);
    if (q->enqueue) {
        rc = __dev_xmit_skb(skb, q, dev, txq);
        goto out;
    }
    ...
}

 4.  调用__dev_xmit_skb()

static inline int __dev_xmit_skb(struct sk_buff *skb, struct Qdisc *q,
                 struct net_device *dev,
                 struct netdev_queue *txq)
{
    ...
    if (unlikely(test_bit(__QDISC_STATE_DEACTIVATED, &q->state))) {
        kfree_skb(skb);
        rc = NET_XMIT_DROP;
    } else if ((q->flags & TCQ_F_CAN_BYPASS) && !qdisc_qlen(q) &&
           qdisc_run_begin(q)) {
        /*
         * This is a work-conserving queue; there are no old skbs
         * waiting to be sent out; and the qdisc is not running -
         * xmit the skb directly.
         */
        if (!(dev->priv_flags & IFF_XMIT_DST_RELEASE))
            skb_dst_force(skb);

        qdisc_bstats_update(q, skb);
        // 注意这里
        if (sch_direct_xmit(skb, q, dev, txq, root_lock)) {
            if (unlikely(contended)) {
                spin_unlock(&q->busylock);
                contended = false;
            }
            __qdisc_run(q);
        } else
            qdisc_run_end(q);

        rc = NET_XMIT_SUCCESS;
    }
    ...
}

 5. 调用sch_direct_xmit()

/*
 * Transmit one skb, and handle the return status as required. Holding the
 * __QDISC_STATE_RUNNING bit guarantees that only one CPU can execute this
 * function.
 *
 * Returns to the caller:
 *                0  - queue is empty or throttled.
 *                >0 - queue is not empty.
 */
int sch_direct_xmit(struct sk_buff *skb, struct Qdisc *q,
            struct net_device *dev, struct netdev_queue *txq,
            spinlock_t *root_lock)
{
    ...
    if (!netif_xmit_frozen_or_stopped(txq))
        ret = dev_hard_start_xmit(skb, dev, txq);
    ...
}

 6. 调用dev_hard_start_xmit()

int dev_hard_start_xmit(struct sk_buff *skb, struct net_device *dev,
            struct netdev_queue *txq)
{
    ...
    do {
        struct sk_buff *nskb = skb->next;

        skb->next = nskb->next;
        nskb->next = NULL;

        /*
         * If device doesn't need nskb->dst, release it right now while
         * its hot in this cpu cache
         */
        if (dev->priv_flags & IFF_XMIT_DST_RELEASE)
            skb_dst_drop(nskb);

        if (!list_empty(&ptype_all))
            dev_queue_xmit_nit(nskb, dev);

        skb_len = nskb->len;
        // 调用了ndo_start_xmit
        rc = ops->ndo_start_xmit(nskb, dev);
        trace_net_dev_xmit(nskb, rc, dev, skb_len);
        if (unlikely(rc != NETDEV_TX_OK)) {
            if (rc & ~NETDEV_TX_MASK)
                goto out_kfree_gso_skb;
            nskb->next = skb->next;
            skb->next = nskb;
            return rc;
        }
        txq_trans_update(txq);
        if (unlikely(netif_xmit_stopped(txq) && skb->next))
            return NETDEV_TX_BUSY;
    } while (skb->next);
    ...
}

 7. 那么ndo_start_xmit对应哪个函数?

在第一节的3.5中提到了ieee80211_register_hw()会调用ieee80211_if_add(),但当时我们只留意了register_netdevice()。
这里再分析一下另外一处比较重要的代码,也就是register_netdevice()之前的ieee80211_setup_sdata()

/* setup type-dependent data */
ieee80211_setup_sdata(sdata, type);

if (ndev) {
    if (params) {
        ndev->ieee80211_ptr->use_4addr = params->use_4addr;
        if (type == NL80211_IFTYPE_STATION)
            sdata->u.mgd.use_4addr = params->use_4addr;
    }

    ndev->features |= local->hw.netdev_features;

    ret = register_netdevice(ndev);
    if (ret) {
        free_netdev(ndev);
        return ret;
    }
}

 继续跟进ieee80211_setup_sdata()

static void ieee80211_setup_sdata(struct ieee80211_sub_if_data *sdata,
                  enum nl80211_iftype type)
{
    ...
    /* only monitor/p2p-device differ */
    if (sdata->dev) {
        sdata->dev->netdev_ops = &ieee80211_dataif_ops;
        sdata->dev->type = ARPHRD_ETHER;
    }

    skb_queue_head_init(&sdata->skb_queue);
    INIT_WORK(&sdata->work, ieee80211_iface_work);
    INIT_WORK(&sdata->recalc_smps, ieee80211_recalc_smps_work);
    ...
}

 ieee80211_dataif_ops()的定义如下:

static const struct net_device_ops ieee80211_dataif_ops = {
    .ndo_open        = ieee80211_open,
    .ndo_stop        = ieee80211_stop,
    .ndo_uninit        = ieee80211_uninit,
    .ndo_start_xmit        = ieee80211_subif_start_xmit,
    .ndo_set_rx_mode    = ieee80211_set_multicast_list,
    .ndo_change_mtu     = ieee80211_change_mtu,
    .ndo_set_mac_address     = ieee80211_change_mac,
    .ndo_select_queue    = ieee80211_netdev_select_queue,
};

 显然,ndo_start_xmit对应的函数是ieee80211_subif_start_xmit()

8. 调用ieee80211_subif_start_xmit()

/**
 * ieee80211_subif_start_xmit - netif start_xmit function for Ethernet-type
 * subinterfaces (wlan#, WDS, and VLAN interfaces)
 * @skb: packet to be sent
 * @dev: incoming interface
 *
 * Returns: 0 on success (and frees skb in this case) or 1 on failure (skb will
 * not be freed, and caller is responsible for either retrying later or freeing
 * skb).
 *
 * This function takes in an Ethernet header and encapsulates it with suitable
 * IEEE 802.11 header based on which interface the packet is coming in. The
 * encapsulated packet will then be passed to master interface, wlan#.11, for
 * transmission (through low-level driver).
 */
netdev_tx_t ieee80211_subif_start_xmit(struct sk_buff *skb,
                    struct net_device *dev)
{
    ...
    ieee80211_xmit(sdata, skb, band);
    rcu_read_unlock();

    return NETDEV_TX_OK;

}

9. 调用ieee80211_xmit()

void ieee80211_xmit(struct ieee80211_sub_if_data *sdata, struct sk_buff *skb,
            enum ieee80211_band band)
{
    ...
    ieee80211_set_qos_hdr(sdata, skb);
    ieee80211_tx(sdata, skb, false, band);
}

10. 调用ieee80211_tx()

static bool ieee80211_tx(struct ieee80211_sub_if_data *sdata,
             struct sk_buff *skb, bool txpending,
             enum ieee80211_band band)
{
    ...
    if (!invoke_tx_handlers(&tx))
        result = __ieee80211_tx(local, &tx.skbs, led_len,
                    tx.sta, txpending);

    return result;
}

11. 调用__ieee80211_tx()

static bool __ieee80211_tx(struct ieee80211_local *local,
               struct sk_buff_head *skbs, int led_len,
               struct sta_info *sta, bool txpending)
{
    ...
    result = ieee80211_tx_frags(local, vif, pubsta, skbs,
                    txpending);

    ieee80211_tpt_led_trig_tx(local, fc, led_len);
    ieee80211_led_tx(local, 1);
    ...
    return result;
}

12. 调用ieee80211_tx_frags()

static bool ieee80211_tx_frags(struct ieee80211_local *local,
                   struct ieee80211_vif *vif,
                   struct ieee80211_sta *sta,
                   struct sk_buff_head *skbs,
                   bool txpending)
{
    ...
    skb_queue_walk_safe(skbs, skb, tmp) {
        ...
        drv_tx(local, &control, skb);
    }

    return true;
}

13. 调用drv_tx()

static inline void drv_tx(struct ieee80211_local *local,
              struct ieee80211_tx_control *control,
              struct sk_buff *skb)
{
    local->ops->tx(&local->hw, control, skb);
}

回忆在ieee80211_alloc_hw()函数体中有这样的代码:
local->ops = ops;

而这个ops又是从ieee80211_alloc_hw()的参数传进来的,也就是brcms_ops.
所以local->ops-tx()实际上就触发了brcms_ops_tx()

 14. 调用brcms_ops_tx()

static void brcms_ops_tx(struct ieee80211_hw *hw,
             struct ieee80211_tx_control *control,
             struct sk_buff *skb)
{
    ...
    if (brcms_c_sendpkt_mac80211(wl->wlc, skb, hw))
        tx_info->rate_driver_data[0] = control->sta;
 done:
    spin_unlock_bh(&wl->lock);
}

15. 调用brcms_c_sendpkt_mac80211()

bool brcms_c_sendpkt_mac80211(struct brcms_c_info *wlc, struct sk_buff *sdu,
                  struct ieee80211_hw *hw)
{
    uint fifo;
    struct scb *scb = &wlc->pri_scb;

    fifo = brcms_ac_to_fifo(skb_get_queue_mapping(sdu));
    brcms_c_d11hdrs_mac80211(wlc, hw, sdu, scb, 0, 1, fifo, 0);
    // 这里是关键
    if (!brcms_c_tx(wlc, sdu))
        return true;

    /* packet discarded */
    dev_kfree_skb_any(sdu);
    return false;
}

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

导航