ath9K 驱动注册过程
2017-02-17 15:29
239 查看
http://blog.csdn.net/ussam/article/details/24393267
这里使用的是ath9k网卡驱动,硬件平台是TP-link TL-WR841N V7.1 路由器
1 ath_pci_init()
驱动的主入口为ath_pci_init()(位于linux-3.3.8 /drivers/net/wireless/ath/ath9k文件夹下的pci.c文件中):
可以看到ath_pci_driver作为参数传递给了pci_register_driver ()。ath_pci_driver的定义如下(位于linux-3.3.8
/drivers/net/wireless/ath/ath9k文件夹下的pci.c文件中):
2 ath_pci_probe()
紧接着,系统会通过probe指针来调用ath_pci_probe函数(ath_pci_probe函数定义于linux-3.3.8 /drivers/net/wireless/ath/ath9k文件夹下的pci.c文件中)。
3 ieee80211_alloc_hw()
注意ath_pci_probe()函数体中的这行代码:
hw = ieee80211_alloc_hw(sizeof(struct ath_softc), &ath9k_ops);
函数ieee80211_alloc_hw()定义于OpenWRT内核文件夹子目录/net/mac80211,文件main.c中,这里传入的参数是结构体ath9k_ops,
该结构体的定义如下(位于linux-3.3.8 /drivers/net/wireless/ath/ath9k文件夹下的main.c文件中):
将参数ath9k_ops传输ieee80211_alloc_hw()函数以后,系统将可以使用参数ath9k_ops中定义的函数指针。以tx为例,在tx.c中将会用到该函数指针。
下面再来看看ieee80211_alloc_hw()函数:
4 ath9k_init_device()
在ath_pci_probe()的函数体中,完成了ieee80211_alloc_hw()之后的另一行重要代码就是:
ret = ath9k_init_device(id->device, sc, &ath_pci_bus_ops);
接着分析ath9k_init_device ()(位于OpenWRT内核文件夹子目录/net/mac80211,文件init.c中),观察设备的初始化过程。
4.1 首先初始化softc
在这个过程中会初始化tasklet(包括beacon帧的tasklet)
4.2 初始化数据收发存储器访问
4.3 注册设备
至此,设备启动,驱动程序成功加载到设备上。
这里使用的是ath9k网卡驱动,硬件平台是TP-link TL-WR841N V7.1 路由器
1 ath_pci_init()
驱动的主入口为ath_pci_init()(位于linux-3.3.8 /drivers/net/wireless/ath/ath9k文件夹下的pci.c文件中):
int ath_pci_init(void) { return pci_register_driver(&ath_pci_driver); }
可以看到ath_pci_driver作为参数传递给了pci_register_driver ()。ath_pci_driver的定义如下(位于linux-3.3.8
/drivers/net/wireless/ath/ath9k文件夹下的pci.c文件中):
static struct pci_driver ath_pci_driver = { .name = "ath9k", .id_table = ath_pci_id_table, .probe = ath_pci_probe, .remove = ath_pci_remove, .driver.pm = ATH9K_PM_OPS, };
2 ath_pci_probe()
紧接着,系统会通过probe指针来调用ath_pci_probe函数(ath_pci_probe函数定义于linux-3.3.8 /drivers/net/wireless/ath/ath9k文件夹下的pci.c文件中)。
static int ath_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id) { … hw = ieee80211_alloc_hw(sizeof(struct ath_softc), &ath9k_ops); if (!hw) { dev_err(&pdev->dev, "No memory for ieee80211_hw\n"); ret = -ENOMEM; goto err_alloc_hw; } SET_IEEE80211_DEV(hw, &pdev->dev); pci_set_drvdata(pdev, hw); sc = hw->priv; sc->hw = hw; sc->dev = &pdev->dev; sc->mem = mem; /* Will be cleared in ath9k_start() */ sc->sc_flags |= SC_OP_INVALID; ret = request_irq(pdev->irq, ath_isr, IRQF_SHARED, "ath9k", sc); if (ret) { dev_err(&pdev->dev, "request_irq failed\n"); goto err_irq; } sc->irq = pdev->irq; ret = ath9k_init_device(id->device, sc, &ath_pci_bus_ops); if (ret) { dev_err(&pdev->dev, "Failed to initialize device\n"); goto err_init; } … }
3 ieee80211_alloc_hw()
注意ath_pci_probe()函数体中的这行代码:
hw = ieee80211_alloc_hw(sizeof(struct ath_softc), &ath9k_ops);
函数ieee80211_alloc_hw()定义于OpenWRT内核文件夹子目录/net/mac80211,文件main.c中,这里传入的参数是结构体ath9k_ops,
该结构体的定义如下(位于linux-3.3.8 /drivers/net/wireless/ath/ath9k文件夹下的main.c文件中):
struct ieee80211_ops ath9k_ops = { .tx = ath9k_tx, .start = ath9k_start, .stop = ath9k_stop, .add_interface = ath9k_add_interface, .change_interface = ath9k_change_interface, .remove_interface = ath9k_remove_interface, .config = ath9k_config, .configure_filter = ath9k_configure_filter, .sta_add = ath9k_sta_add, .sta_remove = ath9k_sta_remove, .sta_notify = ath9k_sta_notify, .conf_tx = ath9k_conf_tx, .bss_info_changed = ath9k_bss_info_changed, .set_key = ath9k_set_key, .get_tsf = ath9k_get_tsf, .set_tsf = ath9k_set_tsf, .reset_tsf = ath9k_reset_tsf, .ampdu_action = ath9k_ampdu_action, .get_survey = ath9k_get_survey, .rfkill_poll = ath9k_rfkill_poll_state, .set_coverage_class = ath9k_set_coverage_class, .flush = ath9k_flush, .tx_frames_pending = ath9k_tx_frames_pending, .tx_last_beacon = ath9k_tx_last_beacon, .get_stats = ath9k_get_stats, .set_antenna = ath9k_set_antenna, .get_antenna = ath9k_get_antenna, };
将参数ath9k_ops传输ieee80211_alloc_hw()函数以后,系统将可以使用参数ath9k_ops中定义的函数指针。以tx为例,在tx.c中将会用到该函数指针。
下面再来看看ieee80211_alloc_hw()函数:
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就变成传进来的参数ath9k_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; } EXPORT_SYMBOL(ieee80211_alloc_hw);
4 ath9k_init_device()
在ath_pci_probe()的函数体中,完成了ieee80211_alloc_hw()之后的另一行重要代码就是:
ret = ath9k_init_device(id->device, sc, &ath_pci_bus_ops);
接着分析ath9k_init_device ()(位于OpenWRT内核文件夹子目录/net/mac80211,文件init.c中),观察设备的初始化过程。
int ath9k_init_device(u16 devid, struct ath_softc *sc, const struct ath_bus_ops *bus_ops) { struct ieee80211_hw *hw = sc->hw; struct ath_common *common; struct ath_hw *ah; int error = 0; struct ath_regulatory *reg; /* Bring up device */ error = ath9k_init_softc(devid, sc, bus_ops); if (error != 0) goto error_init; ah = sc->sc_ah; common = ath9k_hw_common(ah); ath9k_set_hw_capab(sc, hw); /* Initialize regulatory */ error = ath_regd_init(&common->regulatory, sc->hw->wiphy, ath9k_reg_notifier); if (error) goto error_regd; reg = &common->regulatory; /* Setup TX DMA */ error = ath_tx_init(sc, ATH_TXBUF); if (error != 0) goto error_tx; /* Setup RX DMA */ error = ath_rx_init(sc, ATH_RXBUF); if (error != 0) goto error_rx; ath9k_init_txpower_limits(sc); … error = ieee80211_register_hw(hw); if (error) goto error_register; error = ath9k_init_debug(ah); if (error) { ath_err(common, "Unable to create debugfs files\n"); goto error_world; } … }
4.1 首先初始化softc
static int ath9k_init_softc(u16 devid, struct ath_softc *sc, const struct ath_bus_ops *bus_ops) { … tasklet_init(&sc->intr_tq, ath9k_tasklet, (unsigned long)sc); tasklet_init(&sc->bcon_tasklet, ath_beacon_tasklet, (unsigned long)sc); … }
在这个过程中会初始化tasklet(包括beacon帧的tasklet)
4.2 初始化数据收发存储器访问
/* Setup TX DMA */ error = ath_tx_init(sc, ATH_TXBUF); if (error != 0) goto error_tx; /* Setup RX DMA */ error = ath_rx_init(sc, ATH_RXBUF); if (error != 0) goto error_rx;
4.3 注册设备
error = ieee80211_register_hw(hw);
至此,设备启动,驱动程序成功加载到设备上。
相关文章推荐
- linux powerpc i2c驱动 之 i2c设备层的注册过程
- 平台设备与平台驱动注册过程platform_driver_register与platform_device_register
- 设备驱动注册过程
- platform_driver平台驱动注册和注销过程
- linux 内核驱动--Platform Device和Platform_driver注册过程
- linux驱动由浅入深系列:驱动程序的基本结构概览之二(详解驱动注册过程)
- linux下MMC/SD/SDIO驱动系列之二 ---- host注册过程(一)
- Linux驱动学习之---平台驱动的注册过程(转载)
- zturn开发板网口驱动的注册过程
- linux 内核驱动--Platform Device和Platform_driver注册过程
- linux下MMC/SD/SDIO驱动系列之二 ---- host注册过程(一)
- 网卡驱动注册到PCI总线这一过程的分析一
- linux I2C 驱动之----i2c驱动的注册过程(i2c_register_driver->driver_register(&driver->driver)->driver_find)
- linux下MMC/SD/SDIO驱动系列之二 ---- host注册过程(一)
- linux 内核驱动--Platform Device和Platform_driver注册过程
- linux下MMC/SD/SDIO驱动系列之三 ---- host注册过程(二)
- intel dpdk api pci设备驱动注册和初始化过程
- 驱动的注册过程和设备的注册过程(转载)
- zturn开发板网口驱动的注册过程
- 关于PHP-Zend framework2 框架 学习过程。 阅前须知: ZF2中的配置文件是可以静态文件配置来注册和通过相关函数动态注册。 1.EventManager(事件驱动),关于事件驱动,在ZF2相关资料没有详细说明,可以参考ANDROID的事件驱动,MFC的消息响应/事件驱动。