OpenHarmony HDF WIFI 之 FlowControl 原创 精华

杀手来过
发布于 2022-4-9 09:39
浏览
1收藏

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;
}

©著作权归作者所有,如需转载,请注明出处,否则将追究法律责任
1
收藏 1
回复
举报
回复
    相关推荐