> 文档中心 > OpenHarmony WLAN FlowControlModule缓存网络数据

OpenHarmony WLAN FlowControlModule缓存网络数据

文章目录

    • Flow Contrl
      • 一、创建FlowControlModule
      • 二、FlowControlInterface
        • 2.1、schedFCM

Flow Contrl

WIFI驱动可以选择是否使用Flow Control来缓存网络数据的收发。

以下是Flow Control的定义,可以看到有两个线程分别负责处理收发数据的逻辑,有对应的两个信号量来完成线程的同步,以及由进程状态的变量,还有由netbuf组成的两个收发队列,这些是FlowControl模块使用到的数据。

struct FlowControlModule {    OSAL_DECLARE_THREAD(txTransferThread);//发送数据    OSAL_DECLARE_THREAD(rxTransferThread);//接收数据    struct OsalSem sem[FLOW_DIR_COUNT];   //线程信号量    FcThreadStatus threadStatus[FLOW_DIR_COUNT]; //线程状态    struct FlowControlQueues fcmQueue[FLOW_DIR_COUNT];  //netuf缓存队列    struct FlowControlOp *op;      //驱动实现    struct FlowControlInterface *interface;      //提供给开发者的接口    void *fcmPriv;   /< Private data of the flow control module */};

然后还提供给开发者接口来操作FlowControl模块,例如FlowControlInterface,其定义在下面。根据g_fcInterface给我们提供的接口,可以知道开发者使用FlowControl模块主要就是向指定队列发送netbuf就完事了。当然还有最重要的,使用RegisterFlowControlOp注册一个FlowControlOp结构体。

static struct FlowControlInterface g_fcInterface = {    .setQueueThreshold = SetQueueThreshold,//设置队列最大值    .getQueueIdByEtherBuff = GetQueueIdByEtherBuff,//获取队列buff    .sendBuffToFCM = SendBuffToFCM,//发送netbuf到队列    .schedFCM = SchedTransfer,//调度线程    .registerFlowControlOp = RegisterFlowControlOp,//注册FlowControlOp结构体};

FlowControlOp由驱动开发者根据具体的wifi芯片实现,负责操作wifi 芯片的逻辑。

struct FlowControlOp {//检查wifi芯片是否处于station或p2p模式    bool (*isDeviceStaOrP2PClient)(void);    //发送队列中的数据到网口    int32_t (*txDataPacket)(NetBufQueue *q, void *fcmPrivate, int32_t fwPriorityId);//发送队列中的数据到协议栈    int32_t (*rxDataPacket)(NetBufQueue *q, void *fcmPrivate, int32_t fwPriorityId);//获取发送队列id    FlowControlQueueID (*getTxQueueId)(const void *para);//获取接收队列id    FlowControlQueueID (*getRxQueueId)(const void *para);//获取发送队列优先级    int32_t (*getTxPriorityId)(FlowControlQueueID id);//获取接收队列优先级    int32_t (*getRxPriorityId)(FlowControlQueueID id);};

一、创建FlowControlModule

创建FlowContriol模块很简单,就是初始化所有成员变量。

struct FlowControlModule *InitFlowControl(void *fcmPriv){    //创建内存    struct FlowControlModule *fcm = NULL;    fcm = (struct FlowControlModule *)OsalMemCalloc(sizeof(struct FlowControlModule));    (void)memset_s(fcm, sizeof(struct FlowControlModule), 0, sizeof(struct FlowControlModule));    //初始化两个队列    FlowControlQueueInit(fcm);    //初始化信号量    for (i = 0; i < FLOW_DIR_COUNT; i++) { if (OsalSemInit(&fcm->sem[i], 0) != HDF_SUCCESS) {     OsalMemFree(fcm);     return NULL; }    }    //初始化线程    if (CreateFlowControlTask(fcm) != HDF_SUCCESS) { return NULL;    }    //注册接口    fcm->interface = &g_fcInterface;    fcm->fcmPriv = fcmPriv;    g_fcm = fcm;    return fcm;}

1.1、收发线程

在创建flowcontrol 线程中可以看到两个线程的优先级是最高的,因为网络数据需要及时处理,否则会导致队列溢出。

int32_t CreateFlowControlTask(struct FlowControlModule *fcm){    struct OsalThreadParam config = { .priority = OSAL_THREAD_PRI_HIGHEST,//最高的优先级 .stackSize = 0,//为什么栈大小为0?    };    int32_t ret = CreateTask(&fcm->txTransferThread, RX_THREAD_NAME, RunWiFiTxFlowControl, &config, fcm);    ret = CreateTask(&fcm->rxTransferThread, TX_THREAD_NAME, RunWiFiRxFlowControl, &config, fcm);}

两个线程使用同一套代码,根据参数dir来判断是接收还是发送。

static int32_t RunWiFiFlowControl(void *para, FlowDir dir){    struct FlowControlModule *fcm = (struct FlowControlModule *)para;    while (true) { fcm->threadStatus[dir] = THREAD_WAITING; //等待信号量同步 if (OsalSemWait(&fcm->sem[dir], HDF_WAIT_FOREVER) != HDF_SUCCESS) {     HDF_LOGE("%s exit: OsalSemWait return false!", __func__);     continue; } fcm->threadStatus[dir] = THREAD_RUNNING; //分别处理收发逻辑 if (dir == FLOW_TX) {     FlowControlTxTreadProcess(fcm); } else if (dir == FLOW_RX) {     FlowControlRxTreadProcess(fcm); }    }}

1.2、队列

我们知道无论什么类型的网络数据,最终都要进入队列,因为该模块的队列比较多,我们先看看有队列是什么以及有哪些队列:

FlowControlQueue定义如下,其本质是在NetBufQueue的基础上,增加一些变量来给FlowControl管理。

struct FlowControlQueue {    FlowControlQueueID queueID;  //见下文    NetBufQueue dataQueue;//由netbuf组成的队列    uint32_t queueThreshold;     //网络数据最大值    OsalSpinlock lock;    /< Queue lock */    uint32_t pktCount;    /< Number of packets received by the network data queue */};

FlowControlQueueID是用于表示一个队列组所拥有的所有类型的队列,一个队列组包含了9个不同等级(类型)的队列,这些队列是有优先级的。例如其定义:

typedef enum {    CTRL_QUEUE_ID = 0,  /< Control queue ID */    VIP_QUEUE_ID,/< VIP queue ID */    NORMAL_QUEUE_ID,    /< Normal queue ID */    TCP_DATA_QUEUE_ID,  /< TCP data queue ID */    TCP_ACK_QUEUE_ID,   /< TCP ACK queue ID */    BK_QUEUE_ID, /< Background flow queue ID */    BE_QUEUE_ID, /< Best-effort flow queue ID */    VI_QUEUE_ID, /< Video flow queue ID */    VO_QUEUE_ID, /< Voice flow queue ID */    QUEUE_ID_COUNT      /< Total number of queue IDs */} FlowControlQueueID;

在全局变量中,就有如下四个队列组,其意义见代码注释

//sta模式发送线程所拥有的队列类型static FlowControlQueueID g_staPriorityMapTx[QUEUE_ID_COUNT] = {    CTRL_QUEUE_ID, VIP_QUEUE_ID, NORMAL_QUEUE_ID, TCP_ACK_QUEUE_ID, TCP_DATA_QUEUE_ID, VO_QUEUE_ID, VI_QUEUE_ID,    BE_QUEUE_ID, BK_QUEUE_ID};//sta模式接收线程所拥有的队列类型static FlowControlQueueID g_staPriorityMapRx[QUEUE_ID_COUNT] = {    CTRL_QUEUE_ID, VIP_QUEUE_ID, NORMAL_QUEUE_ID, TCP_DATA_QUEUE_ID, TCP_ACK_QUEUE_ID, VO_QUEUE_ID, VI_QUEUE_ID,    BE_QUEUE_ID, BK_QUEUE_ID};//应该是ap模式下的static FlowControlQueueID g_priorityMapTx[QUEUE_ID_COUNT] = {    CTRL_QUEUE_ID, VIP_QUEUE_ID, NORMAL_QUEUE_ID, TCP_DATA_QUEUE_ID, TCP_ACK_QUEUE_ID, VO_QUEUE_ID, VI_QUEUE_ID,    BE_QUEUE_ID, BK_QUEUE_ID};static FlowControlQueueID g_priorityMapRx[QUEUE_ID_COUNT] = {    CTRL_QUEUE_ID, VIP_QUEUE_ID, NORMAL_QUEUE_ID, TCP_ACK_QUEUE_ID, TCP_DATA_QUEUE_ID, VO_QUEUE_ID, VI_QUEUE_ID,    BE_QUEUE_ID, BK_QUEUE_ID};

所以,收发线程的作用就是负责把队列中的netbuf送到他们该去的地方。发送的netbuf就应该传递给网口驱动,而接收到的netbuf就该传递给协议栈。

//发送线程处理队列中的数据static void FlowControlTxTreadProcess(struct FlowControlModule *fcm){  //sta发送队列组    if (isSta) { //遍历队列组的所有队列 for (i = 0; i < FLOW_CONTROL_MAP_SIZE; i++) {     SendFlowControlQueue(fcm, g_staPriorityMapTx[i], FLOW_TX); }    } else { //同上 for (i = 0; i < FLOW_CONTROL_MAP_SIZE; i++) {     SendFlowControlQueue(fcm,  g_priorityMapTx[i], FLOW_TX); }    }}

FlowControlTxTreadProcess和FlowControlRxTreadProcess最后会调用SendFlowControlQueue来入队,最终调用txDataPacket或rxDataPacket来发送接受数据。

int32_t SendFlowControlQueue(struct FlowControlModule *fcm, uint32_t id, uint32_t dir){    //获取指定队列    q = &fcm->fcmQueue[dir].queues[id].dataQueue;    if (dir == FLOW_TX) { if (fcm->op != NULL && fcm->op->getTxPriorityId != NULL) {     //获取队列优先级     fwPriorityId = fcm->op->getTxPriorityId(id); } if (fcm->op != NULL && fcm->op->txDataPacket != NULL) {     //调用FlowControlOp的发送函数,把队列中的netbuf通过网口发送     fcm->op->txDataPacket(q, fcm->fcmPriv, fwPriorityId); } else {     HDF_LOGE("%s fail : fcm->op->txDataPacket = null!", __func__);     return HDF_ERR_INVALID_PARAM; }    }    if (dir == FLOW_RX) { if (fcm->op != NULL && fcm->op->getRxPriorityId != NULL) {     rxPriorityId = fcm->op->getRxPriorityId(id); } if (fcm->op != NULL && fcm->op->rxDataPacket != NULL) {     //调用FlowControlOp的接收函数,把队列中的netbuf传递给协议栈     fcm->op->rxDataPacket(q, fcm->fcmPriv, rxPriorityId); } else {     HDF_LOGE("%s fail : fcm->op->txDataPacket = null!", __func__);     return HDF_ERR_INVALID_PARAM; }    }    return HDF_SUCCESS;}

二、FlowControlInterface

既然我们了解了FlowControl模块的线程的主要作用,即将数据入队。那么是由谁来发起(同步)线程的运行呢?这就需要使用到FlowControlInterface,开发者调用FlowControlInterface的接口来实现对队列的操作。我们重点关注sendBuffToFCM()和schedFCM()

struct FlowControlInterface {    //根据netbuf获取到队列的id    FlowControlQueueID (*getQueueIdByEtherBuff)(const NetBuf *buff);//设置队列最大容量    int32_t (*setQueueThreshold)(struct FlowControlModule *fcm, uint32_t queueThreshold, uint32_t id, uint32_t dir);//发送netbuf到队列    int32_t (*sendBuffToFCM)(struct FlowControlModule *fcm, NetBuf *buff, uint32_t id, uint32_t dir);//发送信号量,同步线程    int32_t (*schedFCM)(struct FlowControlModule *fcm, FlowDir dir);//注册FlowControlOp    int32_t (*registerFlowControlOp)(struct FlowControlModule *fcm, struct FlowControlOp *op);};

2.1、schedFCM

将netbuf推进到指定队列:

static int32_t SendBuffToFCM(struct FlowControlModule *fcm, NetBuf *buff, uint32_t id, uint32_t dir){    struct FlowControlQueue *fcmQueue = NULL;    NetBufQueue *dataQ = NULL;    //检查参数    if (!IsValidSentToFCMPra(fcm, id, dir)) { HDF_LOGE("%s fail : IsValidSentToFCMPra FALSE!", __func__); return HDF_ERR_INVALID_PARAM;    }//获取队列    fcmQueue = &fcm->fcmQueue[dir].queues[id];    //获取netbuf队列    dataQ = &fcmQueue->dataQueue;    //检查fcmQueue的容量    FcmQueuePreProcess(fcmQueue);    if (NetBufQueueIsEmpty(dataQ)) { fcm->fcmQueue[dir].queues[id].pktCount = 0;    }    //netbuf入队    NetBufQueueEnqueue(dataQ, buff);    //数据包+1    fcm->fcmQueue[dir].queues[id].pktCount++;    return HDF_SUCCESS;}

一般驱动程序在调用SendBuffToFCM()后,还要调用SchedTransfer()来同步线程,使线程即使的处理队列中的netbuf

static int32_t SchedTransfer(struct FlowControlModule *fcm, FlowDir dir){    OsalSemPost(&fcm->sem[dir]);    return HDF_SUCCESS;}