ethernet_lib.c 18.4 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
/*
 * Licensed to the OpenAirInterface (OAI) Software Alliance under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The OpenAirInterface Software Alliance licenses this file to You under
 * the OAI Public License, Version 1.0  (the "License"); you may not use this file
 * except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.openairinterface.org/?page_id=698
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *-------------------------------------------------------------------------------
 * For more information about the OpenAirInterface (OAI) Software Alliance:
 *      contact@openairinterface.org
 */
knopp's avatar
knopp committed
21

22
/*! \file ethernet_lib.c 
23
 * \brief API to stream I/Q samples over standard ethernet
24
 * \author  add alcatel Katerina Trilyraki, Navid Nikaein, Pedro Dinis, Lucio Ferreira, Raymond Knopp
25 26 27 28 29
 * \date 2015
 * \version 0.2
 * \company Eurecom
 * \maintainer:  navid.nikaein@eurecom.fr
 * \note
30
 * \warning
31
 */
knopp's avatar
knopp committed
32 33 34 35 36 37 38 39 40 41

#include <arpa/inet.h>
#include <linux/if_packet.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <sys/ioctl.h>
#include <sys/socket.h>
#include <net/if.h>
#include <netinet/ether.h>
knopp's avatar
knopp committed
42 43
#include <unistd.h>
#include <errno.h>
44 45
#include <linux/sysctl.h>
#include <sys/sysctl.h>
knopp's avatar
knopp committed
46 47

#include "common_lib.h"
48
#include "ethernet_lib.h"
knopp's avatar
knopp committed
49

50 51 52 53 54 55

int num_devices_eth = 0;
struct sockaddr_in dest_addr[MAX_INST];
int dest_addr_len[MAX_INST];


56 57 58 59
int trx_eth_start(openair0_device *device) {

  eth_state_t *eth = (eth_state_t*)device->priv;
  
60
  /* initialize socket */
61
  if (eth->flags == ETH_RAW_MODE) {     
62
    printf("Setting ETHERNET to ETH_RAW_IF5_MODE\n");
63 64 65 66 67 68
    if (eth_socket_init_raw(device)!=0)   return -1;
    /* RRH gets openair0 device configuration - BBU sets openair0 device configuration*/
    if (device->host_type == BBU_HOST) {
      if(eth_set_dev_conf_raw(device)!=0)  return -1;
    } else {
      if(eth_get_dev_conf_raw(device)!=0)  return -1;
69
    }
70
    /* adjust MTU wrt number of samples per packet */
71 72 73 74 75
    if(eth->compression == ALAW_COMPRESS) {
      if(ethernet_tune (device,MTU_SIZE,RAW_PACKET_SIZE_BYTES_ALAW(device->openair0_cfg->samples_per_packet))!=0)  return -1;
    } else {
      if(ethernet_tune (device,MTU_SIZE,RAW_PACKET_SIZE_BYTES(device->openair0_cfg->samples_per_packet))!=0)  return -1;
    }
76
    if(ethernet_tune (device,RCV_TIMEOUT,999999)!=0)  return -1;
77
  } else if (eth->flags == ETH_RAW_IF4p5_MODE) {
78

79
    printf("Setting ETHERNET to ETH_RAW_IF4p5_MODE\n");
80 81 82
    if (eth_socket_init_raw(device)!=0)   return -1;
    /* RRH gets openair0 device configuration - BBU sets openair0 device configuration*/
    if (device->host_type == BBU_HOST) {
83
      if(eth_set_dev_conf_raw_IF4p5(device)!=0)  return -1;
84
    } else {
85
      if(eth_get_dev_conf_raw_IF4p5(device)!=0)  return -1;
86 87
    }
    /* adjust MTU wrt number of samples per packet */
88
    if(ethernet_tune (device,MTU_SIZE,RAW_IF4p5_PRACH_SIZE_BYTES)!=0)  return -1;
89

90
    if(ethernet_tune (device,RCV_TIMEOUT,999999)!=0)  return -1;
91
  } else if (eth->flags == ETH_UDP_IF4p5_MODE) {
92
    printf("Setting ETHERNET to UDP_IF4p5_MODE\n");
93
    if (eth_socket_init_udp(device)!=0)   return -1; 
Raymond Knopp's avatar
Raymond Knopp committed
94 95 96 97 98
    if (device->host_type == BBU_HOST) {
      if(eth_set_dev_conf_udp(device)!=0)  return -1;
    } else {
      if(eth_get_dev_conf_udp(device)!=0)  return -1;
    }
99
  } else if (eth->flags == ETH_RAW_IF5_MOBIPASS) {
100
    printf("Setting ETHERNET to RAW_IF5_MODE\n");
101
    if (eth_socket_init_raw(device)!=0)   return -1;
102 103
    if(ethernet_tune (device,RCV_TIMEOUT,999999)!=0)  return -1;

104
  } else {
knopp's avatar
knopp committed
105
    printf("Setting ETHERNET to UDP_IF5_MODE\n");
106
    if (eth_socket_init_udp(device)!=0)   return -1;
107 108 109 110 111
    /* RRH gets openair0 device configuration - BBU sets openair0 device configuration*/
    if (device->host_type == BBU_HOST) {
      if(eth_set_dev_conf_udp(device)!=0)  return -1;
    } else {
      if(eth_get_dev_conf_udp(device)!=0)  return -1;
112
      }
113
  }
114 115 116
  /* apply additional configuration */
  if(ethernet_tune (device, SND_BUF_SIZE,2000000000)!=0)  return -1;
  if(ethernet_tune (device, RCV_BUF_SIZE,2000000000)!=0)  return -1;
117 118 119
  if(ethernet_tune (device, KERNEL_SND_BUF_MAX_SIZE, 200000000)!=0)  return -1;
  if(ethernet_tune (device, KERNEL_RCV_BUF_MAX_SIZE, 200000000)!=0)  return -1;

120
  return 0;
121
}
knopp's avatar
knopp committed
122

