| /**************************************************************************** |
| * (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, ¶m); |
| /* Recv thread priority decreased */ |
| param.sched_priority = 10; |
| pthread_attr_setschedparam(&attr, ¶m); |
| /* 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; |
| } |