/*******************************************************************************
    OpenAirInterface
    Copyright(c) 1999 - 2014 Eurecom

    OpenAirInterface is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.


    OpenAirInterface is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with OpenAirInterface.The full GNU General Public License is
    included in this distribution in the file called "COPYING". If not,
    see <http://www.gnu.org/licenses/>.

   Contact Information
   OpenAirInterface Admin: openair_admin@eurecom.fr
   OpenAirInterface Tech : openair_tech@eurecom.fr
   OpenAirInterface Dev  : openair4g-devel@lists.eurecom.fr

   Address      : Eurecom, Campus SophiaTech, 450 Route des Chappes, CS 50193 - 06904 Biot Sophia Antipolis cedex, FRANCE

 *******************************************************************************/
/*! \file ethernet_lib.h
 * \brief API to stream I/Q samples over standard ethernet
 * \author Katerina Trilyraki, Navid Nikaein
 * \date 2015
 * \version 0.2
 * \company Eurecom
 * \maintainer:  navid.nikaein@eurecom.fr
 * \note
 * \warning 
 */
#ifndef ETHERNET_LIB_H
#define ETHERNET_LIB_H

#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>

#define MAX_INST      4
#define DEFAULT_IF   "lo"

#define TX_FLAG	        1
#define RX_FLAG 	0

#include "if_defs.h"
#define APP_HEADER_SIZE_BYTES (sizeof(int32_t) + sizeof(openair0_timestamp))

/*!\brief opaque ethernet data structure */
typedef struct {
  
  /*!\brief socket file desc */ 
  int sockfd;
  /*!\brief interface name */ 
  char *if_name;
  /*!\brief buffer size */ 
  unsigned int buffer_size;
  /*!\brief destination address for UDP socket*/
  struct sockaddr_in dest_addr;
  /*!\brief local address for UDP socket*/
  struct sockaddr_in local_addr;
  /*!\brief address length for both UDP and RAW socket*/
  int addr_len;
  /*!\brief destination address for RAW socket*/
  struct sockaddr_ll dest_addr_ll;
  /*!\brief local address for RAW socket*/
  struct sockaddr_ll local_addr_ll;
  /*!\brief inteface index for RAW socket*/
  struct ifreq if_index;
  /*!\brief timeout ms */ 
  unsigned int rx_timeout_ms;
  /*!\brief timeout ms */ 
  unsigned int tx_timeout_ms;
  /*!\brief runtime flags */ 
  uint32_t flags;   
  /*!\ time offset between transmiter timestamp and receiver timestamp */ 
  double tdiff;
  /*!\ calibration */
  int tx_forward_nsamps;
  
  // --------------------------------
  // Debug and output control
  // --------------------------------
  
  /*!\brief number of I/Q samples to be printed */ 
  int iqdumpcnt;

  /*!\brief number of underflows in interface */ 
  int num_underflows;
  /*!\brief number of overflows in interface */ 
  int num_overflows;
  /*!\brief number of concesutive errors in interface */ 
  int num_seq_errors;
  /*!\brief number of errors in interface's receiver */ 
  int num_rx_errors;
  /*!\brief umber of errors in interface's transmitter */ 
  int num_tx_errors;
  
  /*!\brief current TX timestamp */ 
  openair0_timestamp tx_current_ts;
  /*!\brief socket file desc */ 
  openair0_timestamp rx_current_ts;
  /*!\brief actual number of samples transmitted */ 
  uint64_t tx_actual_nsamps; 
  /*!\brief actual number of samples received */
  uint64_t rx_actual_nsamps;
  /*!\brief number of samples to be transmitted */
  uint64_t tx_nsamps; 
  /*!\brief number of samples to be received */
  uint64_t rx_nsamps;
  /*!\brief number of packets transmitted */
  uint64_t tx_count; 
  /*!\brief number of packets received */
  uint64_t rx_count;

  struct ether_header eh; 

} eth_state_t;



/*!\brief packet header */
typedef struct {
  /*!\brief packet sequence number max value=packets per frame*/
  uint16_t seq_num ;
  /*!\brief antenna port used to resynchronize */
  uint16_t antenna_id;
  /*!\brief packet's timestamp */ 
  openair0_timestamp timestamp;
} header_t;