123

124
void trx_eth_end(openair0_device *device) {
125 126

  eth_state_t *eth = (eth_state_t*)device->priv;
127
  /* destroys socket only for the processes that call the eth_end fuction-- shutdown() for beaking the pipe */
128
  if ( close(eth->sockfd) <0 ) {
129 130 131
    perror("ETHERNET: Failed to close socket");
    exit(0);
   } else {
132
    printf("[%s] socket has been successfully closed.\n",(device->host_type == BBU_HOST)? "BBU":"RRH");
133
   }
134
}
knopp's avatar
knopp committed
135 136


137
int trx_eth_request(openair0_device *device, void *msg, ssize_t msg_len) {
138 139

  eth_state_t *eth = (eth_state_t*)device->priv;
140 141
 
  /* BBU sends a message to RRH */
142 143
  
  if (sendto(eth->sockfd,msg,msg_len,0,(struct sockaddr *)&eth->dest_addr,eth->addr_len)==-1) {
144 145 146 147
    perror("ETHERNET: ");
    exit(0);
  }
  return 0;
knopp's avatar
knopp committed
148 149 150
}


151
int trx_eth_reply(openair0_device *device, void *msg, ssize_t msg_len) {
knopp's avatar
knopp committed
152

153
  eth_state_t   *eth = (eth_state_t*)device->priv;
knopp's avatar
knopp committed
154

155
  /* RRH receives from BBU a message */
156 157

  if (recvfrom(eth->sockfd,
158 159 160
	       msg,
	       msg_len,
	       0,
161 162 163 164 165 166 167 168
	       (struct sockaddr *)&eth->dest_addr,
	       (socklen_t *)&eth->addr_len)==-1) {
    perror("ETHERNET: recv_from in trx_eth_reply ");
    exit(0);	
  }

    

169 170 171
   return 0;
}

172

knopp's avatar
knopp committed
173

Raymond Knopp's avatar
Raymond Knopp committed
174
int trx_eth_stop(openair0_device *device) {
175
    return(0);
176
}
177

178
int trx_eth_set_freq(openair0_device* device, openair0_config_t *openair0_cfg,int exmimo_dump_config) {
179
    return(0);
180
}
knopp's avatar
knopp committed
181

182
int trx_eth_set_gains(openair0_device* device, openair0_config_t *openair0_cfg) {
183
    return(0);
knopp's avatar
knopp committed
184 185
}

186
int trx_eth_get_stats(openair0_device* device) {
187
    return(0);
188
}
189 190

int trx_eth_reset_stats(openair0_device* device) {
191
    return(0);
192
}
193

194

