OpenHarmony WLAN HDI source code learning

WLAN_HDI

For the introduction of WLAN HDI, you can see the official website document, and its structure can be shown in the figure:

WLAN HDI aims to provide an interface for Wi Fi service calls. Its functions include: starting / closing wlan, viewing connection status, registering callback function, starting wlan scanning, etc.

1, WLAN Hal

As the middle layer of HDI, WLAN HAL module provides Wi Fi service, iwifi object and IWiFiSta, IWiFiAp and IWiFiBaseFeature objects to send and receive WLAN commands.

struct IWiFi {
    //Open the channel between hal layer and driver
    int32_t (*start)(struct IWiFi *iwifi);
    //Close the channel between hal layer and driver
    int32_t (*stop)(struct IWiFi *iwifi);
	//Get the feature s supported by wifi driver
    int32_t (*getSupportFeature)(uint8_t *supType, uint32_t size);
	//Get the feature combination supported by wifi driver
    int32_t (*getSupportCombo)(uint64_t *combo, uint32_t size);
    //Create a feature
    int32_t (*createFeature)(int32_t type, struct IWiFiBaseFeature **ifeature);
    int32_t (*getFeatureByIfName)(const char *ifName, struct IWiFiBaseFeature **ifeature);
	//Register callback function to listen for wifi event 
    int32_t (*registerEventCallback)(CallbackFunc cbFunc, const char *ifName);
    int32_t (*unregisterEventCallback)(CallbackFunc cbFunc, const char *ifName);
    int32_t (*destroyFeature)(struct IWiFiBaseFeature *ifeature);
	//Reset chip driver
    int32_t (*resetDriver)(const uint8_t chipId, const char *ifName);
    //get net device infos.
    int32_t (*getNetDevInfo)(struct NetDeviceInfoResult *netDeviceInfoResult);
};

1.1,IWiFi

WIFI construct is used to create IWiFi objects and assign values to their functions. Developers can call the methods of IWiFi objects to create IWiFiSta, IWiFiAp and IWiFiBaseFeature objects. Then send specific WIFI commands through these three objects

int32_t WifiConstruct(struct IWiFi **wifiInstance)
{
    static bool isInited = false;
    static struct IWiFi singleWifiInstance;

    if (!isInited) {
        if (HalMutexInit() != HDF_SUCCESS) {
            HDF_LOGE("%s: HalMutexInit failed, line: %d\n", __FUNCTION__, __LINE__);
            return HDF_FAILURE;
        }
        //Assign a value to a function
        singleWifiInstance.start = Start;
        singleWifiInstance.stop = Stop;
        singleWifiInstance.getSupportFeature = GetSupportFeature;
        singleWifiInstance.getSupportCombo = GetSupportCombo;
        singleWifiInstance.createFeature = CreateFeature;
        singleWifiInstance.getFeatureByIfName = GetFeatureByIfName;
        singleWifiInstance.destroyFeature = DestroyFeature;
        singleWifiInstance.registerEventCallback = HalRegisterEventCallback;
        singleWifiInstance.unregisterEventCallback = HalUnregisterEventCallback;
        singleWifiInstance.resetDriver = ResetDriver;
        singleWifiInstance.getNetDevInfo = GetNetDevInfo;
        InitIWiFiList();
        isInited = true;
    }
    (*wifiInstance) = &singleWifiInstance;
    return HDF_SUCCESS;
}

1.2,IWiFiBaseFeature

IWiFiSta and IWiFiAp are extensions based on IWiFiBaseFeature. Let's take IWiFiSta as an example to understand how HAL layer sends commands.

struct IWiFiSta {
    //Inherited IWiFiBaseFeature
    struct IWiFiBaseFeature baseFeature; 
    //Scan the specified mac address
    int32_t (*setScanningMacAddress)(const struct IWiFiSta *staFeature, unsigned char *scanMac, uint8_t len);
    //Start scanning
    int32_t (*startScan)(const char *ifName, WifiScan *scan);
};

