天天看点

linux gmac驱动分析,Broadcom mac80211 驱动解析

1. 驱动的主进口如下:

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出来

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

tasklet_init(&wl->tasklet, brcms_dpc, (unsigned long) wl);

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

3.2 Download firmware

if (brcms_request_fw(wl, pdev) 

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 初始化硬件

err = brcms_b_attach(wlc, core, unit, piomode);

3.4 申请中断

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

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

{

...

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

bool brcms_c_dpc(struct brcms_c_info *wlc, bool bounded)

{

...

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

do {

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

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

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

len_mpdu = p->len - D11_PHY_HDR_LEN - FCS_LEN;

skb_pull(p, D11_PHY_HDR_LEN);

__skb_trim(p, len_mpdu);

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

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:

skb->pkt_type = 0;

ieee80211_rx(&local->hw, skb);

break;

...

}

}

}

这里我们只关怀IEEE80211_RX_MSG类型的处理惩罚。

9. 调用ieee80211_rx(), 终于走到了rx.c

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

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)

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

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

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 = ((unsigned long)(skb->data + sizeof(struct ethhdr))) & 3;

if (align) {

if (WARN_ON(skb_headroom(skb) 

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

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 

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

...

// 然后用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()

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

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

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

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

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)

{

...

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

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;

dev_kfree_skb_any(sdu);

returnfalse;

}