195
int ethernet_tune(openair0_device *device, unsigned int option, int value) {
196
  
197
  eth_state_t *eth = (eth_state_t*)device->priv;
198
  struct timeval timeout;
199
  struct ifreq ifr;   
200 201 202 203 204 205
  char system_cmd[256];
  int rname[] = { CTL_NET, NET_CORE, NET_CORE_RMEM_MAX };
  int wname[] = { CTL_NET, NET_CORE, NET_CORE_WMEM_MAX };
  int namelen=3;
  int newval[1];
  int newlen=sizeof(newval);
206
  int ret=0;
Raymond Knopp's avatar
Raymond Knopp committed
207
  //  int i=0;
208 209 210 211
  
  /****************** socket level options ************************/  
  switch(option) {
  case SND_BUF_SIZE:  /* transmit socket buffer size */   
212
    if (setsockopt(eth->sockfd,  
213 214
		   SOL_SOCKET,  
		   SO_SNDBUF,  
215
		   &value,sizeof(value))) {
216 217
      perror("[ETHERNET] setsockopt()");
    } else {
218 219 220 221 222
      printf("send buffer size= %d bytes\n",value); 
    }   
    break;
    
  case RCV_BUF_SIZE:   /* receive socket buffer size */   
223
    if (setsockopt(eth->sockfd,  
224 225
		   SOL_SOCKET,  
		   SO_RCVBUF,  
226
		   &value,sizeof(value))) {
227 228
      perror("[ETHERNET] setsockopt()");
    } else {     
229
      printf("receive bufffer size= %d bytes\n",value);    
230
    }
231 232 233
    break;
    
  case RCV_TIMEOUT:
234 235
    timeout.tv_sec = value/1000000;
    timeout.tv_usec = value%1000000;//less than rt_period?
236
    if (setsockopt(eth->sockfd,  
237 238
		   SOL_SOCKET,  
		   SO_RCVTIMEO,  
239
		   (char *)&timeout,sizeof(timeout))) {
240 241
      perror("[ETHERNET] setsockopt()");  
    } else {   
Raymond Knopp's avatar
Raymond Knopp committed
242
      printf( "receive timeout= %u usec\n",(unsigned int)timeout.tv_usec);  
243
    }  
244 245 246 247 248
    break;
    
  case SND_TIMEOUT:
    timeout.tv_sec = value/1000000000;
    timeout.tv_usec = value%1000000000;//less than rt_period?
249
    if (setsockopt(eth->sockfd,  
250 251
		   SOL_SOCKET,  
		   SO_SNDTIMEO,  
252
		   (char *)&timeout,sizeof(timeout))) {
253 254
      perror("[ETHERNET] setsockopt()");     
    } else {
Raymond Knopp's avatar
Raymond Knopp committed
255
      printf( "send timeout= %d,%d sec\n",(int)timeout.tv_sec,(int)timeout.tv_usec);    
256
    }
257 258 259 260 261
    break;
    
    
    /******************* interface level options  *************************/
  case MTU_SIZE: /* change  MTU of the eth interface */ 
262
    ifr.ifr_addr.sa_family = AF_INET;
263
    strncpy(ifr.ifr_name,eth->if_name, sizeof(ifr.ifr_name));
264
    ifr.ifr_mtu =value;
265
    if (ioctl(eth->sockfd,SIOCSIFMTU,(caddr_t)&ifr) < 0 )
266 267
      perror ("[ETHERNET] Can't set the MTU");
    else 
268
      printf("[ETHERNET] %s MTU size has changed to %d\n",eth->if_name,ifr.ifr_mtu);
269 270 271
    break;
    
  case TX_Q_LEN:  /* change TX queue length of eth interface */ 
272
    ifr.ifr_addr.sa_family = AF_INET;
273
    strncpy(ifr.ifr_name,eth->if_name, sizeof(ifr.ifr_name));
274
    ifr.ifr_qlen =value;
275
    if (ioctl(eth->sockfd,SIOCSIFTXQLEN,(caddr_t)&ifr) < 0 )
276 277
      perror ("[ETHERNET] Can't set the txqueuelen");
    else 
278
      printf("[ETHERNET] %s txqueuelen size has changed to %d\n",eth->if_name,ifr.ifr_qlen);
279 280
    break;
    
281
    /******************* device level options  *************************/
282
  case COALESCE_PAR:
283
    ret=snprintf(system_cmd,sizeof(system_cmd),"ethtool -C %s rx-usecs %d",eth->if_name,value);
284 285 286 287 288 289 290
    if (ret > 0) {
      ret=system(system_cmd);
      if (ret == -1) {
	fprintf (stderr,"[ETHERNET] Can't start shell to execute %s %s",system_cmd, strerror(errno));
      } else {
	printf ("[ETHERNET] status of %s is %i\n",WEXITSTATUS(ret));
      }
291 292 293 294
      printf("[ETHERNET] Coalesce parameters %s\n",system_cmd);
    } else {
      perror("[ETHERNET] Can't set coalesce parameters\n");
    }
295
    break;
296
    
297
  case PAUSE_PAR:
298 299
    if (value==1) ret=snprintf(system_cmd,sizeof(system_cmd),"ethtool -A %s autoneg off rx off tx off",eth->if_name);
    else if (value==0) ret=snprintf(system_cmd,sizeof(system_cmd),"ethtool -A %s autoneg on rx on tx on",eth->if_name);
300 301 302 303 304 305 306 307
    else break;
    if (ret > 0) {
      ret=system(system_cmd);
      if (ret == -1) {
	fprintf (stderr,"[ETHERNET] Can't start shell to execute %s %s",system_cmd, strerror(errno));
      } else {
	printf ("[ETHERNET] status of %s is %i\n",WEXITSTATUS(ret));
      }
308 309 310 311
      printf("[ETHERNET] Pause parameters %s\n",system_cmd);
    } else {
      perror("[ETHERNET] Can't set pause parameters\n");
    }
312 313 314
    break;
    
  case RING_PAR:
315
    ret=snprintf(system_cmd,sizeof(system_cmd),"ethtool -G %s val %d",eth->if_name,value);
316 317 318
    if (ret > 0) {
      ret=system(system_cmd);
      if (ret == -1) {
319
        fprintf (stderr,"[ETHERNET] Can't start shell to execute %s %s",system_cmd, strerror(errno));
320
      } else {
321
        printf ("[ETHERNET] status of %s is %i\n",WEXITSTATUS(ret));
322
      }            
323 324 325 326
      printf("[ETHERNET] Ring parameters %s\n",system_cmd);
    } else {
      perror("[ETHERNET] Can't set ring parameters\n");
    }
327
    break;
328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346
  case KERNEL_RCV_BUF_MAX_SIZE:
    newval[0] = value;
    ret=sysctl(rname, namelen, NULL, 0, newval, newlen);
    if (ret) {
      fprintf(stderr,"[ETHERNET] Error using sysctl():%s\n",strerror(errno));
    } else{
      printf("[ETHERNET] Kernel network receive buffer max size is set to %u\n",newval[0]);
    }
    break;
  case KERNEL_SND_BUF_MAX_SIZE:
    newval[0] = value;
    ret=sysctl(wname, namelen, NULL, 0, newval, newlen);
    if (ret) {
      fprintf(stderr,"[ETHERNET] Error using sysctl():%s\n",strerror(errno));
    } else{
      printf("[ETHERNET] Kernel network send buffer max size is set to %u\n",newval[0]);
    }
    break;
    
347 348
  default:
    break;
349
  }
350
  
351
  return 0;
knopp's avatar
knopp committed
352 353 354
}


