在https://wiki.archlinux.org/index.php/Broadcom_wireless里面關於broadcom的wireless驅動有這樣的說明
The brcm80211 drivers are included in the kernel. They are named brcmsmac for PCI cards and brcmfmac for SDIO devices.
這里我們分析針對SDIO設備的brcmfmac驅動。
1. 驅動主入口
void brcmf_sdio_init(void) { int ret; brcmf_dbg(TRACE, "Enter\n"); ret = sdio_register_driver(&brcmf_sdmmc_driver); if (ret) brcmf_err("sdio_register_driver failed: %d\n", ret); }
可以看到brcmf_sdmmc_driver被傳給了sdio_register_driver(), brcmf_sdmmc_driver的定義如下:
static struct sdio_driver brcmf_sdmmc_driver = { .probe = brcmf_ops_sdio_probe, // probe函數很關鍵! .remove = brcmf_ops_sdio_remove, .name = "brcmfmac", .id_table = brcmf_sdmmc_ids, #ifdef CONFIG_PM_SLEEP .drv = { .pm = &brcmf_sdio_pm_ops, }, #endif /* CONFIG_PM_SLEEP */ };
2. brcmf_ops_sdio_probe()被調用
關於probe函數是如何調用的,網上的文章很多,這里就再不贅述了。
static int brcmf_ops_sdio_probe(struct sdio_func *func, const struct sdio_device_id *id) { int err; struct brcmf_sdio_dev *sdiodev; struct brcmf_bus *bus_if; ... /* Consume func num 1 but dont do anything with it. */ if (func->num == 1) return 0; /* Ignore anything but func 2 */ if (func->num != 2) return -ENODEV; bus_if = kzalloc(sizeof(struct brcmf_bus), GFP_KERNEL); if (!bus_if) return -ENOMEM; sdiodev = kzalloc(sizeof(struct brcmf_sdio_dev), GFP_KERNEL); if (!sdiodev) { kfree(bus_if); return -ENOMEM; } sdiodev->func[0] = func->card->sdio_func[0]; sdiodev->func[1] = func->card->sdio_func[0]; sdiodev->func[2] = func; sdiodev->bus_if = bus_if; bus_if->bus_priv.sdio = sdiodev; bus_if->align = BRCMF_SDALIGN; dev_set_drvdata(&func->dev, bus_if); dev_set_drvdata(&sdiodev->func[1]->dev, bus_if); sdiodev->dev = &sdiodev->func[1]->dev; ... brcmf_dbg(TRACE, "F2 found, calling brcmf_sdio_probe...\n"); // 注意這里 err = brcmf_sdio_probe(sdiodev); if (err) { brcmf_err("F2 error, probe failed %d...\n", err); goto fail; } brcmf_dbg(TRACE, "F2 init completed...\n"); return 0; ... }
3. 調用brcmf_sdio_probe()
int brcmf_sdio_probe(struct brcmf_sdio_dev *sdiodev) { u32 regs = 0; int ret = 0; ret = brcmf_sdioh_attach(sdiodev); if (ret) goto out; regs = SI_ENUM_BASE; /* try to attach to the target device */ sdiodev->bus = brcmf_sdbrcm_probe(regs, sdiodev); if (!sdiodev->bus) { brcmf_err("device attach failed\n"); ret = -ENODEV; goto out; } out: if (ret) brcmf_sdio_remove(sdiodev); return ret; }
4. 調用brcmf_sdbrcm_probe()
void *brcmf_sdbrcm_probe(u32 regsva, struct brcmf_sdio_dev *sdiodev) { int ret; struct brcmf_sdio *bus; struct brcmf_bus_dcmd *dlst; u32 dngl_txglom; u32 dngl_txglomalign; u8 idx; brcmf_dbg(TRACE, "Enter\n"); /* We make an assumption about address window mappings: * regsva == SI_ENUM_BASE*/ /* Allocate private bus interface state */ bus = kzalloc(sizeof(struct brcmf_sdio), GFP_ATOMIC); if (!bus) goto fail; bus->sdiodev = sdiodev; sdiodev->bus = bus; skb_queue_head_init(&bus->glom); bus->txbound = BRCMF_TXBOUND; bus->rxbound = BRCMF_RXBOUND; bus->txminmax = BRCMF_TXMINMAX; bus->tx_seq = SDPCM_SEQUENCE_WRAP - 1; // 初始化工作隊列,用來接收/發送數據 INIT_WORK(&bus->datawork, brcmf_sdio_dataworker); bus->brcmf_wq = create_singlethread_workqueue("brcmf_wq"); if (bus->brcmf_wq == NULL) { brcmf_err("insufficient memory to create txworkqueue\n"); goto fail; } /* attempt to attach to the dongle */ if (!(brcmf_sdbrcm_probe_attach(bus, regsva))) { brcmf_err("brcmf_sdbrcm_probe_attach failed\n"); goto fail; } ... /* Assign bus interface call back */ bus->sdiodev->bus_if->dev = bus->sdiodev->dev; // 注意這里的brcmf_sdio_bus_ops bus->sdiodev->bus_if->ops = &brcmf_sdio_bus_ops; bus->sdiodev->bus_if->chip = bus->ci->chip; bus->sdiodev->bus_if->chiprev = bus->ci->chiprev; /* Attach to the brcmf/OS/network interface */ ret = brcmf_attach(SDPCM_RESERVE, bus->sdiodev->dev); if (ret != 0) { brcmf_err("brcmf_attach failed\n"); goto fail; } /* Allocate buffers */ if (!(brcmf_sdbrcm_probe_malloc(bus))) { brcmf_err("brcmf_sdbrcm_probe_malloc failed\n"); goto fail; } if (!(brcmf_sdbrcm_probe_init(bus))) { brcmf_err("brcmf_sdbrcm_probe_init failed\n"); goto fail; } ... /* if firmware path present try to download and bring up bus */ ret = brcmf_bus_start(bus->sdiodev->dev); // 非常重要! if (ret != 0) { brcmf_err("dongle is not responding\n"); goto fail; } return bus; ... }
brcmf_sdio_bus_ops的定義如下:
static struct brcmf_bus_ops brcmf_sdio_bus_ops = { .stop = brcmf_sdbrcm_bus_stop, .init = brcmf_sdbrcm_bus_init, .txdata = brcmf_sdbrcm_bus_txdata, .txctl = brcmf_sdbrcm_bus_txctl, .rxctl = brcmf_sdbrcm_bus_rxctl, };
5. 調用brcmf_bus_start()
int brcmf_bus_start(struct device *dev) { int ret = -1; struct brcmf_bus *bus_if = dev_get_drvdata(dev); struct brcmf_pub *drvr = bus_if->drvr; struct brcmf_if *ifp; struct brcmf_if *p2p_ifp; brcmf_dbg(TRACE, "\n"); /* Bring up the bus */ // 這里做初始化,request_irq等等 ret = brcmf_bus_init(bus_if); if (ret != 0) { brcmf_err("brcmf_sdbrcm_bus_init failed %d\n", ret); return ret; } /* add primary networking interface */ // 注冊網卡 ifp = brcmf_add_if(drvr, 0, 0, "wlan%d", NULL); if (IS_ERR(ifp)) return PTR_ERR(ifp); if (brcmf_p2p_enable) p2p_ifp = brcmf_add_if(drvr, 1, 0, "p2p%d", NULL); else p2p_ifp = NULL; if (IS_ERR(p2p_ifp)) p2p_ifp = NULL; /* signal bus ready */ bus_if->state = BRCMF_BUS_DATA; /* Bus is ready, do any initialization */ ret = brcmf_c_preinit_dcmds(ifp); if (ret < 0) goto fail; drvr->fw_signals = true; (void)brcmf_fws_init(drvr); // 非常重要 drvr->config = brcmf_cfg80211_attach(drvr, bus_if->dev); if (drvr->config == NULL) { ret = -ENOMEM; goto fail; } ret = brcmf_fweh_activate_events(ifp); if (ret < 0) goto fail; // 注冊net_device ret = brcmf_net_attach(ifp, false); ... return 0; }
5.1 先來看一下brcmf_bus_init()
static inline int brcmf_bus_init(struct brcmf_bus *bus) { return bus->ops->init(bus->dev); }
顯然,由brcmf_sdio_bus_ops的定義可知,brcmf_sdbrcm_bus_init()在這里被觸發
static int brcmf_sdbrcm_bus_init(struct device *dev) { struct brcmf_bus *bus_if = dev_get_drvdata(dev); struct brcmf_sdio_dev *sdiodev = bus_if->bus_priv.sdio; struct brcmf_sdio *bus = sdiodev->bus; unsigned long timeout; u8 ready, enable; int err, ret = 0; u8 saveclk; brcmf_dbg(TRACE, "Enter\n"); /* try to download image and nvram to the dongle */ if (bus_if->state == BRCMF_BUS_DOWN) { // 下載firmware: // brcmf_sdbrcm_download_firmware() -> // _brcmf_sdbrcm_download_firmware() -> // brcmf_sdbrcm_download_code_file() -> // request_firmware() if (!(brcmf_sdbrcm_download_firmware(bus))) return -1; } ... if (ret == 0) { //注冊/申請中斷 ret = brcmf_sdio_intr_register(bus->sdiodev); if (ret != 0) brcmf_err("intr register failed:%d\n", ret); } ... return ret; }
這里有必要跟一下brcmf_sdio_intr_register(),因為這涉及到如何接收數據:
int brcmf_sdio_intr_register(struct brcmf_sdio_dev *sdiodev) { int ret = 0; u8 data; unsigned long flags; brcmf_dbg(TRACE, "Entering: irq %d\n", sdiodev->irq); // 注意這里傳給request_irq的參數 ret = request_irq(sdiodev->irq, brcmf_sdio_irqhandler, sdiodev->irq_flags, "brcmf_oob_intr", &sdiodev->func[1]->dev); ... return 0; }
當有中斷產生時,比如數據到來時,brcmf_sdio_irqhandler()將會以下面的順序處理:
brcmf_sdio_irqhandler() -> brcmf_sdbrcm_isr() -> queue_work(bus->brcmf_wq, &bus->datawork);
根據前面4.的分析,brcmf_sdio_dataworker()會被觸發,從而有下面的call flow:
brcmf_sdio_dataworker() -> brcmf_sdbrcm_dpc() -> brcmf_sdio_readframes()
繼續跟一下brcmf_sdio_readframes()
static uint brcmf_sdio_readframes(struct brcmf_sdio *bus, uint maxframes) { ... for (rd->seq_num = bus->rx_seq, rxleft = maxframes; !bus->rxskip && rxleft && bus->sdiodev->bus_if->state != BRCMF_BUS_DOWN; rd->seq_num++, rxleft--) { ... rd->len_left = rd->len; /* read header first for unknow frame length */ sdio_claim_host(bus->sdiodev->func[1]); if (!rd->len) { // 從SDIO接收的數據保存到buf ret = brcmf_sdcard_recv_buf(bus->sdiodev, bus->sdiodev->sbwad, SDIO_FUNC_2, F2SYNC, bus->rxhdr, BRCMF_FIRSTREAD); ... } ... // 根據buf中的數據生成skb pkt = brcmu_pkt_buf_get_skb(rd->len_left + head_read + BRCMF_SDALIGN); ... skb_queue_head_init(&pktlist); // 放入pktlist skb_queue_tail(&pktlist, pkt); // 調用brcmf_rx_frames()去處理pktlist brcmf_rx_frames(bus->sdiodev->dev, &pktlist); } ... }
brcmf_rx_frames會直接調用netif_rx()把數據發往kernel的網絡子系統。
5.2 再來看一下brcmf_add_if()
struct brcmf_if *brcmf_add_if(struct brcmf_pub *drvr, s32 bssidx, s32 ifidx, char *name, u8 *mac_addr) { struct brcmf_if *ifp; struct net_device *ndev; brcmf_dbg(TRACE, "Enter, idx=%d, ifidx=%d\n", bssidx, ifidx); ifp = drvr->iflist[bssidx]; ... /* Allocate netdev, including space for private structure */ // 分配net_device結構體 ndev = alloc_netdev(sizeof(struct brcmf_if), name, ether_setup); if (!ndev) { brcmf_err("OOM - alloc_netdev\n"); return ERR_PTR(-ENOMEM); } ifp = netdev_priv(ndev); ifp->ndev = ndev; ifp->drvr = drvr; drvr->iflist[bssidx] = ifp; ifp->ifidx = ifidx; ifp->bssidx = bssidx; ... return ifp; }
這里最主要就是分配了net_device結構體。
5.3 分析一下brcmf_cfg80211_attach()
struct brcmf_cfg80211_info *brcmf_cfg80211_attach(struct brcmf_pub *drvr, struct device *busdev) { struct net_device *ndev = drvr->iflist[0]->ndev; struct brcmf_cfg80211_info *cfg; struct wiphy *wiphy; struct brcmf_cfg80211_vif *vif; struct brcmf_if *ifp; s32 err = 0; ... ifp = netdev_priv(ndev); // 非常重要,創建一個新的wiphy並且關聯上cfg80211_ops wiphy = brcmf_setup_wiphy(busdev); if (IS_ERR(wiphy)) return NULL; cfg = wiphy_priv(wiphy); cfg->wiphy = wiphy; cfg->pub = drvr; init_vif_event(&cfg->vif_event); INIT_LIST_HEAD(&cfg->vif_list); vif = brcmf_alloc_vif(cfg, NL80211_IFTYPE_STATION, false); if (IS_ERR(vif)) { wiphy_free(wiphy); return NULL; } vif->ifp = ifp; vif->wdev.netdev = ndev; ndev->ieee80211_ptr = &vif->wdev; SET_NETDEV_DEV(ndev, wiphy_dev(cfg->wiphy)); // wl_init_priv將會調用brcmf_register_event_handlers() // 從而為firemware傳上來的事件注冊相應的handler err = wl_init_priv(cfg); ... }
跟一下brcmf_setup_wiphy():
static struct wiphy *brcmf_setup_wiphy(struct device *phydev) { ... // 注意wl_cfg80211_ops,以rdev_scan()函數為例,其中的rdev->ops->scan將會觸發brcmf_cfg80211_scan() wiphy = wiphy_new(&wl_cfg80211_ops, sizeof(struct brcmf_cfg80211_info)); if (!wiphy) { brcmf_err("Could not allocate wiphy device\n"); return ERR_PTR(-ENOMEM); } set_wiphy_dev(wiphy, phydev); ... err = wiphy_register(wiphy); if (err < 0) { brcmf_err("Could not register wiphy device (%d)\n", err); wiphy_free(wiphy); return ERR_PTR(err); } return wiphy; }
可以看到跟ieee80211_alloc_hw()里面的代碼是很類似的,可以參見http://www.cnblogs.com/hellolwl/archive/2013/04/10/3012569.html中的描述。
5.4 跟進brcmf_net_attach()
int brcmf_net_attach(struct brcmf_if *ifp, bool rtnl_locked) { struct brcmf_pub *drvr = ifp->drvr; struct net_device *ndev; s32 err; brcmf_dbg(TRACE, "Enter, idx=%d mac=%pM\n", ifp->bssidx, ifp->mac_addr); ndev = ifp->ndev; /* set appropriate operations */ // 注意這里的brcmf_netdev_ops_pri ndev->netdev_ops = &brcmf_netdev_ops_pri; ndev->hard_header_len = ETH_HLEN + drvr->hdrlen; ndev->ethtool_ops = &brcmf_ethtool_ops; drvr->rxsz = ndev->mtu + ndev->hard_header_len + drvr->hdrlen; /* set the mac address */ memcpy(ndev->dev_addr, ifp->mac_addr, ETH_ALEN); INIT_WORK(&ifp->setmacaddr_work, _brcmf_set_mac_address); INIT_WORK(&ifp->multicast_work, _brcmf_set_multicast_list); // 注冊net_device if (rtnl_locked) err = register_netdevice(ndev); else err = register_netdev(ndev); if (err != 0) { brcmf_err("couldn't register the net device\n"); goto fail; } brcmf_dbg(INFO, "%s: Broadcom Dongle Host Driver\n", ndev->name); return 0; ... }
根據5.2的分析,net_device已經在之前分配好了,這里就是注冊net_device. 再看一下brcmf_netdev_ops_pri()的定義:
static const struct net_device_ops brcmf_netdev_ops_pri = { .ndo_open = brcmf_netdev_open, .ndo_stop = brcmf_netdev_stop, .ndo_get_stats = brcmf_netdev_get_stats, .ndo_do_ioctl = brcmf_netdev_ioctl_entry, .ndo_start_xmit = brcmf_netdev_start_xmit, .ndo_set_mac_address = brcmf_netdev_set_mac_address, .ndo_set_rx_mode = brcmf_netdev_set_multicast_list };
以ndo_start_xmit()為例,kernel發送數據時將會調用ndo_start_xmit. 那么brcmf_netdev_start_xmit()就會被觸發。這里順便分析一下發送數據的流程:
static netdev_tx_t brcmf_netdev_start_xmit(struct sk_buff *skb, struct net_device *ndev) { ... /* Use bus module to send data frame */ ret = brcmf_bus_txdata(drvr->bus_if, skb); done: if (ret) { ifp->stats.tx_dropped++; } else { ifp->stats.tx_packets++; ifp->stats.tx_bytes += skb->len; } /* Return ok: we always eat the packet */ return NETDEV_TX_OK; }
調用brcmf_bus_tcxdata():
static inline int brcmf_bus_txdata(struct brcmf_bus *bus, struct sk_buff *skb) { return bus->ops->txdata(bus->dev, skb); }
這里bus->ops->txdata就會調用brcmf_sdbrcm_bus_txdata(),參見brcmf_sdio_bus_ops的定義。
static int brcmf_sdbrcm_bus_txdata(struct device *dev, struct sk_buff *pkt) { ... spin_lock_irqsave(&bus->dpc_tl_lock, flags); if (list_empty(&bus->dpc_tsklst)) { spin_unlock_irqrestore(&bus->dpc_tl_lock, flags); brcmf_sdbrcm_adddpctsk(bus); // 注意這里,brcmf_sdio_dataworker()將會被觸發 queue_work(bus->brcmf_wq, &bus->datawork); } else { spin_unlock_irqrestore(&bus->dpc_tl_lock, flags); } return ret; }
brcmf_sdio_dataworker()又會調用brcmf_sdbrcm_dpc():
static void brcmf_sdbrcm_dpc(struct brcmf_sdio *bus) { ... /* Send queued frames (limit 1 if rx may still be pending) */ else if ((bus->clkstate == CLK_AVAIL) && !atomic_read(&bus->fcstate) && brcmu_pktq_mlen(&bus->txq, ~bus->flowcontrol) && txlimit && data_ok(bus)) { framecnt = bus->rxpending ? min(txlimit, bus->txminmax) : txlimit; // 注意這里 framecnt = brcmf_sdbrcm_sendfromq(bus, framecnt); txlimit -= framecnt; } ... }
調用brcmf_sdbrcm_sendfromq()
static uint brcmf_sdbrcm_sendfromq(struct brcmf_sdio *bus, uint maxframes) { ... /* Send frames until the limit or some other event */ for (cnt = 0; (cnt < maxframes) && data_ok(bus); cnt++) { ... ret = brcmf_sdbrcm_txpkt(bus, pkt, SDPCM_DATA_CHANNEL); ... } ... }
接下來就有這樣的調用順序:
brcmf_sdbrcm_txpkt() -> brcmf_sdcard_send_pkt() -> brcmf_sdioh_request_buffer() -> brcmf_sdioh_request_data() -> sdio_memcpy_toio() -> sdio_io_rw_ext_helper()
順便提一下,brcmf_sdbrcm_bus_txctl()跟brcmf_sdbrcm_bus_txdata()在call flow上是有區別的。
brcmf_sdbrcm_bus_txctl() -> brcmf_tx_frame() -> brcmf_sdcard_send_buf() -> brcmf_sdcard_send_pkt() -> ...