struct IWiFiBaseFeature {
    char ifName[IFNAME_MAX_LEN];  
    int32_t type;   
	//Get network card name
    const char *(*getNetworkIfaceName)(const struct IWiFiBaseFeature *baseFeature);
	//Get feature type
    int32_t (*getFeatureType)(const struct IWiFiBaseFeature *baseFeature);
	//Set mac address
    int32_t (*setMacAddress)(const struct IWiFiBaseFeature *baseFeature, unsigned char *mac, uint8_t len);
	//Get device mac address
    int32_t (*getDeviceMacAddress)(const struct IWiFiBaseFeature *baseFeature, unsigned char *mac, uint8_t len);
	//Get wifi bandwidth
    int32_t (*getValidFreqsWithBand)(const struct IWiFiBaseFeature *baseFeature, int32_t band, int32_t *freqs,
        uint32_t count, uint32_t *num);
	//Set transmission power
    int32_t (*setTxPower)(const struct IWiFiBaseFeature *baseFeature, int32_t power);
	//Get chip id
    int32_t (*getChipId)(const struct IWiFiBaseFeature *baseFeature, uint8_t *chipId);
	//Get network port name
    int32_t (*getIfNamesByChipId)(const uint8_t chipId, char **ifNames, uint32_t *num);
};

The first step is to create IWiFiSta. You need to call the createFeature() method of the IWiFiSta object to create it. The specific content is to fill in the methods in the IWiFiSta object.

Let's take createFeature() as an example to learn how the function of feature sends commands to wifi driver.

//createFeature calls CreateFeatureInner
static int32_t CreateFeatureInner(int32_t type, struct IWiFiBaseFeature **ifeature)
{
    //init feature
    ret = InitFeatureByType(type, ifeature);
    ret = FindValidNetwork(type, ifeature);
    return HDF_SUCCESS;
}

//Call different interfaces according to different types of feature s
static int32_t InitFeatureByType(int32_t type, struct IWiFiBaseFeature **ifeature)
{
    int32_t ret;

    switch (type) {
        case PROTOCOL_80211_IFTYPE_AP:
            *ifeature = (struct IWiFiBaseFeature *)malloc(sizeof(struct IWiFiAp));
            (void)memset_s(*ifeature, sizeof(struct IWiFiAp), 0, sizeof(struct IWiFiAp));
            ret = InitApFeature((struct IWiFiAp **)ifeature);
            break;
        case PROTOCOL_80211_IFTYPE_STATION:
            *ifeature = (struct IWiFiBaseFeature *)malloc(sizeof(struct IWiFiSta));
            (void)memset_s(*ifeature, sizeof(struct IWiFiSta), 0, sizeof(struct IWiFiSta));
            ret = InitStaFeature((struct IWiFiSta **)ifeature);
            break;
        default:
            HDF_LOGE("%s: type not support, line: %d", __FUNCTION__, __LINE__);
            return HDF_FAILURE;
    }
    return ret;
}

First call InitBaseFeature() to initialize the basefeature, and then add a function unique to IWiFiSta.

int32_t InitStaFeature(struct IWiFiSta **fe)
{
    if (InitBaseFeature((struct IWiFiBaseFeature **)fe) != HDF_SUCCESS) {
        HDF_LOGE("%s: init base feature, line: %d", __FUNCTION__, __LINE__);
        return HDF_FAILURE;
    }
    (*fe)->setScanningMacAddress = SetScanningMacAddress;
    (*fe)->startScan = StartScan;
    return HDF_SUCCESS;
}

After creating IWiFiSta, you can use its interface to send WIFI commands.

Let's take the startScan command as an example. The reason for adding the mutex lock is that the wifi chip can only respond to one command at a time.

static int32_t StartScan(const char *ifName, WifiScan *scan)
{
    //Add mutex lock and cannot be interrupted by other commands
    HalMutexLock();
    //StartScanInner() finally calls SBUF_ cmd_ adapter. WifiCmdScan() in C
    int32_t ret = StartScanInner(ifName, scan);
    HalMutexUnlock();
    return ret;
}

