天天看點

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;

}