OpenHarmony WLAN FlowControlModule缓存网络数据
文章目录
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;}