StartScanInner() finally calls SBUF_ cmd_ adapter. WifiCmdScan() in C:

int32_t WifiCmdScan(const char *ifName, WifiScan *scan)
{
    int32_t ret;
    struct HdfSBuf *data = NULL;
    data = HdfSBufObtainDefaultSize();
    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) {
        HILOG_ERROR(LOG_DOMAIN, "%s: Serialize failed!", __FUNCTION__);
        ret = RET_CODE_FAILURE;
    } else {
        //Send command to WLAN Driver
        ret = SendCmdSync(WIFI_WPA_CMD_SCAN, data, NULL);
    }
    HdfSBufRecycle(data);
    return ret;
}

sbuf_ cmd_ adapter. The function in C encapsulates all WIFI commands into SBUF type and calls HDF driven message mechanism to HDF_WIFI driver sends command.

static int32_t SendCmdSync(const uint32_t cmd, struct HdfSBuf *reqData, struct HdfSBuf *respData)
{
    int32_t ret = g_wifiService->dispatcher->Dispatch(&g_wifiService->object, cmd, reqData, respData);
    return ret;
}

1.3,Client

G is used in the SendCmdSync() function_ WiFi service variable, which is of type HdfIoService. It will be initialized when calling the start method of IWiFi:

int32_t WifiDriverClientInit(void)
{
    int32_t ret;
    if (g_wifiService == NULL) {
        //Binding hdfwifi service
        g_wifiService = HdfIoServiceBind(DRIVER_SERVICE_NAME);
    }
	//Registering Callbacks 
    g_wifiDevEventListener.onReceive  = OnWiFiEvents;
    return ret;
}

In addition to binding the service and sending messages to the service, the callback function OnWiFiEvents() is also registered. In this function, different functions are called according to the eventId to process the messages reported by the driver and convert the messages from sbuf to ordinary type data. (see the last section for how to register callback functions)

int OnWiFiEvents(struct HdfDevEventlistener *listener,struct HdfIoService *service, uint32_t eventId, struct HdfSBuf *data)
{
    const char *ifName = HdfSbufReadString(data);
    //Handling different wifi events
    switch (eventId) {
        case WIFI_EVENT_NEW_STA:
            WifiEventNewStaProcess(ifName, eventId, data);
            break;
        case WIFI_EVENT_DEL_STA:
            WifiEventDelStaProcess(ifName, eventId, data);
            break;
		......
        default:
            break;
    }
}
//Take WiFi event newstaprocess as an example
static void WifiEventNewStaProcess(const char *ifName, uint32_t event, struct HdfSBuf *reqData)
{
    WifiNewStaInfo staInfo;
    //Extract the data of reqData to staInfo
    if (!HdfSbufReadInt32(reqData, &staInfo.reassoc)) {
        HILOG_ERROR(LOG_DOMAIN, "%s: fail to get reassoc", __FUNCTION__);
        return;
    }
    if (!HdfSbufReadBuffer(reqData, (const void **)(&staInfo.ie), &staInfo.ieLen)) {
        HILOG_ERROR(LOG_DOMAIN, "%s: fail to get ie", __FUNCTION__);
        return;
    }
    if (!HdfSbufReadBuffer(reqData, (const void **)(&staInfo.macAddr), &len) || (len != ETH_ADDR_LEN)) {
        HILOG_ERROR(LOG_DOMAIN, "%s: fail to get macAddr", __FUNCTION__);
        return;
    }
    //Report to IWiFi
    WifiEventReport(ifName, event, &staInfo);
}

WiFi eventreport() calls the callback function registered by HAL layer:

void WifiEventReport(const char *ifName, uint32_t event, void *data)
{
    uint32_t i;
	//The callback function of IWiFi registration is saved in g_callbackEventMap
    for (i = 0; i < MAX_CALL_BACK_COUNT; i++) {
        if (g_callbackEventMap[i] != NULL && (strcmp(g_callbackEventMap[i]->ifName, ifName) == 0) &&
            (((1 << event) & g_callbackEventMap[i]->eventType) != 0)) {
			//Call the callback function to notify the sending of events
            g_callbackEventMap[i]->onRecFunc(event, data, ifName);
        }
    }
}

