blob: 6b76126b9b6be5861c4bdc0e8966a41601aa1992 [file] [log] [blame]
/****************************************************************************
* (c) Copyright 2007 Wi-Fi Alliance. All Rights Reserved
*
*
* LICENSE
*
* License is granted only to Wi-Fi Alliance members and designated
* contractors (“Authorized Licensees”).  Authorized Licensees are granted
* the non-exclusive, worldwide, limited right to use, copy, import, export
* and distribute this software:
* (i) solely for noncommercial applications and solely for testing Wi-Fi
* equipment; and
* (ii) solely for the purpose of embedding the software into Authorized
* Licensee’s proprietary equipment and software products for distribution to
* its customers under a license with at least the same restrictions as
* contained in this License, including, without limitation, the disclaimer of
* warranty and limitation of liability, below.  The distribution rights
* granted in clause (ii), above, include distribution to third party
* companies who will redistribute the Authorized Licensee’s product to their
* customers with or without such third party’s private label. Other than
* expressly granted herein, this License is not transferable or sublicensable,
* and it does not extend to and may not be used with non-Wi-Fi applications.
* Wi-Fi Alliance reserves all rights not expressly granted herein.
*
* Except as specifically set forth above, commercial derivative works of
* this software or applications that use the Wi-Fi scripts generated by this
* software are NOT AUTHORIZED without specific prior written permission from
* Wi-Fi Alliance. Non-Commercial derivative works of this software for
* internal use are authorized and are limited by the same restrictions;
* provided, however, that the Authorized Licensee shall provide Wi-Fi Alliance
* with a copy of such derivative works under a perpetual, payment-free license
* to use, modify, and distribute such derivative works for purposes of testing
* Wi-Fi equipment.
* Neither the name of the author nor "Wi-Fi Alliance" may be used to endorse
* or promote products that are derived from or that use this software without
* specific prior written permission from Wi-Fi Alliance.
*
* THIS SOFTWARE IS PROVIDED BY WI-FI ALLIANCE "AS IS" AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY, NON-INFRINGEMENT AND FITNESS FOR A PARTICULAR PURPOSE,
* ARE DISCLAIMED. IN NO EVENT SHALL WI-FI ALLIANCE BE LIABLE FOR ANY DIRECT,
* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, THE COST OF PROCUREMENT OF SUBSTITUTE
* GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE) ARISING IN ANY WAY OUT OF
* THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
* ****************************************************************************
*/
/*
* File: wfa_dut.c - The main program for DUT agent.
* This is the top level of traffic control. It initializes a local TCP
* socket for command and control link and waits for a connect request
* from a Control Agent. Once the the connection is established, it
* will process the commands from the Control Agent. For details, please
* reference the architecture documents.
*
* Revision History:
* 2006/03/10 -- Initially created by qhu.
* 2006/06/01 -- BETA Release by qhu
* 2006/06/13 -- 00.02 Release by qhu
* 2006/06/30 -- 00.10 Official Release 00.10 by qhu
* 2006/07/10 -- 01.00 Official Release 01.00 by qhu
* 2006/09/01 -- 01.05 Release by qhu
* 2006/10/20 -- bug fix 1. replace MAX_TRAFFIC_BUG_SZ with MAX_UDP_LEN
* that could cause crash by mismatch buf size
* 2006/10/26 -- 01.06 replace all hardcoded buff sizes with global macro
* 2007/01/11 -- 01.10 released by qhu
* 2007/02/15 -- WMM-Extention Beta released by qhu, mkaroshi
* 2007/03/30 -- 01.40 WPA2 and Official WMM Beta Release by qhu
* 2007/04/20 -- 02.00 WPA2 and Official WMM Release by qhu
* 2007/08/15 -- 02.10 WMM-Power Save release by qhu
* 2007/10/10 -- 02.20 Voice SOHO beta -- qhu
* 2007/11/07 -- 02.30 Voice HSO -- qhu
*
*/
#ifndef WIN32
#include <pthread.h>
#include <signal.h>
#include <time.h>
#include <semaphore.h>
#endif
#include "wfa_debug.h"
#include "wfa_main.h"
#include "wfa_types.h"
#include "wfa_sock.h"
#include "wfa_tlv.h"
#include "wfa_tg.h"
#include "wfa_miscs.h"
#include "wfa_agt.h"
#include "wfa_rsp.h"
#include "wfa_wmmps.h"
#include "wfa_cmds.h"
#include "wfa_agtctrl.h"
#include "wfa_ca.h"
char gRespStr[WFA_BUFF_1K], gnetIf[WFA_BUFF_32], gCmdStr[WFA_BUFF_512];
char* rwl_client_path;
unsigned short wfa_defined_debug = WFA_DEBUG_ERR | WFA_DEBUG_WARNING;
unsigned short dfd_lvl = WFA_DEBUG_DEFAULT | WFA_DEBUG_ERR;
int respLen = -1, tag = -1, ret_status = -1, bytesRcvd = -1, gRegSec, gtimeOut, adj_latency, g_pthrCreate =1, IPTVprof = 0, clock_drift_ps;
int gTCPsock = 0;
/* the agent local Socket, Agent Control socket and baseline test socket*/
int gagtSockfd =-1 , gxcSockfd =-1, gCaSockfd = -1, btSockfd=-1 , psSockfd =-1, btRecvSockfd = -1, gTcpRecvSockfd = -1;
/* the WMM traffic streams socket fds - Socket Handler table */
int tgSockfds[WFA_MAX_TRAFFIC_STREAMS];
int e2eCnt;
int rwl_wifi_flag;
unsigned long psTxMsg[WFA_BUFF_512], psRxMsg[WFA_BUFF_512];
double min_rttime ;
/* Global flags for synchronizing the TG functions */
BOOL gtgSend ; /* flag to sync Send traffic */
BOOL gtgRecv ; /* flag to sync Recv traffic */
BOOL gtgTransac ; /* flag to sync Transaction traffic */
BOOL gtgWmmPS ;
BOOL gtgStartSync ; /* flag to sync End2End Time handshaking */
BOOL gtgFinishSync; /* flag to sync End2End Time handshaking */
volatile int gTransactrunLoop;
extern struct apts_msg apts_msgs[];
void tmout_stop_send(int);
int wfaWmmPowerSaveProcess(int sockfd);
#ifdef WIN32
HANDLE thr_flag_cond;
HANDLE thr_stop_cond;
xcCommandFuncPtr gWfaCmdFuncTbl[]; /* command process functions */
dutCommandRespFuncPtr wfaCmdRespProcFuncTbl[]; /* command process functions */
typeNameStr_t nameStr[];
extern HANDLE g_recvEvent[] ;
extern HANDLE g_IPTVSndThr[];
extern HANDLE send_event;
extern int usedThread ;
HANDLE g_RecvhThread;
HANDLE g_IPTVRecvhThread[WFA_MAX_TRAFFIC_STREAMS];
void wfaRecvStart();
extern HANDLE g_hRecvEvent ;
extern DWORD Win32_tmout_stop_send(LPVOID num ) ;
void wfaSetIPTVThreadPrio(HANDLE tid, short class);
void wfaIPTVRecvStart(LPVOID tblidx);
void SendIPTVFile(LPVOID strid);
extern void wfaTxSleepTime(int profile, int rate, int *sleepTime, int *throttledRate);
extern BYTE Send_dutResp[];
#else
extern xcCommandFuncPtr gWfaCmdFuncTbl[]; /* command process functions */
extern dutCommandRespFuncPtr wfaCmdRespProcFuncTbl[]; /* command process functions */
extern typeNameStr_t nameStr[];
#define THREAD_SLEEP_TIME 1000
pthread_t Thread1 = -1;
void wfaIPTVRecvStart(void* tblidx);
void SendIPTVFile(void* strid);
extern void wfaSetThreadPrio(int tid, int class);
#endif
dutCmdResponse_t gGenericResp;
tgStream_t *findStreamProfile(int);
tgStream_t *gStreams; /* streams' buffers */
tgSyncTime_t gtgStartSyncTime; /* End2End Start Sync record */
tgSyncTime_t gtgFinishSyncTime; /* End2End Finish Sync Record */
tgE2EStats_t *e2eStats;
#ifdef WFA_WMM_EXT
#ifndef WIN32
extern void *wfa_wmm_thread(void *thr_param);
extern void *wfa_wmmps_thread();/* Added as per BRCM ASD 2.1 */
sem_t sem_wmm;
sem_t sem_wmm_resp;
sem_t sem_gtgrecv;
sem_t sem_iptv_gtgrecv[WFA_MAX_TRAFFIC_STREAMS];
#else
extern void Uapsd_Recv_Thread(void *thr_param);
#endif
tgWMM_t wmm_thr[WFA_THREADS_NUM];
wfaWmmPS_t wmmps_info;
#endif
int wfa_estimate_timer_latency();
void wfaRecvThrCreate(void);
extern int wfaTGSetPrio(int sockfd, int tgClass);
void wfaSentStatsResp( BYTE *buf);
void wfaIPTVRecvThrCreate(int );
#ifdef WIN32
void SendFile(LPVOID strid);
#else
void SendFile(void);
#endif
void bcmWfaInit(void); /* Added as per BRCM 1.3 ASD */
int maxfdn1 = -1;
fd_set sockSet; /* Set of socket descriptors for select() */
struct sockfds fds;
/* streams' buffers */
extern BYTE *xcCmdBuf, *parmsVal , *trafficBuf, *respBuf ;
extern struct timeval *toutvalp;
extern char gRespStr[];
extern int isString(char *);
extern int isNumber(char *);
extern unsigned short wfa_defined_debug;
#ifndef DONE
#define DONE 1
#endif
/* This is the main function that gets called from wl server
* command buffer (e.g) ca_get_version and length of the command.
* This is replacement of earlier main() function in the DUT
* However some of the earlier main function code is moved into
* different functions
*/
int remote_asd_exec(unsigned char* command, int* cmd_len)
{
int cmdLen = WFA_BUFF_1K, index, isFound;
BYTE xcCmdTag, pcmdBuf[WFA_BUFF_1K];
char *pcmdStr;
char tempbuf[WFA_BUFF_256], cmdName[WFA_BUFF_32];
DPRINT_INFO(WFA_OUT, " received: command %s. len %d.\n", command, *cmd_len);
maxfdn1 = gagtSockfd + 1;
/* set socket file descriptors. For baseline, there are only
* three sockets required. They are an agent main socket,
* Control Agent link socket and Traffic Generator Socket.
*/
fds.agtfd = &gagtSockfd;
fds.tgfd = &btSockfd;
/*btRecvSockfd added to receive the traffic for all the profiles
* and also support the Bi-directional profiles
*/
fds.tgRevfd = &btRecvSockfd;
fds.cafd = &gCaSockfd;
fds.wmmfds = tgSockfds;
#ifdef WFA_WMM_PS_EXT
fds.psfd = &psSockfd;
#endif
#ifndef WIN32
sem_init(&sem_wmm,0,0);
sem_init(&sem_wmm_resp,0,0);
sem_init(&sem_gtgrecv,0,0);
for(index=0; index < WFA_MAX_TRAFFIC_STREAMS; index++) {
sem_init(&sem_iptv_gtgrecv[index],0,0);
}
#endif
DPRINT_INFO(WFA_OUT, "gtgsend = %d, gtgrecv = %d, btSockfd = %d, btRecvSockfd = %d \n", gtgSend, gtgRecv, btSockfd, btRecvSockfd);
/* Receive the asd command here and then tokenize for the command name. Rest of the
* command buffer is copied to pcmdStr for command processing later.
*/
memset(xcCmdBuf, 0, WFA_BUFF_1K);
/*Look for the \n for the end of command*/
strtok((char *)command, "\n");
DPRINT_INFO(WFA_OUT, "message %s. %d\n", command, strlen((char *)command));
strcpy(tempbuf, (char *)command);
strtok(tempbuf, ",");
memcpy(cmdName, strtok_r((char *)command, ",", (char **)&pcmdStr), strlen(tempbuf));
/* The cmdName is appended with '\0' to get rid of the '\r\n' in the command*/
cmdName[strlen(tempbuf)] = '\0';
index = 0;
isFound = 0;
DPRINT_INFO(WFA_OUT, "cmdName %s\n pcmdStr is %s.\n", cmdName, pcmdStr);
/* To check for the command that needs to be executed
* we loop through a function pointer table and mark that as found.
*/
while (nameStr[index].type != -1) {
if (strcmp(nameStr[index].name, cmdName) == 0) {
isFound = 1;
break;
}
index++;
}
memset(pcmdBuf, 0, WFA_BUFF_1K);
/* If the command is not found or if the arguments are invalid, we return
* with invalid response. Otherwise, call for command proc functions where
* the parsing and encoding of the commands take place.
* The function definitions are found in wfa_cmdproc.c file
*/
if (!isFound || (nameStr[index].cmdProcFunc(pcmdStr, pcmdBuf, &cmdLen) == FALSE)) {
asd_sleep(1);
sprintf(gRespStr, "status,INVALID\n");
DPRINT_INFO(WFA_OUT, "Incorrect command syntax\n");
*cmd_len = strlen(gRespStr);
memcpy(command, gRespStr, *cmd_len);
return -1;
}
/* Decode the command to get the tag of the command */
wfaDecodeTLV(pcmdBuf, cmdLen, &xcCmdTag, &cmdLen, parmsVal);
memset(respBuf, 0, WFA_BUFF_512);
respLen = 0;
DPRINT_INFO(WFA_OUT, "total %i cmdLen %i tag %i\n", cmdLen, cmdLen, xcCmdTag);
/* reset two command storages used by control functions */
memset(gCmdStr, 0, WFA_CMD_STR_SZ);
memset(&gGenericResp, 0, sizeof(dutCmdResponse_t));
memset(gRespStr, 0, WFA_BUFF_1K);
/* command process function defined in wfa_cs.c OR wfa_tg.c */
gWfaCmdFuncTbl[xcCmdTag](cmdLen, parmsVal, &respLen, (BYTE *)respBuf);
wfaSetSockFiDesc(&sockSet, &maxfdn1, &fds);
/* If command is other than traffic send command then the reply goes
* from function pointer table in wfaCmdRespProcFuncTbl
* gRespStr variable gets updated there.
*/
tag = ((wfaTLV *)respBuf)->tag;
memcpy(&ret_status, respBuf+4, 4);
DPRINT_INFO(WFA_OUT, "bytes=%i, %i,%i,%x %x %x %x \n", bytesRcvd, ((wfaTLV *)respBuf)->tag,((wfaTLV *)respBuf)->len, *(respBuf+4), *(respBuf+5), *(respBuf+6), *(respBuf+7));
DPRINT_INFO(WFA_OUT, "tag %i \n", tag-WFA_STA_COMMANDS_END);
if ((tag != 0 && tag < WFA_STA_RESPONSE_END) && \
wfaCmdRespProcFuncTbl[tag-WFA_STA_COMMANDS_END] != NULL) {
wfaCmdRespProcFuncTbl[tag-WFA_STA_COMMANDS_END](respBuf);
}
/*Update the command with response buffer and cmd_Len with the response length */
*cmd_len = strlen(gRespStr);
memset(command, 0, *cmd_len);
strncpy((char *)command, gRespStr, (size_t)strlen(gRespStr));
DPRINT_INFO(WFA_OUT, " %s\n", command);
return 0;
}
/* Dut receive is moved into this thread for waiting for the
* UDP packets to arrive in case of file transfer/Multicast (TG)
*/
void wfaRecvStart()
{
int sn, n;
struct timeval ttval;
#ifndef WIN32
struct timeval currTimeVal;
#else
unsigned long nonblocking = 1;
SYSTEMTIME currTimeVal;
#endif
#ifdef WFA_DUT_SYNC
int traffic_start_drift;
#endif
#ifdef WFA_WMM_EXT
#ifdef WFA_WMM_PS_EXT
/*Receiver for WMMPS traffic*/
if(gtgWmmPS > 0){
while (gtgWmmPS > 0)
if(psSockfd > 0 && FD_ISSET(psSockfd, &sockSet))
wfaWmmPowerSaveProcess(psSockfd);
#ifdef WIN32
if(SetEvent(g_hRecvEvent) == 0){
DPRINT_INFO(WFA_OUT, "ResetEvent failure %d \n", GetLastError());
}
while ((WaitForSingleObject(g_RecvhThread, INFINITE) != WAIT_OBJECT_0));
if(g_hRecvEvent != NULL)
CloseHandle(g_hRecvEvent);
#endif /* WIN32 */
}
#endif
#endif
/* calling accept for tcp conection */
if(gTCPsock == 1 && btRecvSockfd == -1)
btRecvSockfd = wfaAcceptTCPConn(gTcpRecvSockfd);
#ifdef WIN32
#else
/*Recieve the traffic untill the user stops the traffic and resets gtgRecv flag*/
while (gtgRecv)
#endif
{
int i = gtgRecv?gtgRecv:gtgTransac;
memset(trafficBuf, 0 , sizeof(trafficBuf));
n = wfaRecvFile(btRecvSockfd, i, (char *)trafficBuf);
/* If it is testing transaction, once it receives a packet
* send a new one right away.
*/
if(n > 0)
{
if(gtgTransac != 0)
{
memset(respBuf, 0, WFA_BUFF_512);
respLen = 0;
if(wfaSendShortFile(btRecvSockfd, gtgTransac,
trafficBuf, n, respBuf, &respLen) == DONE)
{
DPRINT_INFO(WFA_OUT,"Inform to stop\n");
}
}
}
if ((IPTVprof == 1) && (n !=0 )) {
sn = bigEndianBuff2Int(&((tgHeader_t *)trafficBuf)->hdr[8]);
ttval.tv_sec = bigEndianBuff2Int(&((tgHeader_t *)trafficBuf)->hdr[12]);
ttval.tv_usec = bigEndianBuff2Int(&((tgHeader_t *)trafficBuf)->hdr[16]);
#ifndef WIN32
gettimeofday(&currTimeVal, NULL);
#else
GetSystemTime(&currTimeVal);
#endif
#ifdef WFA_DUT_SYNC
/*
* If this is the beginning of the test from packet 10,
* re-adjust the clock for drifting
*/
if (sn >= 10 && sn < 15)
{
double bdftime = wfa_timeval2double(&currTimeVal) - gtgStartSyncTime.dut_time;
if (bdftime > 10.00)
{
//Set the clock to the TM clock again (with delay)
currTimeVal.tv_sec = ttval.tv_sec;
currTimeVal.tv_usec = ttval.tv_usec + gtgStartSyncTime.dtime/2 * 1000000;
// plus drift
traffic_start_drift = clock_drift_ps * bdftime;
currTimeVal.tv_usec += traffic_start_drift;
if(currTimeVal.tv_usec < 0)
{
currTimeVal.tv_sec -=1;
currTimeVal.tv_usec += 1000000;
}
else if(currTimeVal.tv_usec >= 1000000)
{
currTimeVal.tv_sec += 1;
currTimeVal.tv_usec -= 1000000;
}
// clock reset
settimeofday(&currTimeVal, NULL);
}
}
#endif
/*
* take the end2end stats
*/
if (e2eCnt < 6000)
{
tgE2EStats_t *ep = &e2eStats[e2eCnt++];
ep->seqnum = sn;
ep->rsec = ttval.tv_sec;
ep->rusec = ttval.tv_usec;
#ifndef WIN32
ep->lsec = currTimeVal.tv_sec;
ep->lusec = currTimeVal.tv_usec;
#else
ep->lsec = currTimeVal.wSecond;;
ep->lusec = currTimeVal.wMilliseconds * 1000;
#endif
if(ep->lusec < 0)
{
ep->lsec -=1;
ep->lusec += 1000000;
}
else if(ep->lusec >= 1000000)
{
ep->lsec += 1;
ep->lusec -= 1000000;
}
}
} /* if(IPTVprof) */
}/* while (gtgRecv) */
#ifndef WIN32
/* Sending signal to wfaTGRecvStop to close the socket
* since the receive is complete
*/
sem_post(&sem_gtgrecv);
if ((pthread_kill(Thread1, 0)) < 0)
DPRINT_INFO(WFA_OUT,"\nRecvStart thread kill failed\n");
#endif
if(btRecvSockfd != -1){
DPRINT_INFO(WFA_OUT, "cloasing the recv socket \n");
asd_closeSocket( btRecvSockfd);
btRecvSockfd = -1;
}
}
/*Create the thread for receiving the traffic from the TG in the case if File transfer, Multicast and Transaction profiles*/
void wfaRecvThrCreate(void)
{
#ifndef WIN32
//pthread_t Thread1 = -1;
int thrId = -1;
/* Start the thread to receive */
if((thrId = pthread_create(&Thread1, NULL, (void *)wfaRecvStart, NULL)) != 0)
{
DPRINT_ERR(WFA_OUT, "Error created Recv Thread\n");
return;
}
DPRINT_INFO(WFA_OUT, "Created thread %d\n", thrId);
#else
DWORD RecvGenericThread;
g_RecvhThread = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)wfaRecvStart, NULL, 0, &RecvGenericThread);
if (g_RecvhThread == NULL){
DPRINT_INFO(WFA_OUT, "g_RecvhThread not created\n");
} else {
DPRINT_INFO(WFA_OUT, "thread priority = %d\n", GetThreadPriority(g_RecvhThread));
SetThreadPriority(g_RecvhThread, THREAD_PRIORITY_NORMAL + 1);
}
#endif
}
/* Called by send commands of TG */
#ifdef WIN32
void SendFile(LPVOID strid)
#else
void SendFile(void)
#endif
{
#ifdef WIN32
DWORD errnum = 0;
unsigned long nonBlocking = 1;
int sleepTime = 0;
int throttledRate = 0;
int counter = -1;
int throttled_est_cost;
int act_sleep_time;
tgProfile_t *theProf = NULL;
#else
struct timeval lstime;
#endif
int asn = 1, bytesrecv, i;
int nfds;
struct timeval *tovalp;
fd_set socketSet;
tgStream_t *myStream = NULL;
/*
* If the profile is set for file transfer, this will run to
* complete (blocking).
*/
#ifdef WIN32
gtgSend = (BOOL)strid;
#endif
if (gtgSend != 0 && gtgTransac == 0) {
memset(respBuf, 0, WFA_BUFF_512);
respLen = 0;
myStream = findStreamProfile(gtgSend);
if (myStream->profile.startdelay > 0 && myStream->profile.startdelay < 50) {
DPRINT_INFO(WFA_OUT, "sleeping for %d\n", myStream->profile.startdelay);
asd_sleep(myStream->profile.startdelay);
}
#ifndef WIN32
signal(SIGALRM, tmout_stop_send);
alarm(myStream->profile.duration);
#endif
DPRINT_INFO(WFA_OUT, "sendlongfile btsockfd = %d gtgsend = %d\n", btSockfd, gtgSend);
/* For frame rate of zero pump data at high speed. */
if (myStream->profile.rate != 0)
wfaSendLongFile(btSockfd, gtgSend, respBuf, &respLen );
else
wfaImprovePerfSendLongFile(btSockfd, gtgSend, respBuf, &respLen );
if(btSockfd != -1){
asd_closeSocket(btSockfd);
btSockfd= -1;
}
}
/* For transaction test, if it times out without receive a packet,
* here it should initiate/send the next transac packet and count
* a packet loss for the previous one
*/
if (gtgTransac != 0 ) {
gTransactrunLoop = 1;
memset(respBuf, 0, WFA_BUFF_512);
respLen = 0;
myStream = findStreamProfile(gtgSend);
if (myStream->profile.startdelay > 0 && myStream->profile.startdelay < 50) {
DPRINT_INFO(WFA_OUT, "sleeping for %d\n", myStream->profile.startdelay);
asd_sleep(myStream->profile.startdelay);
}
#ifdef WIN32
theProf = &myStream->profile;
if(theProf->duration != 0)
{
DPRINT_INFO(WFA_OUT, "duration %i\n", theProf->duration);
/*
* use this to decide periodical interval sleep time and frames to send
* int the each interval.
* Each device should adopt a own algorithm for better performance
*/
wfaTxSleepTime(theProf->profile, theProf->rate, &sleepTime, &throttledRate);
/*
* Here assumes it takes 20 usec to send a packet and 20 usec to receive one
* Must estimate the cost per packet
* reused concept in SendLongFile in wfa_tg.c
*/
throttled_est_cost = throttledRate * 40; // MUST estimate the cost per ppk
act_sleep_time = sleepTime - throttled_est_cost;
if (act_sleep_time <= 0)
act_sleep_time = sleepTime;
DPRINT_INFO(WFA_OUT, "sleep time %i act_sleep_time %i\n", sleepTime, act_sleep_time);
}
#endif
while(gTransactrunLoop)
{
tovalp = NULL;
if(gtimeOut != 0 || gRegSec != 0) {
/*
* The timer will be set for transaction traffic if no echo is back
* The timeout from the select call force to send a new packet
*/
tovalp = wfaSetTimer(gRegSec, gtimeOut*1000, toutvalp);
}
nfds = 0;
FD_ZERO(&socketSet);
#ifndef WIN32
FD_SET(btSockfd, &socketSet);
#else
FD_SET((u_int)btSockfd, &socketSet);
#endif
if ( (nfds = select(maxfdn1, &socketSet, NULL, NULL, tovalp)) < 0) {
#ifndef WIN32
if (errno == EINTR)
#else
if (WSAGetLastError() == WSAEINTR)
#endif
continue; /* back to for() */
else
#ifndef WIN32
DPRINT_WARNING(WFA_WNG, "select error: %i", errno);
#else
DPRINT_WARNING(WFA_WNG, "select error: %i", WSAGetLastError());
#endif
}
bytesrecv = 0;
#ifdef WFA_WMM_EXT
#ifndef WIN32
/*
* handle end to end time syc
*/
gettimeofday(&lstime, NULL);
/* If your device is BIG ENDIAN, you need to
* modify the the function calls
*/
int2BuffBigEndian(asn++, &((tgHeader_t *)trafficBuf)->hdr[8]);
int2BuffBigEndian(lstime.tv_sec, &((tgHeader_t *)trafficBuf)->hdr[12]);
int2BuffBigEndian(lstime.tv_usec, &((tgHeader_t *)trafficBuf)->hdr[16]);
#endif
#endif
memset(respBuf, 0, WFA_BUFF_512);
respLen = 0;
/* Careful: Since gtgTransac can change outside the scope of this thread
* as 0 we still need to check for this. Otherwise we will end up getting
* no statistics.
*/
#ifdef WIN32
/*
* counter is kept to check the number of frames sending
* If the number of frames sent more than expected (rate*duration)
* do nothing till time out
*/
counter++;
if (counter >= theProf->duration * theProf->rate)
continue;
#endif
if(gtgTransac != 0) {
if(wfaSendShortFile(btSockfd, gtgTransac,
trafficBuf, 0, respBuf, &respLen) == DONE) {
}
}
memset(trafficBuf, 0, sizeof(trafficBuf));
respLen = 0;
i = gtgRecv?gtgRecv:gtgTransac;
#ifdef WIN32
/* Unblock the socket to make wfaRecvFile() non-blocking call in the case of
* Transaction Profile*/
if(ioctlsocket(btSockfd, FIONBIO, &nonBlocking) != 0) {
DPRINT_INFO(WFA_OUT, "Non-Blocking socket failed\n");
}
#endif
if(i!=0) {
bytesrecv = wfaRecvFile(btSockfd, i, (char*)trafficBuf);
if(bytesrecv == 0){
DPRINT_INFO(WFA_OUT, "No packets received\n");
}
}
#ifdef WIN32
/*
* Sleep added to reduce the amount of frame sending
* by dut in case of transaction rpofile
*/
usleep(act_sleep_time);
#endif
} // While
if(btSockfd != -1){
asd_closeSocket(btSockfd);
btSockfd = -1;
}
} // gtgTransac
}
void wfa_dut_deinit()
{
#ifdef WFA_WMM_EXT
int cntThr;
#endif
/* free the buffer that are used in DUT */
free(gStreams);
free(trafficBuf);
free(toutvalp);
free(respBuf);
free(xcCmdBuf);
free(parmsVal);
free(e2eStats);
/* Close sockets */
asd_closeSocket(gagtSockfd);
asd_closeSocket(btSockfd);
asd_closeSocket(btRecvSockfd);
#ifdef WFA_WMM_EXT
for(cntThr = 0; cntThr < WFA_MAX_TRAFFIC_STREAMS; cntThr++)
{
if( tgSockfds[cntThr] != -1 )
{
asd_closeSocket(tgSockfds[cntThr]);
tgSockfds[cntThr] = -1;
}
}
#ifdef WFA_WMM_PS_EXT
asd_closeSocket(psSockfd);
#endif /* WFA_WMM_PS_EXT */
#endif /* WFA_WMM_EXT */
}
/*Create the thread for receiving the WMM traffic from the TG*/
void wfaIPTVRecvThrCreate(int tblIndex)
{
#ifndef WIN32
pthread_t Thread1 = -1;
int thrId = -1;
pthread_attr_t attr;
struct sched_param param;
pthread_attr_init(&attr);
pthread_attr_getschedparam(&attr, &param);
/* Recv thread priority decreased */
param.sched_priority = 10;
pthread_attr_setschedparam(&attr, &param);
/* Start the thread to receive */
if((thrId = pthread_create(&Thread1, &attr, (void *)wfaIPTVRecvStart, &tblIndex)) != 0)
{
DPRINT_ERR(WFA_OUT, "Error created Recv Thread\n");
return;
}
sleep(1);
DPRINT_INFO(WFA_OUT, "Created thread %d\n", thrId);
return;
#else
DWORD RecvGenericThread;
/*Create the thread event for multiple streams and hold the thread handles in the array*/
g_IPTVRecvhThread[tblIndex] = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)wfaIPTVRecvStart, (LPVOID)tblIndex, 0, &RecvGenericThread);
if (g_IPTVRecvhThread[tblIndex] == NULL){
DPRINT_INFO(WFA_OUT, "g_RecvhThread not created\n");
} else {
DPRINT_INFO(WFA_OUT, "thread priority = %d\n", GetThreadPriority(g_IPTVRecvhThread[tblIndex]));
SetThreadPriority(g_IPTVRecvhThread[tblIndex], THREAD_PRIORITY_NORMAL+1);
}
#endif
}
/* This is the receive thread function that receives the traffic in the case of
* IPTV. In the case of multiple streams the the receive happens on multiple sockets
* and waits on multiple events to stop the receive operation.
*/
#ifdef WIN32
void wfaIPTVRecvStart(LPVOID tblidx)
#else
void wfaIPTVRecvStart(void* tblidx)
#endif
{
int sn, n;
struct timeval ttval;
#ifndef WIN32
struct timeval currTimeVal;
#else
unsigned long nonblocking = 1;
SYSTEMTIME currTimeVal;
#endif
#ifdef WFA_DUT_SYNC
int traffic_start_drift;
#endif
int index;
#ifdef WIN32
index = (int)tblidx ;
#else
index = *(int*)tblidx ;
#endif
#ifdef WIN32
DPRINT_INFO(WFA_OUT, "g_recvEvent[index] %i , tblindex = %d, streamid = %d\n ", g_recvEvent[index],
index, gStreams[index].id);
#endif
#ifdef WIN32
/* Each Stream in the WMM is associated with particular event.
* Wait for the particular event to be signalled for that corresponding
* stream.
*/
if (ioctlsocket(tgSockfds[index], FIONBIO, &nonblocking) != 0)
DPRINT_ERR(WFA_OUT, "Error in blocking socket\n");
while ((WaitForSingleObject(g_recvEvent[index],0) != WAIT_OBJECT_0))
#else
/*Recieve the traffic untill the user stops the traffic and resets gtgRecv flag*/
while (gtgRecv)
#endif
{
memset(trafficBuf, 0 , sizeof(trafficBuf));
n = wfaRecvFile(tgSockfds[index], gStreams[index].id, (char *)trafficBuf);
if ((IPTVprof == 1) && (n !=0 )) {
sn = bigEndianBuff2Int(&((tgHeader_t *)trafficBuf)->hdr[8]);
ttval.tv_sec = bigEndianBuff2Int(&((tgHeader_t *)trafficBuf)->hdr[12]);
ttval.tv_usec = bigEndianBuff2Int(&((tgHeader_t *)trafficBuf)->hdr[16]);
#ifndef WIN32
gettimeofday(&currTimeVal, NULL);
#else
GetSystemTime(&currTimeVal);
#endif
/*
* take the end2end stats
*/
if (e2eCnt < 6000)
{
tgE2EStats_t *ep = &e2eStats[e2eCnt++];
ep->seqnum = sn;
ep->rsec = ttval.tv_sec;
ep->rusec = ttval.tv_usec;
#ifndef WIN32
ep->lsec = currTimeVal.tv_sec;
ep->lusec = currTimeVal.tv_usec;
#else
ep->lsec = currTimeVal.wSecond;;
ep->lusec = currTimeVal.wMilliseconds * 1000;
#endif
if(ep->lusec < 0)
{
ep->lsec -=1;
ep->lusec += 1000000;
}
else if(ep->lusec >= 1000000)
{
ep->lsec += 1;
ep->lusec -= 1000000;
}
}
} /* if(IPTVprof) */
}/* while (gtgRecv) */
#ifndef WIN32
/* Sending signal to wfaTGRecvStop to close the socket
* since the receive is complete
*/
sem_post(&sem_iptv_gtgrecv[index]);
pthread_exit(NULL);
#endif
if(tgSockfds[index] != -1){
asd_closeSocket(tgSockfds[index]);
tgSockfds[index] = -1;
}
return ;
}
/*
* wfaSetThreadPrio():
* Set thread priorities for different threads depending on the traffic-class
* Voice- Highest Priority
* Video- One lower than highest
* BackGround- 2 lower than highest
* Best-Effort-3 Lower than highest
*
*/
#ifdef WIN32
void wfaSetIPTVThreadPrio(HANDLE tid, short class)
{
DWORD thrPriority;
switch(class)
{
case TG_WMM_AC_VO:
thrPriority = THREAD_PRIORITY_NORMAL-3;
break;
case TG_WMM_AC_BK:
thrPriority = THREAD_PRIORITY_NORMAL-1;
break;
case TG_WMM_AC_BE:
thrPriority = THREAD_PRIORITY_NORMAL;
break;
case TG_WMM_AC_VI:
thrPriority = THREAD_PRIORITY_NORMAL-2;
default:
/* default */
;
}
DPRINT_INFO(WFA_OUT, " thrPriority = %d\n", thrPriority );
SetThreadPriority(tid, thrPriority);
}
#endif
/* Thread function that is called when we want to send the WMM traffic.
* This thread function sets the TOS bits for the traffic class, sets the priority
* depending on traffic class and then sends the WMM streams
*/
#ifdef WIN32
void SendIPTVFile(LPVOID strid)
#else
void SendIPTVFile(void * strid)
#endif
{
tgStream_t *myStream = NULL;
tgProfile_t *theProfile;
int status = STATUS_COMPLETE;
int mySock = -1;
int streamid;
#ifdef WIN32
streamid = (int)strid;
#else
streamid = *(int*)strid;
#endif
/*
* If the profile is set for file transfer, this will run to
* complete (blocking).
*/
DPRINT_INFO(WFA_OUT, "streamid WMM %d , strid = %d\n", streamid, (int)strid);
memset(respBuf, 0, WFA_BUFF_512);
respLen = 0;
myStream = findStreamProfile(streamid);
if(myStream == NULL)
{
status = STATUS_INVALID;
return ;
}
theProfile = &myStream->profile;
if(theProfile == NULL)
{
status = STATUS_INVALID;
return ;
}
mySock = wfaCreateUDPSock(theProfile->sipaddr, theProfile->sport);
mySock = wfaConnectUDPPeer(mySock, theProfile->dipaddr, theProfile->dport);
/*
* Set packet/socket priority TOS field
*/
wfaTGSetPrio(mySock, theProfile->trafficClass);
#ifdef WIN32
wfaSetIPTVThreadPrio(GetCurrentThread(),theProfile->trafficClass);
#else
wfaSetThreadPrio(pthread_self(), theProfile->trafficClass);
#endif
DPRINT_INFO(WFA_OUT, "mySock %d , index =%d\n", mySock, myStream->tblidx);
if (theProfile->startdelay > 0 && theProfile->startdelay < 50) {
DPRINT_INFO(WFA_OUT, "sleeping for %d\n", theProfile->startdelay);
asd_sleep(theProfile->startdelay);
}
DPRINT_INFO(WFA_OUT, "sendlongfile mySock = %d gtgsend = %d\n", mySock, streamid);
if (theProfile->rate != 0)
wfaSendLongFile(mySock, streamid, respBuf, &respLen );
else
wfaImprovePerfSendLongFile(mySock, streamid, respBuf, &respLen );
if(mySock != -1) {
asd_closeSocket(mySock);
}
}
/* This function is specific to IPTV.
* We take the response from all the streams and update the buffer
* will all the statistics.
*/
void wfaSentStatsResp( BYTE *buf)
{
int i;
tgStream_t *allStreams = gStreams;
dutCmdResponse_t *sendStatsResp = (dutCmdResponse_t *)buf;
if(sendStatsResp == NULL)
return;
for(i = 0; i < WFA_MAX_TRAFFIC_STREAMS; i++) {
if((allStreams->id != 0) && (allStreams->profile.direction == DIRECT_SEND)) {
sendStatsResp->status = STATUS_COMPLETE;
sendStatsResp->streamId = allStreams->id;
memcpy(&sendStatsResp->cmdru.stats, &allStreams->stats, sizeof(tgStats_t));
sendStatsResp++;
}
allStreams++;
}
return;
}