355
int transport_init(openair0_device *device, openair0_config_t *openair0_cfg, eth_params_t * eth_params ) {
356 357 358

  eth_state_t *eth = (eth_state_t*)malloc(sizeof(eth_state_t));
  memset(eth, 0, sizeof(eth_state_t));
359
  
360 361
  if (eth_params->transp_preference == 1) {
    eth->flags = ETH_RAW_MODE;
362
  } else if (eth_params->transp_preference == 0) {
363
    eth->flags = ETH_UDP_MODE;
364
  } else if (eth_params->transp_preference == 3) {
365
    eth->flags = ETH_RAW_IF4p5_MODE;
366
  } else if (eth_params->transp_preference == 2) {
367
    eth->flags = ETH_UDP_IF4p5_MODE;
368 369
  } else if (eth_params->transp_preference == 4) {
    eth->flags = ETH_RAW_IF5_MOBIPASS;
370
  } else {
371 372
    printf("transport_init: Unknown transport preference %d - default to RAW", eth_params->transp_preference);
    eth->flags = ETH_RAW_MODE;
373
  }
374 375 376 377 378 379 380 381 382

  if (eth_params->if_compress == 0) {
    eth->compression = NO_COMPRESS;
  } else if (eth_params->if_compress == 1) {
    eth->compression = ALAW_COMPRESS;
  } else {
    printf("transport_init: Unknown compression scheme %d - default to ALAW", eth_params->if_compress);
    eth->compression = ALAW_COMPRESS;
  }
383
  
384
  printf("[ETHERNET]: Initializing openair0_device for %s ...\n", ((device->host_type == BBU_HOST) ? "BBU": "RRH"));
385
  device->Mod_id           = 0;//num_devices_eth++;
386
  device->transp_type      = ETHERNET_TP;
navid's avatar
navid committed
387
  device->trx_start_func   = trx_eth_start;
388 389
  device->trx_request_func = trx_eth_request;
  device->trx_reply_func   = trx_eth_reply;
390 391
  device->trx_get_stats_func   = trx_eth_get_stats;
  device->trx_reset_stats_func = trx_eth_reset_stats;
392 393
  device->trx_end_func         = trx_eth_end;
  device->trx_stop_func        = trx_eth_stop;
394 395
  device->trx_set_freq_func = trx_eth_set_freq;
  device->trx_set_gains_func = trx_eth_set_gains;
396
  
397
  if (eth->flags == ETH_RAW_MODE) {
398 399
    device->trx_write_func   = trx_eth_write_raw;
    device->trx_read_func    = trx_eth_read_raw;     
400
  } else if (eth->flags == ETH_UDP_MODE) {
401 402
    device->trx_write_func   = trx_eth_write_udp;
    device->trx_read_func    = trx_eth_read_udp;     
403 404 405
  } else if (eth->flags == ETH_RAW_IF4p5_MODE) {
    device->trx_write_func   = trx_eth_write_raw_IF4p5;
    device->trx_read_func    = trx_eth_read_raw_IF4p5;     
Raymond Knopp's avatar
Raymond Knopp committed
406 407 408
  } else if (eth->flags == ETH_UDP_IF4p5_MODE) {
    device->trx_write_func   = trx_eth_write_udp_IF4p5;
    device->trx_read_func    = trx_eth_read_udp_IF4p5;     
409
  } else if (eth->flags == ETH_RAW_IF5_MOBIPASS) {
410
    device->trx_write_func   = trx_eth_write_raw_IF4p5;
411
    device->trx_read_func    = trx_eth_read_raw_IF5_mobipass;   
412
  } else {
413 414
    //device->trx_write_func   = trx_eth_write_udp_IF4p5;
    //device->trx_read_func    = trx_eth_read_udp_IF4p5;     
415
  }
416
  
417
  eth->if_name = eth_params->local_if_name;
418 419 420
  device->priv = eth;
 	
  /* device specific */
421
  openair0_cfg[0].iq_rxrescale = 15;//rescale iqs
422 423
  openair0_cfg[0].iq_txshift = eth_params->iq_txshift;// shift
  openair0_cfg[0].tx_sample_advance = eth_params->tx_sample_advance;
424 425 426 427 428 429

  /* RRH does not have any information to make this configuration atm */
  if (device->host_type == BBU_HOST) {
    /*Note scheduling advance values valid only for case 7680000 */    
    switch ((int)openair0_cfg[0].sample_rate) {
    case 30720000:
430
      openair0_cfg[0].samples_per_packet    = 3840;     
431 432
      break;
    case 23040000:     
433
      openair0_cfg[0].samples_per_packet    = 2880;
434 435
      break;
    case 15360000:
436
      openair0_cfg[0].samples_per_packet    = 1920;      
437 438
      break;
    case 7680000:
439
      openair0_cfg[0].samples_per_packet    = 960;     
440
      break;
441
    case 1920000:
442
      openair0_cfg[0].samples_per_packet    = 240;     
443 444 445 446 447 448 449 450
      break;
    default:
      printf("Error: unknown sampling rate %f\n",openair0_cfg[0].sample_rate);
      exit(-1);
      break;
    }
  }
  device->openair0_cfg=&openair0_cfg[0];
451
  return 0;
knopp's avatar
knopp committed
452
}
453 454 455 456 457 458