/*!\brief different options for ethernet tuning in socket and driver level */
typedef enum {
  MIN_OPT = 0,  
  /*!\brief socket send buffer size in bytes */
  SND_BUF_SIZE,
  /*!\brief socket receive buffer size in bytes */
  RCV_BUF_SIZE,
  /*!\brief receiving timeout */
  RCV_TIMEOUT,
  /*!\brief sending timeout */
  SND_TIMEOUT,
  /*!\brief maximun transmission unit size in bytes */
  MTU_SIZE,
  /*!\brief TX queue length */
  TX_Q_LEN,
  /*!\brief RX/TX  ring parameters of ethernet device */
  RING_PAR,
  /*!\brief interruptions coalesence mechanism of ethernet device */
  COALESCE_PAR,
  /*!\brief pause parameters of ethernet device */
  PAUSE_PAR,
  MAX_OPT
} eth_opt_t;

/*
#define SND_BUF_SIZE	1
#define RCV_BUF_SIZE	1<<1
#define SND_TIMEOUT	1<<2
#define RCV_TIMEOUT	1<<3
#define MTU_SIZE        1<<4
#define TX_Q_LEN	1<<5
#define RING_PAR	1<<5
#define COALESCE_PAR	1<<6
#define PAUSE_PAR       1<<7
*/

/*!\brief I/Q samples */
typedef struct {
  /*!\brief phase  */
  short i;
  /*!\brief quadrature */
  short q;
} iqoai_t ;

void dump_packet(char *title, unsigned char* pkt, int bytes, unsigned int tx_rx_flag);
unsigned short calc_csum (unsigned short *buf, int nwords);
void dump_dev(openair0_device *device);
/*void inline dump_buff(openair0_device *device, char *buff,unsigned int tx_rx_flag,int nsamps);
void inline dump_rxcounters(openair0_device *device);
void inline dump_txcounters(openair0_device *device);
*/
void dump_iqs(char * buff, int iq_cnt);



/*! \fn int ethernet_tune (openair0_device *device, unsigned int option, int value);
* \brief this function allows you to configure certain ethernet parameters in socket or device level
* \param[in] openair0 device which bears the socket
* \param[in] name of parameter to configure
* \return 0 on success, otherwise -1
* \note
* @ingroup  _oai
*/
int ethernet_tune(openair0_device *device, unsigned int option, int value);



/*! \fn int eth_socket_init_udp(openair0_device *device)
* \brief initialization of UDP Socket to communicate with one destination
* \param[in] *device openair device for which the socket will be created
* \param[out]
* \return 0 on success, otherwise -1
* \note
* @ingroup  _oai
*/
int eth_socket_init_udp(openair0_device *device);
int trx_eth_write_udp(openair0_device *device, openair0_timestamp timestamp, void **buff, int nsamps,int cc, int flags);
int trx_eth_read_udp(openair0_device *device, openair0_timestamp *timestamp, void **buff, int nsamps, int cc);
//int trx_eth_write_udp_IF4(openair0_device *device, openair0_timestamp timestamp, void **buff, int nsamps,int cc, int flags);
//int trx_eth_read_udp_IF4(openair0_device *device, openair0_timestamp *timestamp, void **buff, int nsamps, int cc);
int eth_get_dev_conf_udp(openair0_device *device);

/*! \fn static int eth_set_dev_conf_udp(openair0_device *device)
* \brief
* \param[in] *device openair device
* \param[out]
* \return 0 on success, otherwise -1
* \note
* @ingroup  _oai
*/
int eth_set_dev_conf_udp(openair0_device *device);
int eth_socket_init_raw(openair0_device *device);
int trx_eth_write_raw(openair0_device *device, openair0_timestamp timestamp, void **buff, int nsamps,int cc, int flags);
int trx_eth_read_raw(openair0_device *device, openair0_timestamp *timestamp, void **buff, int nsamps, int cc);
int trx_eth_write_raw_IF4p5(openair0_device *device, openair0_timestamp timestamp, void **buff, int nsamps,int cc, int flags);
int trx_eth_read_raw_IF4p5(openair0_device *device, openair0_timestamp *timestamp, void **buff, int nsamps, int cc);
int trx_eth_write_udp_IF4p5(openair0_device *device, openair0_timestamp timestamp, void **buff, int nsamps,int cc, int flags);
int trx_eth_read_udp_IF4p5(openair0_device *device, openair0_timestamp *timestamp, void **buff, int nsamps, int cc);
int eth_get_dev_conf_raw(openair0_device *device);
int eth_set_dev_conf_raw(openair0_device *device);
int eth_get_dev_conf_raw_IF4p5(openair0_device *device);
int eth_set_dev_conf_raw_IF4p5(openair0_device *device);

#endif