ATH9K驱动学习
==
ATH9K驱动结构
--
**首先,借用前人的一张图**[图片来源](http://www.tuicool.com/articles/2uiaUfv)
![Linux网线网络结构示意图](!web)
* .1首先,从底层的WiFi driver开始看,打开ATH9k的源代码目录,林林总总有数十个文件,ath9k的驱动入口有俩个,实现在在ahb.c和pci.c中,分别对应USB接口和PCI接口的无线网卡,驱动按照linux platform device的方式实现,以USB接口的驱动为例
```
static struct platform_driver ath_ahb_driver = {
.probe = ath_ahb_probe,
.remove = ath_ahb_remove,
.driver = {
.name = "ath9k",
.owner = THIS_MODULE,
},
.id_table = ath9k_platform_id_table,
};
int ath_ahb_init(void)
{
return platform_driver_register(&ath_ahb_driver);
}
void ath_ahb_exit(void)
{
platform_driver_unregister(&ath_ahb_driver);
}
```
在ahb.c中,注册了一个名为ath9k的platform driver,一旦检测到对应device插入,就会马上调用ath_ahb_probe函数,在该函数中,会完善俩个结构体,第一个是描述设备的结构体sc,描述硬件的结构体hw,这俩个结构体的具体内容可以自己查看源码,在得到这俩个结构体后,调用ath9k_init_device完成剩下的工作
```
static int ath_ahb_probe(struct platform_device *pdev)
{
struct ath_softc *sc;
struct ieee80211_hw *hw;
......
hw = ieee80211_alloc_hw(sizeof(struct ath_softc), &ath9k_ops);
......
ret = ath9k_init_device(id->driver_data, sc, &ath_ahb_bus_ops);
......
}
```
在ath9k_init_device中,会调用ieee80211_register_hw函数,该函数最终会调用 ieee80211_if_add函数向内核注册一个名为wlanX的net设备,并将该设备添加到全局网络,如果该函数成功执行的话,此时就能用ifconfig命令看到wlanX的网卡了,在注册成功后,会调用ath_start_rfkill_poll函数,该函数会向内核请求一个work,该work的内容就是不断接收来自空中的信号,此时,如果你的一切配置OK的话,拿出手机,你就可以搜索到配置好的WiFi了。
```
int ath9k_init_device(u16 devid, struct ath_softc *sc,const struct ath_bus_ops *bus_ops)
{
......
error = ieee80211_register_hw(hw);
......
ath_start_rfkill_poll(sc);
return 0;
}
```
```
int ieee80211_register_hw(struct ieee80211_hw *hw)
{
/* add one default STA interface if supported */
if (local->hw.wiphy->interface_modes & BIT(NL80211_IFTYPE_STATION)) {
result = ieee80211_if_add(local, "wlan%d", NULL,
NL80211_IFTYPE_STATION, NULL);
if (result)
wiphy_warn(local->hw.wiphy,
"Failed to add default virtual ifacen");
}
......
}
```
在驱动注册过程中,有一个不可忽略的结构体,ieee80211_ops,初略扫视一下硬件驱动的实现,每一个硬件驱动都填充了一个 ieee80211_ops类型的结构体,该结构体中的函数接口,将直接对硬件进行操作,以ath9k为例,
```
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,
```
以ath9k_tx为例,通过ath9k_tx->ath_tx_start->ath_tx_send_normal->ath_tx_txqaddbuf->ath9k_hw_txstart->REG_WRITE(ah, AR_Q_TXE, 1 << q);最终实现对硬件的直接操作。
```
void ath9k_hw_txstart(struct ath_hw *ah, u32 q)
{
ath_dbg(ath9k_hw_common(ah), QUEUE, "Enable TXE on queue: %un", q);
REG_WRITE(ah, AR_Q_TXE, 1 << q);
}
```
当驱动注册成功,并且ieee80211_ops结构体的必要函数被完善之后,从WiFi driver到mac80211的工作就完成了。此时,数据要继续往上层走,就要区分数据帧和管理帧了。
首先来看数据帧,说到底,wireless还是一个net_device,按照linux设备驱动的习惯,每一个dev,一般会有对应的ops,这个ops是实现对不同硬件操作的接口,同样,wireless也不例外,在net_device中,对应的ops是netdev_ops
```
struct net_device {
......
const struct net_device_ops *netdev_ops;
......
}
```
在wireless中,定义了结构体ieee80211_dataif_ops,该结构体也是struct net_device_ops类型的,linux将用ieee80211_dataif_ops填充net_device中的netdev_ops成员。
```
static const struct net_device_ops ieee80211_dataif_ops = {
.ndo_open = ieee80211_open,
.ndo_stop = ieee80211_stop,
.ndo_uninit = ieee80211_uninit,
.ndo_start_xmit = ieee80211_subif_start_xmit,
.ndo_set_rx_mode = ieee80211_set_multicast_list,
.ndo_change_mtu = ieee80211_change_mtu,
.ndo_set_mac_address = ieee80211_change_mac,
.ndo_select_queue = ieee80211_netdev_select_queue,
};
```
对netdev_ops的填充将在函数ieee80211_if_setup中实现,
```
static void ieee80211_if_setup(struct net_device *dev)
{
ether_setup(dev);
dev->priv_flags &= ~IFF_TX_SKB_SHARING;
dev->netdev_ops = &ieee80211_dataif_ops;
dev->destructor = free_netdev;
}
```
最终,在ieee80211_if_add函数中,调用时alloc_netdev_mqs,会执行ieee80211_if_setup函数
```
int ieee80211_if_add(struct ieee80211_local *local, const char *name,
struct wireless_dev **new_wdev, enum nl80211_iftype type,
struct vif_params *params)
......
ndev = alloc_netdev_mqs(sizeof(*sdata) + local->hw.vif_data_size,
name, NET_NAME_UNKNOWN,
ieee80211_if_setup, txqs, 1);
....
```
通过上面的一系列操作,数据帧会完全交由设备无关层进行管理,完全与WIFI驱动无关了,不再继续往下深究。
再来看管理帧,这其中一个关键的结构体是cfg80211_ops,改结构体中的成员将处理来自mac80211的管理帧,上层应用通过一系列的调用最终会调用到该结构体中的操作函数。
```
struct cfg80211_ops {
int (*suspend)(struct wiphy *wiphy, struct cfg80211_wowlan *wow);
int (*resume)(struct wiphy *wiphy);
void (*set_wakeup)(struct wiphy *wiphy, bool enabled);
struct wireless_dev * (*add_virtual_intf)(struct wiphy *wiphy,
const char *name,
enum nl80211_iftype type,
u32 *flags,
struct vif_params *params);
int (*del_virtual_intf)(struct wiphy *wiphy,
struct wireless_dev *wdev);
int (*change_virtual_intf)(struct wiphy *wiphy,
struct net_device *dev,
enum nl80211_iftype type, u32 *flags,
struct vif_params *params);
int (*add_key)(struct wiphy *wiphy, struct net_device *netdev,
u8 key_index, bool pairwise, const u8 *mac_addr,
struct key_params *params);
int (*get_key)(struct wiphy *wiphy, struct net_device *netdev,
u8 key_index, bool pairwise, const u8 *mac_addr,
void *cookie,
void (*callback)(void *cookie, struct key_params*));
int (*del_key)(struct wiphy *wiphy, struct net_device *netdev,
u8 key_index, bool pairwise, const u8 *mac_addr);
int (*set_default_key)(struct wiphy *wiphy,
struct net_device *netdev,
u8 key_index, bool unicast, bool multicast);
int (*set_default_mgmt_key)(struct wiphy *wiphy,
struct net_device *netdev,
u8 key_index);
......
}
```
到这里,整个驱动的结构有了一个大概的了解,至于具体细节的实现,暂时不去关注。
**参考资料**
[链接1:http://blog.csdn.net/myarrow/article/details/9274443](http://blog.csdn.net/myarrow/article/details/9274443)
[链接2:http://www.tuicool.com/articles/2uiaUfv](http://www.tuicool.com/articles/2uiaUfv)
|