2, HDI Service

The upper layer of HAL layer is HDI Service, which encapsulates the IWiFiSta, IWiFiAp and IWiFiBaseFeature of WLAN HAL layer into an object IWifiInterface to make it more convenient for users.

HDI Service is divided into two parts:

  • Services: as an HDF driver module, call the object provided by WLAN HAL and provide services to IWifiInterface.
  • IWifiInterface: subscribe to HDI Service driver service and provide unified interface to users.

2.1,Service

As an HDF driver module, it must have an initialization function, bind function:

struct HdfDriverEntry g_wlanHdiDriverEntry = {
    .moduleVersion = 1,
    .moduleName = "wlan_device",
    .Bind = HdfWlanHdiDriverBind,
    .Init = HdfWlanHdiDriverInit,
    .Release = HdfWlanHdiDriverRelease,
};

HDF_INIT(g_wlanHdiDriverEntry);

The focus is on the implementation of the dispatch() function: WlanHdiServiceDispatch()

In wlanhdicerviceonremoterequest(), call different functions according to the command id, and these functions will eventually call the interface provided by WLAN HAL layer.

static int32_t WlanHdiServiceDispatch(struct HdfDeviceIoClient *client, int cmdId,
    struct HdfSBuf *data, struct HdfSBuf *reply)
{
    return WlanHdiServiceOnRemoteRequest(client, cmdId, data, reply);
}
int32_t WlanHdiServiceOnRemoteRequest(struct HdfDeviceIoClient *client, int cmdId,
    struct HdfSBuf *data, struct HdfSBuf *reply)
{
    switch (cmdId) {
        case WLAN_SERVICE_CONSTRUCT:
            return HdiWifiConstruct(client, data, reply);
        case WLAN_SERVICE_DECONSTRUCT:
            return HdiWifiDeConstruct(client, data, reply);
        case WLAN_SERVICE_START:
            return WlanServiceStubStart(client, data, reply);
        ......
        default:
            HDF_LOGW("SampleServiceDispatch: not support cmd %d", cmdId);
            return HDF_ERR_INVALID_PARAM;
    }
    return HDF_SUCCESS;
}

For example: wlanservicesubstart()

static int32_t WlanServiceStubStart(struct HdfDeviceIoClient *client, struct HdfSBuf *data, struct HdfSBuf *reply)
{
    ret = g_wifi->start(g_wifi);
    return ret;
}

2.2,IWifiInterface

The IWifiInterface object integrates all WIFI commands by subscribing to HDI Service driver services, which is simpler and more convenient than using HAL layer interface directly.

Calling HdIWifiInterfaceGet will help us create the IWifiInterface object:

struct IWifiInterface *HdIWifiInterfaceGet(const char *serviceName)
{
    struct HDIServiceManager *serviceMgr = HDIServiceManagerGet();
	//Bind hdi service
    struct HdfRemoteService *remote = serviceMgr->GetService(serviceMgr, serviceName);
	//create IWifiInterface
    struct IWifiInterface *wlanClient = (struct IWifiInterface *)OsalMemAlloc(sizeof(struct IWifiInterface));
    wlanClient->remote = remote;
    IwifiConstruct(wlanClient);
    return wlanClient;
}
//Building IWifiInterface objects
static void IwifiConstruct(struct IWifiInterface *inst)
{
    inst->construct = WlanConstruct;
    inst->destruct = WlanDestruct;
	......
    inst->getNetDevInfo = WlanGetNetDevInfo;
    inst->startScan = WlanStartScan;
}

IWifiInterface uses HDF driven message mechanism to communicate with service. Take WlanStart command as an example:

