Broadcom fullmac WLAN 驱动解析(1)

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

posted on 2013-04-14 22:14  hellolwl  阅读(6013)  评论(1编辑  收藏  举报

导航