Broadcom softmac WLAN 驱动解析(1)

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.
 */
static void 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的定义如下:

static struct 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将会被调用。 

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

    return 0;
}

 注意brcms_bcma_probe()函数体中的这行代码:

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

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

static const struct 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. 

struct ieee80211_hw *ieee80211_alloc_hw(size_t priv_data_len,
                    const struct ieee80211_ops *ops)
{
    struct ieee80211_local *local;
    int priv_size, i;
    struct wiphy *wiphy;
    bool use_chanctx;
    ...
    /* 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;

    // 注意这里的mac80211_config_ops
    // 以其中的scan成员为例,scan时就会触发对应的函数ieee80211_scan()
    wiphy = wiphy_new(&mac80211_config_ops, priv_size); // 注意这里的mac80211_config_ops

    if (!wiphy)
        return NULL;
    ...
    // 这里的local->ops就变成传进来的参数brcms_ops
    local->ops = ops;
    ...
    // 这个工作队列很重要, sw scan的情况下会schedule这个工作队列来做scan
    INIT_DELAYED_WORK(&local->scan_work, ieee80211_scan_work);
    ...
    // 这个tasklet跟接受数据紧密相关,以后会提到
    tasklet_init(&local->tasklet,
             ieee80211_tasklet_handler,
             (unsigned long) local);
    ..
    return &local->hw;
}

 在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, const char *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;
        }
    }
    ...
}

转载于:https://www.cnblogs.com/hellolwl/archive/2013/04/10/3012569.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值