ath9K 驱动注册过程

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文件中): 
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);
至此,设备启动,驱动程序成功加载到设备上。 
  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值