/**************************************************************************************************************************
 *                                         DEBUGING-RELATED FUNCTIONS                                                     *
 **************************************************************************************************************************/
void dump_packet(char *title, unsigned char* pkt, int bytes, unsigned int tx_rx_flag) {
459 460 461 462 463 464 465 466 467 468 469

    static int numSend = 1;
    static int numRecv = 1;
    int num, k;
    char tmp[48];
    unsigned short int cksum;

    num = (tx_rx_flag)? numSend++:numRecv++;
    for (k = 0; k < 24; k++) sprintf(tmp+k, "%02X", pkt[k]);
    cksum = calc_csum((unsigned short *)pkt, bytes>>2);
    printf("%s-%s (%06d): %s 0x%04X\n", title,(tx_rx_flag)? "TX":"RX", num, tmp, cksum);
470 471 472
}

unsigned short calc_csum (unsigned short *buf, int nwords) {
473 474 475 476 477 478 479

    unsigned long sum;
    for (sum = 0; nwords > 0; nwords--)
        sum += *buf++;
    sum = (sum >> 16) + (sum & 0xffff);
    sum += (sum >> 16);
    return ~sum;
480 481 482 483
}

void dump_dev(openair0_device *device) {

484 485 486 487 488 489 490 491 492 493 494 495 496
    eth_state_t *eth = (eth_state_t*)device->priv;

    printf("Ethernet device interface %i configuration:\n" ,device->openair0_cfg->Mod_id);
    printf("       Log level is %i :\n" ,device->openair0_cfg->log_level);
    printf("       RB number: %i, sample rate: %lf \n" ,
           device->openair0_cfg->num_rb_dl, device->openair0_cfg->sample_rate);
    printf("       BBU configured for %i tx/%i rx channels)\n",
           device->openair0_cfg->tx_num_channels,device->openair0_cfg->rx_num_channels);
    printf("       Running flags: %s %s (\n",
           ((eth->flags & ETH_RAW_MODE)  ? "RAW socket mode - ":""),
           ((eth->flags & ETH_UDP_MODE)  ? "UDP socket mode - ":""));
    printf("       Number of iqs dumped when displaying packets: %i\n\n",eth->iqdumpcnt);

497 498 499
}

