| |
| /**************************************************************************** |
| * (c) Copyright 2007 Wi-Fi Alliance. All Rights Reserved |
| * |
| * |
| * LICENSE |
| * |
| * License is granted only to Wi-Fi Alliance members and designated |
| * contractors ($B!H(BAuthorized Licensees$B!I(B)..AN 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$B!G(Bs 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..AN The distribution rights |
| * granted in clause |
| * (ii), above, include distribution to third party companies who will |
| * redistribute the Authorized Licensee$B!G(Bs product to their customers with or |
| * without such third party$B!G(Bs 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..AN Wi-Fi Alliance |
| * reserves all rights not expressly granted herein..AN |
| *.AN |
| * 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. |
| *.AN |
| * 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. |
| *.AN |
| * 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.AN 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_tg.c |
| * Library functions for traffic generator. |
| * They are shared with both TC and DUT agent. |
| * |
| * 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 Release by qhu |
| * 2006/07/10 -- 01.00 Release by qhu |
| * 2006/08/01 -- 01.03 fix ping result unreachable caused by random output |
| * timing. Add 2 seconds wait-time. |
| * change priority level to 40 max while traffic |
| * sending/receiving and 60 min for normal state. |
| * 2006/09/01 -- 01.05 Release by qhu |
| * 2006/10/26 -- 01.06 released by qhu |
| * 2006/10.20 -- bug fixes report by n. ojannen |
| * 2006/12/30 -- bug fix. a potential race condition for transaction test, * reported by c.benson. |
| * 2006/01/11 -- 01.10 released by qhu |
| * 2007/02/15 -- WMM Extension Beta released by qhu, mkaroshi |
| * 2007/03/15 -- develop a better rate control method for sendLong |
| * a major bug fix for File receive that opens socket with |
| * source port. It should be destination port. This was |
| * an early mistake because of interpretation. |
| * 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 |
| * -- revise the code for traffic throttled control |
| * -- take care of EAGAIN error return by socket sendto() -- suggested |
| * by tterhaar |
| */ |
| #ifndef WIN32 |
| #include <sys/socket.h> |
| #include <arpa/inet.h> |
| #include <unistd.h> |
| #include <string.h> |
| #include <stdlib.h> |
| #include <sys/time.h> |
| #include <time.h> |
| #include <signal.h> |
| #include <fcntl.h> |
| #include <netdb.h> |
| #include <pthread.h> |
| #include <semaphore.h> |
| #else |
| #include <stdio.h> |
| #include <string.h> |
| #include <stdlib.h> |
| #endif /* !WIN32 */ |
| #include "wfa_debug.h" |
| #include "wfa_ver.h" |
| #include "wfa_main.h" |
| #include "wfa_tlv.h" |
| #include "wfa_tg.h" |
| #include "wfa_cmds.h" |
| #include "wfa_sock.h" |
| #include "wfa_rsp.h" |
| #include "wfa_wmmps.h" |
| #include "wfa_types.h" |
| #include "wfa_miscs.h" |
| |
| |
| int usedThread = 0; |
| BYTE Send_dutResp[WFA_BUFF_512]; |
| extern void wfaSentStatsResp(BYTE*); |
| #ifndef WIN32 |
| tgWMM_t wmm_thr[WFA_THREADS_NUM]; |
| extern sem_t sem_wmm; |
| extern sem_t sem_wmm_resp; |
| extern sem_t sem_gtgrecv; |
| extern sem_t sem_iptv_gtgrecv[WFA_MAX_TRAFFIC_STREAMS]; |
| int gnumStreams; |
| pthread_t thrid[WFA_MAX_TRAFFIC_STREAMS]; |
| #else |
| extern HANDLE thr_flag_cond; |
| DWORD dwpingThread; |
| DWORD dwGenericThread; |
| HANDLE pinghThread = NULL; |
| HANDLE hTimerThread = NULL; |
| HANDLE hThread1 = NULL; |
| HANDLE hThread2 = NULL; |
| HANDLE g_hRecvEvent = NULL; |
| HANDLE send_event = NULL; |
| extern HANDLE g_RecvhThread; |
| extern HANDLE g_IPTVRecvhThread[WFA_MAX_TRAFFIC_STREAMS]; |
| HANDLE g_recvEvent[WFA_MAX_TRAFFIC_STREAMS]; |
| DWORD dwGenericThread2; |
| DWORD dwGenericThread1; |
| DWORD dwIPTVThread; |
| HANDLE g_TGSndThr[WFA_MAX_TRAFFIC_STREAMS]; |
| extern ULONG exitcode; |
| extern HANDLE processHandle; |
| extern void SendIPTVFile(LPVOID); |
| #endif /* !WIN32 */ |
| extern tgStream_t *gStreams; |
| extern BOOL gtgRecv; |
| extern BOOL gtgSend; |
| extern BOOL gtgTransac; |
| extern int btSockfd; |
| extern int btRecvSockfd; |
| extern int gTcpRecvSockfd; |
| extern int gTCPsock; |
| extern int gtimeOut; |
| extern int gRegSec; |
| extern int isExit; |
| extern int adj_latency; |
| |
| extern tgStream_t *findStreamProfile(int); |
| extern int wfaTrafficSendTo(int, char *, int, struct sockaddr *); |
| extern int wfaTrafficRecv(int, char *, struct sockaddr *); |
| extern unsigned short wfa_defined_debug; |
| extern int tgSockfds[]; |
| |
| extern int wfaTGSetPrio(int sockfd, int tgClass); |
| extern double min_rttime; |
| extern tgSyncTime_t gtgFinishSyncTime; |
| extern BOOL gtgStartSync; |
| extern BOOL gtgFinishSync; |
| extern tgE2EStats_t *e2eStats; |
| extern int e2eCnt; |
| extern void wfaRecvThrCreate(void); |
| extern void wfaIPTVRecvThrCreate(int); |
| extern void SendFile(void); |
| extern int IPTVprof; |
| |
| |
| extern tgSyncTime_t gtgStartSyncTime; |
| #ifdef WFA_WMM_EXT |
| extern tgWMM_t wmm_thr[]; |
| |
| #ifdef WFA_WMM_PS_EXT |
| extern int gtgWmmPS; |
| extern wfaWmmPS_t wmmps_info; |
| extern int psSockfd; |
| extern unsigned long psTxMsg[]; |
| extern unsigned long psRxMsg[]; |
| extern int gtgPsPktRecvd; |
| |
| dutCmdResponse_t gGenericResp; |
| |
| extern void wfaSetDUTPwrMgmt(int mode); |
| void wmmps_wait_state_proc(); |
| |
| #endif /* WFA_WMM_PS_EXT */ |
| |
| #endif /* WFA_WMM_EXT */ |
| |
| static int runLoop = 0; |
| extern volatile int gTransactrunLoop; |
| static int streamId = 0; |
| static int totalTranPkts = 0, sentTranPkts = 0; |
| static int slotCnt = 0; |
| |
| int clock_drift_ps = 0; // drift(usec) per second |
| char e2eResults[32]; |
| |
| extern dutCmdResponse_t gGenericResp; |
| |
| #ifdef WIN32 |
| unsigned int Timerid; |
| extern DWORD Win32_tmout_stop_send(LPVOID num ) ; |
| extern DWORD PingStart(); |
| char PingStr[WFA_BUFF_512]; |
| |
| #ifndef MAX_UDP_LEN |
| #define MAX_UDP_LEN 2048 |
| #endif /*MAX_UDP_LEN*/ |
| |
| #define HIGH_FRAME_RATE_VAL 500 |
| #define SLEEP_DENOMINATOR 5 |
| #endif /*WIN32*/ |
| |
| /* Some devices may only support UDP ECHO and do not have ICMP level ping */ |
| // #define WFA_PING_UDP_ECHO_ONLY 1 |
| |
| /* |
| * Each packet will have the header in the data field |
| */ |
| |
| void wfa_syncd_time(tgSyncTime_t *startTime, tgSyncTime_t *finishTime) |
| { |
| #ifdef WFA_WMM_EXT |
| #ifndef WIN32 |
| DPRINT_INFO(WFA_OUT, "finish tm time %f start tm time %f\n", finishTime->tm_time, startTime->tm_time); |
| DPRINT_INFO(WFA_OUT, "finish dut time %f startTime dut time %f \n", finishTime->dut_time, startTime->dut_time); |
| |
| if(startTime->dtime > finishTime->dtime) |
| startTime->dtime = finishTime->dtime; |
| |
| DPRINT_INFO(WFA_OUT, "roundtrip delay time (usec): %i\n", (int) (1000000 * startTime->dtime)); |
| |
| clock_drift_ps = 1000000*( (finishTime->tm_time - startTime->tm_time)-(finishTime->dut_time - startTime->dut_time) ) / |
| (finishTime->dut_time - startTime->dut_time); |
| |
| DPRINT_INFO(WFA_OUT, "drift per second %i\n", clock_drift_ps); |
| #endif /* !WIN32 */ |
| #endif /* !WFA_WMM_EXT */ |
| } |
| |
| #ifndef WIN32 |
| /* this is to stop sending packets by timer */ |
| void tmout_stop_send(int num) |
| { |
| #ifdef WFA_WMM_EXT |
| int i; |
| #endif /* WFA_WMM_EXT */ |
| DPRINT_INFO(WFA_OUT, "timer fired, stop sending traffic\n"); |
| |
| /* |
| * After runLoop reset, all sendLong will stop |
| */ |
| runLoop = 0; |
| gTransactrunLoop = 0; |
| |
| #ifdef WFA_WMM_EXT |
| /* |
| * once usedThread is reset, WMM tests using multithread is ended |
| * the threads will be reused for the next test. |
| */ |
| usedThread--; |
| //if(usedThread == 0) |
| sem_post(&sem_wmm_resp); |
| #endif /* WFA_WMM_EXT */ |
| |
| /* |
| * once the stream table slot count is reset, it implies that the test |
| * is done. When the next set profile command comes in, it will reset/clean |
| * the stream table. |
| */ |
| slotCnt = 0; |
| |
| /* |
| * The test is for DT3 transaction test. |
| * Timeout to stop it. |
| */ |
| if(gtgTransac != 0) |
| { |
| gtgSend = 0; |
| gtgRecv = 0; |
| gtgTransac = 0; |
| asd_closeSocket(btSockfd); |
| btSockfd = -1; |
| #ifdef WFA_WMM_EXT |
| /* Voice End 2 End Sync */ |
| min_rttime = 0xFFFFFFFF; |
| if(gtgStartSync != 0) |
| { |
| gtgStartSync = 0; |
| DPRINT_INFO(WFA_OUT, "stopping StartSync\n"); |
| } |
| |
| if(gtgFinishSync != 0) |
| { |
| /* derive the clock drift */ |
| wfa_syncd_time(>gStartSyncTime, >gFinishSyncTime); |
| |
| gtgFinishSync = 0; |
| DPRINT_INFO(WFA_OUT, "stoping Finish Sync\n"); |
| } |
| #endif /* WFA_WMM_EXT */ |
| } |
| |
| #ifdef WFA_WMM_EXT |
| /* |
| * all WMM streams also stop |
| */ |
| for(i=0; i<WFA_TRAFFIC_CLASS_NUM; i++) |
| { |
| wmm_thr[i].thr_flag = 0; |
| } |
| #endif /* WFA_WMM_EXT */ |
| /* all alarms need to reset */ |
| |
| alarm(0); |
| } |
| #endif /* !WIN32 */ |
| /* |
| * findStreamProfile(): search existing stream profile by stream id |
| * input: id - stream id; |
| * return: matched stream profile |
| */ |
| tgStream_t *findStreamProfile(int id) |
| { |
| int i; |
| tgStream_t *myStream = gStreams; |
| |
| for(i = 0; i< WFA_MAX_TRAFFIC_STREAMS; i++) |
| { |
| if(myStream->id == id) |
| return myStream; |
| |
| myStream++; |
| } |
| |
| return NULL; |
| } |
| |
| /* |
| * wfaTGSendPing(): Instruct Traffic Generator to send ping packets |
| * |
| */ |
| int wfaTGSendPing(int len, BYTE *caCmdBuf, int *respLen, BYTE *respBuf) |
| { |
| int totalpkts; |
| |
| char cmdStr[WFA_BUFF_128]; |
| |
| int streamid = ++streamId; |
| float interval; /* it could be subseconds/100s minisecond */ |
| tgPingStart_t *staPing = (tgPingStart_t *)caCmdBuf; |
| dutCmdResponse_t *spresp = &gGenericResp; |
| |
| #ifdef WFA_PING_UDP_ECHO_ONLY |
| tgStream_t *myStream = NULL; |
| #endif |
| DPRINT_INFO(WFA_OUT, "Entering wfaTSendPing ...\n"); |
| if(staPing->frameSize == 0) |
| staPing->frameSize = 100; |
| |
| if(staPing->frameRate == 0) |
| staPing->frameRate = 1; |
| |
| interval = (float)1/staPing->frameRate; |
| |
| if(staPing->duration == 0) |
| staPing->duration = 30; |
| |
| switch(staPing->type) |
| { |
| case WFA_PING_ICMP_ECHO: |
| #ifndef WFA_PING_UDP_ECHO_ONLY |
| totalpkts = staPing->duration * staPing->frameRate; |
| |
| #ifndef WIN32 |
| sprintf(cmdStr, "echo streamid=%i > %s; %s -c %i -s %i -q %s >> %s 2>/dev/null&", |
| streamid, SPOUT_FILE_PATH, PING_PATH, totalpkts, staPing->frameSize, staPing->dipaddr, SPOUT_FILE_PATH); |
| system(cmdStr); |
| |
| #endif |
| |
| spresp->status = STATUS_COMPLETE; |
| spresp->streamId = streamid; |
| #else |
| DPRINT_INFO(WFA_OUT, "Only support UDP ECHO\n"); |
| #endif |
| break; |
| |
| case WFA_PING_UDP_ECHO: |
| { |
| #ifdef WFA_PING_UDP_ECHO_ONLY |
| /* |
| * Make this like a transaction testing |
| * Then make it a profile and run it |
| */ |
| myStream = &gStreams[slotCnt++]; |
| memset(myStream, 0, sizeof(tgStream_t)); |
| memcpy(&myStream->profile, caCmdBuf, len); |
| myStream->id = streamid; /* the id start from 1 */ |
| myStream->tblidx = slotCnt-1; |
| |
| btSockfd = wfaCreateUDPSock("127.0.0.1", WFA_UDP_ECHO_PORT); |
| if((btSockfd = wfaConnectUDPPeer(btSockfd, staPing->dipaddr, WFA_UDP_ECHO_PORT)) > 0) |
| { |
| gtgTransac = streamid; |
| gtgSend = streamid; |
| totalTranPkts = 512; |
| sentTranPkts = 0; |
| |
| /* |
| * the framerate here is used to derive the timeout |
| * value for waiting transaction echo responses. |
| */ |
| gtimeOut = MINISECONDS/staPing->frameRate; /* in msec */ |
| gRegSec = 0; |
| |
| /* set to longest time */ |
| if(staPing->duration == 0) |
| staPing->duration = 3600; |
| } |
| #else |
| DPRINT_INFO(WFA_OUT, "Doesn't support UDP Echo\n"); |
| #endif |
| break; |
| } |
| default: |
| { |
| spresp->status = STATUS_INVALID; |
| spresp->streamId = streamid; |
| } |
| } |
| |
| |
| wfaEncodeTLV(WFA_TRAFFIC_SEND_PING_RESP_TLV, sizeof(dutCmdResponse_t), (BYTE *)spresp, respBuf); |
| *respLen = WFA_TLV_HDR_LEN + sizeof(dutCmdResponse_t); |
| |
| return TRUE; |
| } |
| |
| /* |
| * tgStopPing(): Instruct Traffic Generator to stop ping packets |
| * |
| */ |
| int wfaTGStopPing(int len, BYTE *caCmdBuf, int *respLen, BYTE *respBuf) |
| { |
| int streamid = (int )(caCmdBuf+4); |
| dutCmdResponse_t *stpResp = &gGenericResp; |
| FILE *tmpfile = NULL; |
| tgStream_t *myStream; |
| #ifndef WIN32 |
| char strout[WFA_BUFF_256]; |
| #else |
| FILE *stptmpFile = NULL; |
| unsigned int position =0, recbfping =0, sentbfping =0, received =0, sent =0; |
| #endif |
| stpResp->status = STATUS_COMPLETE; |
| |
| DPRINT_INFO(WFA_OUT, "Entering wfaTStopPing ...\n"); |
| if(( gtgTransac == streamid) && (gtgSend == streamid)) |
| { |
| gtgTransac =0; |
| gtgSend = 0; |
| gtimeOut = 0; |
| gtgRecv = 0; |
| #ifndef WIN32 |
| alarm(0); |
| #endif |
| gRegSec = 1; |
| myStream = findStreamProfile(streamid); |
| if(myStream == NULL) |
| { |
| stpResp->status = STATUS_INVALID; |
| } |
| |
| stpResp->cmdru.pingStp.sendCnt = myStream->stats.txFrames; |
| stpResp->cmdru.pingStp.repliedCnt = myStream->stats.rxFrames; |
| } |
| else |
| { |
| #ifndef WIN32 |
| /* Ping will be stopped after the specified duration which will |
| * be specified by the user at the time send ping. For some reason popen with grep |
| * does not work well in Intel PXA with Linux |
| * Use system() call for such things and avoid using popen |
| */ |
| |
| sprintf(strout, "cat %s |grep transmitted | awk '{print $1,$4}' >%s 2>&1", |
| SPOUT_FILE_PATH, TMP_FILE_PATH); |
| |
| system(strout); |
| |
| tmpfile = fopen(TMP_FILE_PATH, "rb"); |
| |
| if (tmpfile == NULL) { |
| DPRINT_ERR(WFA_ERR, "\nShell Cmd:File open error\n"); |
| stpResp->status = STATUS_COMPLETE; |
| |
| wfaEncodeTLV(WFA_TRAFFIC_STOP_PING_RESP_TLV, sizeof(dutCmdResponse_t), (BYTE *)stpResp, respBuf); |
| *respLen = WFA_TLV_HDR_LEN + sizeof(dutCmdResponse_t); |
| |
| return TRUE; |
| } |
| DPRINT_INFO(WFA_OUT,"\nCmdStr is:%s\n", strout); |
| fgets(strout, sizeof(strout), tmpfile); |
| stpResp->cmdru.pingStp.sendCnt = atoi(strout); |
| fgets(strout, sizeof(strout), tmpfile); |
| stpResp->cmdru.pingStp.repliedCnt = atoi(strout); |
| fclose(tmpfile); |
| |
| remove(TMP_FILE_PATH); |
| #else |
| if(exitcode ){ |
| TerminateProcess(processHandle, exitcode); |
| } |
| |
| tmpfile = fopen("temp\\RWL\\pingstats.txt", "r+"); |
| if(tmpfile == NULL) |
| { |
| stpResp->status = STATUS_COMPLETE; |
| |
| wfaEncodeTLV(WFA_TRAFFIC_STOP_PING_RESP_TLV, sizeof(dutCmdResponse_t), (BYTE *)stpResp, respBuf); |
| *respLen = WFA_TLV_HDR_LEN + sizeof(dutCmdResponse_t); |
| |
| return FALSE; |
| } |
| |
| GetStats(tmpfile, L"Sent =", &position, " "); |
| stpResp->cmdru.pingStp.sendCnt = position; |
| GetStats(tmpfile, L"Received =", &position, " "); |
| stpResp->cmdru.pingStp.repliedCnt = position; |
| fclose(tmpfile); |
| #endif |
| } |
| wfaEncodeTLV(WFA_TRAFFIC_STOP_PING_RESP_TLV, sizeof(dutCmdResponse_t), (BYTE *)stpResp, respBuf); |
| *respLen = WFA_TLV_HDR_LEN + sizeof(dutCmdResponse_t); |
| |
| return TRUE; |
| } |
| |
| /* |
| * wfaTGConfig: store the traffic profile setting that will be used to |
| * instruct traffic generation. |
| * input: cmd -- not used |
| * response: send success back to controller |
| * return: success or fail |
| * Note: the profile storage is a global space. |
| */ |
| int wfaTGConfig(int len, BYTE *caCmdBuf, int *respLen, BYTE *respBuf) |
| { |
| int ret = FALSE; |
| tgStream_t *myStream = NULL; |
| dutCmdResponse_t *confResp = &gGenericResp; |
| |
| DPRINT_INFO(WFA_OUT, "slotCnt = %d\n", slotCnt); |
| /* if the stream table over maximum, reset it */ |
| if(slotCnt == WFA_MAX_TRAFFIC_STREAMS) |
| slotCnt = 0; |
| if(slotCnt == 0) |
| { |
| memset(gStreams, 0, WFA_MAX_TRAFFIC_STREAMS*sizeof(tgStream_t)); |
| } |
| |
| DPRINT_INFO(WFA_OUT, "entering tcConfig ...\n"); |
| myStream = &gStreams[slotCnt++]; |
| memset(myStream, 0, sizeof(tgStream_t)); |
| memcpy(&myStream->profile, caCmdBuf, len); |
| myStream->id = ++streamId; /* the id start from 1 */ |
| myStream->tblidx = slotCnt-1; |
| |
| DPRINT_INFO(WFA_OUT, "profile %i direction %i dest ip %s dport %i source %s sport %i rate %i duration %i size %i class %i delay %i\n", myStream->profile.profile, myStream->profile.direction, myStream->profile.dipaddr, myStream->profile.dport, myStream->profile.sipaddr, myStream->profile.sport, myStream->profile.rate, myStream->profile.duration, myStream->profile.pksize, myStream->profile.trafficClass, myStream->profile.startdelay); |
| |
| confResp->status = STATUS_COMPLETE; |
| confResp->streamId = myStream->id; |
| wfaEncodeTLV(WFA_TRAFFIC_AGENT_CONFIG_RESP_TLV, sizeof(dutCmdResponse_t), (BYTE *)confResp, respBuf); |
| *respLen = WFA_TLV_HDR_LEN + sizeof(dutCmdResponse_t); |
| |
| |
| return ret; |
| } |
| |
| /* |
| * wfaTGRecvStart: instruct traffic generator to start receiving |
| * based on a profile |
| * input: cmd -- not used |
| * response: inform controller for "running" |
| * return: success or failed |
| * |
| * In this function we create the UDP sockets for receiving the traffic and then |
| * set the global flag gtgRecv to indicate the receive thread to receive the incoming traffic. |
| * In the case of IPTV profile we multiple Create Events for multiple streams |
| */ |
| |
| int wfaTGRecvStart(int len, BYTE *parms, int *respLen, BYTE *respBuf) |
| { |
| int status = STATUS_COMPLETE, i; |
| int numStreams = len/4; |
| int streamid, so; |
| tgProfile_t *theProfile; |
| tgStream_t *myStream; |
| |
| DPRINT_INFO(WFA_OUT, "entering tgRecvStart %i\n", numStreams); |
| |
| /* |
| * The function wfaSetProcPriority called here is to enhance the real-time |
| * performance for packet receiving. It is only for tuning and optional |
| * to implement |
| */ |
| //wfaSetProcPriority(60); |
| for(i=0; i<numStreams; i++) { |
| memcpy(&streamid, parms+(4*i), 4); /* changed from 2 to 4, bug reported by n.ojanen */ |
| |
| myStream = findStreamProfile(streamid); |
| if(myStream == NULL) |
| { |
| status = STATUS_INVALID; |
| return status; |
| } |
| |
| theProfile = &myStream->profile; |
| if(theProfile == NULL) |
| { |
| status = STATUS_INVALID; |
| return status; |
| } |
| |
| /* calculate the frame interval which is used to derive its jitter */ |
| if(theProfile->rate != 0 && theProfile->rate < 5000) |
| myStream->fmInterval = 1000000/theProfile->rate; /* in ms */ |
| else |
| myStream->fmInterval = 0; |
| |
| if(theProfile->direction != DIRECT_RECV) |
| { |
| status = STATUS_INVALID; |
| return status; |
| } |
| |
| memset(&myStream->stats, 0, sizeof(tgStats_t)); |
| switch(theProfile->profile) |
| { |
| case PROF_FILE_TX: |
| status = STATUS_COMPLETE; |
| btRecvSockfd = wfaCreateUDPSock(theProfile->dipaddr, theProfile->dport); |
| if(btRecvSockfd > 0) { |
| gtgRecv = streamid; |
| #ifdef WIN32 |
| /*Create receive event which is initially non-signalled and |
| * later reset the state of the signal when we call the command |
| * WfaRecvStop() */ |
| if((g_hRecvEvent = CreateEvent(NULL, FALSE, TRUE, NULL)) == 0) { |
| DPRINT_INFO(WFA_OUT, "CreateEvent Failed = %d\r\n", GetLastError()); |
| |
| } |
| #endif |
| /*Creating a thread for receiving traffic in case of File_Transfer, Transac,Multicast */ |
| wfaRecvThrCreate(); |
| } |
| else |
| { |
| status = STATUS_ERROR; |
| wfaEncodeTLV(WFA_TRAFFIC_AGENT_RECV_START_RESP_TLV, 4, (BYTE *)&status, respBuf); |
| *respLen = WFA_TLV_HDR_LEN + 4; |
| |
| return TRUE; |
| } |
| |
| break; |
| case PROF_FILE_TX_TCP: |
| gTCPsock = 1; |
| status = STATUS_COMPLETE; |
| gTcpRecvSockfd = wfaCreateTCPServSock(theProfile->dport); |
| if(gTcpRecvSockfd > 0) { |
| gtgRecv = streamid; |
| #ifdef WIN32 |
| /*Create receive event which is initially non-signalled and |
| * later reset the state of the signal when we call the command |
| * WfaRecvStop() */ |
| if((g_hRecvEvent = CreateEvent(NULL, FALSE, TRUE, NULL)) == 0) { |
| DPRINT_INFO(WFA_OUT, "CreateEvent Failed = %d\r\n", GetLastError()); |
| } |
| #endif |
| /*Creating a thread for receiving traffic in case of File_Transfer, Transac,Multicast */ |
| wfaRecvThrCreate(); |
| } |
| else |
| { |
| status = STATUS_ERROR; |
| wfaEncodeTLV(WFA_TRAFFIC_AGENT_RECV_START_RESP_TLV, 4, (BYTE *)&status, respBuf); |
| respLen = WFA_TLV_HDR_LEN + 4; |
| return TRUE; |
| } |
| break; |
| |
| case PROF_MCAST: |
| status = STATUS_COMPLETE; |
| btRecvSockfd = wfaCreateUDPSock(theProfile->dipaddr, theProfile->dport); |
| DPRINT_INFO(WFA_OUT, "MCAST: RecvStart: btRecvSockfd = %d\r\n",btRecvSockfd); |
| if(btRecvSockfd > 0) |
| { |
| #ifdef WIN32 |
| /*Create receive event which is initially non-signalled and |
| * later automatically reset the state of the signal |
| * when the receive thread exits. */ |
| if((g_hRecvEvent = CreateEvent(NULL, FALSE, TRUE, NULL)) == 0){ |
| DPRINT_INFO(WFA_OUT, "CreateEvent Failed = %d\r\n", GetLastError()); |
| } |
| #endif |
| gtgRecv = streamid; |
| /*Creating a thread for receiving traffic in case of File_Transfer, Transac,Multicast */ |
| wfaRecvThrCreate(); |
| |
| } |
| else |
| { |
| status = STATUS_ERROR; |
| wfaEncodeTLV(WFA_TRAFFIC_AGENT_RECV_START_RESP_TLV, 4, (BYTE *)&status, respBuf); |
| *respLen = WFA_TLV_HDR_LEN + 4; |
| return TRUE; |
| } |
| DPRINT_INFO(WFA_OUT, "MCAST: RecvStart:before McastRecvOpt: btRecvSockfd = %d\r\n",btRecvSockfd); |
| /* set multicast socket option for receiver */ |
| DPRINT_INFO(WFA_OUT, "MCAST: RecvStart: McastRecvOpt: theProfile->dipaddr = %s\r\n",theProfile->dipaddr); |
| so = wfaSetSockMcastRecvOpt(btRecvSockfd, theProfile->dipaddr); |
| if(so < 0) |
| { |
| DPRINT_INFO(WFA_OUT, "MCAST: RecvStart: McastRecvOpt: so<0: btRecvSockfd = %d\r\n",btRecvSockfd); |
| asd_closeSocket(btRecvSockfd); |
| gtgRecv = 0; |
| btRecvSockfd = -1; |
| status = STATUS_ERROR; |
| wfaEncodeTLV(WFA_TRAFFIC_AGENT_RECV_START_RESP_TLV, 4, (BYTE *)&status, respBuf); |
| *respLen = WFA_TLV_HDR_LEN + 4; |
| |
| return TRUE; |
| } |
| |
| break; |
| |
| case PROF_IPTV: |
| status = STATUS_COMPLETE; |
| /*Create multiple streams and get the socket values in the array*/ |
| tgSockfds[myStream->tblidx] = wfaCreateUDPSock(theProfile->dipaddr, theProfile->dport); |
| DPRINT_INFO(WFA_OUT, "sock fd prof iptv = %d , streamid = %d\n", tgSockfds[myStream->tblidx], streamid); |
| if(tgSockfds[myStream->tblidx] > 0){ |
| #ifdef WIN32 |
| /* Create multiple event for different recv streams and get |
| * the values in the array of recv handles, Which will be later used in |
| * WfaRecvStop() to signal the recv thread to stop the recv process. */ |
| if((g_recvEvent[myStream->tblidx] = CreateEvent(NULL, FALSE, TRUE, NULL)) == 0){ |
| DPRINT_INFO(WFA_OUT, "CreateEvent Failed = %d\r\n", GetLastError()); |
| |
| } |
| |
| DPRINT_INFO(WFA_OUT, "recvstart g_recvEvent[myStream->tblidx] = %i\r\n", g_recvEvent[myStream->tblidx]); |
| #endif |
| gtgRecv = streamid; |
| /*Create a thread to receive IPTV traffic. */ |
| wfaIPTVRecvThrCreate(myStream->tblidx); |
| } |
| else |
| { |
| status = STATUS_ERROR; |
| wfaEncodeTLV(WFA_TRAFFIC_AGENT_RECV_START_RESP_TLV, 4, (BYTE *)&status, respBuf); |
| *respLen = WFA_TLV_HDR_LEN + 4; |
| return TRUE; |
| } |
| break; |
| |
| case PROF_TRANSC: |
| case PROF_START_SYNC: |
| case PROF_CALI_RTD: /* Calibrate roundtrip delay */ |
| status = STATUS_COMPLETE; |
| btRecvSockfd = wfaCreateUDPSock(theProfile->sipaddr, theProfile->sport); |
| totalTranPkts = 0xFFFFFFF0; |
| sentTranPkts = 0; |
| if(btRecvSockfd > 0) |
| { |
| gtgTransac = streamid; |
| gtgRecv = streamid; |
| #ifdef WIN32 |
| if((g_hRecvEvent = CreateEvent(NULL, FALSE, TRUE, NULL)) == 0){ |
| DPRINT_INFO(WFA_OUT, "CreateEvent Failed = %d\r\n", GetLastError()); |
| |
| } |
| #endif |
| /*Creating a thread for receiving traffic in case of File_Transfer, Transac,Multicast */ |
| wfaRecvThrCreate(); |
| } |
| else |
| { |
| status = STATUS_ERROR; |
| wfaEncodeTLV(WFA_TRAFFIC_AGENT_RECV_START_RESP_TLV, 4, (BYTE *)&status, respBuf); |
| *respLen = WFA_TLV_HDR_LEN + 4; |
| return TRUE; |
| } |
| |
| break; |
| |
| case PROF_TRANSC_TCP: |
| gTCPsock = 1; |
| status = STATUS_COMPLETE; |
| gTcpRecvSockfd = wfaCreateTCPServSock(theProfile->dport); |
| if(gTcpRecvSockfd > 0) |
| { |
| gtgTransac = streamid; |
| gtgRecv = streamid; |
| #ifdef WIN32 |
| if((g_hRecvEvent = CreateEvent(NULL, FALSE, TRUE, NULL)) == 0){ |
| DPRINT_INFO(WFA_OUT, "CreateEvent Failed = %d\r\n", GetLastError()); |
| } |
| #endif |
| /*Creating a thread for receiving traffic in case of File_Transfer, Transac,Multicast */ |
| wfaRecvThrCreate(); |
| } |
| else |
| { |
| status = STATUS_ERROR; |
| wfaEncodeTLV(WFA_TRAFFIC_AGENT_RECV_START_RESP_TLV, 4, (BYTE *)&status, respBuf); |
| *respLen = WFA_TLV_HDR_LEN + 4; |
| return TRUE; |
| } |
| break; |
| |
| case PROF_UAPSD: |
| #ifdef WFA_WMM_EXT |
| #ifdef WFA_WMM_PS_EXT |
| status = STATUS_COMPLETE; |
| psSockfd = wfaCreateUDPSock(theProfile->dipaddr, WFA_WMMPS_UDP_PORT); |
| |
| wmmps_info.sta_state = 0; |
| wmmps_info.wait_state = WFA_WAIT_STAUT_00; |
| #ifdef WIN32 |
| init_wmmps_thr(); |
| if((g_hRecvEvent = CreateEvent(NULL, FALSE, TRUE, NULL)) == 0){ |
| DPRINT_INFO(WFA_OUT, "CreateEvent Failed = %d\r\n", GetLastError()); |
| |
| } |
| #endif |
| memset(&wmmps_info.psToAddr, 0, sizeof(wmmps_info.psToAddr)); |
| wmmps_info.psToAddr.sin_family = AF_INET; |
| wmmps_info.psToAddr.sin_addr.s_addr = inet_addr(theProfile->sipaddr); |
| wmmps_info.psToAddr.sin_port = htons(theProfile->sport); |
| wmmps_info.reset = 0; |
| |
| wmm_thr[usedThread].thr_flag = streamid; |
| wmmps_info.streamid = streamid; |
| #ifndef WIN32 |
| pthread_mutex_lock(&wmm_thr[usedThread].thr_flag_mutex); |
| pthread_cond_signal(&wmm_thr[usedThread].thr_flag_cond); |
| gtgWmmPS = streamid;; |
| pthread_mutex_unlock(&wmm_thr[usedThread].thr_flag_mutex); |
| usedThread++; |
| #else |
| SetEvent(thr_flag_cond); |
| gtgWmmPS = streamid; |
| #endif /* WIN32 */ |
| gtimeOut = MINISECONDS/10; /* in msec */ |
| gRegSec = 0; |
| |
| /* Create the receiver thread */ |
| wfaRecvThrCreate(); |
| #endif /* WFA_WMM_PS_EXT */ |
| #endif /* WFA_WMM_EXT */ |
| break; |
| } |
| } |
| |
| /* encode a TLV for response for "complete/error ..." */ |
| wfaEncodeTLV(WFA_TRAFFIC_AGENT_RECV_START_RESP_TLV, 4, |
| (BYTE *)&status, respBuf); |
| *respLen = WFA_TLV_HDR_LEN + 4; |
| |
| return TRUE; |
| } |
| |
| /* |
| * tgRecvStop: instruct traffic generator to stop receiving based on a profile |
| * input: cmd -- not used |
| * response: inform controller for "complete" |
| * return: success or failed |
| * |
| * In this function we will reset the gtgRecv flag, close the UDP sockets opened in the |
| * receive start to stop receiving the traffic from TG. We also kill the thread that was created |
| * in receive start*/ |
| int wfaTGRecvStop(int len, BYTE *parms, int *respLen, BYTE *respBuf) |
| { |
| int status = STATUS_COMPLETE, i, j; |
| int numStreams = len/4; |
| int streamid; |
| tgProfile_t *theProfile; |
| tgStream_t *myStream=NULL; |
| dutCmdResponse_t statResp; |
| BYTE dutRspBuf[WFA_BUFF_1K]; |
| int id_cnt = 0; |
| FILE *e2eoutp = NULL; |
| #ifndef WIN32 |
| struct timeval currtime; |
| #else |
| SYSTEMTIME currtime; |
| #endif |
| |
| DPRINT_INFO(WFA_OUT, "entering tgRecvStop\n"); |
| |
| memset(dutRspBuf, 0, WFA_BUFF_1K); |
| |
| for(i=0; i<numStreams; i++) |
| { |
| memcpy(&streamid, parms+(4*i), 4); |
| myStream = findStreamProfile(streamid); |
| if(myStream == NULL) |
| { |
| status = STATUS_INVALID; |
| wfaEncodeTLV(WFA_TRAFFIC_AGENT_RECV_STOP_RESP_TLV, 4, (BYTE *)&status, respBuf); |
| *respLen = WFA_TLV_HDR_LEN + 4; |
| return TRUE; |
| } |
| |
| theProfile = &myStream->profile; |
| if(theProfile == NULL) |
| { |
| status = STATUS_INVALID; |
| wfaEncodeTLV(WFA_TRAFFIC_AGENT_RECV_STOP_RESP_TLV, 4, (BYTE *)&status, respBuf); |
| *respLen = WFA_TLV_HDR_LEN + 4; |
| |
| return TRUE; |
| } |
| |
| if(theProfile->direction != DIRECT_RECV) |
| { |
| status = STATUS_INVALID; |
| wfaEncodeTLV(WFA_TRAFFIC_AGENT_RECV_STOP_RESP_TLV, 4, (BYTE *)&status, respBuf); |
| *respLen = WFA_TLV_HDR_LEN + 4; |
| |
| return TRUE; |
| } |
| /* reset its flags , close sockets */ |
| gtgRecv = 0; |
| switch(theProfile->profile) |
| { |
| case PROF_FILE_TX: |
| case PROF_FILE_TX_TCP: |
| #ifdef WIN32 |
| /*Set the recv event and signal the recv thread function to stop |
| * Receiving the File transfer Traffic, also close the handle of the event*/ |
| 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); |
| |
| #else |
| /* Waiting for signal from wfaRecvStart once the receive |
| * operation is complete in order to close the socket |
| */ |
| sem_wait(&sem_gtgrecv); |
| #endif |
| if(gTcpRecvSockfd != -1) { |
| gTCPsock = 0; |
| asd_closeSocket(gTcpRecvSockfd); |
| gTcpRecvSockfd = -1; |
| } |
| if(btRecvSockfd != -1) { |
| asd_closeSocket(btRecvSockfd); |
| btRecvSockfd = -1; |
| } |
| break; |
| |
| case PROF_MCAST: |
| #ifdef WIN32 |
| /* Set the recv event and signal the recv thread function to stop |
| * Receiving the Multicast Traffic, also close the handle of the event*/ |
| 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); |
| #else |
| /* Waiting for signal from wfaRecvStart once the receive |
| * operation is complete in order to close the socket |
| */ |
| sem_wait(&sem_gtgrecv); |
| #endif |
| if(btRecvSockfd != -1){ |
| asd_closeSocket(btRecvSockfd); |
| btRecvSockfd = -1; |
| } |
| break; |
| |
| case PROF_IPTV: |
| IPTVprof = 0; |
| #ifdef WIN32 |
| /* Set the recv event and signal the recv thread function to stop |
| * Receiving the IPTV traffic in the case of multiple streams, |
| * also close the handles of the multiple events*/ |
| if(SetEvent(g_recvEvent[myStream->tblidx]) == 0) { |
| DPRINT_INFO(WFA_OUT, "ResetEvent failure %d \n", GetLastError()); |
| } |
| while ((WaitForSingleObject(g_IPTVRecvhThread[myStream->tblidx], INFINITE) != WAIT_OBJECT_0)); |
| if(g_recvEvent[myStream->tblidx] != NULL) |
| CloseHandle(g_recvEvent[myStream->tblidx]); |
| #else |
| /* Waiting for signal from wfaIPTVRecvStart once the receive |
| * operation is complete in order to close the socket |
| */ |
| sem_wait(&sem_iptv_gtgrecv[myStream->tblidx]); |
| #endif |
| if(tgSockfds[myStream->tblidx] != -1){ |
| asd_closeSocket(tgSockfds[myStream->tblidx]); |
| tgSockfds[myStream->tblidx] = -1; |
| } |
| /* the following to report the result. There will be a |
| * solution to send it back to TM |
| */ |
| //#endif |
| break; |
| |
| case PROF_TRANSC: |
| case PROF_TRANSC_TCP: |
| case PROF_START_SYNC: |
| case PROF_CALI_RTD: /* Calibrate roundtrip delay */ |
| gtgTransac = 0; |
| #ifdef WIN32 |
| /* Set the recv event and signal the recv thread function to stop |
| * Receiving the Transaction Traffic, also close the handle of the event*/ |
| 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); |
| #else |
| /* Waiting for signal from wfaRecvStart once the receive |
| * operation is complete in order to close the socket |
| */ |
| sem_wait(&sem_gtgrecv); |
| #endif |
| if(gTcpRecvSockfd != -1) { |
| gTCPsock = 0; |
| asd_closeSocket(gTcpRecvSockfd); |
| gTcpRecvSockfd = -1; |
| } |
| if(btRecvSockfd != -1){ |
| asd_closeSocket(btRecvSockfd); |
| btRecvSockfd = -1; |
| } |
| break; |
| case PROF_UAPSD: |
| #ifdef WFA_WMM_EXT |
| #ifdef WFA_WMM_PS_EXT |
| gtgWmmPS = 0; |
| gtgPsPktRecvd = 0; |
| |
| if(psSockfd != -1) |
| { |
| asd_closeSocket(psSockfd); |
| psSockfd = -1; |
| } |
| |
| memset(&wmmps_info, 0, sizeof(wfaWmmPS_t)); |
| |
| wfaSetDUTPwrMgmt(PS_OFF); |
| #endif /* WFA_WMM_PS_EXT */ |
| #endif /* WFA_WMM_EXT */ |
| break; |
| } |
| /* encode a TLV for response for "complete/error ..." */ |
| statResp.status = STATUS_COMPLETE; |
| statResp.streamId = streamid; |
| DPRINT_INFO(WFA_OUT, "stream Id %i rx %i total %i\n", streamid, myStream->stats.rxFrames, myStream->stats.rxPayloadBytes); |
| memcpy(&statResp.cmdru.stats, &myStream->stats, sizeof(tgStats_t)); |
| memcpy((dutRspBuf + i * sizeof(dutCmdResponse_t)), (BYTE *)&statResp, sizeof(dutCmdResponse_t)); |
| id_cnt++; |
| memset(myStream, 0, sizeof(tgStream_t)); |
| } |
| |
| wfaEncodeTLV(WFA_TRAFFIC_AGENT_RECV_STOP_RESP_TLV, (WORD)(id_cnt * sizeof(dutCmdResponse_t)), dutRspBuf, respBuf); |
| |
| /* done here */ |
| *respLen = WFA_TLV_HDR_LEN + numStreams * sizeof(dutCmdResponse_t); |
| return TRUE; |
| } |
| |
| /* |
| * wfaTGSendStart: instruct traffic generator to start sending based on a profile |
| * input: cmd -- not used |
| * response: inform controller for "running" |
| * return: success or failed |
| * |
| * In this function we will create the UDP sockets to send the traffic |
| * and set the global flag gtgSend, increment usedThread for each stream |
| * also set the socket option and type ofservice bits (TOS) in |
| * the case of Multicast profiles . We also |
| * Call the thread Win32_tmout_stop_send() which sleeps for the duration specified in the profile |
| * and then stops the sending of the traffic by resetting the runloop, decrement usedThread . |
| */ |
| int wfaTGSendStart(int len, BYTE *parms, int *respLen, BYTE *respBuf) |
| { |
| int i=0, streamid=0, so; |
| int numStreams = len/4; |
| tgProfile_t *theProfile; |
| tgStream_t *myStream = NULL; |
| dutCmdResponse_t sendResp; |
| #ifdef WIN32 |
| DWORD errnum; |
| #endif |
| #ifdef WFA_DUT_SYNC |
| #ifndef WIN32 |
| pthread_attr_t ptAttr; |
| int ptPolicy; |
| |
| struct sched_param ptSchedParam; |
| struct timeval btime; |
| #else |
| SYSTEMTIME btime; |
| #endif |
| #endif |
| dutCmdResponse_t staSendResp; |
| |
| |
| #ifdef WIN32 |
| /*Create an event which will wait for the traffic streams to complete the send operation */ |
| if((send_event = CreateEvent(NULL, FALSE, TRUE, NULL)) == 0){ |
| DPRINT_INFO(WFA_OUT, "CreateEvent Failed = %d\r\n", GetLastError()); |
| } |
| #else |
| |
| gnumStreams = numStreams; |
| #endif |
| DPRINT_INFO(WFA_OUT, "Entering tgSendStart for %i streams ...\n", numStreams); |
| |
| |
| for(i=0; i<numStreams; i++) |
| { |
| memcpy(&streamid, parms+(4*i), 4); |
| myStream = findStreamProfile(streamid); |
| if(myStream == NULL) |
| { |
| staSendResp.status = STATUS_INVALID; |
| wfaEncodeTLV(WFA_TRAFFIC_AGENT_SEND_RESP_TLV, 4, (BYTE *)&staSendResp, respBuf); |
| *respLen = WFA_TLV_HDR_LEN + 4; |
| return TRUE; |
| } |
| |
| theProfile = &myStream->profile; |
| if(theProfile == NULL) |
| { |
| staSendResp.status = STATUS_INVALID; |
| wfaEncodeTLV(WFA_TRAFFIC_AGENT_SEND_RESP_TLV, 4, (BYTE *)&staSendResp, respBuf); |
| *respLen = WFA_TLV_HDR_LEN + 4; |
| |
| return TRUE; |
| } |
| |
| if(theProfile->direction != DIRECT_SEND) |
| { |
| staSendResp.status = STATUS_INVALID; |
| wfaEncodeTLV(WFA_TRAFFIC_AGENT_SEND_RESP_TLV, 4, (BYTE *)&staSendResp, respBuf); |
| *respLen = WFA_TLV_HDR_LEN + 4; |
| |
| return TRUE; |
| } |
| |
| /* |
| * need to reset the stats |
| */ |
| memset(&myStream->stats, 0, sizeof(tgStats_t)); |
| /* Here increment the usedThread for every time we wish to send a stream |
| * so as to maintain the count of streams that are running at the same time. |
| */ |
| switch(theProfile->profile) |
| { |
| case PROF_FILE_TX: |
| btSockfd = wfaCreateUDPSock(theProfile->sipaddr, theProfile->sport); |
| if((btSockfd=wfaConnectUDPPeer(btSockfd, theProfile->dipaddr, theProfile->dport)) > 0) |
| { |
| gtgSend = streamid; |
| usedThread++; |
| #ifndef WIN32 |
| SendFile(); |
| #else |
| g_TGSndThr[i] = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SendFile, (LPVOID)streamid, 0, &dwIPTVThread); |
| if(g_TGSndThr[i] == NULL){ |
| errnum = GetLastError(); |
| DPRINT_INFO(WFA_OUT, "Could not create the thread1\n"); |
| return TRUE; |
| } |
| #endif |
| } |
| else |
| { |
| gtgSend = 0; |
| staSendResp.status = STATUS_ERROR; |
| wfaEncodeTLV(WFA_TRAFFIC_AGENT_SEND_RESP_TLV, 4, (BYTE *)&staSendResp, respBuf); |
| *respLen = WFA_TLV_HDR_LEN + 4; |
| |
| return TRUE; |
| } |
| |
| break; |
| case PROF_FILE_TX_TCP: |
| if((btSockfd=wfaConnectTCPPeer(theProfile->sport, theProfile->dipaddr, theProfile->dport)) > 0) |
| { |
| gtgSend = streamid; |
| usedThread++; |
| #ifndef WIN32 |
| SendFile(); |
| #else |
| g_TGSndThr[i] = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SendFile, (LPVOID)streamid, 0, &dwIPTVThread); |
| if(g_TGSndThr[i] == NULL){ |
| errnum = GetLastError(); |
| DPRINT_INFO(WFA_OUT, "Could not create the thread1\n"); |
| return TRUE; |
| } |
| #endif |
| } |
| else |
| { |
| gtgSend = 0; |
| staSendResp.status = STATUS_ERROR; |
| wfaEncodeTLV(WFA_TRAFFIC_AGENT_SEND_RESP_TLV, 4, (BYTE *)&staSendResp, respBuf); |
| *respLen = WFA_TLV_HDR_LEN + 4; |
| return TRUE; |
| } |
| break; |
| case PROF_MCAST: |
| btSockfd = wfaCreateUDPSock(theProfile->sipaddr, theProfile->sport); |
| if(btSockfd > 0) |
| { |
| gtgSend = streamid; |
| usedThread ++; |
| } |
| else |
| { |
| staSendResp.status = STATUS_ERROR; |
| |
| wfaEncodeTLV(WFA_TRAFFIC_AGENT_SEND_RESP_TLV, 4, (BYTE *)&staSendResp, respBuf); |
| *respLen = WFA_TLV_HDR_LEN + 4; |
| |
| return TRUE; |
| } |
| |
| so = wfaSetSockMcastSendOpt(btSockfd); |
| if (so < 0) |
| { |
| #ifdef WIN32 |
| DPRINT_INFO(WFA_OUT, "setsockopt for mcast: %d\n",WSAGetLastError()); |
| #endif |
| gtgSend = 0; |
| asd_closeSocket(btSockfd); |
| btSockfd = -1; |
| staSendResp.status = STATUS_ERROR; |
| |
| wfaEncodeTLV(WFA_TRAFFIC_AGENT_SEND_RESP_TLV, 4, (BYTE *)&staSendResp, respBuf); |
| *respLen = WFA_TLV_HDR_LEN + 4; |
| |
| return TRUE; |
| } |
| #ifndef WIN32 |
| SendFile(); |
| #else |
| g_TGSndThr[i] = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SendFile, (LPVOID)gtgSend, 0, &dwIPTVThread); |
| if(g_TGSndThr[i] == NULL){ |
| errnum = GetLastError(); |
| DPRINT_INFO(WFA_OUT, "Could not create the thread1\n"); |
| return TRUE; |
| } |
| #endif |
| break; |
| case PROF_IPTV: |
| |
| IPTVprof = 1; |
| gtgSend = streamid; |
| usedThread++; |
| #ifndef WIN32 |
| |
| /* |
| * singal the thread to Sending WMM traffic |
| */ |
| wmm_thr[usedThread].thr_flag = streamid; |
| pthread_mutex_lock(&wmm_thr[usedThread].thr_flag_mutex); |
| pthread_cond_signal(&wmm_thr[usedThread].thr_flag_cond); |
| pthread_mutex_unlock(&wmm_thr[usedThread].thr_flag_mutex); |
| #else |
| /* Create thread to send the IPTV stream, and store the handle of the thread in the array of the handles. |
| * New thread is created everytime in the case of multiple streams, to send the traffic simultaneously. |
| */ |
| g_TGSndThr[i] = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SendIPTVFile, (LPVOID)streamid, 0, &dwIPTVThread); |
| DPRINT_INFO(WFA_OUT, "Thread create Handle %x\n", g_TGSndThr[i]); |
| if(g_TGSndThr[i] == NULL){ |
| errnum = GetLastError(); |
| DPRINT_INFO(WFA_OUT, "Could not create the thread1\n"); |
| return TRUE; |
| } |
| |
| #endif |
| DPRINT_INFO(WFA_OUT, "wfa_wmm_thread: myProfile->startdelay %i\n", theProfile->startdelay); |
| /* if delay is too long, it must be something wrong */ |
| DPRINT_INFO(WFA_OUT, "sleep for %d\n", theProfile->startdelay); |
| |
| |
| #ifdef WFA_DUT_SYNC |
| //reset clock by estimate drift |
| gettimeofday(&btime, NULL); |
| |
| DPRINT_INFO(WFA_OUT, "btime.tv_sec %u, btime.tv_usec %u last sync time %f\n", (unsigned int) btime.tv_sec, (unsigned int) btime.tv_usec, gtgStartSyncTime.dut_time); |
| |
| double bdftime = wfa_timeval2double(&btime) - (gtgStartSyncTime.dut_time); |
| traf_start_drift = clock_drift_ps * bdftime; |
| |
| DPRINT_INFO(WFA_OUT, "time gap %lf traf_start_drift %i\n", bdftime, traf_start_drift); |
| |
| if(abs(traf_start_drift) > 50) |
| { |
| DPRINT_INFO(WFA_OUT, "traf_start_drift %i\n", traf_start_drift); |
| |
| btime.tv_usec += traf_start_drift; |
| if(btime.tv_usec < 0) |
| { |
| btime.tv_sec -= 1; |
| btime.tv_usec +=1000000; |
| } |
| else if(btime.tv_usec >=1000000) |
| { |
| btime.tv_sec +=1; |
| btime.tv_usec -= 1000000; |
| } |
| |
| // clock get reset |
| settimeofday(&btime, NULL); |
| |
| // ajust the sync time for the next run. |
| gtgStartSyncTime.dut_time = wfa_timeval2double(&btime); |
| clock_drift_ppk = (double) clock_drift_ps / (double) theProfile->rate; |
| } |
| #endif |
| break; |
| |
| case PROF_TRANSC: |
| { |
| btSockfd = wfaCreateUDPSock(theProfile->sipaddr, theProfile->sport); |
| if((btSockfd = wfaConnectUDPPeer(btSockfd, theProfile->dipaddr, theProfile->dport)) > 0) |
| { |
| gtgTransac = streamid; |
| gtgSend = streamid; |
| totalTranPkts = theProfile->rate * theProfile->duration; |
| sentTranPkts = 0; |
| /* |
| * the framerate here is used to derive the timeout |
| * value for waiting transaction echo responses. |
| */ |
| if(theProfile->rate == 0){ |
| gtimeOut = MINISECONDS; /* in msec */ |
| } else { |
| gtimeOut = MINISECONDS/theProfile->rate; /* in msec */ |
| } |
| gRegSec = 0; |
| usedThread++; |
| } |
| else |
| { |
| staSendResp.status = STATUS_ERROR; |
| wfaEncodeTLV(WFA_TRAFFIC_AGENT_SEND_RESP_TLV, 4, (BYTE *)&staSendResp, respBuf); |
| *respLen = WFA_TLV_HDR_LEN + 4; |
| |
| return TRUE; |
| } |
| |
| /* set duration for the test */ |
| #ifndef WIN32 |
| signal(SIGALRM, tmout_stop_send); |
| alarm(theProfile->duration); |
| #else |
| if(hThread1 != NULL){ |
| CloseHandle(hThread1); |
| } |
| hThread1 = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) Win32_tmout_stop_send, (LPVOID)(theProfile->duration *1000), 0, &dwGenericThread1); |
| if(hThread1 == NULL){ |
| errnum = GetLastError(); |
| DPRINT_INFO(WFA_OUT, "Could not create the thread1\n"); |
| return TRUE; |
| } |
| #endif |
| #ifndef WIN32 |
| SendFile(); |
| #else |
| g_TGSndThr[i] = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SendFile, (LPVOID)gtgSend, 0, &dwIPTVThread); |
| if(g_TGSndThr[i] == NULL){ |
| errnum = GetLastError(); |
| DPRINT_INFO(WFA_OUT, "Could not create the thread1\n"); |
| return TRUE; |
| } |
| #endif |
| } |
| break; |
| case PROF_TRANSC_TCP: |
| if((btSockfd=wfaConnectTCPPeer(theProfile->dport, theProfile->dipaddr, theProfile->dport)) > 0) |
| { |
| gtgTransac = streamid; |
| gtgSend = streamid; |
| totalTranPkts = theProfile->rate * theProfile->duration; |
| sentTranPkts = 0; |
| /* |
| * the framerate here is used to derive the timeout |
| * value for waiting transaction echo responses. |
| */ |
| if(theProfile->rate == 0){ |
| gtimeOut = MINISECONDS; /* in msec */ |
| } else { |
| gtimeOut = MINISECONDS/theProfile->rate; /* in msec */ |
| } |
| gRegSec = 0; |
| usedThread++; |
| } |
| else |
| { |
| staSendResp.status = STATUS_ERROR; |
| wfaEncodeTLV(WFA_TRAFFIC_AGENT_SEND_RESP_TLV, 4, (BYTE *)&staSendResp, respBuf); |
| *respLen = WFA_TLV_HDR_LEN + 4; |
| return TRUE; |
| } |
| |
| /* set duration for the test */ |
| #ifndef WIN32 |
| signal(SIGALRM, tmout_stop_send); |
| alarm(theProfile->duration); |
| #else |
| if(hThread1 != NULL){ |
| CloseHandle(hThread1); |
| } |
| hThread1 = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) Win32_tmout_stop_send, (LPVOID)(theProfile->duration *1000), 0, &dwGenericThread1); |
| if(hThread1 == NULL){ |
| errnum = GetLastError(); |
| DPRINT_INFO(WFA_OUT, "Could not create the thread1\n"); |
| return TRUE; |
| } |
| #endif |
| #ifndef WIN32 |
| SendFile(); |
| #else |
| g_TGSndThr[i] = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SendFile, (LPVOID)gtgSend, 0, &dwIPTVThread); |
| if(g_TGSndThr[i] == NULL){ |
| errnum = GetLastError(); |
| DPRINT_INFO(WFA_OUT, "Could not create the thread1\n"); |
| return TRUE; |
| } |
| #endif |
| break; |
| #ifdef WFA_WMM_EXT |
| case PROF_START_SYNC: |
| case PROF_CALI_RTD: |
| { |
| DPRINT_INFO(WFA_OUT, "profile port %i\n", theProfile->sport); |
| btSockfd = wfaCreateUDPSock(theProfile->sipaddr, theProfile->sport); |
| if((btSockfd = wfaConnectUDPPeer(btSockfd, theProfile->dipaddr, theProfile->dport)) > 0) |
| { |
| gtgTransac = streamid; |
| gtgSend = streamid; |
| totalTranPkts = theProfile->rate * theProfile->duration; |
| sentTranPkts = 0; |
| if(theProfile->profile == PROF_START_SYNC) |
| gtgStartSync = streamid; |
| else if(theProfile->profile == PROF_CALI_RTD) |
| gtgFinishSync = streamid; |
| |
| /* |
| * the framerate here is used to derive the timeout |
| * value for waiting transaction echo responses. |
| */ |
| gtimeOut = MINISECONDS/theProfile->rate; /* in msec */ |
| } |
| else |
| { |
| DPRINT_INFO(WFA_OUT, "connection failed\n"); |
| } |
| |
| /* set duration for the test */ |
| #ifndef WIN32 |
| signal(SIGALRM, tmout_stop_send); |
| alarm(theProfile->duration+1); |
| #else |
| if(hThread2 != NULL){ |
| CloseHandle(hThread2); |
| } |
| hThread2 = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) Win32_tmout_stop_send, (LPVOID)((theProfile->duration+1) *1000), 0, &dwGenericThread2); |
| if(hThread2 == NULL){ |
| errnum = GetLastError(); |
| DPRINT_INFO(WFA_OUT, "Could not create the thread1\n"); |
| return TRUE; |
| } |
| #endif |
| } |
| break; |
| #endif |
| default : |
| DPRINT_INFO(WFA_OUT, "No profile match findStreams problem\n"); |
| } |
| } |
| |
| #ifdef WIN32 |
| /* Wait fo the event to be signalled from the timeout function, after all |
| * the threads have finished sending the traffic. |
| * |
| */ |
| |
| Sleep(theProfile->duration*1000); |
| if(SetEvent(send_event)== 0 ){ |
| DPRINT_INFO(WFA_OUT, "SetEvent failure %d \n", GetLastError()); |
| } |
| for(i = 0; i < numStreams; i++) |
| while ((WaitForSingleObject(g_TGSndThr[i], INFINITE) != WAIT_OBJECT_0)); |
| if(IPTVprof == 1) { |
| memset(Send_dutResp, 0, WFA_BUFF_512); |
| wfaSentStatsResp(Send_dutResp); |
| } |
| |
| |
| if(send_event != NULL) { |
| CloseHandle(send_event); |
| send_event = NULL; |
| } |
| |
| #else |
| sem_wait(&sem_wmm_resp); |
| if(IPTVprof) { |
| sem_wait(&sem_wmm); |
| memset(Send_dutResp, 0, WFA_BUFF_512); |
| wfaSentStatsResp(Send_dutResp); |
| } |
| #endif |
| /* update the response */ |
| if(! IPTVprof) { |
| memset(Send_dutResp, 0, WFA_BUFF_512); |
| sendResp.status = STATUS_COMPLETE; |
| sendResp.streamId = myStream->id; |
| memcpy(&sendResp.cmdru.stats, &myStream->stats, sizeof(tgStats_t)); |
| memcpy(Send_dutResp , (BYTE*)&sendResp, sizeof(dutCmdResponse_t)); |
| } |
| wfaEncodeTLV(WFA_TRAFFIC_AGENT_SEND_RESP_TLV, (WORD)(numStreams* sizeof(dutCmdResponse_t)),Send_dutResp, (BYTE*)respBuf); |
| |
| /* done here */ |
| *respLen = WFA_TLV_HDR_LEN + numStreams * sizeof(dutCmdResponse_t); |
| |
| DPRINT_INFO(WFA_OUT, "Leaving send start Check btsockfd = %d\n", btSockfd); |
| return TRUE; |
| } |
| |
| int wfaTGReset(int len, BYTE *parms, int *respLen, BYTE *respBuf) |
| { |
| dutCmdResponse_t *resetResp = &gGenericResp; |
| |
| gtgRecv = 0; |
| gtgSend = 0; |
| /* need to reset all traffic socket fds */ |
| if(btSockfd != -1 ) { |
| asd_closeSocket(btSockfd); |
| btSockfd = -1; |
| } |
| |
| if (btRecvSockfd != -1) { |
| asd_closeSocket(btRecvSockfd); |
| btRecvSockfd = -1; |
| } |
| |
| /* reset the timer alarm if it was armed */ |
| #ifndef WIN32 |
| alarm(0); |
| #endif |
| |
| /* just reset the flags for the command */ |
| usedThread = 0; |
| gtgTransac = 0; |
| totalTranPkts = 0; |
| gtimeOut = 0; |
| gRegSec = 1; /* mewly added */ |
| IPTVprof = 0; |
| runLoop = 0; |
| gTransactrunLoop = 0; |
| |
| #ifdef WFA_WMM_EXT |
| e2eCnt = 0; |
| /* changed now */ |
| memset(e2eStats, 0, 6000*sizeof(tgE2EStats_t)); |
| |
| /* Start : Modified as per BRCM 1.3 ASD */ |
| |
| #ifdef WFA_WMM_PS_EXT |
| gtgWmmPS = 0; |
| gtgPsPktRecvd = 0; |
| |
| if(psSockfd != -1) |
| { |
| asd_closeSocket(psSockfd); |
| psSockfd = -1; |
| } |
| |
| memset(&wmmps_info, 0, sizeof(wfaWmmPS_t)); |
| |
| wfaSetDUTPwrMgmt(PS_OFF); |
| #endif /* WFA_WMM_EXT */ |
| |
| /* End : Modified as per BRCM 1.3 ASD */ |
| |
| |
| #endif |
| |
| /* Also need to clean up WMM streams NOT DONE YET!*/ |
| slotCnt = 0; /* reset stream profile container */ |
| |
| |
| /* encode a TLV for response for "complete ..." */ |
| resetResp->status = STATUS_COMPLETE; |
| wfaEncodeTLV(WFA_TRAFFIC_AGENT_RESET_RESP_TLV, 4, |
| (BYTE *)resetResp, respBuf); |
| *respLen = WFA_TLV_HDR_LEN + 4; |
| |
| return TRUE; |
| } |
| |
| /* |
| * calculate the sleep time for different frame rate |
| * It should be done according the device |
| * This is just one way to adjust the packet delivery speed. If you find |
| * you device does not meet the test requirements, you MUST re-adjust |
| * the method. |
| */ |
| |
| /* The HZ value could be found in the build header file */ |
| /* 100 -> 10ms, 1000 -> 1ms , etc */ |
| #define WFA_KERNEL_MIN_TIMER_RES 100 /* HZ Value for 10 ms */ |
| |
| void wfaTxSleepTime(int profile, int rate, int *sleepTime, int *throttledRate) |
| { |
| *sleepTime=0; /* in microseconds */ |
| /* calculate the sleep time based on frame rate */ |
| |
| /* |
| * Framerate is still required for Multicast traffic |
| * Sleep and hold for a timeout. |
| * |
| * For WMM traffic, the framerate must also need for VO and VI. |
| * the framerate 500, OS may not handle it precisely. |
| */ |
| switch(profile) |
| { |
| /* |
| * Vendor must find ways to better adjust the speed for their own device |
| */ |
| case PROF_MCAST: |
| case PROF_IPTV: |
| case PROF_FILE_TX: |
| case PROF_FILE_TX_TCP: |
| case PROF_TRANSC: |
| case PROF_TRANSC_TCP: |
| if(rate >=100 || rate == 0) |
| { |
| /* |
| * this sleepTime indeed is now being used for time period |
| * to send packets in the throttled Rate. |
| * The idea here is that in each fixed 50 minisecond period, |
| * The device will send rate/20 (rate = packets / second), |
| * then go sleep for rest of time. |
| */ |
| #ifndef WIN32 |
| *sleepTime = 50000; /* fixed 50 miniseconds */ |
| #else |
| *sleepTime = 50; /* CHANGED FROM 50000 TO 50 fixed 50 miniseconds */ |
| #endif |
| *throttledRate = (rate?rate:3000)/20; |
| DPRINT_INFO(WFA_OUT, "Sleep time %i, throttledRate %i\n", *sleepTime, *throttledRate); |
| } |
| else if (rate > 0 && rate <= 50) /* typically for voice */ |
| { |
| *throttledRate = 1; |
| #ifndef WIN32 |
| *sleepTime = 1000*1000/rate; |
| #else |
| *sleepTime = 1000/rate; |
| #endif |
| } |
| break; |
| default: |
| DPRINT_ERR(WFA_ERR, "Incorrect profile\n"); |
| } |
| } |
| |
| #ifndef WIN32 |
| #define WFA_TIME_DIFF(before, after, rtime, dtime) \ |
| dtime = rtime + (after.tv_sec*1000000 + after.tv_usec) - (before.tv_sec*1000000 + before.tv_usec); |
| #else |
| #define WFA_TIME_DIFF(before, after, rtime, dtime) \ |
| dtime = rtime + (after.wSecond +after.wMilliseconds ) - (before.wSecond + before.wMilliseconds ); |
| #endif |
| |
| void buzz_time(int delay) |
| { |
| #ifndef WIN32 |
| struct timeval now, stop; |
| #else |
| SYSTEMTIME now, stop; |
| #endif |
| int diff; |
| int remain_time = 0; |
| |
| #ifndef WIN32 |
| gettimeofday(&stop, 0); |
| |
| stop.tv_usec += delay; |
| if(stop.tv_usec > 1000000) |
| { |
| stop.tv_usec -=1000000; |
| stop.tv_sec +=1; |
| } |
| #else |
| GetSystemTime(&stop); |
| stop.wMilliseconds +=delay; |
| if(stop.wMilliseconds > 1000) |
| { |
| stop.wMilliseconds -=1000; |
| stop.wSecond+=1; |
| } |
| #endif |
| |
| do |
| { |
| #ifndef WIN32 |
| gettimeofday(&now, 0); |
| WFA_TIME_DIFF(now, stop, remain_time, diff); |
| #else |
| GetSystemTime(&now); |
| WFA_TIME_DIFF(stop, now, remain_time, diff); |
| #endif |
| |
| } while(diff>0); |
| } |
| |
| /**************************************************/ |
| /* the actually functions to send/receive packets */ |
| /**************************************************/ |
| |
| /* This is going to be a blocking SEND till it finishes */ |
| int wfaSendLongFile(int mySockfd, int streamid, BYTE *aRespBuf, int *aRespLen) |
| { |
| tgProfile_t *theProf = NULL; |
| tgStream_t *myStream = NULL; |
| struct sockaddr_in toAddr; |
| char *packBuf; |
| int packLen; |
| int bytesSent; |
| dutCmdResponse_t sendResp; |
| int sleepTime = 0; |
| int throttledRate = 0; |
| int errsv; |
| #ifndef WIN32 |
| struct timeval before, after, stime; |
| #else |
| SYSTEMTIME before, after, stime; |
| #endif |
| int difftime = 0; |
| int counter = 0; |
| int throttled_est_cost; |
| int act_sleep_time; |
| |
| DPRINT_INFO(WFA_OUT, "Entering sendLongFile %i\n", streamid); |
| |
| /* find the profile */ |
| myStream = findStreamProfile(streamid); |
| if(myStream == NULL) |
| { |
| return FALSE; |
| } |
| |
| theProf = &myStream->profile; |
| |
| if(theProf == NULL) |
| { |
| return FALSE; |
| } |
| |
| packLen = theProf->pksize; |
| |
| /* allocate a buf */ |
| packBuf = (char *)malloc(packLen); |
| memset(packBuf, 1, packLen); |
| |
| /* fill in the header */ |
| strncpy(packBuf, "1345678", sizeof(tgHeader_t)); |
| |
| /* initialize the destination address */ |
| memset(&toAddr, 0, sizeof(toAddr)); |
| toAddr.sin_family = AF_INET; |
| toAddr.sin_addr.s_addr = inet_addr(theProf->dipaddr); |
| toAddr.sin_port = htons(theProf->dport); |
| |
| /* if a frame rate and duration are defined, then we know |
| * interval for each packet and how many packets it needs to |
| * send. |
| */ |
| |
| 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); |
| /* |
| * alright, we need to raise the priority level of the process |
| * to improve the real-time performance of packet sending. |
| * Since this is for tuning purpose, it is optional implementation. |
| */ |
| //wfaSetProcPriority(60); |
| |
| //interval = 1*1000000/theProf->rate ; // in usec; |
| |
| // Here assumes it takes 20 usec to send a packet |
| throttled_est_cost = throttledRate * 20; // MUST estimate the cost per ppk |
| act_sleep_time = sleepTime - adj_latency - 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); |
| |
| runLoop=1; |
| #ifndef WIN32 |
| while (runLoop) |
| #else |
| while ((WaitForSingleObject(send_event,0) != WAIT_OBJECT_0)) |
| #endif |
| { |
| counter++; |
| /* fill in the counter */ |
| int2BuffBigEndian(counter, &((tgHeader_t *)packBuf)->hdr[8]); |
| |
| |
| /* |
| * the following code is only used to slow down |
| * over fast traffic flooding the buffer and cause |
| * packet drop or the other end not able to receive due to |
| * some limitations, purely for experiment purpose. |
| * each implementation needs some fine tune to it. |
| */ |
| if(counter ==1) |
| { |
| #ifndef WIN32 |
| gettimeofday(&before, NULL); |
| |
| before.tv_usec += sleepTime; |
| if(before.tv_usec > 1000000) |
| { |
| before.tv_usec -= 1000000; |
| before.tv_sec +=1; |
| } |
| #else |
| GetSystemTime(&before); |
| before.wMilliseconds +=sleepTime; |
| if(before.wMilliseconds > 1000) |
| { |
| before.wMilliseconds -= 1000; |
| before.wMilliseconds += 1; |
| |
| } |
| #endif |
| } |
| |
| if(throttledRate != 0) |
| { |
| if(counter%throttledRate == 0) |
| { |
| // sleep that much time |
| #ifndef WIN32 |
| usleep(act_sleep_time); |
| gettimeofday(&after, NULL); |
| difftime = wfa_itime_diff(&after, &before); |
| #else |
| /* In case of win32 for higher frame rate we are sleeping |
| * less to meet the required Tx frame rate efficiency. |
| */ |
| |
| if (theProf->rate > HIGH_FRAME_RATE_VAL) |
| usleep(act_sleep_time / SLEEP_DENOMINATOR); |
| else |
| usleep(act_sleep_time); |
| GetSystemTime(&after); |
| difftime = wfa_Winitime_diff(&before,&after ); |
| #endif |
| // burn the rest to absort latency |
| if(difftime >0) |
| buzz_time(difftime); |
| #ifndef WIN32 |
| before.tv_usec += sleepTime; |
| if(before.tv_usec > 1000000) |
| { |
| before.tv_usec -= 1000000; |
| before.tv_sec +=1; |
| } |
| } |
| } // otherwise, it floods |
| #else |
| before.wMilliseconds += sleepTime; |
| if(before.wMilliseconds > 1000) |
| { |
| before.wMilliseconds -= 1000; |
| before.wSecond +=1; |
| } |
| } |
| } // otherwise, it floods |
| #endif |
| |
| /* |
| * Fill the timestamp to the header. |
| */ |
| #ifndef WIN32 |
| gettimeofday(&stime, NULL); |
| |
| int2BuffBigEndian(stime.tv_sec, &((tgHeader_t *)packBuf)->hdr[12]); |
| int2BuffBigEndian(stime.tv_usec, &((tgHeader_t *)packBuf)->hdr[16]); |
| #else |
| GetSystemTime(&stime); |
| int2BuffBigEndian(stime.wSecond, &((tgHeader_t *)packBuf)->hdr[12]); |
| int2BuffBigEndian(stime.wMilliseconds , &((tgHeader_t *)packBuf)->hdr[16]); |
| #endif |
| |
| if(theProf->profile == PROF_FILE_TX_TCP || theProf->profile == PROF_TRANSC_TCP) |
| bytesSent = wfaCtrlSend(mySockfd, (unsigned char*)packBuf, packLen); |
| else |
| bytesSent = wfaTrafficSendTo(mySockfd, packBuf, packLen, (struct sockaddr *)&toAddr); |
| |
| if(bytesSent != -1) |
| { |
| myStream->stats.txPayloadBytes += bytesSent; |
| myStream->stats.txFrames++ ; |
| } |
| else |
| { |
| #ifndef WIN32 |
| errsv = errno; |
| switch(errsv) |
| { |
| case EAGAIN: |
| case ENOBUFS: |
| //DPRINT_INFO(WFA_OUT, "send error\n"); |
| usleep(50); |
| counter-- ; |
| //myStream->stats.txFrames--; |
| |
| break; |
| case ECONNRESET: |
| runLoop = 0; |
| break; |
| case EPIPE: |
| runLoop = 0; |
| break; |
| default: |
| { |
| DPRINT_INFO(WFA_OUT, "Packet sent error\n"); |
| } |
| } |
| #else |
| errsv = WSAGetLastError(); |
| |
| switch(errsv) |
| { |
| case WSATRY_AGAIN: |
| case WSAENOBUFS: |
| DPRINT_INFO(WFA_OUT, "send error\n"); |
| usleep(1); /* hold for 1 ms */ |
| counter-- ; |
| // myStream->stats.txFrames--; |
| |
| break; |
| case WSAECONNRESET: |
| runLoop = 0; |
| break; |
| case WSAESHUTDOWN: |
| runLoop = 0; |
| break; |
| default: |
| DPRINT_INFO(WFA_OUT, "Packet sent error\n"); |
| break; |
| } |
| #endif |
| } |
| } |
| |
| /* |
| * lower back to an original level if the process is raised previously |
| * It is optional. |
| */ |
| //wfaSetProcPriority(30); |
| } |
| else /* invalid parameters */ |
| { |
| /* encode a TLV for response for "invalid ..." */ |
| sendResp.status = STATUS_INVALID; |
| memcpy(Send_dutResp , (BYTE*)&sendResp, sizeof(dutCmdResponse_t)); |
| return DONE; |
| } |
| |
| gtgSend = 0; |
| |
| /* free the buffer */ |
| free(packBuf); |
| |
| /* Populate the statistics of the sent response in Send_dutResp . In the case of multiple streams |
| * Send_dutResp buffer will have all the response at the end of the send process of all the streams. |
| */ |
| sendResp.status = STATUS_COMPLETE; |
| sendResp.streamId = myStream->id; |
| memcpy(&sendResp.cmdru.stats, &myStream->stats, sizeof(tgStats_t)); |
| memcpy(Send_dutResp , (BYTE*)&sendResp, sizeof(dutCmdResponse_t)); |
| #ifndef WIN32 |
| gnumStreams = gnumStreams -1; |
| if(IPTVprof == 1) { |
| if(gnumStreams == 0) |
| sem_post(&sem_wmm); |
| |
| } |
| #endif |
| return DONE; |
| } |
| |
| /* this only sends one packet a time */ |
| int wfaSendShortFile(int mySockfd, int streamid, BYTE *sendBuf, int pksize, BYTE *aRespBuf, int *aRespLen) |
| { |
| BYTE *packBuf = sendBuf; |
| struct sockaddr_in toAddr; |
| tgProfile_t *theProf; |
| tgStream_t *myStream; |
| int packLen, bytesSent=-1; |
| dutCmdResponse_t sendResp; |
| int errsv; |
| DPRINT_INFO(WFA_OUT, "---------SendShortFile----------:%d\r\n", mySockfd); |
| if(mySockfd == -1) |
| { |
| /* stop */ |
| gtgTransac = 0; |
| gtimeOut = 0; |
| gtgRecv = 0; |
| gtgSend = 0; |
| DPRINT_INFO(WFA_OUT, "stop short traffic\n"); |
| |
| myStream = findStreamProfile(streamid); |
| if(myStream != NULL) |
| { |
| sendResp.status = STATUS_COMPLETE; |
| sendResp.streamId = streamid; |
| memcpy(&sendResp.cmdru.stats, &myStream->stats, sizeof(tgStats_t)); |
| memcpy(Send_dutResp , (BYTE*)&sendResp, sizeof(dutCmdResponse_t)); |
| |
| } |
| |
| return DONE; |
| } |
| |
| /* find the profile */ |
| myStream = findStreamProfile(streamid); |
| |
| theProf = &myStream->profile; |
| if(theProf == NULL) { |
| return FALSE; |
| } |
| |
| if(pksize == 0) |
| packLen = theProf->pksize; |
| else |
| packLen = pksize; |
| |
| memset(&toAddr, 0, sizeof(toAddr)); |
| toAddr.sin_family = AF_INET; |
| toAddr.sin_addr.s_addr = inet_addr(theProf->sipaddr); |
| toAddr.sin_port = htons(theProf->sport); |
| |
| if(gtgRecv && gtgTransac) |
| { |
| toAddr.sin_addr.s_addr = inet_addr(theProf->sipaddr); |
| toAddr.sin_port = htons(theProf->sport); |
| } |
| else if(gtgSend && gtgTransac) |
| { |
| toAddr.sin_addr.s_addr = inet_addr(theProf->dipaddr); |
| toAddr.sin_port = htons(theProf->dport); |
| } |
| |
| int2BuffBigEndian(myStream->stats.txFrames, &((tgHeader_t *)packBuf)->hdr[8]); |
| |
| if(mySockfd != -1) |
| { |
| if(theProf->profile == PROF_FILE_TX_TCP || theProf->profile == PROF_TRANSC_TCP) |
| bytesSent = wfaCtrlSend(mySockfd, (unsigned char*)packBuf, packLen); |
| else |
| bytesSent = wfaTrafficSendTo(mySockfd, (char *)packBuf, packLen, (struct sockaddr *)&toAddr); |
| } |
| |
| |
| if(bytesSent > 0) |
| { |
| myStream->stats.txFrames++; |
| myStream->stats.txPayloadBytes += bytesSent; |
| } |
| else if(bytesSent == -1) |
| { |
| #ifndef WIN32 |
| errsv = errno; |
| switch(errsv) |
| { |
| case EAGAIN: |
| case ENOBUFS: |
| DPRINT_ERR(WFA_ERR, "send error\n"); |
| usleep(1000); /* hold for 1 ms */ |
| // myStream->stats.txFrames--; |
| break; |
| default: |
| break; |
| } |
| #else |
| errsv = WSAGetLastError(); |
| switch(errsv) |
| { |
| case WSATRY_AGAIN: |
| case WSAENOBUFS: |
| usleep(1); /* hold for 1 ms */ |
| // myStream->stats.txFrames--; |
| break; |
| default: |
| DPRINT_INFO(WFA_OUT, "sendto: "); |
| DPRINT_ERR(WFA_ERR, "send error\n"); |
| break; |
| } |
| |
| #endif |
| } |
| sentTranPkts++; |
| DPRINT_INFO(WFA_OUT, "SendShortFile: sentTranPkts = %d\r\n",sentTranPkts); |
| return DONE; |
| } |
| |
| /* In this function we always receive from a specified IP address and Port. We change the receive |
| * socket from blocking to non-blocking */ |
| int wfaRecvFile(int mySockfd, int streamid, char *recvBuf) |
| { |
| /* how many packets are received */ |
| char *packBuf = recvBuf; |
| struct sockaddr_in fromAddr; |
| tgProfile_t *theProf; |
| tgStream_t *myStream; |
| int bytesRecvd; |
| #ifndef WIN32 |
| int ioflags; |
| #endif |
| int lostPkts; |
| |
| /* find the profile */ |
| myStream = findStreamProfile(streamid); |
| |
| theProf = &myStream->profile; |
| if(theProf == NULL) |
| { |
| return FALSE; |
| } |
| |
| memset(packBuf, 0, MAX_UDP_LEN); |
| |
| memset(&fromAddr, 0, sizeof(fromAddr)); |
| fromAddr.sin_family = AF_INET; |
| fromAddr.sin_addr.s_addr = inet_addr(theProf->dipaddr); |
| fromAddr.sin_port = htons(theProf->dport); |
| |
| if(gtgRecv && gtgTransac) |
| { |
| fromAddr.sin_addr.s_addr = inet_addr(theProf->sipaddr); |
| fromAddr.sin_port = htons(theProf->sport); |
| } |
| else if(gtgSend && gtgTransac) |
| { |
| fromAddr.sin_addr.s_addr = inet_addr(theProf->dipaddr); |
| fromAddr.sin_port = htons(theProf->dport); |
| } |
| |
| /* get current flags setting */ |
| #ifndef WIN32 |
| ioflags = fcntl(mySockfd, F_GETFL, 0); |
| |
| /* set only BLOCKING flag to non-blocking */ |
| fcntl(mySockfd, F_SETFL, ioflags | O_NONBLOCK); |
| |
| #endif |
| |
| /* it is always to receive at least one packet, in case more in the |
| queue, just pick them up. |
| */ |
| if(theProf->profile == PROF_FILE_TX_TCP || theProf->profile == PROF_TRANSC_TCP) |
| bytesRecvd = wfaCtrlRecv(mySockfd, (unsigned char*)packBuf); |
| else |
| bytesRecvd = wfaTrafficRecv(mySockfd, packBuf, (struct sockaddr *)&fromAddr); |
| |
| if(bytesRecvd > 0) |
| { |
| myStream->stats.rxFrames++; |
| myStream->stats.rxPayloadBytes +=bytesRecvd; |
| #ifdef WIN32 |
| /* |
| * This sleep is added to avoid driver crash at Dut side when TG sends |
| * traffic at unlimited frame rate for longer duration |
| */ |
| if (myStream->stats.rxFrames % 20 == 0) |
| usleep(1); |
| #endif |
| /* |
| * Get the lost packet count |
| */ |
| lostPkts =bigEndianBuff2Int(&((tgHeader_t *)packBuf)->hdr[8]) - 1 - myStream->lastPktSN; |
| myStream->stats.lostPkts += lostPkts; |
| myStream->lastPktSN = bigEndianBuff2Int(&((tgHeader_t *)packBuf)->hdr[8]); |
| } |
| return (bytesRecvd); |
| } |
| |
| /* This is going to be a blocking SEND till it finishes |
| * The code is optimized for to achieve higher through put |
| * by using hardcoded header buffers |
| */ |
| int wfaImprovePerfSendLongFile(int mySockfd, int streamid, BYTE *aRespBuf, int *aRespLen) |
| { |
| tgProfile_t *theProf = NULL; |
| tgStream_t *myStream = NULL; |
| struct sockaddr_in toAddr; |
| char *packBuf; |
| int packLen; |
| int bytesSent; |
| dutCmdResponse_t sendResp; |
| int errsv; |
| #ifdef WIN32 |
| SYSTEMTIME stime; |
| #else |
| struct timeval stime; |
| #endif |
| |
| int counter = 0; |
| |
| /* find the profile */ |
| myStream = findStreamProfile(streamid); |
| if(myStream == NULL) |
| { |
| return FALSE; |
| } |
| |
| theProf = &myStream->profile; |
| |
| if(theProf == NULL || theProf->duration == 0) /* invalid parameters */ |
| { |
| /* encode a TLV for response for "invalid ..." */ |
| sendResp.status = STATUS_INVALID; |
| memcpy(Send_dutResp , (BYTE*)&sendResp, sizeof(dutCmdResponse_t)); |
| return DONE; |
| } |
| |
| packLen = theProf->pksize; |
| |
| /* allocate a buf */ |
| packBuf = (char *)malloc(packLen); |
| memset(packBuf, 1, packLen); |
| |
| /* fill in the header */ |
| strncpy(packBuf, "1345678", sizeof(tgHeader_t)); |
| |
| /* initialize the destination address */ |
| memset(&toAddr, 0, sizeof(toAddr)); |
| toAddr.sin_family = AF_INET; |
| toAddr.sin_addr.s_addr = inet_addr(theProf->dipaddr); |
| toAddr.sin_port = htons(theProf->dport); |
| |
| /* if a frame rate and duration are defined, then we know |
| * interval for each packet and how many packets it needs to |
| * send. |
| */ |
| |
| runLoop=1; |
| /* |
| * Fill the hardcoded timestamp to the header. |
| */ |
| #ifndef WIN32 |
| gettimeofday(&stime, NULL); |
| int2BuffBigEndian(stime.tv_sec, &((tgHeader_t *)packBuf)->hdr[12]); |
| int2BuffBigEndian(stime.tv_usec, &((tgHeader_t *)packBuf)->hdr[16]); |
| #else |
| GetSystemTime(&stime); |
| int2BuffBigEndian(stime.wSecond, &((tgHeader_t *)packBuf)->hdr[12]); |
| int2BuffBigEndian(stime.wMilliseconds , &((tgHeader_t *)packBuf)->hdr[16]); |
| #endif |
| |
| #ifndef WIN32 |
| while (runLoop) |
| #else |
| while ((WaitForSingleObject(send_event,0) != WAIT_OBJECT_0)) |
| #endif |
| { |
| counter++; |
| /* fill in the counter */ |
| int2BuffBigEndian(counter, &((tgHeader_t *)packBuf)->hdr[8]); |
| /* |
| * the following code is only used to slow down |
| * over fast traffic flooding the buffer and cause |
| * packet drop or the other end not able to receive due to |
| * some limitations, purely for experiment purpose. |
| * each implementation needs some fine tune to it. |
| */ |
| if(theProf->profile == PROF_FILE_TX_TCP || theProf->profile == PROF_TRANSC_TCP) |
| bytesSent = wfaCtrlSend(mySockfd, (unsigned char*)packBuf, packLen); |
| else |
| bytesSent = wfaTrafficSendTo(mySockfd, packBuf, packLen, (struct sockaddr *)&toAddr); |
| |
| if(bytesSent > 0) |
| { |
| myStream->stats.txPayloadBytes += bytesSent; |
| myStream->stats.txFrames++ ; |
| } |
| else if(bytesSent == -1) |
| { |
| #ifndef WIN32 |
| errsv = errno; |
| switch(errsv) |
| { |
| case EAGAIN: |
| case ENOBUFS: |
| //DPRINT_INFO(WFA_OUT, "send error\n"); |
| usleep(50); |
| counter-- ; |
| //myStream->stats.txFrames--; |
| |
| break; |
| case ECONNRESET: |
| runLoop = 0; |
| break; |
| case EPIPE: |
| runLoop = 0; |
| break; |
| default: |
| { |
| DPRINT_INFO(WFA_OUT, "Packet sent error\n"); |
| } |
| } |
| #else |
| errsv = WSAGetLastError(); |
| |
| switch(errsv) |
| { |
| case WSATRY_AGAIN: |
| case WSAENOBUFS: |
| DPRINT_INFO(WFA_OUT, "send error\n"); |
| usleep(1); /* hold for 1 ms */ |
| counter-- ; |
| break; |
| case WSAECONNRESET: |
| runLoop = 0; |
| break; |
| case WSAESHUTDOWN: |
| runLoop = 0; |
| break; |
| default: |
| DPRINT_INFO(WFA_OUT, "Packet sent error\n"); |
| break; |
| } |
| #endif |
| } |
| #ifdef WIN32 |
| /* In case of win32 we are sleeping after every 20 frame sent. |
| * This is to avoid driver hang in case of windows mobile |
| */ |
| if (myStream->stats.txFrames % 20 == 0) |
| usleep(1); |
| #endif |
| } |
| gtgSend = 0; |
| |
| /* free the buffer */ |
| free(packBuf); |
| |
| /* Populate the statistics of the sent response in Send_dutResp . In the case of multiple streams |
| * Send_dutResp buffer will have all the response at the end of the send process of all the streams. |
| */ |
| sendResp.status = STATUS_COMPLETE; |
| sendResp.streamId = myStream->id; |
| memcpy(&sendResp.cmdru.stats, &myStream->stats, sizeof(tgStats_t)); |
| memcpy(Send_dutResp , (BYTE*)&sendResp, sizeof(dutCmdResponse_t)); |
| |
| #ifndef WIN32 |
| gnumStreams = gnumStreams -1; |
| if(IPTVprof == 1) { |
| if(gnumStreams == 0) |
| sem_post(&sem_wmm); |
| |
| } |
| #endif |
| return DONE; |
| } |
| |
| #ifdef WIN32 |
| DWORD Win32_tmout_stop_send(LPVOID num ) |
| { |
| int i; |
| |
| Sleep((DWORD)num); |
| DPRINT_INFO(WFA_OUT, "timer fired, stop sending traffic\n"); |
| /* |
| * After runLoop reset, all sendLong will stop |
| */ |
| runLoop = 0; |
| /*Reset to stop sending the short file*/ |
| gTransactrunLoop = 0; |
| /* |
| * Decrement the usedThread everytime the timeout function is called . |
| */ |
| --usedThread ; |
| |
| if(usedThread ==0 && IPTVprof == 1) { |
| asd_sleep(1); |
| /* Set the event when there are no more streams to send the traffic */ |
| if(SetEvent(send_event)== 0 ){ |
| DPRINT_INFO(WFA_OUT, "SetEvent failure %d \n", GetLastError()); |
| } |
| /* |
| * all WMM streams also stop |
| */ |
| for(i=0; i<WFA_TRAFFIC_CLASS_NUM; i++) |
| { |
| if(g_TGSndThr[i] != NULL){ |
| CloseHandle(g_TGSndThr[i]); |
| g_TGSndThr[i] = NULL; |
| } |
| } |
| } |
| /* |
| * once the stream table slot count is reset, it implies that the test |
| * is done. When the next set profile command comes in, it will reset/clean |
| * the stream table. |
| */ |
| slotCnt = 0; |
| |
| /* |
| * The test is for DT3 transaction test. |
| * Timeout to stop it. |
| */ |
| if(gtgTransac != 0) |
| { |
| gtgTransac = 0; |
| gtgSend = 0; |
| gtimeOut = 0; |
| gRegSec = 1; |
| gtgRecv = 0; |
| } |
| |
| return 0; |
| } |
| |
| #endif |