无线驱动设计思路
struct xxx_wifi_priv {
struct ieee80211_hw *hw;
struct wiphy *wiphy;
...
bool scanning;
bool is_up;
...
struct ieee80211_channel channel;
struct ieee80211_channel channels;
struct work_struct scan_work;
struct workqueue_struct *wq;
struct address address;
};
hw:保存硬件信息
wiphy:保存phy信息
scanning:记录扫描状态
is_up:是否被启用
scan_work:扫描时使用.
static struct ieee80211_os xxx_ops = {
.tx = xxx_tx,
.add_interface = xxx_add_interface,
.remove_interface = xxx_remove_interface,
.config = xxx_config,
.bss_info_changed = xxx_bss_info_changed,
.configure_filter = xxx_configure_filter,
.start = xxx_start,
.stop = xxx_stop,
.hw_scan = xxx_hw_scan,
.set_key = xxx_set_key,
...
};
以上为必要的几个功能回调。
# 信道信息
static struct ieee80211_channel xxx_channels = {
{ .center_freq = 2412, .hw_value = 1},
{ .center_freq = 2417, .hw_value = 2},
....
};
# 速率信息
static struct ieee80211_rate xxx_rates = {
{ .bitrate = 10, .hw_value = 0},
{ .bitrate = 20, .hw_value = 1},
...
};
struct ieee80211_supported_band xxx_supported_band = {
.channels = xxx_channels,
.n_channels = ARRAY_SIZE(xxx_channels),
.bitrates = xxx_rates,
.n_bitrates = ARRAY_SIZE(xxx_rates),
.ht_caps = {
.ht_supported = true,
.cap = IEEE80211_HT_CAP_SUP_WIDTH_20_40 |
IEEE80211_HT_CAP_SGI_20 |
IEEE80211_HT_CAP_DSSSCCK40,
.ampdu_factor = 0x3,
.ampdu_density = 0x6,
.mcs = {
.rx_mask = {0xff, 0xff},
.tx_params = IEEE80211_HT_MCS_TX_DEFINED,
},
},
};
probe处理流程
struct xxx_wifi_priv *priv;
struct ieee80211_hw *hw;
struct wiphy *wiphy;
hw = ieee80211_alloc_hw(sizeof(*priv), &xxx_ops);
#这里需要检查返回值。
if (!hw)
return -ENOMEM;
priv = hw->priv;
priv->hw = hw;
priv->wiphy = hw->wiphy;
#wiphy在alloc时会自动分配置并配置默认的回调函数
wiphy->max_scan_ssids = 1;
wiphy->max_scan_ie_len = 0;
wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION);
wiphy->bands[NL80211_BAND_2GHZ] &xxx_supported_band;
ieee80211_hw_set(hw, RX_INCLUDES_FCS);
ieee80211_hw_set(hw, SIGNAL_DBM)
# interface_modes表示支持哪些模式,可以一同设置多个。
hw->max_signal = -30;
# 当设置了SIGNAL_DBM之后,max_signal为负数.
SET_IEEE80211_DEV(hw, priv->dev); # 设置hw的父设备
# 设置默认MAC地址
eth_random_addr(priv->address.addr);
wiphy->addresses = &priv->address;
wiphy->n_addresses = 1;
wiphy->hw_version = 1;
# 进行一些基础的配置,比如初始化timer, 队列等。
#注册hw, 注册成功后,使用ifconfig就能看到wlanx设备。
ret = ieee80211_register_hw(hw);
# 扫描的实现与设计思路
#在用户态使用iw scan时,通过一些检查之后,如果ops实现了hw_scan将会调用
hw_scan.
#如果没有实现,可以return 1,表示使用sw_scan。
此时就必须实现sw_scan_start和sw_scan_stop.
在调用iw scan后,会一直等待内核返回结果,可以返回,也可以返回扫描结果。
可以在hw_scan的回调函数设置priv->scanning = true。
然后调度一个延迟任务。
在延迟任务中检查网卡的扫描状态。
如果网卡还处于扫描中,等继续等待和,如果等待时间过长。可以在调用
ieee80211_scan_completed中第二个参数中将aborted设置成true。
如果是正常扫描结束结设置为fase
struct ieee80211_scan_info info = {
.aborted = false,
};
ieee80211_scan_completed(priv->hw, &info);
需要注意的是,ieee80211_scan_completed必须延迟一段时间后调用。
否则会导致用户态接收不到消息。
关于扫描结果返回
在常规的无线网卡中,扫描行为是offload的,无需用户去干预。
网卡会将扫描结果放在网卡的正常数据流中。
所以在接收数据的时候,接收到数据之后调用mac80211实现的ieee80211_rx_napi。
这样,当接收到扫描数据时会通过netlink返回给用户态。