static int32_t WlanStart(struct IWifiInterface *self)
{
    int32_t ec = HDF_FAILURE;
    //Create sbuf
    struct HdfSBuf *data = HdfSBufTypedObtain(SBUF_IPC);
    struct HdfSBuf *reply = HdfSBufTypedObtain(SBUF_IPC);
    //Send WLAN_SERVICE_START command
    ec = WlanProxyCall(self, WLAN_SERVICE_START command, data, reply);
}

static int32_t WlanProxyCall(struct IWifiInterface *self, int32_t id, struct HdfSBuf *data, struct HdfSBuf *reply)
{
	//Send message to hdi service driver service
    return self->remote->dispatcher->Dispatch(self->remote, id, data, reply);
}

3, Register WLAN callback function

The HAL layer provides a callback function to listen to WiFi events. The specific implementation is in wifi_driver_client.c

HalRegisterEventCallback() implements the assignment of the onRecFunc function to the global variable g_callbackEventMap[];

static int32_t HalRegisterEventCallback(OnReceiveFunc onRecFunc, const char *ifName)
{
    HalMutexLock();
    int32_t ret = RegisterEventCallbackInner(onRecFunc, ifName);
    HalMutexUnlock();
    return ret;
}
static int32_t RegisterEventCallbackInner(OnReceiveFunc onRecFunc, const char *ifName)
{
    if (WifiRegisterEventCallback(onRecFunc, WIFI_KERNEL_TO_HAL_CLIENT, ifName) != HDF_SUCCESS) {
        HDF_LOGE("%s: callback function has been registered, line: %d", __FUNCTION__, __LINE__);
        return HDF_FAILURE;
    }
    return HDF_SUCCESS;
}
int32_t WifiRegisterEventCallback(OnReceiveFunc onRecFunc, uint32_t eventType, const char *ifName)
{
    uint32_t i;
    struct CallbackEvent *callbackEvent = NULL;

    for (i = 0; i < MAX_CALL_BACK_COUNT; i++) {
        //checkout event type and ifname
        if (g_callbackEventMap[i] != NULL && g_callbackEventMap[i]->eventType == eventType &&
            (strcmp(g_callbackEventMap[i]->ifName, ifName) == 0) && g_callbackEventMap[i]->onRecFunc == onRecFunc) 
        {
            HILOG_INFO(LOG_DOMAIN, "%s the onRecFunc has been registered!", __FUNCTION__);
            return RET_CODE_SUCCESS;
        }
    }
    //create new callbackEvent
    callbackEvent = (struct CallbackEvent *)malloc(sizeof(struct CallbackEvent));
	//config callbackEvent
    callbackEvent->eventType = eventType;
    if (strcpy_s(callbackEvent->ifName, IFNAMSIZ, ifName) != RET_CODE_SUCCESS) {
        free(callbackEvent);
        return RET_CODE_FAILURE;
    }
    
    callbackEvent->onRecFunc = onRecFunc;
    //add onRecFunc to the g_callbackEventMap[]
    for (i = 0; i < MAX_CALL_BACK_COUNT; i++) {
        if (g_callbackEventMap[i] == NULL) {
            g_callbackEventMap[i] = callbackEvent;
            return RET_CODE_SUCCESS;
        }
    }
    free(callbackEvent);
    HILOG_ERROR(LOG_DOMAIN, "%s fail: register onRecFunc num more than %d!", __FUNCTION__, MAX_CALL_BACK_COUNT);
    return RET_CODE_FAILURE;
}

g_callbackEventMap [] is a fixed length array, which stores the callback functions of all wifi events. The type of wifi event is defined in wifi_common_cmd.h medium.

struct CallbackEvent {
    uint32_t eventType;   //eventmap
    char ifName[IFNAMSIZ + 1];
    OnReceiveFunc onRecFunc;
};

static struct CallbackEvent *g_callbackEventMap[MAX_CALL_BACK_COUNT] = {NULL};

Keywords: WLAN OpenHarmony

Added by SocomNegotiator on Fri, 04 Mar 2022 18:40:26 +0200