void inline dump_txcounters(openair0_device *device) {
500 501 502
    eth_state_t *eth = (eth_state_t*)device->priv;
    printf("   Ethernet device interface %i, tx counters:\n" ,device->openair0_cfg->Mod_id);
    printf("   Sent packets: %llu send errors: %i\n",   (long long unsigned int)eth->tx_count, eth->num_tx_errors);
503 504 505 506
}

void inline dump_rxcounters(openair0_device *device) {

507 508 509 510
    eth_state_t *eth = (eth_state_t*)device->priv;
    printf("   Ethernet device interface %i rx counters:\n" ,device->openair0_cfg->Mod_id);
    printf("   Received packets: %llu missed packets errors: %i\n", (long long unsigned int)eth->rx_count, eth->num_underflows);
}
511 512

void inline dump_buff(openair0_device *device, char *buff,unsigned int tx_rx_flag, int nsamps) {
513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529

    char *strptr;
    eth_state_t *eth = (eth_state_t*)device->priv;
    /*need to add ts number of iqs in printf need to fix dump iqs call */
    strptr = (( tx_rx_flag == TX_FLAG) ? "TX" : "RX");
    printf("\n %s, nsamps=%i \n" ,strptr,nsamps);

    if (tx_rx_flag == 1) {
        dump_txcounters(device);
        printf("  First %i iqs of TX buffer\n",eth->iqdumpcnt);
        dump_iqs(buff,eth->iqdumpcnt);
    } else {
        dump_rxcounters(device);
        printf("  First %i iqs of RX buffer\n",eth->iqdumpcnt);
        dump_iqs(buff,eth->iqdumpcnt);
    }

530 531 532
}

void dump_iqs(char * buff, int iq_cnt) {
533 534 535 536 537 538 539
    int i;
    for (i=0; i<iq_cnt; i++) {
        printf("s%02i: Q=%+ij I=%+i%s",i,
               ((iqoai_t *)(buff))[i].q,
               ((iqoai_t *)(buff))[i].i,
               ((i+1)%3 == 0) ? "\n" : "  ");
    }
540
}