本文首发于: LHM’s notes欢迎关注我的新博客
一入wifi深似海~
用户态(wpas)适配
首先找到如下目录,是开源wpas代码,由于之前看了点wpas的具体实现代码,知道在linux系统中wpas与内核打交道是通过两种标准接口,要么是nl80211接口,要么是wext(Wireless Extensions无线拓展接口); 由于当前内核系统更换,那么nl80211和wext自然是不适用了。因此猜想鸿蒙在wpas上有做适配,先到wpas源码上瞧了瞧,果然发现有新增文件。
/home/hmos/sourceCode/third_party/wpa_supplicant/wpa_supplicant-2.9/src/drivers
下面这个是鸿蒙框架下的wpa_driver_ops结构体,是继nl80211和wext之后的又一套新标准。由于上层只是调用指定的方法,如 get_ssid 、scan2
等等,而对其具体实现不会关注,因此在整个鸿蒙适配wpas代码的过程中,只需要将这个g_wifiDriverOps
对象的方法通过HDF框架实现即可,说实话,我对nl80211里面又臭又长的代码已经忍了很久了,希望这个不要让我失望:)
如上图所示,左边是wpas使用nl80211标准接口,右边是wpas使用鸿蒙wifi 接口;两者在上层业务面的改动基本不大,只是和内核交互的接口区域需要适配鸿蒙LiteOS。如下是接口部分的代码:
接下来我们以扫描业务为例子,分析鸿蒙wifi接口与内核态的交互过程。
扫描流程分析
首先看WifiWpaScan2
内部实现
static int32_t WifiWpaScan2(void *priv, struct wpa_driver_scan_params *params)
{
WifiScan *scan = NULL;
WifiDriverData *drv = NULL;
int32_t timeout;
int32_t ret;
if ((priv == NULL) || (params == NULL) || (params->num_ssids > WPAS_MAX_SCAN_SSIDS)) {
return -EFAIL;
}
drv = (WifiDriverData *)priv;
scan = (WifiScan *)os_zalloc(sizeof(WifiScan));
if (scan == NULL) {
return -EFAIL;
}
// 以下四个函数都是参数赋值,将param中的参数赋值到scan结构体中
if ((WifiWpaScanProcessSsid(params, scan) != SUCC) || (WifiWpaScanProcessBssid(params, scan) != SUCC) ||
(WifiWpaScanProcessExtraIes(params, scan) != SUCC) || (WifiWpaScanProcessFreq(params, scan) != SUCC)) {
WifiWpaScanFree(&scan);
return -EFAIL;
}
// scan结构体继续被填充
scan->fastConnectFlag = WPA_FLAG_OFF;
scan->prefixSsidScanFlag = WPA_FLAG_OFF;
// 将填充好的scan 消息结构体发送给内核
ret = WifiWpaCmdScan(drv->iface, scan);
WifiWpaScanFree(&scan);
timeout = SCAN_TIME_OUT;
//在事件调度中注册超时处理机制
eloop_cancel_timeout(WifiWpaScanTimeout, drv, drv->ctx);
eloop_register_timeout(timeout, 0, WifiWpaScanTimeout, drv, drv->ctx);
return ret;
}
可以看到WifiWpaScan2
只是一个结构体的填充并将该消息通过WifiWpaCmdScan
去发送,具体发送流程进入看下:
int32_t WifiWpaCmdScan(const char *ifname, WifiScan *scan)
{
int32_t ret;
if (ifname == NULL || scan == NULL) {
return -EFAIL;
}
struct HdfSBuf *data = HdfSBufObtainDefaultSize();
if (data == NULL) {
return -EFAIL;
}
bool isSerializeFailed = false;
isSerializeFailed = isSerializeFailed || !HdfSbufWriteString(data, ifname);
if (scan->bssid == NULL) {
isSerializeFailed = isSerializeFailed || !HdfSbufWriteBuffer(data, scan->bssid, 0);
} else {
isSerializeFailed = isSerializeFailed || !HdfSbufWriteBuffer(data, scan->bssid, ETH_ADDR_LEN);
}
isSerializeFailed =
isSerializeFailed || !HdfSbufWriteBuffer(data, scan->ssids, sizeof(scan->ssids[0]) * scan->numSsids);
isSerializeFailed = isSerializeFailed || !HdfSbufWriteBuffer(data, scan->extraIes, scan->extraIesLen);
isSerializeFailed =
isSerializeFailed || !HdfSbufWriteBuffer(data, scan->freqs, sizeof(scan->freqs[0]) * scan->numFreqs);
isSerializeFailed = isSerializeFailed || !HdfSbufWriteUint8(data, scan->prefixSsidScanFlag);
isSerializeFailed = isSerializeFailed || !HdfSbufWriteUint8(data, scan->fastConnectFlag);
if (isSerializeFailed) {
wpa_printf(MSG_ERROR, "Serialize failed!");
ret = -EFAIL;
} else {
ret = WifiWpaCmdBlockSyncSend(WIFI_WPA_CMD_SCAN, data, NULL);
}
HdfSBufRecycle(data);
return ret;
}
看过鸿蒙驱动开发实战那章的对这个代码一定不会陌生,由于鸿蒙dispatch服务的参数类型是固定的,是struct HdfSBuf *
类型,因此这里将发送数据通过HdfSbufWriteBuffer
重新又拷贝到了struct HdfSBuf *
指针里面, 并通过WifiWpaCmdBlockSyncSend
进行发送,打开进入
int32_t WifiWpaCmdBlockSyncSend(const uint32_t cmd, struct HdfSBuf *reqData, struct HdfSBuf *respData)
{
if (reqData == NULL) {
HDF_LOGE("%s params is NULL", __func__);
return HDF_FAILURE;
}
if (g_wifiService == NULL || g_wifiService->dispatcher == NULL || g_wifiService->dispatcher->Dispatch == NULL) {
HDF_LOGE("%s:bad remote service found!", __func__);
return HDF_FAILURE;
}
int32_t ret = g_wifiService->dispatcher->Dispatch(&g_wifiService->object, cmd, reqData, respData);
HDF_LOGI("%s: cmd=%d, ret=%d", __func__, cmd, ret);
return ret;
}
可以看到通信手法正是上面两章讲到的HDF service机制。
总结:
除了扫描流程、还有认证流程、关联流程,这些都是接下来去看的内容。目前对内核通信的机制基本有了了解,下面需要关注的有两点。
1、业务:消息中填充的每个数据,代表了什么含义;各个业务之间又是如何关联起来的。
2、驱动实现:消息发送到了内核,内核如何去处理,这驱动具体实现又和wifi芯片相关,也需要去了解。