mac80211解析六
2017-02-22 23:24
363 查看
mac80211的扫描请求由用户空间通过nl80211发起,调用了mac80211中的
经过选择扫描接口类型之后,调用
如果支持硬件扫描,最后通过
延时唤醒
根据
mac80211中扫描状态机路径和扫描请求的路径相似,不同的是如果存在硬件扫描请求,调用
如果存在扫描请求,同时未进行扫描,调用
ieee80211_scan,该函数内容如下:
static int ieee80211_scan(struct wiphy *wiphy, struct cfg80211_scan_request *req) { struct ieee80211_sub_if_data *sdata; sdata = IEEE80211_WDEV_TO_SUB_IF(req->wdev); switch (ieee80211_vif_type_p2p(&sdata->vif)) { case NL80211_IFTYPE_STATION: case NL80211_IFTYPE_ADHOC: case NL80211_IFTYPE_MESH_POINT: case NL80211_IFTYPE_P2P_CLIENT: case NL80211_IFTYPE_P2P_DEVICE: break; case NL80211_IFTYPE_P2P_GO: if (sdata->local->ops->hw_scan) break; /* * FIXME: implement NoA while scanning in software, * for now fall through to allow scanning only when * beaconing hasn't been configured yet */ case NL80211_IFTYPE_AP: /* * If the scan has been forced (and the driver supports * forcing), don't care about being beaconing already. * This will create problems to the attached stations (e.g. all * the frames sent while scanning on other channel will be * lost) */ if (sdata->u.ap.beacon && (!(wiphy->features & NL80211_FEATURE_AP_SCAN) || !(req->flags & NL80211_SCAN_FLAG_AP))) return -EOPNOTSUPP; break; default: return -EOPNOTSUPP; } return ieee80211_request_scan(sdata, req); }
经过选择扫描接口类型之后,调用
ieee80211_request_scan函数,在进一步使用
__ieee80211_start_scan函数实现扫描:
static int __ieee80211_start_scan(struct ieee80211_sub_if_data *sdata, struct cfg80211_scan_request *req) { struct ieee80211_local *local = sdata->local; int rc; lockdep_assert_held(&local->mtx); if (local->scan_req) return -EBUSY; if (!ieee80211_can_scan(local, sdata)) { /* wait for the work to finish/time out */ local->scan_req = req; rcu_assign_pointer(local->scan_sdata, sdata); return 0; } if (local->ops->hw_scan) { u8 *ies; local->hw_scan_ies_bufsize = local->scan_ies_len + req->ie_len; local->hw_scan_req = kmalloc( sizeof(*local->hw_scan_req) + req->n_channels * sizeof(req->channels[0]) + local->hw_scan_ies_bufsize, GFP_KERNEL); if (!local->hw_scan_req) return -ENOMEM; local->hw_scan_req->ssids = req->ssids; local->hw_scan_req->n_ssids = req->n_ssids; ies = (u8 *)local->hw_scan_req + sizeof(*local->hw_scan_req) + req->n_channels * sizeof(req->channels[0]); local->hw_scan_req->ie = ies; local->hw_scan_req->flags = req->flags; local->hw_scan_band = 0; /* * After allocating local->hw_scan_req, we must * go through until ieee80211_prep_hw_scan(), so * anything that might be changed here and leave * this function early must not go after this * allocation. */ } local->scan_req = req; rcu_assign_pointer(local->scan_sdata, sdata); if (local->ops->hw_scan) { __set_bit(SCAN_HW_SCANNING, &local->scanning); } else if ((req->n_channels == 1) && (req->channels[0] == local->_oper_chandef.chan)) { /* * If we are scanning only on the operating channel * then we do not need to stop normal activities */ unsigned long next_delay; __set_bit(SCAN_ONCHANNEL_SCANNING, &local->scanning); ieee80211_recalc_idle(local); /* Notify driver scan is starting, keep order of operations * same as normal software scan, in case that matters. */ drv_sw_scan_start(local); ieee80211_configure_filter(local); /* accept probe-responses */ /* We need to ensure power level is at max for scanning. */ ieee80211_hw_config(local, 0); if ((req->channels[0]->flags & IEEE80211_CHAN_NO_IR) || !local->scan_req->n_ssids) { next_delay = IEEE80211_PASSIVE_CHANNEL_TIME; } else { ieee80211_scan_state_send_probe(local, &next_delay); next_delay = IEEE80211_CHANNEL_TIME; } /* Now, just wait a bit and we are all done! */ ieee80211_queue_delayed_work(&local->hw, &local->scan_work, next_delay); return 0; } else { /* Do normal software scan */ __set_bit(SCAN_SW_SCANNING, &local->scanning); } ieee80211_recalc_idle(local); if (local->ops->hw_scan) { WARN_ON(!ieee80211_prep_hw_scan(local)); rc = drv_hw_scan(local, sdata, local->hw_scan_req); } else rc = ieee80211_start_sw_scan(local); if (rc) { kfree(local->hw_scan_req); local->hw_scan_req = NULL; local->scanning = 0; ieee80211_recalc_idle(local); local->scan_req = NULL; RCU_INIT_POINTER(local->scan_sdata, NULL); } return rc; }
如果支持硬件扫描,最后通过
drv_hw_scan函数调用
local->ops->hw_scan()执行硬件扫描,否则,调用
ieee80211_start_sw_scan函数进行软件扫描。
static int ieee80211_start_sw_scan(struct ieee80211_local *local) { /* Software scan is not supported in multi-channel cases */ if (local->use_chanctx) return -EOPNOTSUPP; /* * Hardware/driver doesn't support hw_scan, so use software * scanning instead. First send a nullfunc frame with power save * bit on so that AP will buffer the frames for us while we are not * listening, then send probe requests to each channel and wait for * the responses. After all channels are scanned, tune back to the * original channel and send a nullfunc frame with power save bit * off to trigger the AP to send us all the buffered frames. * * Note that while local->sw_scanning is true everything else but * nullfunc frames and probe requests will be dropped in * ieee80211_tx_h_check_assoc(). */ drv_sw_scan_start(local); local->leave_oper_channel_time = jiffies; local->next_scan_state = SCAN_DECISION; local->scan_channel_idx = 0; ieee80211_offchannel_stop_vifs(local); /* ensure nullfunc is transmitted before leaving operating channel */ ieee80211_flush_queues(local, NULL); ieee80211_configure_filter(local); /* We need to set power level at maximum rate for scanning. */ ieee80211_hw_config(local, 0); ieee80211_queue_delayed_work(&local->hw, &local->scan_work, 0); return 0; }
延时唤醒
ieee80211_scan_work()函数:
void ieee80211_scan_work(struct work_struct *work) { struct ieee80211_local *local = container_of(work, struct ieee80211_local, scan_work.work); struct ieee80211_sub_if_data *sdata; unsigned long next_delay = 0; bool aborted; mutex_lock(&local->mtx); sdata = rcu_dereference_protected(local->scan_sdata, lockdep_is_held(&local->mtx)); /* When scanning on-channel, the first-callback means completed. */ if (test_bit(SCAN_ONCHANNEL_SCANNING, &local->scanning)) { aborted = test_and_clear_bit(SCAN_ABORTED, &local->scanning); goto out_complete; } if (test_and_clear_bit(SCAN_COMPLETED, &local->scanning)) { aborted = test_and_clear_bit(SCAN_ABORTED, &local->scanning); goto out_complete; } if (!sdata || !local->scan_req) goto out; if (local->scan_req && !local->scanning) { struct cfg80211_scan_request *req = local->scan_req; int rc; local->scan_req = NULL; RCU_INIT_POINTER(local->scan_sdata, NULL); rc = __ieee80211_start_scan(sdata, req); if (rc) { /* need to complete scan in cfg80211 */ local->scan_req = req; aborted = true; goto out_complete; } else goto out; } /* * as long as no delay is required advance immediately * without scheduling a new work */ do { if (!ieee80211_sdata_running(sdata)) { aborted = true; goto out_complete; } switch (local->next_scan_state) { case SCAN_DECISION: /* if no more bands/channels left, complete scan */ if (local->scan_channel_idx >= local->scan_req->n_channels) { aborted = false; goto out_complete; } ieee80211_scan_state_decision(local, &next_delay); break; case SCAN_SET_CHANNEL: ieee80211_scan_state_set_channel(local, &next_delay); break; case SCAN_SEND_PROBE: ieee80211_scan_state_send_probe(local, &next_delay); break; case SCAN_SUSPEND: ieee80211_scan_state_suspend(local, &next_delay); break; case SCAN_RESUME: ieee80211_scan_state_resume(local, &next_delay); break; case SCAN_ABORT: aborted = true; goto out_complete; } } while (next_delay == 0); ieee80211_queue_delayed_work(&local->hw, &local->scan_work, next_delay); goto out; out_complete: __ieee80211_scan_completed(&local->hw, aborted); out: mutex_unlock(&local->mtx); }
根据
next_scan_state调用相应的处理函数,果
next_delay==0,则继续根据
next_scan_state调用相应的处理函数。
mac80211中扫描状态机路径和扫描请求的路径相似,不同的是如果存在硬件扫描请求,调用
drv_hw_scan()进行扫描,如果失败,调用
ieee80211_scan_completed()完成扫描
void ieee80211_scan_completed(struct ieee80211_hw *hw, bool aborted) { struct ieee80211_local *local = hw_to_local(hw); trace_api_scan_completed(local, aborted); set_bit(SCAN_COMPLETED, &local->scanning); if (aborted) set_bit(SCAN_ABORTED, &local->scanning); ieee80211_queue_delayed_work(&local->hw, &local->scan_work, 0); }
如果存在扫描请求,同时未进行扫描,调用
__ieee80211_start_scan()进行软件扫描,如果失败,调用
ieee80211_scan_completed()完成扫描
static void __ieee80211_scan_completed(struct ieee80211_hw *hw, bool aborted) { struct ieee80211_local *local = hw_to_local(hw); bool hw_scan = local->ops->hw_scan; bool was_scanning = local->scanning; lockdep_assert_held(&local->mtx); /* * It's ok to abort a not-yet-running scan (that * we have one at all will be verified by checking * local->scan_req next), but not to complete it * successfully. */ if (WARN_ON(!local->scanning && !aborted)) aborted = true; if (WARN_ON(!local->scan_req)) return; if (hw_scan && !aborted && ieee80211_prep_hw_scan(local)) { int rc; rc = drv_hw_scan(local, rcu_dereference_protected(local->scan_sdata, lockdep_is_held(&local->mtx)), local->hw_scan_req); if (rc == 0) return; } kfree(local->hw_scan_req); local->hw_scan_req = NULL; if (local->scan_req != local->int_scan_req) cfg80211_scan_done(local->scan_req, aborted); local->scan_req = NULL; RCU_INIT_POINTER(local->scan_sdata, NULL); local->scanning = 0; local->scan_chandef.chan = NULL; /* Set power back to normal operating levels. */ ieee80211_hw_config(local, 0); if (!hw_scan) { ieee80211_configure_filter(local); drv_sw_scan_complete(local); ieee80211_offchannel_return(local); } ieee80211_recalc_idle(local); ieee80211_mlme_notify_scan_completed(local); ieee80211_ibss_notify_scan_completed(local); ieee80211_mesh_notify_scan_completed(local); if (was_scanning) ieee80211_start_next_roc(local); }
相关文章推荐
- S3C6410 看门狗驱动程序代码
- ARM11 paltform驱动代码完成,最简单的测试直接在装载设备中运行,实现秒读
- 字符设备驱动程序中重要的三个数据结构file_operations、inode、file
- Linux设备驱动并发控制详解(自旋锁,信号量)
- 无线网络以及安全资源
- 无线局域网(Wireless LAN)经典问答
- 本本做无线AP
- AP常用的五种模式
- wpa_supplicant无线网络配置
- CarPlay wireless(蓝牙+WiFi)连接方案(蓝牙部分)
- iw的安装
- 在 Debian 8 上安装 bcm43142 驱动
- inode获取设备号
- Lenovo Y430P bcm43142 wireless driver install
- Platform设备驱动
- 第二次给ubuntu装wl一定要把过程记下,万一还有第三次呢
- RTC设备驱动
- 关于linux模块编程
- 怎样配置Ruckus AP
- 字符设备驱动相关内容