Broadcom mac80211 驱动解析

1. 驱动的主进口如下:

/**
* This is the main entry point for the brcmsmac driver.
*
* This function is scheduled upon module initialization and
* does the driver registration, which result in brcms_bcma_probe()
* call resulting in the driver bringup.
*/
staticvoid brcms_driver_init(struct work_struct *work)
{
int error;

error = bcma_driver_register(&brcms_bcma_driver);
if (error)
pr_err("%s: register returned %d\n", __func__, error);
}

可以看到brcms_bcma_driver作为参数传递给了bcma_driver_register(). brcms_bcma_driver的定义如下:

staticstruct bcma_driver brcms_bcma_driver = {
.name     = KBUILD_MODNAME,
.probe    = brcms_bcma_probe,
.suspend  = brcms_suspend,
.resume   = brcms_resume,
.remove   = brcms_remove,
.id_table = brcms_coreid_table,
};

重视此中的probe函数。

2. 按照上方的解析,紧接着brcms_bcma_probe将会被调用。

staticint brcms_bcma_probe(struct bcma_device *pdev)
{
struct brcms_info *wl;
struct ieee80211_hw *hw;

dev_info(&pdev->dev, "mfg %x core %x rev %d class %d irq %d\n",
pdev->id.manuf, pdev->id.id, pdev->id.rev, pdev->id.class,
pdev->irq);

if ((pdev->id.manuf != BCMA_MANUF_BCM) ||
(pdev->id.id != BCMA_CORE_80211))
return -ENODEV;

hw = ieee80211_alloc_hw(sizeof(struct brcms_info), &brcms_ops);
if (!hw) {
pr_err("%s: ieee80211_alloc_hw failed\n", __func__);
return -ENOMEM;
}

SET_IEEE80211_DEV(hw, &pdev->dev);

bcma_set_drvdata(pdev, hw);

memset(hw->priv, 0, sizeof(*wl));

wl = brcms_attach(pdev);
if (!wl) {
pr_err("%s: brcms_attach failed!\n", __func__);
return -ENODEV;
}
brcms_led_register(wl);

return0;
}

重视brcms_bcma_probe()函数体中的这行代码:

hw = ieee80211_alloc_hw(sizeof(struct brcms_info), &brcms_ops);

这里的参数是brcms_ops, 它的定义如下:

staticconststruct ieee80211_ops brcms_ops = {
.tx = brcms_ops_tx,
.start = brcms_ops_start,
.stop = brcms_ops_stop,
.add_interface = brcms_ops_add_interface,
.remove_interface = brcms_ops_remove_interface,
.config = brcms_ops_config,
.bss_info_changed = brcms_ops_bss_info_changed,
.configure_filter = brcms_ops_configure_filter,
.sw_scan_start = brcms_ops_sw_scan_start,
.sw_scan_complete = brcms_ops_sw_scan_complete,
.conf_tx = brcms_ops_conf_tx,
.sta_add = brcms_ops_sta_add,
.ampdu_action = brcms_ops_ampdu_action,
.rfkill_poll = brcms_ops_rfkill_poll,
.flush = brcms_ops_flush,
};

以tx为例,在tx.c中将会用到该函数指针。

这里有须要跟一下ieee80211_alloc_hw(), 关于该函数,Johannes Berg在他的《The mac80211 subsystem for kernel developers》中的申明是:
ieee80211_alloc_hw― Allocate a new hardware device

This must be called once for each hardware device. The returned pointer must be used to refer to this
device when calling other functions. mac80211 allocates a private data area for the driver pointed to by
priv in struct ieee80211_hw, the size of this area is given as priv_data_len.

我们重视到brcms_ops作为参数被传递给了ieee80211_alloc_hw(). 并且在该函数体中有如许的代码:

local->ops = ops;

这很首要,我们在后面会说起到。之后就是要new一个struct wiphy - wireless hardware description出来

/* Ensure 32-byte alignment of our private data and hw private data.
* We use the wiphy priv data for both our ieee80211_local and for
* the driver""s private data
*
* In memory it""ll be like this:
*
* +-------------------------+
* | struct wiphy        |
* +-------------------------+
* | struct ieee80211_local  |
* +-------------------------+
* | driver""s private data   |
* +-------------------------+
*
*/
priv_size = ALIGN(sizeof(*local), NETDEV_ALIGN) + priv_data_len;
wiphy = wiphy_new(&mac80211_config_ops, priv_size);

