int wfaGetifAddr(char *ifname, struct sockaddr_in *sa) { struct ifreq ifr; int fd = wSOCKET(PF_INET, SOCK_DGRAM, IPPROTO_IP); if(fd < 0) { DPRINT_ERR(WFA_ERR, "socket open error\n"); return WFA_FAILURE; } wSTRCPY(ifr.ifr_name, ifname); ifr.ifr_addr.sa_family = AF_INET; if(wIOCTL(fd, SIOCGIFADDR, &ifr) == 0) { wMEMCPY(sa, (struct sockaddr_in *)&ifr.ifr_addr, sizeof(struct sockaddr_in)); } else { return WFA_FAILURE; } wCLOSE(fd); return WFA_FAILURE; }
/* * 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; /* if the stream table over maximum, reset it */ if(slotCnt == WFA_MAX_TRAFFIC_STREAMS) slotCnt = 0; if(slotCnt == 0) { printf("resetting stream table\n"); wMEMSET(gStreams, 0, WFA_MAX_TRAFFIC_STREAMS*sizeof(tgStream_t)); } DPRINT_INFO(WFA_OUT, "entering tcConfig ...\n"); myStream = &gStreams[slotCnt++]; wMEMSET(myStream, 0, sizeof(tgStream_t)); wMEMCPY(&myStream->profile, caCmdBuf, len); myStream->id = ++streamId; /* the id start from 1 */ myStream->tblidx = slotCnt-1; #if 0 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); #endif 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; }
BOOL wfaGetTLVvalue(int value_len, BYTE *tlv_data, BYTE *pvalue) { if(tlv_data == NULL) return FALSE; wMEMCPY(pvalue, tlv_data+WFA_TLV_HEAD_LEN, value_len); return TRUE; }
/* * collects the traffic statistics from other threads and * sends the collected information to CA */ void wfaSentStatsResp(int sock, BYTE *buf) { int i, total=0, pkLen; tgStream_t *allStreams = gStreams; dutCmdResponse_t *sendStatsResp = (dutCmdResponse_t *)buf, *first; char buff[WFA_RESP_BUF_SZ]; if(sendStatsResp == NULL) return; first = sendStatsResp; for(i = 0; i < WFA_MAX_TRAFFIC_STREAMS; i++) { if((allStreams->id != 0) && (allStreams->profile.direction == DIRECT_SEND) && (allStreams->state == WFA_STREAM_ACTIVE)) { sendStatsResp->status = STATUS_COMPLETE; sendStatsResp->streamId = allStreams->id; printf("stats stream id %i\n", allStreams->id); wMEMCPY(&sendStatsResp->cmdru.stats, &allStreams->stats, sizeof(tgStats_t)); sendStatsResp++; total++; } allStreams->state = WFA_STREAM_INACTIVE; allStreams++; } #if 1 printf("%u %u %llu %llu\n", first->cmdru.stats.txFrames, first->cmdru.stats.rxFrames, first->cmdru.stats.txPayloadBytes, first->cmdru.stats.rxPayloadBytes); #endif wfaEncodeTLV(WFA_TRAFFIC_AGENT_SEND_RESP_TLV, total*sizeof(dutCmdResponse_t), (BYTE *)first, (BYTE *)buff); pkLen = WFA_TLV_HDR_LEN + total*sizeof(dutCmdResponse_t); printf("pkLen %i\n", pkLen); #if 0 for(i = 0; i< pkLen; i++) printf("%x ", buff[i]); printf("\n"); #endif if(wfaCtrlSend(sock, (BYTE *)buff, pkLen) != pkLen) { DPRINT_WARNING(WFA_WNG, "wfaCtrlSend Error\n"); } return; }
/* * wfaEncodeTLV(): Encoding a packet to TLV format * input: the_tag - packet type * the_len - the value length * the_value - the value buffer * * output: tlv_data - encoded TLV packet buffer. Caller must allocate the buffer */ BOOL wfaEncodeTLV(WORD the_tag, WORD the_len, BYTE *the_value, BYTE *tlv_data) { void *data = tlv_data; ((wfaTLV *)data)->tag = the_tag; ((wfaTLV *)data)->len = the_len; if(the_value != NULL && the_len != 0) wMEMCPY((data+4), (BYTE *)the_value, the_len); return TRUE; }
BOOL wfaDecodeTLV(BYTE *tlv_data, int tlv_len, WORD *ptag, int *pval_len, BYTE *pvalue) { wfaTLV *data = (wfaTLV *)tlv_data; if(pvalue == NULL) { sigma_dut_print(DUT_MSG_ERROR, "Parm buf invalid\n"); return FALSE; } *ptag = data->tag; *pval_len = data->len; if(tlv_len < *pval_len) return FALSE; if(*pval_len != 0 && *pval_len < MAX_PARMS_BUFF) { wMEMCPY(pvalue, tlv_data+4, *pval_len); } return TRUE; }
/* 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; struct timeval before, after,af; int difftime = 0, counter = 0; struct timeval stime; int throttled_est_cost; int act_sleep_time; gettimeofday(&af,0); 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); wMEMSET(packBuf, 1, packLen); /* fill in the header */ wSTRNCPY(packBuf, "1345678", sizeof(tgHeader_t)); /* initialize the destination address */ wMEMSET(&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) { printf("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; if (act_sleep_time <= 0) act_sleep_time = sleepTime; printf("sleep time %i act_sleep_time %i\n", sleepTime, act_sleep_time); runLoop=1; while(runLoop) { 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) { wGETTIMEOFDAY(&before, NULL); before.tv_usec += sleepTime; if(before.tv_usec > 1000000) { before.tv_usec -= 1000000; before.tv_sec +=1; } } if(throttledRate != 0) { if(counter%throttledRate == 0) { wGETTIMEOFDAY(&after, NULL); difftime = wfa_itime_diff(&after, &before); if(difftime > adj_latency) { // too much time left, go sleep wUSLEEP(difftime-adj_latency); wGETTIMEOFDAY(&after, NULL); difftime = wfa_itime_diff(&after, &before); } // burn the rest to absort latency if(difftime >0) buzz_time(difftime); before.tv_usec += sleepTime; if(before.tv_usec > 1000000) { before.tv_usec -= 1000000; before.tv_sec +=1; } } } // otherwise, it floods /* * Fill the timestamp to the header. */ wGETTIMEOFDAY(&stime, NULL); int2BuffBigEndian(stime.tv_sec, &((tgHeader_t *)packBuf)->hdr[12]); int2BuffBigEndian(stime.tv_usec, &((tgHeader_t *)packBuf)->hdr[16]); bytesSent = wfaTrafficSendTo(mySockfd, packBuf, packLen, (struct sockaddr *)&toAddr); if(bytesSent != -1) { myStream->stats.txPayloadBytes += bytesSent; myStream->stats.txFrames++ ; } else { int errsv = errno; switch(errsv) { case EAGAIN: case ENOBUFS: DPRINT_ERR(WFA_ERR, "send error\n"); wUSLEEP(1000); /* hold for 1 ms */ counter-- ; myStream->stats.txFrames--; break; case ECONNRESET: runLoop = 0; break; case EPIPE: runLoop = 0; break; default: perror("sendto: "); DPRINT_ERR(WFA_ERR, "Packet sent error\n"); } } } /* * 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; wfaEncodeTLV(WFA_TRAFFIC_AGENT_SEND_RESP_TLV, 4, (BYTE *)&sendResp, (BYTE *)aRespBuf); /* done here */ *aRespLen = WFA_TLV_HDR_LEN + 4; return DONE; } gtgSend = 0; /* free the buffer */ wFREE(packBuf); //printf("done sending long\n"); /* return statistics */ sendResp.status = STATUS_COMPLETE; sendResp.streamId = myStream->id; wMEMCPY(&sendResp.cmdru.stats, &myStream->stats, sizeof(tgStats_t)); #if 0 DPRINT_INFO(WFA_OUT, "stream Id %u tx %u total %llu\n", myStream->id, myStream->stats.txFrames, myStream->stats.txPayloadBytes); #endif wfaEncodeTLV(WFA_TRAFFIC_AGENT_SEND_RESP_TLV, sizeof(dutCmdResponse_t), (BYTE *)&sendResp, (BYTE *)aRespBuf); *aRespLen = WFA_TLV_HDR_LEN + sizeof(dutCmdResponse_t); return DONE; }
/* * wfaTGSendStart: instruct traffic generator to start sending based on a profile * input: cmd -- not used * response: inform controller for "running" * return: success or failed */ int wfaTGSendStart(int len, BYTE *parms, int *respLen, BYTE *respBuf) { int i=0, streamid=0; int numStreams = len/4; tgProfile_t *theProfile; tgStream_t *myStream = NULL; dutCmdResponse_t staSendResp; DPRINT_INFO(WFA_OUT, "Entering tgSendStart for %i streams ...\n", numStreams); for(i=0; i<numStreams; i++) { wMEMCPY(&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 */ wMEMSET(&myStream->stats, 0, sizeof(tgStats_t)); // mark the stream active; myStream->state = WFA_STREAM_ACTIVE; switch(theProfile->profile) { case PROF_FILE_TX: case PROF_MCAST: case PROF_TRANSC: gtgTransac = streamid; gtgSend = streamid; case PROF_CALI_RTD: gtgCaliRTD = streamid; case PROF_IPTV: gtgSend = streamid; /* * singal the thread to Sending WMM traffic */ //if(usedThread < wmm_thr[usedThread].thr_flag = streamid; wPT_MUTEX_LOCK(&wmm_thr[usedThread].thr_flag_mutex); wPT_COND_SIGNAL(&wmm_thr[usedThread].thr_flag_cond); wPT_MUTEX_UNLOCK(&wmm_thr[usedThread].thr_flag_mutex); usedThread++; //wfaSetProcPriority(90); break; } } *respLen = 0; 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 */ int wfaTGRecvStop(int len, BYTE *parms, int *respLen, BYTE *respBuf) { int status = STATUS_COMPLETE, i; int numStreams = len/4; unsigned int streamid; tgProfile_t *theProfile; tgStream_t *myStream=NULL; dutCmdResponse_t statResp; BYTE dutRspBuf[WFA_RESP_BUF_SZ]; int id_cnt = 0; DPRINT_INFO(WFA_OUT, "entering tgRecvStop with length %d\n",len); /* in case that send-stream not done yet, an optional delay */ while(sendThrId != 0) sleep(1); /* * After finishing the receiving command, it should lower itself back to * normal level. It is optional implementation if it is not called * while it starts receiving for raising priority level. */ //wfaSetProcPriority(30); wMEMSET(dutRspBuf, 0, WFA_RESP_BUF_SZ); for(i=0; i<numStreams; i++) { wMEMCPY(&streamid, parms+(4*i), 4); printf(" stop stream id %i\n", streamid); 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; printf("stream table empty\n"); continue; } 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 */ switch(theProfile->profile) { case PROF_TRANSC: case PROF_CALI_RTD: gtgTransac = 0; case PROF_MCAST: case PROF_FILE_TX: case PROF_IPTV: gtgRecv = 0; if(tgSockfds[myStream->tblidx] != -1) { wCLOSE(tgSockfds[myStream->tblidx]); tgSockfds[myStream->tblidx] = -1; } break; case PROF_UAPSD: #ifdef WFA_WMM_PS_EXT gtgWmmPS = 0; gtgPsPktRecvd = 0; if(psSockfd != -1) { wCLOSE(psSockfd); psSockfd = -1; } wMEMSET(&wmmps_info, 0, sizeof(wfaWmmPS_t)); wfaSetDUTPwrMgmt(PS_OFF); #endif /* WFA_WMM_PS_EXT */ break; } /* encode a TLV for response for "complete/error ..." */ statResp.status = STATUS_COMPLETE; statResp.streamId = streamid; #if 1 DPRINT_INFO(WFA_OUT, "stream Id %u rx %u total %llu\n", streamid, myStream->stats.rxFrames, myStream->stats.rxPayloadBytes); #endif wMEMCPY(&statResp.cmdru.stats, &myStream->stats, sizeof(tgStats_t)); wMEMCPY((dutRspBuf + i * sizeof(dutCmdResponse_t)), (BYTE *)&statResp, sizeof(dutCmdResponse_t)); id_cnt++; // Not empty it but require to reset the entire table before test starts. //wMEMSET(myStream, 0, sizeof(tgStream_t)); } // mark the stream inactive myStream->state = WFA_STREAM_INACTIVE; printf("Sending back the statistics at recvstop\n"); wfaEncodeTLV(WFA_TRAFFIC_AGENT_RECV_STOP_RESP_TLV, id_cnt * sizeof(dutCmdResponse_t), dutRspBuf, respBuf); /* done here */ *respLen = WFA_TLV_HDR_LEN + numStreams * sizeof(dutCmdResponse_t); return TRUE; }
/* RecvStart: instruct traffic generator to start receiving * based on a profile * input: cmd -- not used * response: inform controller for "running" * return: success or failed */ int wfaTGRecvStart(int len, BYTE *parms, int *respLen, BYTE *respBuf) { int status = STATUS_COMPLETE, i; int numStreams = len/4; int streamid; tgProfile_t *theProfile; tgStream_t *myStream; DPRINT_INFO(WFA_OUT, "entering tgRecvStart\n"); /* * 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++) { wMEMCPY(&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; } wMEMSET(&myStream->stats, 0, sizeof(tgStats_t)); // mark the stream active myStream->state = WFA_STREAM_ACTIVE; switch(theProfile->profile) { #ifdef WFA_WPA2_SINGLE_THREAD case PROF_MCAST: case PROF_FILE_TX: // case PROF_IPTV: btSockfd = wfaCreateUDPSock(theProfile->dipaddr, theProfile->dport); gtgRecv = streamid; if(btSockfd < 0) status = STATUS_ERROR; else { /* get current flags setting */ int ioflags = wFCNTL(btSockfd, F_GETFL, 0); /* set only BLOCKING flag to non-blocking */ wFCNTL(btSockfd, F_SETFL, ioflags | O_NONBLOCK); } break; #else case PROF_TRANSC: case PROF_CALI_RTD: /* Calibrate roundtrip delay */ gtgTransac = streamid; case PROF_MCAST: case PROF_FILE_TX: case PROF_IPTV: gtgRecv = streamid; wmm_thr[usedThread].thr_flag = streamid; wPT_MUTEX_LOCK(&wmm_thr[usedThread].thr_flag_mutex); wPT_COND_SIGNAL(&wmm_thr[usedThread].thr_flag_cond); wPT_MUTEX_UNLOCK(&wmm_thr[usedThread].thr_flag_mutex); printf("Recv Start in thread %i for streamid %i\n", usedThread, streamid); usedThread++; break; #endif case PROF_UAPSD: #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; wMEMSET(&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; wPT_MUTEX_LOCK(&wmm_thr[usedThread].thr_flag_mutex); wPT_COND_SIGNAL(&wmm_thr[usedThread].thr_flag_cond); gtgWmmPS = streamid;; wPT_MUTEX_UNLOCK(&wmm_thr[usedThread].thr_flag_mutex); usedThread++; #endif /* WFA_WMM_PS_EXT */ break; } } /* encode a TLV for response for "complete/error ..." */ wfaEncodeTLV(WFA_TRAFFIC_AGENT_RECV_START_RESP_TLV, sizeof(int), (BYTE *)&status, respBuf); *respLen = WFA_TLV_HDR_LEN + sizeof(int); return TRUE; }
/* 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; if(mySockfd == -1) { /* stop */ gtgTransac = 0; //gtimeOut = 0; gtgRecv = 0; gtgSend = 0; printf("stop short traffic\n"); myStream = findStreamProfile(streamid); if(myStream != NULL) { sendResp.status = STATUS_COMPLETE; sendResp.streamId = streamid; wMEMCPY(&sendResp.cmdru.stats, &myStream->stats, sizeof(tgStats_t)); wfaEncodeTLV(WFA_TRAFFIC_AGENT_SEND_RESP_TLV, sizeof(dutCmdResponse_t), (BYTE *)&sendResp, aRespBuf); *aRespLen = WFA_TLV_HDR_LEN + sizeof(dutCmdResponse_t); } return DONE; } /* find the profile */ myStream = findStreamProfile(streamid); if(myStream == NULL) { return FALSE; } theProf = &myStream->profile; if(theProf == NULL) { return FALSE; } if(pksize == 0) packLen = theProf->pksize; else packLen = pksize; wMEMSET(&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) { // printf("mySock %i sipaddr %s sport %i\n", mySockfd, theProf->sipaddr, theProf->sport); toAddr.sin_addr.s_addr = inet_addr(theProf->sipaddr); toAddr.sin_port = htons(theProf->sport); } else if(gtgSend && gtgTransac) { // printf("mySock %i dipaddr %s dport %i\n", mySockfd, theProf->dipaddr, theProf->dport); 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) bytesSent = wfaTrafficSendTo(mySockfd, (char *)packBuf, packLen, (struct sockaddr *)&toAddr); if(bytesSent != -1) { myStream->stats.txFrames++; myStream->stats.txPayloadBytes += bytesSent; } else { int errsv = errno; switch(errsv) { case EAGAIN: case ENOBUFS: DPRINT_ERR(WFA_ERR, "send error\n"); wUSLEEP(1000); /* hold for 1 ms */ myStream->stats.txFrames--; break; default: ;; //perror("sendto: "); } } sentTranPkts++; return TRUE; }