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; } } ... }