别的还有tasklet初始化的代码,是跟接管数据慎密相干的:

tasklet_init(&local->tasklet,
ieee80211_tasklet_handler,
(unsigned long) local);

在后面一节谈数据接管的时辰会胪陈。

在brcms_bcma_probe()的函数体中,完成了ieee80211_alloc_hw()之后的另一行首要代码就是:

wl = brcms_attach(pdev);

3. 接着解析brcms_attach()

3.1 起首初始化Tasklet

/* setup the bottom half handler */
tasklet_init(&wl->tasklet, brcms_dpc, (unsigned long) wl);

这里可以看到中断处理惩罚函数是brcms_dpc().

 

3.2 Download firmware

/* prepare ucode */
if (brcms_request_fw(wl, pdev) < 0) {
wiphy_err(wl->wiphy, "%s: Failed to find firmware usually in "
"%s\n", KBUILD_MODNAME, "/lib/firmware/brcm");
brcms_release_fw(wl);
brcms_remove(pdev);
return NULL;
}

3.3 初始化硬件

/*
 * low level attach steps(all hw accesses go
 * inside, no more in rest of the attach)
*/
err = brcms_b_attach(wlc, core, unit, piomode);

3.4 申请中断

/* register our interrupt handler */
if (request_irq(pdev->irq, brcms_isr,
IRQF_SHARED, KBUILD_MODNAME, wl)) {
wiphy_err(wl->wiphy, "wl%d: request_irq() failed\n", unit);
goto fail;
}
那么傍边断产生时比如稀有据过来须要接管时,brcms_isr()就会被触发。

3.5 注册设备

err = ieee80211_register_hw(hw);
if (err)
wiphy_err(wl->wiphy, "%s: ieee80211_register_hw failed, status"
"%d\n", __func__, err);

ieee80211_register_hw()将会调用到ieee80211_if_add()来注册网卡设备

经由过程ieee80211_if_add()的代码可以看到这里终于调用了熟悉的register_netdevice()

int ieee80211_if_add(struct ieee80211_local *local, constchar *name,
struct wireless_dev **new_wdev, enum nl80211_iftype type,
struct vif_params *params){
...
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;
}
}
...
}

如今我们来看看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.
*/
staticvoid 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()

staticvoid
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  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()

staticvoid ieee80211_tasklet_handler(unsigned long data)
{
struct ieee80211_local *local = (struct ieee80211_local *) data;
struct sta_info *sta, *tmp;
struct skb_eos p_msg_data *eos p_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  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()

staticvoid __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;
gotoout;
}
}
...
}

可以看到若是是probe response或者beacon如许的frame, 会有别的的处理惩罚。我们这里只解析是data的景象。

11. 调用ieee80211_prepare_and_rx_handle()

staticbool ieee80211_prepare_and_rx_handle(struct ieee80211_rx_data *rx,
struct sk_buff *skb, bool consume)
{
    ...
    ieee80211_invoke_rx_handlers(rx);
returntrue;
}

12. 调用ieee80211_invoke_rx_handlers()


staticvoid ieee80211_invoke_rx_handlers(struct ieee80211_rx_data *rx)
{
    ...
    ieee80211_rx_handlers(rx, &reorder_release);
return;
    ...
}

13. 调用ieee80211_rx_handlers()


staticvoid 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  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
*/
staticvoid
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 kernel发送数据的接口函数是packet_sendmsg,本质上对应了users pace的sendmsg实现。
比如在wpa_supplicant中,wpa_driver_nl80211_send_frame()就是用sendmsg发送数据的:

staticint wpa_driver_nl80211_send_frame(struct wpa_driver_nl80211_data *drv,
constvoid *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;
}
return0;
}
1. 起首来看packet_sendmsg()的实现
staticint 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()

staticint packet_snd(struct socket *sock,
struct msghdr *msg, size_t len)
{
...
// 起首把数据从user space拷贝到kernel space
err = memcpy_iovec((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  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__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);
gotoout;
}
...
}

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;
} elseif ((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_(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_(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()

staticvoid 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()的定义如下:

staticconststruct 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__queue    = ieee80211_netdev__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()

staticbool 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()

staticbool __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()

staticbool 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);
}

returntrue;
}

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()

staticvoid 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))
returntrue;

/* packet discarded */
dev_kfree_skb_any(sdu);
returnfalse;
}

你可能感兴趣的:(linux)