diff options
81 files changed, 4929 insertions, 1317 deletions
| diff --git a/firmware/microblaze/apps/Makefile.am b/firmware/microblaze/apps/Makefile.am index 6d993ef8c..ff426cf8c 100644 --- a/firmware/microblaze/apps/Makefile.am +++ b/firmware/microblaze/apps/Makefile.am @@ -21,7 +21,7 @@ include $(top_srcdir)/Makefile.common  LDADD = $(top_srcdir)/lib/libu2fw.a -AM_CFLAGS += -I$(top_srcdir)/../../host/lib/usrp/mboard +AM_CFLAGS += -I$(top_srcdir)/../../host/lib/usrp  noinst_PROGRAMS = txrx.elf diff --git a/firmware/microblaze/apps/txrx.c b/firmware/microblaze/apps/txrx.c index 77c8e498c..b82c7702b 100644 --- a/firmware/microblaze/apps/txrx.c +++ b/firmware/microblaze/apps/txrx.c @@ -47,6 +47,8 @@  #include <i2c.h>  #include <lsdac.h>  #include <lsadc.h> +#include <ethertype.h> +#include <arp_cache.h>  #define FW_SETS_SEQNO	1	// define to 0 or 1 (FIXME must be 1 for now) @@ -136,30 +138,42 @@ static int          streaming_frame_count = 0;  bool is_streaming(void){ return streaming_p; } -  // ---------------------------------------------------------------- +// the fast-path setup global variables +// ---------------------------------------------------------------- +static eth_mac_addr_t fp_mac_addr_src, fp_mac_addr_dst; +static struct socket_address fp_socket_src, fp_socket_dst; -void start_rx_streaming_cmd(void *p); +// ---------------------------------------------------------------- +void start_rx_streaming_cmd(void);  void stop_rx_cmd(void); -static eth_mac_addr_t get_my_eth_mac_addr(void){ -    return *ethernet_mac_addr(); +static void print_ip_addr(const void *t){ +    uint8_t *p = (uint8_t *)t; +    printf("%d.%d.%d.%d", p[0], p[1], p[2], p[3]);  } -static struct ip_addr get_my_ip_addr(void){ -    struct ip_addr addr; -    addr.addr = 192 << 24 | 168 << 16 | 10 << 8 | 2 << 0; -    return addr; -} - -static bool _is_data; -  void handle_udp_data_packet(      struct socket_address src, struct socket_address dst,      unsigned char *payload, int payload_len  ){ -    //TODO store the reply port -    _is_data = true; +    //its a tiny payload, load the fast-path variables +    fp_mac_addr_src = *ethernet_mac_addr(); +    arp_cache_lookup_mac(&src.addr, &fp_mac_addr_dst); +    fp_socket_src = dst; +    fp_socket_dst = src; +    printf("Storing for fast path:\n"); +    printf("  source mac addr: "); +    print_mac_addr(fp_mac_addr_src.addr); newline(); +    printf("  source ip addr: "); +    print_ip_addr(&fp_socket_src.addr); newline(); +    printf("  source udp port: %d\n", fp_socket_src.port); +    printf("  destination mac addr: "); +    print_mac_addr(fp_mac_addr_dst.addr); newline(); +    printf("  destination ip addr: "); +    print_ip_addr(&fp_socket_dst.addr); newline(); +    printf("  destination udp port: %d\n", fp_socket_dst.port); +    newline();  }  #define OTW_GPIO_BANK_TO_NUM(bank) \ @@ -190,14 +204,24 @@ void handle_udp_ctrl_packet(       ******************************************************************/      case USRP2_CTRL_ID_GIVE_ME_YOUR_IP_ADDR_BRO:          ctrl_data_out.id = USRP2_CTRL_ID_THIS_IS_MY_IP_ADDR_DUDE; -        struct ip_addr ip_addr = get_my_ip_addr(); -        memcpy(&ctrl_data_out.data.ip_addr, &ip_addr, sizeof(ip_addr)); +        memcpy(&ctrl_data_out.data.ip_addr, get_ip_addr(), sizeof(struct ip_addr)); +        break; + +    case USRP2_CTRL_ID_HERE_IS_A_NEW_IP_ADDR_BRO: +        ctrl_data_out.id = USRP2_CTRL_ID_THIS_IS_MY_IP_ADDR_DUDE; +        set_ip_addr((struct ip_addr *)&ctrl_data_in->data.ip_addr); +        memcpy(&ctrl_data_out.data.ip_addr, get_ip_addr(), sizeof(struct ip_addr));          break;      case USRP2_CTRL_ID_GIVE_ME_YOUR_MAC_ADDR_BRO:          ctrl_data_out.id = USRP2_CTRL_ID_THIS_IS_MY_MAC_ADDR_DUDE; -        eth_mac_addr_t mac_addr = get_my_eth_mac_addr(); -        memcpy(&ctrl_data_out.data.mac_addr, &mac_addr, sizeof(mac_addr)); +        memcpy(&ctrl_data_out.data.mac_addr, ethernet_mac_addr(), sizeof(eth_mac_addr_t)); +        break; + +    case USRP2_CTRL_ID_HERE_IS_A_NEW_MAC_ADDR_BRO: +        ctrl_data_out.id = USRP2_CTRL_ID_THIS_IS_MY_MAC_ADDR_DUDE; +        ethernet_set_mac_addr((eth_mac_addr_t *)&ctrl_data_in->data.mac_addr); +        memcpy(&ctrl_data_out.data.mac_addr, ethernet_mac_addr(), sizeof(eth_mac_addr_t));          break;      case USRP2_CTRL_ID_GIVE_ME_YOUR_DBOARD_IDS_BRO: @@ -394,6 +418,7 @@ void handle_udp_ctrl_packet(       ******************************************************************/      case USRP2_CTRL_ID_SETUP_THIS_DDC_FOR_ME_BRO:          dsp_rx_regs->freq = ctrl_data_in->data.ddc_args.freq_word; +        dsp_rx_regs->scale_iq = ctrl_data_in->data.ddc_args.scale_iq;          //setup the interp and half band filters          { @@ -419,11 +444,13 @@ void handle_udp_ctrl_packet(      case USRP2_CTRL_ID_CONFIGURE_STREAMING_FOR_ME_BRO:          time_secs =  ctrl_data_in->data.streaming.secs;          time_ticks = ctrl_data_in->data.streaming.ticks; +        streaming_items_per_frame = ctrl_data_in->data.streaming.samples; +          if (ctrl_data_in->data.streaming.enabled == 0){              stop_rx_cmd();          }          else{ -            start_rx_streaming_cmd(NULL); +            start_rx_streaming_cmd();          }          ctrl_data_out.id = USRP2_CTRL_ID_CONFIGURED_THAT_STREAMING_DUDE; @@ -457,6 +484,25 @@ void handle_udp_ctrl_packet(          ctrl_data_out.id = USRP2_CTRL_ID_TOTALLY_SETUP_THE_DUC_DUDE;          break; +    /******************************************************************* +     * Time Config +     ******************************************************************/ +    case USRP2_CTRL_ID_GOT_A_NEW_TIME_FOR_YOU_BRO: +        sr_time64->imm = (ctrl_data_in->data.time_args.now == 0)? 0 : 1; +        sr_time64->ticks = ctrl_data_in->data.time_args.ticks; +        sr_time64->secs = ctrl_data_in->data.time_args.secs; //set this last to latch the regs +        ctrl_data_out.id = USRP2_CTRL_ID_SWEET_I_GOT_THAT_TIME_DUDE; +        break; + +    /******************************************************************* +     * MUX Config +     ******************************************************************/ +    case USRP2_CTRL_ID_UPDATE_THOSE_MUX_SETTINGS_BRO: +        dsp_rx_regs->rx_mux = ctrl_data_in->data.mux_args.rx_mux; +        dsp_tx_regs->tx_mux = ctrl_data_in->data.mux_args.tx_mux; +        ctrl_data_out.id = USRP2_CTRL_ID_UPDATED_THE_MUX_SETTINGS_DUDE; +        break; +      default:          ctrl_data_out.id = USRP2_CTRL_ID_HUH_WHAT; @@ -472,15 +518,52 @@ void handle_udp_ctrl_packet(  static bool  eth_pkt_inspector(dbsm_t *sm, int bufno)  { -  _is_data = false; -  handle_eth_packet(buffer_ram(bufno), buffer_pool_status->last_line[bufno] - 3); -  return !_is_data; +  //point me to the ethernet frame +  uint32_t *buff = (uint32_t *)buffer_ram(bufno); + +  //treat this as fast-path data? +  // We have to do this operation as fast as possible. +  // Therefore, we do not check all the headers, +  // just check that the udp port matches +  // and that the vrt header is non zero. +  // In the future, a hardware state machine will do this... +  if ( //warning! magic numbers approaching.... +      (((buff + ((2 + 14 + 20)/sizeof(uint32_t)))[0] & 0xffff) == USRP2_UDP_DATA_PORT) && +      ((buff + ((2 + 14 + 20 + 8)/sizeof(uint32_t)))[0] != 0) +  ) return false; + +  //test if its an ip recovery packet +  typedef struct{ +      padded_eth_hdr_t eth_hdr; +      char code[4]; +      union { +        struct ip_addr ip_addr; +      } data; +  }recovery_packet_t; +  recovery_packet_t *recovery_packet = (recovery_packet_t *)buff; +  if (recovery_packet->eth_hdr.ethertype == 0xbeee && strncmp(recovery_packet->code, "addr", 4) == 0){ +      printf("Got ip recovery packet: "); print_ip_addr(&recovery_packet->data.ip_addr); newline(); +      set_ip_addr(&recovery_packet->data.ip_addr); +      return true; +  } + +  //pass it to the slow-path handler +  size_t len = buffer_pool_status->last_line[bufno] - 3; +  handle_eth_packet(buff, len); +  return true;  }  //------------------------------------------------------------------ -#define VRT_HEADER_WORDS 5 -#define VRT_TRAILER_WORDS 1 +static uint16_t get_vrt_packet_words(void){ +    return streaming_items_per_frame + \ +        USRP2_HOST_RX_VRT_HEADER_WORDS32 + \ +        USRP2_HOST_RX_VRT_TRAILER_WORDS32; +} + +static bool vrt_has_trailer(void){ +    return USRP2_HOST_RX_VRT_TRAILER_WORDS32 > 0; +}  void  restart_streaming(void) @@ -491,10 +574,10 @@ restart_streaming(void)    sr_rx_ctrl->clear_overrun = 1;			// reset    sr_rx_ctrl->vrt_header = (0       | VRTH_PT_IF_DATA_WITH_SID -     | VRTH_HAS_TRAILER +     | (vrt_has_trailer()? VRTH_HAS_TRAILER : 0)       | VRTH_TSI_OTHER       | VRTH_TSF_SAMPLE_CNT -     | (VRT_HEADER_WORDS+streaming_items_per_frame+VRT_TRAILER_WORDS)); +  );    sr_rx_ctrl->vrt_stream_id = 0;    sr_rx_ctrl->vrt_trailer = 0; @@ -524,14 +607,14 @@ restart_streaming(void)   *   * init chksum to zero to start.   */ -/*static unsigned int +static unsigned int  CHKSUM(unsigned int x, unsigned int *chksum)  {    *chksum += x;    *chksum = (*chksum & 0xffff) + (*chksum>>16);    *chksum = (*chksum & 0xffff) + (*chksum>>16);    return x; -}*/ +}  /*   * Called when eth phy state changes (w/ interrupts disabled) @@ -546,7 +629,7 @@ link_changed_callback(int speed)  }  void -start_rx_streaming_cmd(void *p) +start_rx_streaming_cmd(void)  {    /*     * Construct  ethernet header and preload into two buffers @@ -556,31 +639,32 @@ start_rx_streaming_cmd(void *p)    } mem _AL4;    memset(&mem, 0, sizeof(mem)); -  //p->items_per_frame = (1500)/sizeof(uint32_t) - (DSP_TX_FIRST_LINE + VRT_HEADER_WORDS + VRT_TRAILER_WORDS); //FIXME -  //mem.ctrl_word = (VRT_HEADER_WORDS+p->items_per_frame+VRT_TRAILER_WORDS)*sizeof(uint32_t) | 1 << 16; +  printf("samples per frame: %d\n", streaming_items_per_frame); +  printf("words in a vrt packet %d\n", get_vrt_packet_words()); +  mem.ctrl_word = get_vrt_packet_words()*sizeof(uint32_t) | 1 << 16;    memcpy_wa(buffer_ram(DSP_RX_BUF_0), &mem, sizeof(mem));    memcpy_wa(buffer_ram(DSP_RX_BUF_1), &mem, sizeof(mem));    //setup ethernet header machine -  /*sr_udp_sm->eth_hdr.mac_dst_0_1 = (host_dst_mac_addr.addr[0] << 8) | host_dst_mac_addr.addr[1]; -  sr_udp_sm->eth_hdr.mac_dst_2_3 = (host_dst_mac_addr.addr[2] << 8) | host_dst_mac_addr.addr[3]; -  sr_udp_sm->eth_hdr.mac_dst_4_5 = (host_dst_mac_addr.addr[4] << 8) | host_dst_mac_addr.addr[5]; -  sr_udp_sm->eth_hdr.mac_src_0_1 = (host_src_mac_addr.addr[0] << 8) | host_src_mac_addr.addr[1]; -  sr_udp_sm->eth_hdr.mac_src_2_3 = (host_src_mac_addr.addr[2] << 8) | host_src_mac_addr.addr[3]; -  sr_udp_sm->eth_hdr.mac_src_4_5 = (host_src_mac_addr.addr[4] << 8) | host_src_mac_addr.addr[5]; -  sr_udp_sm->eth_hdr.ether_type = ETHERTYPE_IPV4;*/ +  sr_udp_sm->eth_hdr.mac_dst_0_1 = (fp_mac_addr_dst.addr[0] << 8) | fp_mac_addr_dst.addr[1]; +  sr_udp_sm->eth_hdr.mac_dst_2_3 = (fp_mac_addr_dst.addr[2] << 8) | fp_mac_addr_dst.addr[3]; +  sr_udp_sm->eth_hdr.mac_dst_4_5 = (fp_mac_addr_dst.addr[4] << 8) | fp_mac_addr_dst.addr[5]; +  sr_udp_sm->eth_hdr.mac_src_0_1 = (fp_mac_addr_src.addr[0] << 8) | fp_mac_addr_src.addr[1]; +  sr_udp_sm->eth_hdr.mac_src_2_3 = (fp_mac_addr_src.addr[2] << 8) | fp_mac_addr_src.addr[3]; +  sr_udp_sm->eth_hdr.mac_src_4_5 = (fp_mac_addr_src.addr[4] << 8) | fp_mac_addr_src.addr[5]; +  sr_udp_sm->eth_hdr.ether_type = ETHERTYPE_IPV4;    //setup ip header machine -  /*unsigned int chksum = 0; +  unsigned int chksum = 0;    sr_udp_sm->ip_hdr.ver_ihl_tos = CHKSUM(0x4500, &chksum);    // IPV4,  5 words of header (20 bytes), TOS=0    sr_udp_sm->ip_hdr.total_length = UDP_SM_INS_IP_LEN;             // Don't checksum this line in SW    sr_udp_sm->ip_hdr.identification = CHKSUM(0x0000, &chksum);    // ID    sr_udp_sm->ip_hdr.flags_frag_off = CHKSUM(0x4000, &chksum);    // don't fragment    sr_udp_sm->ip_hdr.ttl_proto = CHKSUM(0x2011, &chksum);    // TTL=32, protocol = UDP (17 decimal)    //sr_udp_sm->ip_hdr.checksum .... filled in below -  uint32_t src_ip_addr = host_src_ip_addr.s_addr; -  uint32_t dst_ip_addr = host_dst_ip_addr.s_addr; +  uint32_t src_ip_addr = fp_socket_src.addr.addr; +  uint32_t dst_ip_addr = fp_socket_dst.addr.addr;    sr_udp_sm->ip_hdr.src_addr_high = CHKSUM(src_ip_addr >> 16, &chksum);    // IP src high    sr_udp_sm->ip_hdr.src_addr_low = CHKSUM(src_ip_addr & 0xffff, &chksum); // IP src low    sr_udp_sm->ip_hdr.dst_addr_high = CHKSUM(dst_ip_addr >> 16, &chksum);    // IP dst high @@ -588,17 +672,14 @@ start_rx_streaming_cmd(void *p)    sr_udp_sm->ip_hdr.checksum = UDP_SM_INS_IP_HDR_CHKSUM | (chksum & 0xffff);    //setup the udp header machine -  sr_udp_sm->udp_hdr.src_port = host_src_udp_port; -  sr_udp_sm->udp_hdr.dst_port = host_dst_udp_port; +  sr_udp_sm->udp_hdr.src_port = fp_socket_src.port; +  sr_udp_sm->udp_hdr.dst_port = fp_socket_dst.port;    sr_udp_sm->udp_hdr.length = UDP_SM_INS_UDP_LEN; -  sr_udp_sm->udp_hdr.checksum = UDP_SM_LAST_WORD;		// zero UDP checksum*/ +  sr_udp_sm->udp_hdr.checksum = UDP_SM_LAST_WORD;		// zero UDP checksum    if (FW_SETS_SEQNO)      fw_seqno = 0; -  //streaming_items_per_frame = p->items_per_frame; -  //time_secs = p->time_secs; -  //time_ticks = p->time_ticks;    restart_streaming();  } @@ -617,25 +698,6 @@ stop_rx_cmd(void)  } - -/*static void -setup_tx() -{ -  sr_tx_ctrl->clear_state = 1; -  bp_clear_buf(DSP_TX_BUF_0); -  bp_clear_buf(DSP_TX_BUF_1); - -  int tx_scale = 256; -  int interp = 32; - -  // setup some defaults - -  dsp_tx_regs->freq = 0; -  dsp_tx_regs->scale_iq = (tx_scale << 16) | tx_scale; -  dsp_tx_regs->interp_rate = interp; -}*/ - -  #if (FW_SETS_SEQNO)  /*   * Debugging ONLY.  This will be handled by the tx_protocol_engine. @@ -677,12 +739,14 @@ main(void)    putstr("\nTxRx-NEWETH\n");    print_mac_addr(ethernet_mac_addr()->addr);    newline(); +  print_ip_addr(get_ip_addr()); newline();    ethernet_register_link_changed_callback(link_changed_callback);    ethernet_init(); -  register_get_eth_mac_addr(get_my_eth_mac_addr); -  register_get_ip_addr(get_my_ip_addr); +  register_mac_addr(ethernet_mac_addr()); +  register_ip_addr(get_ip_addr()); +    register_udp_listener(USRP2_UDP_CTRL_PORT, handle_udp_ctrl_packet);    register_udp_listener(USRP2_UDP_DATA_PORT, handle_udp_data_packet); @@ -724,9 +788,9 @@ main(void)    // tell app_common that this dbsm could be sending to the ethernet    ac_could_be_sending_to_eth = &dsp_rx_sm; - -  // program tx registers -  //setup_tx(); +  sr_tx_ctrl->clear_state = 1; +  bp_clear_buf(DSP_TX_BUF_0); +  bp_clear_buf(DSP_TX_BUF_1);    // kick off the state machine    dbsm_start(&dsp_tx_sm); diff --git a/firmware/microblaze/include/usrp2_i2c_addr.h b/firmware/microblaze/include/usrp2_i2c_addr.h index 4111cdd55..46f5a7556 100644 --- a/firmware/microblaze/include/usrp2_i2c_addr.h +++ b/firmware/microblaze/include/usrp2_i2c_addr.h @@ -41,6 +41,7 @@  #define MBOARD_REV_LSB	0x00  #define	MBOARD_REV_MSB  0x01  #define	MBOARD_MAC_ADDR 0x02 +#define MBOARD_IP_ADDR  0x0C  // format of daughterboard EEPROM diff --git a/firmware/microblaze/lib/ethernet.c b/firmware/microblaze/lib/ethernet.c index 5402a6c9f..34a3ad7c1 100644 --- a/firmware/microblaze/lib/ethernet.c +++ b/firmware/microblaze/lib/ethernet.c @@ -271,53 +271,95 @@ ethernet_init(void)  }  static bool  -unprogrammed(const eth_mac_addr_t *t) +unprogrammed(const void *t, size_t len)  {    int i; +  uint8_t *p = (uint8_t *)t;    bool all_zeros = true;    bool all_ones =  true; -  for (i = 0; i < 6; i++){ -    all_zeros &= t->addr[i] == 0x00; -    all_ones  &= t->addr[i] == 0xff; +  for (i = 0; i < len; i++){ +    all_zeros &= p[i] == 0x00; +    all_ones  &= p[i] == 0xff;    }    return all_ones | all_zeros;  } -static int8_t src_addr_initialized = false; -static eth_mac_addr_t src_addr = {{ +//////////////////// MAC Addr Stuff /////////////////////// + +static int8_t src_mac_addr_initialized = false; +static eth_mac_addr_t src_mac_addr = {{      0x00, 0x50, 0xC2, 0x85, 0x3f, 0xff    }};  const eth_mac_addr_t *  ethernet_mac_addr(void)  { -  if (!src_addr_initialized){    // fetch from eeprom -    src_addr_initialized = true; +  if (!src_mac_addr_initialized){    // fetch from eeprom +    src_mac_addr_initialized = true;      // if we're simulating, don't read the EEPROM model, it's REALLY slow -    if (hwconfig_simulation_p())	 -      return &src_addr; +    if (hwconfig_simulation_p()) +      return &src_mac_addr;      eth_mac_addr_t tmp; -    bool ok = eeprom_read(I2C_ADDR_MBOARD, MBOARD_MAC_ADDR, &tmp.addr[0], 6); -    if (!ok || unprogrammed(&tmp)){ +    bool ok = eeprom_read(I2C_ADDR_MBOARD, MBOARD_MAC_ADDR, &tmp, sizeof(tmp)); +    if (!ok || unprogrammed(&tmp, sizeof(tmp))){        // use the default      }      else -      src_addr = tmp; +      src_mac_addr = tmp;    } -  return &src_addr; +  return &src_mac_addr;  }  bool  ethernet_set_mac_addr(const eth_mac_addr_t *t)  { -  bool ok = eeprom_write(I2C_ADDR_MBOARD, MBOARD_MAC_ADDR, &t->addr[0], 6); +  bool ok = eeprom_write(I2C_ADDR_MBOARD, MBOARD_MAC_ADDR, t, sizeof(eth_mac_addr_t)); +  if (ok){ +    src_mac_addr = *t; +    src_mac_addr_initialized = true; +    //eth_mac_set_addr(t); //this breaks the link +  } + +  return ok; +} + +//////////////////// IP Addr Stuff /////////////////////// + +static int8_t src_ip_addr_initialized = false; +static struct ip_addr src_ip_addr = { +    (192 << 24 | 168 << 16 | 10 << 8 | 2 << 0) +}; + + +const struct ip_addr *get_ip_addr(void) +{ +  if (!src_ip_addr_initialized){    // fetch from eeprom +    src_ip_addr_initialized = true; + +    // if we're simulating, don't read the EEPROM model, it's REALLY slow +    if (hwconfig_simulation_p()) +      return &src_ip_addr; +     +    struct ip_addr tmp; +    bool ok = eeprom_read(I2C_ADDR_MBOARD, MBOARD_IP_ADDR, &tmp, sizeof(tmp)); +    if (!ok || unprogrammed(&tmp, sizeof(tmp))){ +      // use the default +    } +    else +      src_ip_addr = tmp; +  } + +  return &src_ip_addr; +} + +bool set_ip_addr(const struct ip_addr *t){ +  bool ok = eeprom_write(I2C_ADDR_MBOARD, MBOARD_IP_ADDR, t, sizeof(struct ip_addr));    if (ok){ -    src_addr = *t; -    src_addr_initialized = true; -    eth_mac_set_addr(t); +    src_ip_addr = *t; +    src_ip_addr_initialized = true;    }    return ok; diff --git a/firmware/microblaze/lib/ethernet.h b/firmware/microblaze/lib/ethernet.h index 70b7077c6..8c6d8b567 100644 --- a/firmware/microblaze/lib/ethernet.h +++ b/firmware/microblaze/lib/ethernet.h @@ -20,6 +20,7 @@  #define INCLUDED_ETHERNET_H  #include <net/eth_mac_addr.h> +#include <lwip/ip_addr.h>  #include <stdbool.h>  typedef void (*ethernet_link_changed_callback_t)(int speed); @@ -48,6 +49,16 @@ const eth_mac_addr_t *ethernet_mac_addr(void);   */  bool ethernet_set_mac_addr(const eth_mac_addr_t *t); +/*! + * \returns IP address + */ +const struct ip_addr *get_ip_addr(void); + +/*! + * \brief write ip address to eeprom and begin using it + */ +bool set_ip_addr(const struct ip_addr *t); +  /*   * \brief read RMON regs and return error mask diff --git a/firmware/microblaze/lib/net_common.c b/firmware/microblaze/lib/net_common.c index 693502d18..67a3ff964 100644 --- a/firmware/microblaze/lib/net_common.c +++ b/firmware/microblaze/lib/net_common.c @@ -51,16 +51,14 @@ ip_addr_eq(const struct ip_addr a, const struct ip_addr b)  // ------------------------------------------------------------------------ -get_eth_mac_addr_t _get_eth_mac_addr = NULL; - -void register_get_eth_mac_addr(get_eth_mac_addr_t get_eth_mac_addr){ -    _get_eth_mac_addr = get_eth_mac_addr; +static eth_mac_addr_t _local_mac_addr; +void register_mac_addr(const eth_mac_addr_t *mac_addr){ +    _local_mac_addr = *mac_addr;  } -get_ip_addr_t _get_ip_addr = NULL; - -void register_get_ip_addr(get_ip_addr_t get_ip_addr){ -    _get_ip_addr = get_ip_addr; +static struct ip_addr _local_ip_addr; +void register_ip_addr(const struct ip_addr *ip_addr){ +    _local_ip_addr = *ip_addr;  }  //------------------------------------------------------------------------- @@ -140,7 +138,7 @@ send_pkt(eth_mac_addr_t dst, int ethertype,    padded_eth_hdr_t	ehdr;    ehdr.pad = 0;    ehdr.dst = dst; -  ehdr.src = _get_eth_mac_addr(); +  ehdr.src = _local_mac_addr;    ehdr.ethertype = ethertype;    uint32_t *p = buffer_ram(CPU_TX_BUF); @@ -218,7 +216,6 @@ send_ip_pkt(struct ip_addr dst, int protocol,  	    const void *buf0, size_t len0,  	    const void *buf1, size_t len1)  { -  struct ip_addr src = _get_ip_addr();    int ttl = 32;    struct ip_hdr ip; @@ -228,7 +225,7 @@ send_ip_pkt(struct ip_addr dst, int protocol,    IPH_OFFSET_SET(&ip, IP_DF);	/* don't fragment */    ip._ttl_proto = (ttl << 8) | (protocol & 0xff);    ip._chksum = 0; -  ip.src = src; +  ip.src = _local_ip_addr;    ip.dest = dst;    ip._chksum = ~chksum_buffer((unsigned short *) &ip, @@ -373,8 +370,8 @@ handle_arp_packet(struct arp_eth_ipv4 *p, size_t size)    sip.addr = get_int32(p->ar_sip);    tip.addr = get_int32(p->ar_tip); -  if (ip_addr_eq(tip, _get_ip_addr())){	// They're looking for us... -    send_arp_reply(p, _get_eth_mac_addr()); +  if (ip_addr_eq(tip, _local_ip_addr)){	// They're looking for us... +    send_arp_reply(p, _local_mac_addr);    }  } diff --git a/firmware/microblaze/lib/net_common.h b/firmware/microblaze/lib/net_common.h index cfba43412..112669b46 100644 --- a/firmware/microblaze/lib/net_common.h +++ b/firmware/microblaze/lib/net_common.h @@ -33,21 +33,12 @@ extern dbsm_t *ac_could_be_sending_to_eth;  void stop_streaming(void); -/*! - * Helpful typedefs for callback - */  typedef void (*udp_receiver_t)(struct socket_address src, struct socket_address dst,  			       unsigned char *payload, int payload_len); -typedef eth_mac_addr_t (*get_eth_mac_addr_t)(void); -typedef struct ip_addr (*get_ip_addr_t)(void); - -/*! - * Functions to register callbacks - */ -void register_get_eth_mac_addr(get_eth_mac_addr_t get_eth_mac_addr); +void register_mac_addr(const eth_mac_addr_t *mac_addr); -void register_get_ip_addr(get_ip_addr_t get_ip_addr); +void register_ip_addr(const struct ip_addr *ip_addr);  void register_udp_listener(int port, udp_receiver_t rcvr); @@ -56,5 +47,4 @@ void send_udp_pkt(int src_port, struct socket_address dst,  void handle_eth_packet(uint32_t *p, size_t nlines); -  #endif /* INCLUDED_NET_COMMON_H */ diff --git a/host/CMakeLists.txt b/host/CMakeLists.txt index 70c04631b..2f5d03f7d 100644 --- a/host/CMakeLists.txt +++ b/host/CMakeLists.txt @@ -53,17 +53,32 @@ FUNCTION(UHD_ADD_OPTIONAL_CXX_COMPILER_FLAG flag have)      ENDIF(${have})  ENDFUNCTION(UHD_ADD_OPTIONAL_CXX_COMPILER_FLAG) -UHD_ADD_OPTIONAL_CXX_COMPILER_FLAG(-Wall      HAVE_WALL) -UHD_ADD_OPTIONAL_CXX_COMPILER_FLAG(-Wextra    HAVE_WEXTRA) -UHD_ADD_OPTIONAL_CXX_COMPILER_FLAG(-pedantic  HAVE_PEDANTIC) -UHD_ADD_OPTIONAL_CXX_COMPILER_FLAG(-ansi      HAVE_ANSI) +IF(UNIX) +    UHD_ADD_OPTIONAL_CXX_COMPILER_FLAG(-Wall      HAVE_WALL) +    UHD_ADD_OPTIONAL_CXX_COMPILER_FLAG(-Wextra    HAVE_WEXTRA) +    UHD_ADD_OPTIONAL_CXX_COMPILER_FLAG(-pedantic  HAVE_PEDANTIC) +    UHD_ADD_OPTIONAL_CXX_COMPILER_FLAG(-ansi      HAVE_ANSI) +    #only export symbols that are declared to be part of the uhd api: +    UHD_ADD_OPTIONAL_CXX_COMPILER_FLAG(-fvisibility=hidden HAVE_VISIBILITY_HIDDEN) +ENDIF(UNIX) + +IF(WIN32) +    ADD_DEFINITIONS(-Dnot=! -Dand=&& -Dor=||) #logical operators +    ADD_DEFINITIONS(-D_WIN32_WINNT=0x0501) #minimum version required is windows xp +    ADD_DEFINITIONS(-DNOMINMAX) #disables stupidity and enables std::min and std::max +    ADD_DEFINITIONS(-D_SCL_SECURE_NO_WARNINGS) #avoid warnings from boost::split +    ADD_DEFINITIONS(-DBOOST_ALL_DYN_LINK) #setup boost auto-linking in msvc +ENDIF(WIN32)  ########################################################################  # Setup Boost  ######################################################################## +SET(Boost_ADDITIONAL_VERSIONS "1.42.0" "1.42")  FIND_PACKAGE(Boost 1.36 REQUIRED      date_time +    filesystem      program_options +    regex      system      thread      unit_test_framework @@ -73,6 +88,15 @@ INCLUDE_DIRECTORIES(${Boost_INCLUDE_DIRS})  LINK_DIRECTORIES(${Boost_LIBRARY_DIRS})  ######################################################################## +# Setup Endianess +######################################################################## +INCLUDE(TestBigEndian) +TEST_BIG_ENDIAN(HAVE_BIG_ENDIAN) +IF(HAVE_BIG_ENDIAN) +    ADD_DEFINITIONS(-DHAVE_BIG_ENDIAN) +ENDIF(HAVE_BIG_ENDIAN) + +########################################################################  # Create Uninstall Target  ########################################################################  CONFIGURE_FILE( diff --git a/host/apps/CMakeLists.txt b/host/apps/CMakeLists.txt index f4428f958..58f73fcd6 100644 --- a/host/apps/CMakeLists.txt +++ b/host/apps/CMakeLists.txt @@ -16,7 +16,11 @@  #  ADD_EXECUTABLE(discover_usrps discover_usrps.cpp) -  TARGET_LINK_LIBRARIES(discover_usrps uhd) -  INSTALL(TARGETS discover_usrps RUNTIME DESTINATION ${RUNTIME_DIR}) + +ADD_EXECUTABLE(usrp1e_load_fpga usrp1e_load_fpga.cpp) +TARGET_LINK_LIBRARIES(usrp1e_load_fpga uhd) + +ADD_EXECUTABLE(usrp2_burner usrp2_burner.cpp) +TARGET_LINK_LIBRARIES(usrp2_burner uhd) diff --git a/host/apps/discover_usrps.cpp b/host/apps/discover_usrps.cpp index 02c05b7cc..d670d1651 100644 --- a/host/apps/discover_usrps.cpp +++ b/host/apps/discover_usrps.cpp @@ -15,20 +15,20 @@  // along with this program.  If not, see <http://www.gnu.org/licenses/>.  // -#include <uhd.hpp> +#include <uhd/device.hpp>  #include <uhd/props.hpp>  #include <boost/program_options.hpp>  #include <boost/format.hpp>  #include <iostream>  namespace po = boost::program_options; -using namespace uhd;  int main(int argc, char *argv[]){      po::options_description desc("Allowed options");      desc.add_options()          ("help", "help message") -        ("ip-addr", po::value<std::string>(), "usrp2 ip address") +        ("addr", po::value<std::string>(), "resolvable network address") +        ("node", po::value<std::string>(), "path to linux device node")      ;      po::variables_map vm; @@ -36,31 +36,34 @@ int main(int argc, char *argv[]){      po::notify(vm);       //print the help message -    if (vm.count("help")) { +    if (vm.count("help")){          std::cout << boost::format("Discover USRPs %s") % desc << std::endl;          return ~0;      } -    //extract the ip address (not optional for now) +    //load the options into the address      uhd::device_addr_t device_addr; -    device_addr["type"] = "udp"; -    if (vm.count("ip-addr")) { -        device_addr["addr"] = vm["ip-addr"].as<std::string>(); -    } else { -        std::cout << "IP Addess was not set" << std::endl; +    if (vm.count("addr")){ +        device_addr["addr"] = vm["addr"].as<std::string>(); +    } +    if (vm.count("node")){ +        device_addr["node"] = vm["node"].as<std::string>(); +    } + +    //discover the usrps and print the results +    uhd::device_addrs_t device_addrs = uhd::device::discover(device_addr); + +    if (device_addrs.size() == 0){ +        std::cerr << "No USRP Devices Found" << std::endl;          return ~0;      } -    //discover the usrps -    std::vector<uhd::device_addr_t> device_addrs = uhd::device::discover(device_addr);      for (size_t i = 0; i < device_addrs.size(); i++){          std::cout << "--------------------------------------------------" << std::endl;          std::cout << "-- USRP Device " << i << std::endl;          std::cout << "--------------------------------------------------" << std::endl;          std::cout << device_addrs[i] << std::endl << std::endl; -        //make each device just to test (TODO: remove this) -        uhd::device::sptr dev = device::make(device_addrs[i]); -        std::cout << wax::cast<std::string>((*dev)[uhd::DEVICE_PROP_MBOARD][uhd::MBOARD_PROP_NAME]) << std::endl; +        uhd::device::make(device_addrs[i]); //test make      }      return 0; diff --git a/host/apps/omap_debug/set_debug_pins.py b/host/apps/omap_debug/set_debug_pins.py index bedabc20c..fdd085c9e 100755 --- a/host/apps/omap_debug/set_debug_pins.py +++ b/host/apps/omap_debug/set_debug_pins.py @@ -3,12 +3,12 @@  import os  # Memory Map -misc_base = 0 -uart_base = 1 -spi_base = 2 -i2c_base = 3 -gpio_base = 4 -settings_base = 5 +misc_base = 0 << 7 +uart_base = 1 << 7 +spi_base = 2 << 7 +i2c_base = 3 << 7 +gpio_base = 4 << 7 +settings_base = 5 << 7  # GPIO offset  gpio_pins = 0 diff --git a/host/apps/usrp1e_load_fpga.cpp b/host/apps/usrp1e_load_fpga.cpp new file mode 100644 index 000000000..d5960b391 --- /dev/null +++ b/host/apps/usrp1e_load_fpga.cpp @@ -0,0 +1,47 @@ +// +// Copyright 2010 Ettus Research LLC +// +// This program 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. +// +// This program 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 this program.  If not, see <http://www.gnu.org/licenses/>. +// + +#include <uhd/usrp/usrp1e.hpp> +#include <boost/program_options.hpp> +#include <boost/format.hpp> +#include <iostream> + +namespace po = boost::program_options; + +int main(int argc, char *argv[]){ +    po::options_description desc("Allowed options"); +    desc.add_options() +        ("help", "help message") +        ("file", po::value<std::string>(), "path to fpga bin file") +    ; + +    po::variables_map vm; +    po::store(po::parse_command_line(argc, argv, desc), vm); +    po::notify(vm);  + +    //print the help message +    if (vm.count("help") or vm.count("file") == 0){ +        std::cout << boost::format("USRP1E Load FPGA %s") % desc << std::endl; +        return ~0; +    } + +    //load the fpga +    std::string file = vm["file"].as<std::string>(); +    uhd::usrp::usrp1e::load_fpga(file); + +    return 0; +} diff --git a/host/apps/usrp2_burner.cpp b/host/apps/usrp2_burner.cpp new file mode 100644 index 000000000..941e71d0c --- /dev/null +++ b/host/apps/usrp2_burner.cpp @@ -0,0 +1,84 @@ +// +// Copyright 2010 Ettus Research LLC +// +// This program 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. +// +// This program 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 this program.  If not, see <http://www.gnu.org/licenses/>. +// + +#include <uhd/usrp/usrp2.hpp> +#include <uhd/props.hpp> +#include <boost/program_options.hpp> +#include <boost/format.hpp> +#include <iostream> + +namespace po = boost::program_options; + +int main(int argc, char *argv[]){ +    po::options_description desc("Allowed options"); +    desc.add_options() +        ("help", "help message") +        ("addr", po::value<std::string>(), "resolvable network address") +        ("new-ip", po::value<std::string>(), "new ip address (optional)") +        ("new-mac", po::value<std::string>(), "new mac address (optional)") +    ; + +    po::variables_map vm; +    po::store(po::parse_command_line(argc, argv, desc), vm); +    po::notify(vm);  + +    //print the help message +    if (vm.count("help")){ +        std::cout << boost::format("USRP2 Burner %s") % desc << std::endl; +        return ~0; +    } + +    //load the options into the address +    uhd::device_addr_t device_addr; +    if (vm.count("addr")){ +        device_addr["addr"] = vm["addr"].as<std::string>(); +    } +    else{ +        std::cerr << "Error: missing addr option" << std::endl; +        return ~0; +    } + +    //create a usrp2 device +    uhd::device::sptr u2_dev = uhd::usrp::usrp2::make(device_addr); +    //FIXME usees the default mboard for now (until the mimo link is supported) +    wax::obj u2_mb = (*u2_dev)[uhd::DEVICE_PROP_MBOARD]; + +    //try to set the new ip (if provided) +    if (vm.count("new-ip")){ +        std::cout << "Burning a new ip address into the usrp2 eeprom:" << std::endl; +        std::string old_ip = u2_mb[std::string("ip-addr")].as<std::string>(); +        std::cout << boost::format("  Old IP Address: %s") % old_ip << std::endl; +        std::string new_ip = vm["new-ip"].as<std::string>(); +        std::cout << boost::format("  New IP Address: %s") % new_ip << std::endl; +        u2_mb[std::string("ip-addr")] = new_ip; +        std::cout << "  Done" << std::endl; +    } + +    //try to set the new mac (if provided) +    if (vm.count("new-mac")){ +        std::cout << "Burning a new mac address into the usrp2 eeprom:" << std::endl; +        std::string old_mac = u2_mb[std::string("mac-addr")].as<std::string>(); +        std::cout << boost::format("  Old MAC Address: %s") % old_mac << std::endl; +        std::string new_mac = vm["new-mac"].as<std::string>(); +        std::cout << boost::format("  New MAC Address: %s") % new_mac << std::endl; +        u2_mb[std::string("mac-addr")] = new_mac; +        std::cout << "  Done" << std::endl; +    } + +    std::cout << "Power-cycle the usrp2 for the changes to take effect." << std::endl; +    return 0; +} diff --git a/host/apps/usrp2_recovery.py b/host/apps/usrp2_recovery.py new file mode 100755 index 000000000..48c1121cb --- /dev/null +++ b/host/apps/usrp2_recovery.py @@ -0,0 +1,52 @@ +#!/usr/bin/env python + +""" +The usrp2 recovery app: + +When the usrp2 has an unknown or bad ip address in its eeprom, +it may not be possible to communicate with the usrp2 over ip/udp. + +This app will send a raw ethernet packet to bypass the ip layer. +The packet will contain a known ip address to burn into eeprom. +Because the recovery packet is sent with a broadcast mac address, +only one usrp2 should be present on the interface upon execution. + +This app requires super-user privileges and only works on linux.  +""" + +import socket +import struct +import optparse + +BCAST_MAC_ADDR = 'ff:ff:ff:ff:ff:ff' +RECOVERY_ETHERTYPE = 0xbeee +IP_RECOVERY_CODE = 'addr' + +def mac_addr_repr_to_binary_string(mac_addr): +    return ''.join(map(lambda x: chr(int(x, 16)), mac_addr.split(':'))) + +if __name__ == '__main__': +    parser = optparse.OptionParser(usage='usage: %prog [options]\n'+__doc__) +    parser.add_option('--ifc', type='string', help='ethernet interface name [default=%default]', default='eth0') +    parser.add_option('--new-ip', type='string', help='ip address to set [default=%default]', default='192.168.10.2') +    (options, args) = parser.parse_args() + +    #create the raw socket +    print "Opening raw socket on interface:", options.ifc +    soc = socket.socket(socket.PF_PACKET, socket.SOCK_RAW) +    soc.bind((options.ifc, RECOVERY_ETHERTYPE)) + +    #create the recovery packet +    print "Loading packet with ip address:", options.new_ip +    packet = struct.pack( +        '!6s6sH4s4s', +        mac_addr_repr_to_binary_string(BCAST_MAC_ADDR), +        mac_addr_repr_to_binary_string(BCAST_MAC_ADDR), +        RECOVERY_ETHERTYPE, +        IP_RECOVERY_CODE, +        socket.inet_aton(options.new_ip), +    ) + +    print "Sending packet (%d bytes)"%len(packet) +    soc.send(packet) +    print "Done" diff --git a/host/include/CMakeLists.txt b/host/include/CMakeLists.txt index 34b705cab..3f7ca2cb7 100644 --- a/host/include/CMakeLists.txt +++ b/host/include/CMakeLists.txt @@ -17,8 +17,3 @@  ADD_SUBDIRECTORY(uhd) - -INSTALL(FILES -    uhd.hpp -    DESTINATION ${HEADER_DIR} -) diff --git a/host/include/uhd/CMakeLists.txt b/host/include/uhd/CMakeLists.txt index 006c54f22..3d00462cf 100644 --- a/host/include/uhd/CMakeLists.txt +++ b/host/include/uhd/CMakeLists.txt @@ -20,13 +20,16 @@ ADD_SUBDIRECTORY(transport)  ADD_SUBDIRECTORY(usrp)  INSTALL(FILES +    config.hpp      device.hpp      device_addr.hpp      dict.hpp      gain_handler.hpp +    metadata.hpp      props.hpp -    shared_iovec.hpp +    simple_device.hpp      time_spec.hpp +    types.hpp      utils.hpp      wax.hpp      DESTINATION ${HEADER_DIR}/uhd diff --git a/host/include/uhd/config.hpp b/host/include/uhd/config.hpp new file mode 100644 index 000000000..10f9c093f --- /dev/null +++ b/host/include/uhd/config.hpp @@ -0,0 +1,76 @@ +//
 +// Copyright 2010 Ettus Research LLC
 +//
 +// This program 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.
 +//
 +// This program 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 this program.  If not, see <http://www.gnu.org/licenses/>.
 +//
 +
 +#ifndef INCLUDED_UHD_CONFIG_HPP
 +#define INCLUDED_UHD_CONFIG_HPP
 +
 +// suppress warnings
 +#include <boost/config.hpp>
 +#ifdef BOOST_MSVC
 +# pragma warning(push)
 +//# pragma warning(disable: 4511) // copy constructor can't not be generated
 +//# pragma warning(disable: 4512) // assignment operator can't not be generated
 +//# pragma warning(disable: 4100) // unreferenced formal parameter 
 +//# pragma warning(disable: 4996) // <symbol> was declared deprecated 
 +//# pragma warning(disable: 4355) // 'this' : used in base member initializer list
 +//# pragma warning(disable: 4706) // assignment within conditional expression
 +# pragma warning(disable: 4251) // class 'A<T>' needs to have dll-interface to be used by clients of class 'B'
 +//# pragma warning(disable: 4127) // conditional expression is constant
 +//# pragma warning(disable: 4290) // C++ exception specification ignored except to ...
 +//# pragma warning(disable: 4180) // qualifier applied to function type has no meaning; ignored
 +# pragma warning(disable: 4275) // non dll-interface class ... used as base for dll-interface class ...
 +//# pragma warning(disable: 4267) // 'var' : conversion from 'size_t' to 'type', possible loss of data
 +//# pragma warning(disable: 4511) // 'class' : copy constructor could not be generated
 +#endif
 +
 +// http://gcc.gnu.org/wiki/Visibility
 +// Generic helper definitions for shared library support
 +#if defined _WIN32 || defined __CYGWIN__
 +  #define UHD_HELPER_DLL_IMPORT __declspec(dllimport)
 +  #define UHD_HELPER_DLL_EXPORT __declspec(dllexport)
 +  #define UHD_HELPER_DLL_LOCAL
 +#else
 +  #if __GNUC__ >= 4
 +    #define UHD_HELPER_DLL_IMPORT __attribute__ ((visibility("default")))
 +    #define UHD_HELPER_DLL_EXPORT __attribute__ ((visibility("default")))
 +    #define UHD_HELPER_DLL_LOCAL  __attribute__ ((visibility("hidden")))
 +  #else
 +    #define UHD_HELPER_DLL_IMPORT
 +    #define UHD_HELPER_DLL_EXPORT
 +    #define UHD_HELPER_DLL_LOCAL
 +  #endif
 +#endif
 +
 +// Now we use the generic helper definitions above to define UHD_API and UHD_LOCAL.
 +// UHD_API is used for the public API symbols. It either DLL imports or DLL exports (or does nothing for static build)
 +// UHD_LOCAL is used for non-api symbols.
 +
 +#define UHD_DLL // defined here, put into configuration if we need to make static libs
 +
 +#ifdef UHD_DLL // defined if UHD is compiled as a DLL
 +  #ifdef UHD_DLL_EXPORTS // defined if we are building the UHD DLL (instead of using it)
 +    #define UHD_API UHD_HELPER_DLL_EXPORT
 +  #else
 +    #define UHD_API UHD_HELPER_DLL_IMPORT
 +  #endif // UHD_DLL_EXPORTS
 +  #define UHD_LOCAL UHD_HELPER_DLL_LOCAL
 +#else // UHD_DLL is not defined: this means UHD is a static lib.
 +  #define UHD_API
 +  #define UHD_LOCAL
 +#endif // UHD_DLL
 +
 +#endif /* INCLUDED_UHD_CONFIG_HPP */
 diff --git a/host/include/uhd/device.hpp b/host/include/uhd/device.hpp index dfbfbd7c0..13b40febe 100644 --- a/host/include/uhd/device.hpp +++ b/host/include/uhd/device.hpp @@ -18,15 +18,15 @@  #ifndef INCLUDED_UHD_DEVICE_HPP  #define INCLUDED_UHD_DEVICE_HPP +#include <uhd/config.hpp>  #include <uhd/device_addr.hpp>  #include <uhd/props.hpp> +#include <uhd/metadata.hpp>  #include <uhd/wax.hpp>  #include <boost/utility.hpp>  #include <boost/shared_ptr.hpp>  #include <boost/function.hpp>  #include <boost/asio/buffer.hpp> -#include <uhd/shared_iovec.hpp> -#include <vector>  namespace uhd{ @@ -34,14 +34,23 @@ namespace uhd{   * The usrp device interface represents the usrp hardware.   * The api allows for discovery, configuration, and streaming.   */ -class device : boost::noncopyable, public wax::obj{ +class UHD_API device : boost::noncopyable, public wax::obj{  public:      typedef boost::shared_ptr<device> sptr; +    typedef boost::function<device_addrs_t(const device_addr_t &)> discover_t; +    typedef boost::function<sptr(const device_addr_t &)> make_t; -    //structors -    device(void); -    virtual ~device(void); +    /*! +     * Register a device into the discovery and factory system. +     * +     * \param discover a function that discovers devices +     * \param make a factory function that makes a device +     */ +    static void register_device( +        const discover_t &discover, +        const make_t &make +    );      /*!       * \brief Discover usrp devices attached to the host. @@ -68,13 +77,60 @@ public:      static sptr make(const device_addr_t &hint, size_t which = 0);      /*! -     * Get the device address for this board. +     * Send a buffer containing IF data with its metadata. +     * +     * Send handles fragmentation as follows: +     * If the buffer has more samples than the maximum supported, +     * the send method will send the maximum number of samples +     * as supported by the transport and return the number sent. +     * It is up to the caller to call send again on the un-sent +     * portions of the buffer, until the buffer is exhausted. +     * +     * This is a blocking call and will not return until the number +     * of samples returned have been read out of the buffer. +     * +     * \param buff a buffer pointing to some read-only memory +     * \param metadata data describing the buffer's contents +     * \param the type of data loaded in the buffer (32fc, 16sc) +     * \return the number of samples sent       */ -    device_addr_t get_device_addr(void); +    virtual size_t send( +        const boost::asio::const_buffer &buff, +        const tx_metadata_t &metadata, +        const std::string &type = "32fc" +    ) = 0; -    //the io interface -    virtual void send_raw(const std::vector<boost::asio::const_buffer> &) = 0; -    virtual uhd::shared_iovec recv_raw(void) = 0; +    /*! +     * Receive a buffer containing IF data and its metadata. +     * +     * Receive handles fragmentation as follows: +     * If the buffer has insufficient space to hold all samples +     * that were received in a single packet over-the-wire, +     * then the buffer will be completely filled and the implementation +     * will hold a pointer into the remaining portion of the packet. +     * Subsequent calls will load from the remainder of the packet, +     * and will flag the metadata to show that this is a fragment. +     * The next call to receive, after the remainder becomes exahausted, +     * will perform an over-the-wire receive as usual. +     * +     * This is a blocking call and will not return until the number +     * of samples returned have been written into the buffer. +     * However, a call to receive may timeout and return zero samples. +     * The timeout duration is decided by the underlying transport layer. +     * The caller should assume that the call to receive will not return +     * immediately when no packets are available to the transport layer, +     * and that the timeout duration is reasonably tuned for performance. +     * +     * \param buff the buffer to fill with IF data +     * \param metadata data to fill describing the buffer +     * \param the type of data to fill into the buffer (32fc, 16sc) +     * \return the number of samples received +     */ +    virtual size_t recv( +        const boost::asio::mutable_buffer &buff, +        rx_metadata_t &metadata, +        const std::string &type = "32fc" +    ) = 0;  };  } //namespace uhd diff --git a/host/include/uhd/device_addr.hpp b/host/include/uhd/device_addr.hpp index 8ea580321..7673faff0 100644 --- a/host/include/uhd/device_addr.hpp +++ b/host/include/uhd/device_addr.hpp @@ -18,11 +18,11 @@  #ifndef INCLUDED_UHD_DEVICE_ADDR_HPP  #define INCLUDED_UHD_DEVICE_ADDR_HPP +#include <uhd/config.hpp>  #include <uhd/dict.hpp> +#include <boost/cstdint.hpp>  #include <string>  #include <iostream> -#include <netinet/ether.h> -#include <stdint.h>  #include <vector>  namespace uhd{ @@ -31,8 +31,8 @@ namespace uhd{      * Wrapper for an ethernet mac address.      * Provides conversion between string and binary formats.      */ -    struct mac_addr_t{ -        struct ether_addr mac_addr; +    struct UHD_API mac_addr_t{ +        boost::uint8_t mac_addr[6];          mac_addr_t(const std::string &mac_addr_str = "00:00:00:00:00:00");          std::string to_string(void) const;      }; @@ -56,12 +56,14 @@ namespace uhd{       * \param device_addr a device address instance       * \return the string representation       */ -    std::string device_addr_to_string(const device_addr_t &device_addr); +    namespace device_addr{ +        UHD_API std::string to_string(const device_addr_t &device_addr); +    }  } //namespace uhd  //ability to use types with stream operators -std::ostream& operator<<(std::ostream &, const uhd::device_addr_t &); -std::ostream& operator<<(std::ostream &, const uhd::mac_addr_t &); +UHD_API std::ostream& operator<<(std::ostream &, const uhd::device_addr_t &); +UHD_API std::ostream& operator<<(std::ostream &, const uhd::mac_addr_t &);  #endif /* INCLUDED_UHD_DEVICE_ADDR_HPP */ diff --git a/host/include/uhd/dict.hpp b/host/include/uhd/dict.hpp index 1ed28551a..f08493952 100644 --- a/host/include/uhd/dict.hpp +++ b/host/include/uhd/dict.hpp @@ -18,7 +18,7 @@  #ifndef INCLUDED_UHD_DICT_HPP  #define INCLUDED_UHD_DICT_HPP -#include <map> +#include <list>  #include <vector>  #include <stdexcept>  #include <boost/foreach.hpp> @@ -27,11 +27,9 @@ namespace uhd{      /*!       * A templated dictionary class with a python-like interface. -     * Its wraps around a std::map internally.       */      template <class Key, class Val> class dict{      public: -        typedef std::map<Key, Val> map_t;          typedef std::pair<Key, Val> pair_t;          /*! @@ -42,11 +40,16 @@ namespace uhd{          }          /*! -         * Create a dictionary from a map. -         * \param map a map with key value pairs +         * Input iterator constructor: +         * Makes boost::assign::map_list_of work. +         * \param first the begin iterator +         * \param last the end iterator           */ -        dict(const map_t &map){ -            _map = map; +        template <class InputIterator> +        dict(InputIterator first, InputIterator last){ +            for(InputIterator it = first; it != last; it++){ +                _map.push_back(*it); +            }          }          /*! @@ -57,12 +60,21 @@ namespace uhd{          }          /*! +         * Get the number of elements in this dict. +         * \param the number of elements +         */ +        std::size_t size(void) const{ +            return _map.size(); +        } + +        /*!           * Get a list of the keys in this dict. +         * Key order depends on insertion precedence.           * \return vector of keys           */          std::vector<Key> get_keys(void) const{              std::vector<Key> keys; -            BOOST_FOREACH(pair_t p, _map){ +            BOOST_FOREACH(const pair_t &p, _map){                  keys.push_back(p.first);              }              return keys; @@ -70,11 +82,12 @@ namespace uhd{          /*!           * Get a list of the values in this dict. +         * Value order depends on insertion precedence.           * \return vector of values           */          std::vector<Val> get_vals(void) const{              std::vector<Val> vals; -            BOOST_FOREACH(pair_t p, _map){ +            BOOST_FOREACH(const pair_t &p, _map){                  vals.push_back(p.second);              }              return vals; @@ -86,7 +99,7 @@ namespace uhd{           * \return true if found           */          bool has_key(const Key &key) const{ -            BOOST_FOREACH(pair_t p, _map){ +            BOOST_FOREACH(const pair_t &p, _map){                  if (p.first == key) return true;              }              return false; @@ -100,8 +113,8 @@ namespace uhd{           * \throw an exception when not found           */          const Val &operator[](const Key &key) const{ -            if (has_key(key)){ -                return _map.find(key)->second; +            BOOST_FOREACH(const pair_t &p, _map){ +                if (p.first == key) return p.second;              }              throw std::invalid_argument("key not found in dict");          } @@ -113,7 +126,11 @@ namespace uhd{           * \return a reference to the value           */          Val &operator[](const Key &key){ -            return _map[key]; +            BOOST_FOREACH(pair_t &p, _map){ +                if (p.first == key) return p.second; +            } +            _map.push_back(pair_t(key, Val())); +            return _map.back().second;          }          /*! @@ -122,17 +139,14 @@ namespace uhd{           * \return the value of the item           * \throw an exception when not found           */ -        Val pop_key(const Key &key){ -            if (has_key(key)){ -                Val val = _map.find(key)->second; -                _map.erase(key); -                return val; -            } -            throw std::invalid_argument("key not found in dict"); +        Val pop(const Key &key){ +            Val val = (*this)[key]; +            _map.remove(pair_t(key, val)); +            return val;          }      private: -        map_t _map; //private container +        std::list<pair_t> _map; //private container      };  } //namespace uhd diff --git a/host/include/uhd/gain_handler.hpp b/host/include/uhd/gain_handler.hpp index 06800315a..65d6cecf9 100644 --- a/host/include/uhd/gain_handler.hpp +++ b/host/include/uhd/gain_handler.hpp @@ -15,43 +15,50 @@  // along with this program.  If not, see <http://www.gnu.org/licenses/>.  // -#include <boost/shared_ptr.hpp> -#include <uhd/wax.hpp> -#include <uhd/props.hpp> -#include <boost/function.hpp> -#include <boost/bind.hpp> -  #ifndef INCLUDED_UHD_GAIN_HANDLER_HPP  #define INCLUDED_UHD_GAIN_HANDLER_HPP +#include <uhd/config.hpp> +#include <uhd/wax.hpp> +#include <boost/shared_ptr.hpp> +#include <boost/function.hpp> +  namespace uhd{ -class gain_handler{ +class UHD_API gain_handler{  public:      typedef boost::shared_ptr<gain_handler> sptr; +    typedef boost::function<bool(const wax::obj &, const wax::obj &)> is_equal_t; -    template <class T> gain_handler( -        wax::obj *wax_obj_ptr, const T &gain_prop, -        const T &gain_min_prop, const T &gain_max_prop, -        const T &gain_step_prop, const T &gain_names_prop -    ){ -        _wax_obj_ptr = wax_obj_ptr; -        _gain_prop = gain_prop; -        _gain_min_prop = gain_min_prop; -        _gain_max_prop = gain_max_prop; -        _gain_step_prop = gain_step_prop; -        _gain_names_prop = gain_names_prop; -        _is_equal = boost::bind(&gain_handler::is_equal<T>, _1, _2); -    } +    /*! +     * A set of properties for dealing with gains. +     */ +    struct UHD_API props_t{ +        wax::obj value, range, names; +        props_t(void); //default constructor +    }; -    ~gain_handler(void); +    /*! +     * Make a new gain handler. +     * The construction arguments are agnostic to the property type. +     * It is up to the caller to provide an "is_equal" function that +     * can tell weather two properties (in a wax obj) are equal. +     * \param link a link to the wax obj with properties +     * \param props a struct of properties keys +     * \param is_equal the function that tests for equal properties +     */ +    static sptr make( +        const wax::obj &link, +        const props_t &props, +        is_equal_t is_equal +    );      /*!       * Intercept gets for overall gain, min, max, step.       * Ensures that the gain name is valid.       * \return true for handled, false to pass on       */ -    bool intercept_get(const wax::obj &key, wax::obj &val); +    virtual bool intercept_get(const wax::obj &key, wax::obj &val) = 0;      /*!       * Intercept sets for overall gain. @@ -59,39 +66,21 @@ public:       * Ensures that the new gain is within range.       * \return true for handled, false to pass on       */ -    bool intercept_set(const wax::obj &key, const wax::obj &val); - -private: - -    wax::obj     *_wax_obj_ptr; -    wax::obj      _gain_prop; -    wax::obj      _gain_min_prop; -    wax::obj      _gain_max_prop; -    wax::obj      _gain_step_prop; -    wax::obj      _gain_names_prop; +    virtual bool intercept_set(const wax::obj &key, const wax::obj &val) = 0;      /*! -     * Verify that the key is valid: -     * If its a named prop for gain, ensure that name is valid. -     * If the name if not valid, throw a std::invalid_argument. -     * The name can only be valid if its in the list of gain names. -     */ -    void _check_key(const wax::obj &key); - -    /* -     * Private interface to test if two wax types are equal: +     * Function template to test if two wax types are equal:       * The constructor will bind an instance of this for a specific type.       * This bound equals functions allows the intercept methods to be non-templated.       */      template <class T> static bool is_equal(const wax::obj &a, const wax::obj &b){          try{ -            return wax::cast<T>(a) == wax::cast<T>(b); +            return a.as<T>() == b.as<T>();          }          catch(const wax::bad_cast &){              return false;          }      } -    boost::function<bool(const wax::obj &, const wax::obj &)> _is_equal;  }; diff --git a/host/include/uhd/metadata.hpp b/host/include/uhd/metadata.hpp new file mode 100644 index 000000000..6d80f070d --- /dev/null +++ b/host/include/uhd/metadata.hpp @@ -0,0 +1,61 @@ +// +// Copyright 2010 Ettus Research LLC +// +// This program 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. +// +// This program 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 this program.  If not, see <http://www.gnu.org/licenses/>. +// + +#ifndef INCLUDED_UHD_METADATA_HPP +#define INCLUDED_UHD_METADATA_HPP + +#include <uhd/config.hpp> +#include <uhd/time_spec.hpp> + +namespace uhd{ + +/*! + * RX metadata structure for describing sent IF data. + * Includes stream ID, time specification, and fragmentation flags. + * The receive routines will convert IF data headers into metadata. + */ +struct UHD_API rx_metadata_t{ +    boost::uint32_t stream_id; +    time_spec_t time_spec; +    bool has_stream_id; +    bool has_time_spec; +    bool is_fragment; + +    //default constructor +    rx_metadata_t(void); +}; + +/*! + * TX metadata structure for describing received IF data. + * Includes stream ID, time specification, and burst flags. + * The send routines will convert the metadata to IF data headers. + */ +struct UHD_API tx_metadata_t{ +    boost::uint32_t stream_id; +    time_spec_t time_spec; +    bool has_stream_id; +    bool has_time_spec; +    bool start_of_burst; +    bool end_of_burst; + +    //default constructor +    tx_metadata_t(void); +}; + +} //namespace uhd + +#endif /* INCLUDED_UHD_METADATA_HPP */ diff --git a/host/include/uhd/props.hpp b/host/include/uhd/props.hpp index 2b6daf6c5..41e0eff63 100644 --- a/host/include/uhd/props.hpp +++ b/host/include/uhd/props.hpp @@ -15,30 +15,16 @@  // along with this program.  If not, see <http://www.gnu.org/licenses/>.  // -#include <boost/tuple/tuple.hpp> -#include <uhd/time_spec.hpp> -#include <uhd/wax.hpp> -#include <complex> -#include <vector> -  #ifndef INCLUDED_UHD_PROPS_HPP  #define INCLUDED_UHD_PROPS_HPP -namespace uhd{ - -    //common typedefs for board properties -    typedef float gain_t; -    typedef double freq_t; - -    //scalar types (have not used yet, dont uncomment until needed) -    //typedef int int_scalar_t; -    //typedef float real_scalar_t; -    //typedef std::complex<real_scalar_t> complex_scalar_t; +#include <uhd/config.hpp> +#include <uhd/wax.hpp> +#include <boost/tuple/tuple.hpp> +#include <vector> +#include <string> -    //vector types (have not used yet, dont uncomment until needed) -    //typedef std::vector<int_scalar_t> int_vec_t; -    //typedef std::vector<real_scalar_t> real_vec_t; -    //typedef std::vector<complex_scalar_t> complex_vec_t; +namespace uhd{      //typedef for handling named properties      typedef std::vector<std::string> prop_names_t; @@ -49,9 +35,10 @@ namespace uhd{       * \param key a reference to the prop object       * \param name a reference to the name object       */ -    inline named_prop_t extract_named_prop(const wax::obj &key, const std::string &name = ""){ +    inline UHD_API named_prop_t //must be exported as part of the api to work (TODO move guts to cpp file) +    extract_named_prop(const wax::obj &key, const std::string &name = ""){          if (key.type() == typeid(named_prop_t)){ -            return wax::cast<named_prop_t>(key); +            return key.as<named_prop_t>();          }          return named_prop_t(key, name);      } @@ -65,7 +52,9 @@ namespace uhd{      enum device_prop_t{          DEVICE_PROP_NAME,              //ro, std::string          DEVICE_PROP_MBOARD,            //ro, wax::obj -        DEVICE_PROP_MBOARD_NAMES       //ro, prop_names_t +        DEVICE_PROP_MBOARD_NAMES,      //ro, prop_names_t +        DEVICE_PROP_MAX_RX_SAMPLES,    //ro, size_t +        DEVICE_PROP_MAX_TX_SAMPLES     //ro, size_t      };      /*! @@ -77,7 +66,6 @@ namespace uhd{      enum mboard_prop_t{          MBOARD_PROP_NAME,              //ro, std::string          MBOARD_PROP_OTHERS,            //ro, prop_names_t -        MBOARD_PROP_MTU,               //ro, size_t          MBOARD_PROP_CLOCK_RATE,        //ro, freq_t          MBOARD_PROP_RX_DSP,            //ro, wax::obj          MBOARD_PROP_RX_DSP_NAMES,      //ro, prop_names_t @@ -87,10 +75,8 @@ namespace uhd{          MBOARD_PROP_RX_DBOARD_NAMES,   //ro, prop_names_t          MBOARD_PROP_TX_DBOARD,         //ro, wax::obj          MBOARD_PROP_TX_DBOARD_NAMES,   //ro, prop_names_t -        MBOARD_PROP_PPS_SOURCE,        //rw, std::string (sma, mimo) +        MBOARD_PROP_CLOCK_CONFIG,      //rw, clock_config_t          MBOARD_PROP_PPS_SOURCE_NAMES,  //ro, prop_names_t -        MBOARD_PROP_PPS_POLARITY,      //rw, std::string (pos, neg) -        MBOARD_PROP_REF_SOURCE,        //rw, std::string (int, sma, mimo)          MBOARD_PROP_REF_SOURCE_NAMES,  //ro, prop_names_t          MBOARD_PROP_TIME_NOW,          //wo, time_spec_t          MBOARD_PROP_TIME_NEXT_PPS      //wo, time_spec_t @@ -116,24 +102,23 @@ namespace uhd{          DBOARD_PROP_NAME,              //ro, std::string          DBOARD_PROP_SUBDEV,            //ro, wax::obj          DBOARD_PROP_SUBDEV_NAMES,      //ro, prop_names_t -        DBOARD_PROP_CODEC              //ro, wax::obj -    }; +        DBOARD_PROP_USED_SUBDEVS       //ro, prop_names_t +        //DBOARD_PROP_CODEC              //ro, wax::obj //----> not sure, dont have to deal with yet +    };  -    /*! +    /*! ------ not dealing with yet, commented out ------------      * Possible device codec properties:      *   A codec is expected to have a rate and gain elements.      *   Other properties can be discovered through the others prop.      */ -    enum codec_prop_t{ +    /*enum codec_prop_t{          CODEC_PROP_NAME,               //ro, std::string          CODEC_PROP_OTHERS,             //ro, prop_names_t          CODEC_PROP_GAIN,               //rw, gain_t -        CODEC_PROP_GAIN_MAX,           //ro, gain_t -        CODEC_PROP_GAIN_MIN,           //ro, gain_t -        CODEC_PROP_GAIN_STEP,          //ro, gain_t +        CODEC_PROP_GAIN_RANGE,         //ro, gain_range_t          CODEC_PROP_GAIN_NAMES,         //ro, prop_names_t -        CODEC_PROP_CLOCK_RATE          //ro, freq_t -    }; +        //CODEC_PROP_CLOCK_RATE          //ro, freq_t //----> not sure we care to know +    };*/      /*!      * Possible device subdev properties @@ -142,22 +127,19 @@ namespace uhd{          SUBDEV_PROP_NAME,              //ro, std::string          SUBDEV_PROP_OTHERS,            //ro, prop_names_t          SUBDEV_PROP_GAIN,              //rw, gain_t -        SUBDEV_PROP_GAIN_MAX,          //ro, gain_t -        SUBDEV_PROP_GAIN_MIN,          //ro, gain_t -        SUBDEV_PROP_GAIN_STEP,         //ro, gain_t +        SUBDEV_PROP_GAIN_RANGE,        //ro, gain_range_t          SUBDEV_PROP_GAIN_NAMES,        //ro, prop_names_t          SUBDEV_PROP_FREQ,              //rw, freq_t -        SUBDEV_PROP_FREQ_MAX,          //ro, freq_t -        SUBDEV_PROP_FREQ_MIN,          //ro, freq_t +        SUBDEV_PROP_FREQ_RANGE,        //ro, freq_range_t          SUBDEV_PROP_ANTENNA,           //rw, std::string          SUBDEV_PROP_ANTENNA_NAMES,     //ro, prop_names_t          SUBDEV_PROP_ENABLED,           //rw, bool          SUBDEV_PROP_QUADRATURE,        //ro, bool          SUBDEV_PROP_IQ_SWAPPED,        //ro, bool          SUBDEV_PROP_SPECTRUM_INVERTED, //ro, bool -        SUBDEV_PROP_IS_TX,             //ro, bool -        SUBDEV_PROP_RSSI,              //ro, gain_t -        SUBDEV_PROP_BANDWIDTH          //rw, freq_t +        SUBDEV_PROP_LO_INTERFERES      //ro, bool +        //SUBDEV_PROP_RSSI,              //ro, gain_t //----> not on all boards, use named prop +        //SUBDEV_PROP_BANDWIDTH          //rw, freq_t //----> not on all boards, use named prop      };  } //namespace uhd diff --git a/host/include/uhd/shared_iovec.hpp b/host/include/uhd/shared_iovec.hpp deleted file mode 100644 index a120e55d5..000000000 --- a/host/include/uhd/shared_iovec.hpp +++ /dev/null @@ -1,54 +0,0 @@ -// -// Copyright 2010 Ettus Research LLC -// -// This program 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. -// -// This program 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 this program.  If not, see <http://www.gnu.org/licenses/>. -// - -#ifndef INCLUDED_UHD_SHARED_IOVEC_HPP -#define INCLUDED_UHD_SHARED_IOVEC_HPP - -#include <boost/shared_array.hpp> -#include <stdint.h> - -namespace uhd{ - -/*! - * A shared iovec contains a shared array and its length. - * Creating a new shared iovec allocates new memory. - * This memory is freed when all copies are destroyed. - */ -class shared_iovec{ -public: -    /*! -     * Create a shared iovec and allocate memory. -     * \param len the length in bytes -     */ -    shared_iovec(size_t len=0); - -    /*! -     * Destroy a shared iovec. -     * Will not free the memory unless this is the last copy. -     */ -    ~shared_iovec(void); - -    void *base; -    size_t len; - -private: -    boost::shared_array<uint8_t> _shared_array; -}; - -} //namespace uhd - -#endif /* INCLUDED_UHD_SHARED_IOVEC_HPP */ diff --git a/host/include/uhd/simple_device.hpp b/host/include/uhd/simple_device.hpp new file mode 100644 index 000000000..ad25eccdc --- /dev/null +++ b/host/include/uhd/simple_device.hpp @@ -0,0 +1,90 @@ +// +// Copyright 2010 Ettus Research LLC +// +// This program 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. +// +// This program 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 this program.  If not, see <http://www.gnu.org/licenses/>. +// + +#ifndef INCLUDED_UHD_SIMPLE_DEVICE_HPP +#define INCLUDED_UHD_SIMPLE_DEVICE_HPP + +#include <uhd/config.hpp> +#include <uhd/device.hpp> +#include <uhd/types.hpp> +#include <boost/shared_ptr.hpp> +#include <boost/utility.hpp> +#include <vector> + +namespace uhd{ + +/*! + * The simple UHD device class: + * A simple device facilitates ease-of-use for most use-case scenarios. + * The wrapper provides convenience functions to tune the devices + * as well as to set the dboard gains, antennas, and other properties. + */ +class UHD_API simple_device : boost::noncopyable{ +public: +    typedef boost::shared_ptr<simple_device> sptr; +    static sptr make(const std::string &args); + +    virtual device::sptr get_device(void) = 0; + +    virtual std::string get_name(void) = 0; + +    /******************************************************************* +     * Streaming +     ******************************************************************/ +    virtual void set_streaming(bool enb) = 0; +    virtual bool get_streaming(void) = 0; + +    /******************************************************************* +     * RX methods +     ******************************************************************/ +    virtual void set_rx_rate(double rate) = 0; +    virtual double get_rx_rate(void) = 0; +    virtual std::vector<double> get_rx_rates(void) = 0; + +    virtual tune_result_t set_rx_freq(double freq) = 0; +    virtual freq_range_t get_rx_freq_range(void) = 0; + +    virtual void set_rx_gain(float gain) = 0; +    virtual float get_rx_gain(void) = 0; +    virtual gain_range_t get_rx_gain_range(void) = 0; + +    virtual void set_rx_antenna(const std::string &ant) = 0; +    virtual std::string get_rx_antenna(void) = 0; +    virtual std::vector<std::string> get_rx_antennas(void) = 0; + +    /******************************************************************* +     * TX methods +     ******************************************************************/ +    virtual void set_tx_rate(double rate) = 0; +    virtual double get_tx_rate(void) = 0; +    virtual std::vector<double> get_tx_rates(void) = 0; + +    virtual tune_result_t set_tx_freq(double freq) = 0; +    virtual freq_range_t get_tx_freq_range(void) = 0; + +    virtual void set_tx_gain(float gain) = 0; +    virtual float get_tx_gain(void) = 0; +    virtual gain_range_t get_tx_gain_range(void) = 0; + +    virtual void set_tx_antenna(const std::string &ant) = 0; +    virtual std::string get_tx_antenna(void) = 0; +    virtual std::vector<std::string> get_tx_antennas(void) = 0; +}; + +} //namespace uhd + +#endif /* INCLUDED_UHD_SIMPLE_DEVICE_HPP */ diff --git a/host/include/uhd/time_spec.hpp b/host/include/uhd/time_spec.hpp index e5657e555..c1e7cf3a1 100644 --- a/host/include/uhd/time_spec.hpp +++ b/host/include/uhd/time_spec.hpp @@ -15,11 +15,13 @@  // along with this program.  If not, see <http://www.gnu.org/licenses/>.  // -#include <stdint.h> -  #ifndef INCLUDED_UHD_TIME_SPEC_HPP  #define INCLUDED_UHD_TIME_SPEC_HPP +#include <uhd/config.hpp> +#include <boost/cstdint.hpp> +#include <boost/date_time/posix_time/posix_time.hpp> +  namespace uhd{      /*! @@ -28,28 +30,30 @@ namespace uhd{       * The time_spec_t can be used when setting the time on devices       * and for controlling the start of streaming for applicable dsps.       */ -    struct time_spec_t{ -        uint32_t secs; -        uint32_t ticks; +    struct UHD_API time_spec_t{ +        boost::uint32_t secs; +        boost::uint32_t ticks;          /*!           * Create a time_spec_t that holds a wildcard time.           * This will have implementation-specific meaning.           */ -        time_spec_t(void){ -            secs = ~0; -            ticks = ~0; -        } +        time_spec_t(void);          /*!           * Create a time_spec_t from seconds and ticks.           * \param new_secs the new seconds           * \param new_ticks the new ticks (default = 0)           */ -        time_spec_t(uint32_t new_secs, uint32_t new_ticks = 0){ -            secs = new_secs; -            ticks = new_ticks; -        } +        time_spec_t(boost::uint32_t new_secs, boost::uint32_t new_ticks = 0); + +        /*! +         * Create a time_spec_t from boost posix time. +         * \param time fine-grained boost posix time +         * \param tick_rate the rate of ticks per second +         */ +        time_spec_t(boost::posix_time::ptime time, double tick_rate); +      };  } //namespace uhd diff --git a/host/include/uhd/transport/CMakeLists.txt b/host/include/uhd/transport/CMakeLists.txt index b786eb945..7f5db2128 100644 --- a/host/include/uhd/transport/CMakeLists.txt +++ b/host/include/uhd/transport/CMakeLists.txt @@ -17,6 +17,9 @@  INSTALL(FILES -    udp.hpp +    smart_buffer.hpp +    udp_simple.hpp +    udp_zero_copy.hpp +    vrt.hpp      DESTINATION ${HEADER_DIR}/uhd/transport  ) diff --git a/host/include/uhd/transport/smart_buffer.hpp b/host/include/uhd/transport/smart_buffer.hpp new file mode 100644 index 000000000..9e1032feb --- /dev/null +++ b/host/include/uhd/transport/smart_buffer.hpp @@ -0,0 +1,45 @@ +// +// Copyright 2010 Ettus Research LLC +// +// This program 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. +// +// This program 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 this program.  If not, see <http://www.gnu.org/licenses/>. +// + +#ifndef INCLUDED_UHD_TRANSPORT_SMART_BUFFER_HPP +#define INCLUDED_UHD_TRANSPORT_SMART_BUFFER_HPP + +#include <boost/asio.hpp> +#include <boost/utility.hpp> +#include <boost/shared_ptr.hpp> + +namespace uhd{ namespace transport{ + +/*! + * A buffer that knows how to free itself: + * + * This is just the smart buffer interface. + * A transport implementation will have its own + * internal (custom) smart buffer implementation. + * + * A smart buffer contains a boost asio const buffer. + * On destruction, the buffer contents will be freed. + */ +class smart_buffer : boost::noncopyable{ +public: +    typedef boost::shared_ptr<smart_buffer> sptr; +    virtual const boost::asio::const_buffer &get(void) const = 0; +}; + +}} //namespace + +#endif /* INCLUDED_UHD_TRANSPORT_SMART_BUFFER_HPP */ diff --git a/host/include/uhd/transport/udp.hpp b/host/include/uhd/transport/udp.hpp deleted file mode 100644 index 6db6bd377..000000000 --- a/host/include/uhd/transport/udp.hpp +++ /dev/null @@ -1,75 +0,0 @@ -// -// Copyright 2010 Ettus Research LLC -// -// This program 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. -// -// This program 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 this program.  If not, see <http://www.gnu.org/licenses/>. -// - -#include <boost/asio.hpp> -#include <boost/utility.hpp> -#include <boost/shared_ptr.hpp> -#include <uhd/shared_iovec.hpp> - -#ifndef INCLUDED_UHD_TRANSPORT_UDP_HPP -#define INCLUDED_UHD_TRANSPORT_UDP_HPP - -namespace uhd{ namespace transport{ - -class udp : boost::noncopyable{ -public: -    typedef boost::shared_ptr<udp> sptr; - -    /*! -     * Constructor. -     * The address will be resolved, it can be a host name or ipv4. -     * The port will be resolved, it can be a port type or number. -     * \param addr a string representing the destination address -     * \param port a string representing the destination port -     * \param bcast if true, enable the broadcast option on the socket -     */ -    udp(const std::string &addr, const std::string &port, bool bcast = false); - -    /*! -     * Destructor -     */ -    ~udp(void); - -    /*! -     * Send a vector of buffer (like send_msg). -     * \param buffs a vector of asio buffers -     */ -    void send(const std::vector<boost::asio::const_buffer> &buffs); - -    /*! -     * Send a single buffer. -     * \param buff single asio buffer -     */ -    void send(const boost::asio::const_buffer &buff); - -    /*! -     * Receive a buffer. The memory is managed internally. -     * Calling recv will invalidate the buffer of the previous recv. -     * \return a shared iovec with allocated memory -     */ -    uhd::shared_iovec recv(void); - -private: -    boost::asio::ip::udp::socket   *_socket; -    boost::asio::ip::udp::endpoint _receiver_endpoint; -    boost::asio::ip::udp::endpoint _sender_endpoint; -    boost::asio::io_service        _io_service; -}; - -}} //namespace - -#endif /* INCLUDED_UHD_TRANSPORT_UDP_HPP */ diff --git a/host/include/uhd/transport/udp_simple.hpp b/host/include/uhd/transport/udp_simple.hpp new file mode 100644 index 000000000..40e60d091 --- /dev/null +++ b/host/include/uhd/transport/udp_simple.hpp @@ -0,0 +1,80 @@ +// +// Copyright 2010 Ettus Research LLC +// +// This program 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. +// +// This program 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 this program.  If not, see <http://www.gnu.org/licenses/>. +// + +#ifndef INCLUDED_UHD_TRANSPORT_UDP_SIMPLE_HPP +#define INCLUDED_UHD_TRANSPORT_UDP_SIMPLE_HPP + +#include <uhd/config.hpp> +#include <boost/asio.hpp> +#include <boost/utility.hpp> +#include <boost/shared_ptr.hpp> + +namespace uhd{ namespace transport{ + +class UHD_API udp_simple : boost::noncopyable{ +public: +    typedef boost::shared_ptr<udp_simple> sptr; + +    /*! +     * Make a new connected udp transport: +     * This transport is for sending and receiving +     * between this host and a single endpoint. +     * The primary usage for this transport will be control transactions. +     * The underlying implementation is simple and portable (not fast). +     * +     * The address will be resolved, it can be a host name or ipv4. +     * The port will be resolved, it can be a port type or number. +     * +     * \param addr a string representing the destination address +     * \param port a string representing the destination port +     */ +    static sptr make_connected(const std::string &addr, const std::string &port); + +    /*! +     * Make a new broadcasting udp transport: +     * This transport can send udp broadcast datagrams +     * and receive datagrams from multiple sources. +     * The primary usage for this transport will be to discover devices. +     * +     * The address will be resolved, it can be a host name or ipv4. +     * The port will be resolved, it can be a port type or number. +     * +     * \param addr a string representing the destination address +     * \param port a string representing the destination port +     */ +    static sptr make_broadcast(const std::string &addr, const std::string &port); + +    /*! +     * Send a single buffer. +     * Blocks until the data is sent. +     * \param buff single asio buffer +     * \return the number of bytes sent +     */ +    virtual size_t send(const boost::asio::const_buffer &buff) = 0; + +    /*! +     * Receive into the provided buffer. +     * Blocks until data is received or a timeout occurs. +     * \param buff a mutable buffer to receive into +     * \return the number of bytes received or zero on timeout +     */ +    virtual size_t recv(const boost::asio::mutable_buffer &buff) = 0; +}; + +}} //namespace + +#endif /* INCLUDED_UHD_TRANSPORT_UDP_SIMPLE_HPP */ diff --git a/host/include/uhd/transport/udp_zero_copy.hpp b/host/include/uhd/transport/udp_zero_copy.hpp new file mode 100644 index 000000000..03d89b3a5 --- /dev/null +++ b/host/include/uhd/transport/udp_zero_copy.hpp @@ -0,0 +1,77 @@ +// +// Copyright 2010 Ettus Research LLC +// +// This program 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. +// +// This program 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 this program.  If not, see <http://www.gnu.org/licenses/>. +// + +#ifndef INCLUDED_UHD_TRANSPORT_UDP_ZERO_COPY_HPP +#define INCLUDED_UHD_TRANSPORT_UDP_ZERO_COPY_HPP + +#include <uhd/config.hpp> +#include <uhd/transport/smart_buffer.hpp> +#include <boost/asio.hpp> +#include <boost/utility.hpp> +#include <boost/shared_ptr.hpp> + +namespace uhd{ namespace transport{ + +/*! + * A zero copy udp transport provides an efficient way to handle data. + * by avoiding the extra copy when recv() is called on the socket. + * Rather, the zero copy transport gives the caller a memory reference. + * The caller informs the transport when it is finished with the reference. + * + * On linux systems, the zero copy transport can use a kernel packet ring. + * If no platform specific solution is available, make returns a boost asio + * implementation that wraps the functionality around a standard recv() call. + */ +class UHD_API udp_zero_copy : boost::noncopyable{ +public: +    typedef boost::shared_ptr<udp_zero_copy> sptr; + +    /*! +     * Make a new zero copy udp transport: +     * This transport is for sending and receiving +     * between this host and a single endpoint. +     * The primary usage for this transport will be data transactions. +     * The underlying implementation is fast and platform specific. +     * +     * The address will be resolved, it can be a host name or ipv4. +     * The port will be resolved, it can be a port type or number. +     * +     * \param addr a string representing the destination address +     * \param port a string representing the destination port +     */ +    static sptr make(const std::string &addr, const std::string &port); + +    /*! +     * Send a single buffer. +     * Blocks until the data is sent. +     * \param buff single asio buffer +     * \return the number of bytes sent +     */ +    virtual size_t send(const boost::asio::const_buffer &buff) = 0; + +    /*! +     * Receive a buffer. +     * Blocks until data is received or a timeout occurs. +     * The memory is managed by the implementation. +     * \return a smart buffer (empty on timeout) +     */ +    virtual smart_buffer::sptr recv(void) = 0; +}; + +}} //namespace + +#endif /* INCLUDED_UHD_TRANSPORT_UDP_ZERO_COPY_HPP */ diff --git a/host/include/uhd/transport/vrt.hpp b/host/include/uhd/transport/vrt.hpp new file mode 100644 index 000000000..db2c57eba --- /dev/null +++ b/host/include/uhd/transport/vrt.hpp @@ -0,0 +1,71 @@ +// +// Copyright 2010 Ettus Research LLC +// +// This program 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. +// +// This program 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 this program.  If not, see <http://www.gnu.org/licenses/>. +// + +#ifndef INCLUDED_UHD_TRANSPORT_VRT_HPP +#define INCLUDED_UHD_TRANSPORT_VRT_HPP + +#include <uhd/config.hpp> +#include <uhd/metadata.hpp> +#include <cstddef> + +namespace uhd{ namespace transport{ + +namespace vrt{ + +    static const size_t max_header_words32 = 7; + +    /*! +     * Pack a vrt header from metadata. +     * \param metadata the tx metadata with flags and timestamps +     * \param header_buff memory to write the packed vrt header +     * \param num_header_words32 number of words in the vrt header +     * \param num_payload_words32 the length of the payload +     * \param num_packet_words32 the length of the packet +     * \param packet_count the packet count sequence number +     */ +    UHD_API void pack( +        const tx_metadata_t &metadata, //input +        boost::uint32_t *header_buff,  //output +        size_t &num_header_words32,    //output +        size_t num_payload_words32,    //input +        size_t &num_packet_words32,    //output +        size_t packet_count            //input +    ); + +    /*! +     * Unpack a vrt header to metadata. +     * \param metadata the rx metadata with flags and timestamps +     * \param header_buff memory to read the packed vrt header +     * \param num_header_words32 number of words in the vrt header +     * \param num_payload_words32 the length of the payload +     * \param num_packet_words32 the length of the packet +     * \param packet_count the packet count sequence number +     */ +    UHD_API void unpack( +        rx_metadata_t &metadata,            //output +        const boost::uint32_t *header_buff, //input +        size_t &num_header_words32,         //output +        size_t &num_payload_words32,        //output +        size_t num_packet_words32,          //input +        size_t &packet_count                //output +    ); + +} //namespace vrt + +}} //namespace + +#endif /* INCLUDED_UHD_TRANSPORT_VRT_HPP */ diff --git a/host/include/uhd/types.hpp b/host/include/uhd/types.hpp new file mode 100644 index 000000000..1439f57a1 --- /dev/null +++ b/host/include/uhd/types.hpp @@ -0,0 +1,83 @@ +// +// Copyright 2010 Ettus Research LLC +// +// This program 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. +// +// This program 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 this program.  If not, see <http://www.gnu.org/licenses/>. +// + +#ifndef INCLUDED_UHD_TYPES_HPP +#define INCLUDED_UHD_TYPES_HPP + +#include <uhd/config.hpp> +#include <string> + +namespace uhd{ + +    typedef float gain_t; //TODO REMOVE +    typedef double freq_t; //TODO REMOVE + +    /*! +     * The gain range struct describes possible gain settings. +     * The mimumum gain, maximum gain, and step size are in dB. +     */ +    struct UHD_API gain_range_t{ +        float min, max, step; +        gain_range_t(float min = 0.0, float max = 0.0, float step = 0.0); +    }; + +    /*! +     * The frequency range struct describes possible frequency settings. +     * Because tuning is very granular (sub-Hz), step size is not listed. +     * The mimumum frequency and maximum frequency are in Hz. +     */ +    struct UHD_API freq_range_t{ +        double min, max; +        freq_range_t(double min = 0.0, double max = 0.0); +    }; + +    /*! +     * The tune result struct holds result of a 2-phase tuning: +     * The struct hold the result of tuning the dboard as +     * the target and actual intermediate frequency. +     * The struct hold the result of tuning the DDC/DUC as +     * the target and actual digital converter frequency. +     * It also tell us weather or not the spectrum is inverted. +     */ +    struct UHD_API tune_result_t{ +        double target_inter_freq; +        double actual_inter_freq; +        double target_dxc_freq; +        double actual_dxc_freq; +        bool spectrum_inverted; +        tune_result_t(void); +    }; + +    /*! +     * Clock configuration settings: +     * The source for the 10MHz reference clock. +     * The source and polarity for the PPS clock. +     * Possible settings for the reference and pps source +     * are implementation specific motherboard properties. +     * See the MBOARD_PROP_XXX_SOURCE_NAMES properties. +     */ +    struct clock_config_t{ +        enum polarity_t {POLARITY_NEG, POLARITY_POS}; +        std::string ref_source; +        std::string pps_source; +        polarity_t  pps_polarity; +        clock_config_t(void); +    }; + +} //namespace uhd + +#endif /* INCLUDED_UHD_TYPES_HPP */ diff --git a/host/include/uhd/usrp/CMakeLists.txt b/host/include/uhd/usrp/CMakeLists.txt index e7bdc1784..4e0a92365 100644 --- a/host/include/uhd/usrp/CMakeLists.txt +++ b/host/include/uhd/usrp/CMakeLists.txt @@ -21,6 +21,7 @@ INSTALL(FILES      dboard_id.hpp      dboard_interface.hpp      dboard_manager.hpp +    usrp1e.hpp      usrp2.hpp      DESTINATION ${HEADER_DIR}/uhd/usrp  ) diff --git a/host/include/uhd/usrp/dboard_base.hpp b/host/include/uhd/usrp/dboard_base.hpp index b5c0d40ed..907a7814a 100644 --- a/host/include/uhd/usrp/dboard_base.hpp +++ b/host/include/uhd/usrp/dboard_base.hpp @@ -18,6 +18,7 @@  #ifndef INCLUDED_UHD_USRP_DBOARD_BASE_HPP  #define INCLUDED_UHD_USRP_DBOARD_BASE_HPP +#include <uhd/config.hpp>  #include <uhd/wax.hpp>  #include <boost/utility.hpp>  #include <boost/shared_ptr.hpp> @@ -31,7 +32,7 @@ namespace uhd{ namespace usrp{   * A daughter board dboard_base class for all dboards.   * Only other dboard dboard_base classes should inherit this.   */ -class dboard_base : boost::noncopyable{ +class UHD_API dboard_base : boost::noncopyable{  public:      typedef boost::shared_ptr<dboard_base> sptr;      //the constructor args consist of a subdev name and an interface @@ -56,21 +57,22 @@ protected:      dboard_id_t get_tx_id(void);  private: -    std::string        _subdev_name; +    std::string               _subdev_name;      dboard_interface::sptr    _dboard_interface; -    dboard_id_t        _rx_id, _tx_id; +    dboard_id_t               _rx_id, _tx_id;  };  /*!   * A xcvr daughter board implements rx and tx methods   * Sub classes for xcvr boards should inherit this.   */ -class xcvr_dboard_base : public dboard_base{ +class UHD_API xcvr_dboard_base : public dboard_base{  public:      /*!       * Create a new xcvr dboard object, override in subclasses.       */      xcvr_dboard_base(ctor_args_t const&); +      virtual ~xcvr_dboard_base(void);  }; @@ -78,7 +80,7 @@ public:   * A rx daughter board only implements rx methods.   * Sub classes for rx-only boards should inherit this.   */ -class rx_dboard_base : public dboard_base{ +class UHD_API rx_dboard_base : public dboard_base{  public:      /*!       * Create a new rx dboard object, override in subclasses. @@ -96,7 +98,7 @@ public:   * A tx daughter board only implements tx methods.   * Sub classes for rx-only boards should inherit this.   */ -class tx_dboard_base : public dboard_base{ +class UHD_API tx_dboard_base : public dboard_base{  public:      /*!       * Create a new rx dboard object, override in subclasses. diff --git a/host/include/uhd/usrp/dboard_id.hpp b/host/include/uhd/usrp/dboard_id.hpp index 8e904ff33..4b2392bee 100644 --- a/host/include/uhd/usrp/dboard_id.hpp +++ b/host/include/uhd/usrp/dboard_id.hpp @@ -15,22 +15,23 @@  // along with this program.  If not, see <http://www.gnu.org/licenses/>.  // -#include <string> -  #ifndef INCLUDED_UHD_USRP_DBOARD_ID_HPP  #define INCLUDED_UHD_USRP_DBOARD_ID_HPP +#include <uhd/config.hpp> +#include <boost/cstdint.hpp> +#include <string> +  namespace uhd{ namespace usrp{ -enum dboard_id_t{ -    ID_NONE     = 0xffff, -    ID_BASIC_TX = 0x0000, -    ID_BASIC_RX = 0x0001 -}; +typedef boost::uint16_t dboard_id_t; + +static const dboard_id_t ID_NONE = 0xffff; //TODO: REMOVE ME -struct dboard_id{ -    static std::string to_string(const dboard_id_t &id); -}; +namespace dboard_id{ +    static const dboard_id_t NONE = 0xffff; +    UHD_API std::string to_string(const dboard_id_t &id); +}  }} //namespace diff --git a/host/include/uhd/usrp/dboard_interface.hpp b/host/include/uhd/usrp/dboard_interface.hpp index 84e1f5b22..5b40616f0 100644 --- a/host/include/uhd/usrp/dboard_interface.hpp +++ b/host/include/uhd/usrp/dboard_interface.hpp @@ -18,9 +18,10 @@  #ifndef INCLUDED_UHD_USRP_DBOARD_INTERFACE_HPP  #define INCLUDED_UHD_USRP_DBOARD_INTERFACE_HPP +#include <uhd/config.hpp>  #include <boost/shared_ptr.hpp> +#include <boost/cstdint.hpp>  #include <vector> -#include <stdint.h>  namespace uhd{ namespace usrp{ @@ -30,10 +31,10 @@ namespace uhd{ namespace usrp{   * This dboard_interface provides i2c, spi, gpio, atr, aux dac/adc access.   * Each mboard should have a specially tailored dboard dboard_interface.   */ -class dboard_interface{ +class UHD_API dboard_interface{  public:      typedef boost::shared_ptr<dboard_interface> sptr; -    typedef std::vector<uint8_t> byte_vector_t; +    typedef std::vector<boost::uint8_t> byte_vector_t;      //tells the host which unit to use      enum unit_type_t{ @@ -96,7 +97,7 @@ public:       * \param rx_value  16-bits, 0=FPGA output low, 1=FPGA output high       * \param mask      16-bits, 0=software, 1=atr       */ -    virtual void set_atr_reg(gpio_bank_t bank, uint16_t tx_value, uint16_t rx_value, uint16_t mask) = 0; +    virtual void set_atr_reg(gpio_bank_t bank, boost::uint16_t tx_value, boost::uint16_t rx_value, boost::uint16_t mask) = 0;      /*!       * Set daughterboard GPIO data direction register. @@ -105,7 +106,7 @@ public:       * \param value     16-bits, 0=FPGA input, 1=FPGA output       * \param mask      16-bits, 0=ignore, 1=set       */ -    virtual void set_gpio_ddr(gpio_bank_t bank, uint16_t value, uint16_t mask) = 0; +    virtual void set_gpio_ddr(gpio_bank_t bank, boost::uint16_t value, boost::uint16_t mask) = 0;      /*!       * Set daughterboard GPIO pin values. @@ -114,7 +115,7 @@ public:       * \param value    16 bits, 0=low, 1=high       * \param mask     16 bits, 0=ignore, 1=set       */ -    virtual void write_gpio(gpio_bank_t bank, uint16_t value, uint16_t mask) = 0; +    virtual void write_gpio(gpio_bank_t bank, boost::uint16_t value, boost::uint16_t mask) = 0;      /*!       * Read daughterboard GPIO pin values @@ -122,7 +123,7 @@ public:       * \param bank GPIO_TX_BANK or GPIO_RX_BANK       * \return the value of the gpio bank       */ -    virtual uint16_t read_gpio(gpio_bank_t bank) = 0; +    virtual boost::uint16_t read_gpio(gpio_bank_t bank) = 0;      /*!       * \brief Write to I2C peripheral diff --git a/host/include/uhd/usrp/dboard_manager.hpp b/host/include/uhd/usrp/dboard_manager.hpp index 042947ac4..6a105d1de 100644 --- a/host/include/uhd/usrp/dboard_manager.hpp +++ b/host/include/uhd/usrp/dboard_manager.hpp @@ -18,6 +18,7 @@  #ifndef INCLUDED_UHD_USRP_DBOARD_MANAGER_HPP  #define INCLUDED_UHD_USRP_DBOARD_MANAGER_HPP +#include <uhd/config.hpp>  #include <uhd/props.hpp>  #include <uhd/usrp/dboard_base.hpp>  #include <uhd/usrp/dboard_id.hpp> @@ -31,7 +32,7 @@ namespace uhd{ namespace usrp{   * Create subdev instances for each subdev on a dboard.   * Provide wax::obj access to the subdevs inside.   */ -class dboard_manager : boost::noncopyable{ +class UHD_API dboard_manager : boost::noncopyable{  public:      typedef boost::shared_ptr<dboard_manager> sptr; @@ -40,15 +41,17 @@ public:      typedef dboard_base::sptr(*dboard_ctor_t)(dboard_base::ctor_args_t const&);      /*! -     * Register subdevices for a given dboard id. +     * Register a dboard into the system.       *       * \param dboard_id the dboard id (rx or tx)       * \param dboard_ctor the dboard constructor function pointer +     * \param name the canonical name for the dboard represented       * \param subdev_names the names of the subdevs on this dboard       */ -    static void register_subdevs( +    static void register_dboard(          dboard_id_t dboard_id,          dboard_ctor_t dboard_ctor, +        const std::string &name,          const prop_names_t &subdev_names      ); diff --git a/host/include/uhd/usrp/usrp1e.hpp b/host/include/uhd/usrp/usrp1e.hpp new file mode 100644 index 000000000..f4cc39d8e --- /dev/null +++ b/host/include/uhd/usrp/usrp1e.hpp @@ -0,0 +1,55 @@ +// +// Copyright 2010 Ettus Research LLC +// +// This program 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. +// +// This program 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 this program.  If not, see <http://www.gnu.org/licenses/>. +// + +#ifndef INCLUDED_UHD_USRP_USRP1E_HPP +#define INCLUDED_UHD_USRP_USRP1E_HPP + +#include <uhd/config.hpp> +#include <uhd/device.hpp> + +namespace uhd{ namespace usrp{ + +/*! + * The usrp1e device class. + */ +class UHD_API usrp1e : public device{ +public: +    /*! +     * Discover usrp1e devices on the system via the device node. +     * This static method will be called by the device::discover. +     * \param hint a device addr with the usrp1e address filled in +     * \return a vector of device addresses for all usrp1es found +     */ +    static device_addrs_t discover(const device_addr_t &hint); + +    /*! +     * Make a usrp1e from a device address. +     * \param addr the device address +     * \return a device sptr to a new usrp1e +     */ +    static device::sptr make(const device_addr_t &addr); + +    /*! +     * Load the FPGA with an image file. +     * \param bin_file the name of the fpga image file +     */ +    static void load_fpga(const std::string &bin_file); +}; + +}} //namespace + +#endif /* INCLUDED_UHD_USRP_USRP1E_HPP */ diff --git a/host/include/uhd/usrp/usrp2.hpp b/host/include/uhd/usrp/usrp2.hpp index f6e49cbd6..277ddc131 100644 --- a/host/include/uhd/usrp/usrp2.hpp +++ b/host/include/uhd/usrp/usrp2.hpp @@ -15,9 +15,10 @@  // along with this program.  If not, see <http://www.gnu.org/licenses/>.  // -#ifndef INCLUDED_UHD_USRP_MBOARD_USRP2_HPP -#define INCLUDED_UHD_USRP_MBOARD_USRP2_HPP +#ifndef INCLUDED_UHD_USRP_USRP2_HPP +#define INCLUDED_UHD_USRP_USRP2_HPP +#include <uhd/config.hpp>  #include <uhd/device.hpp>  namespace uhd{ namespace usrp{ @@ -25,10 +26,15 @@ namespace uhd{ namespace usrp{  /*!   * The usrp2 device class.   */ -class usrp2 : public device{ +class UHD_API usrp2 : public device{  public:      /*!       * Discover usrp2 devices over the ethernet. +     * +     * Recommended key/value pairs for the device hint address: +     * hint["addr"] = address, where address is a resolvable address +     * or ip address, which may or may not be a broadcast address. +     *       * This static method will be called by the device::discover.       * \param hint a device addr with the usrp2 address filled in       * \return a vector of device addresses for all usrp2s found @@ -37,6 +43,11 @@ public:      /*!       * Make a usrp2 from a device address. +     * +     * Required key/value pairs for the device address: +     * hint["addr"] = address, where address is a resolvable address +     * or ip address, which must be the specific address of a usrp2. +     *       * \param addr the device address       * \return a device sptr to a new usrp2       */ @@ -45,4 +56,4 @@ public:  }} //namespace -#endif /* INCLUDED_UHD_USRP_MBOARD_USRP2_HPP */ +#endif /* INCLUDED_UHD_USRP_USRP2_HPP */ diff --git a/host/include/uhd/utils.hpp b/host/include/uhd/utils.hpp index 4331aba7e..e5333539f 100644 --- a/host/include/uhd/utils.hpp +++ b/host/include/uhd/utils.hpp @@ -15,17 +15,28 @@  // along with this program.  If not, see <http://www.gnu.org/licenses/>.  // -#include <uhd/wax.hpp> -#include <boost/foreach.hpp> +#ifndef INCLUDED_UHD_UTILS_HPP +#define INCLUDED_UHD_UTILS_HPP + +#include <uhd/config.hpp>  #include <boost/format.hpp> -#include <boost/function.hpp> +#include <boost/current_function.hpp>  #include <stdexcept>  #include <algorithm> -#include <vector> -#include <map> -#ifndef INCLUDED_UHD_UTILS_HPP -#define INCLUDED_UHD_UTILS_HPP +/*! + * Defines a function that implements the "construct on first use" idiom + * \param _t the type definition for the instance + * \param _x the name of the defined function + * \return a reference to the lazy instance + */ +#define STATIC_INSTANCE(_t, _x) static _t &_x(){static _t _x; return _x;} + +/*! + * Defines a static code block that will be called before main() + * \param _x the name of the defined struct (must be unique in file) + */ +#define STATIC_BLOCK(_x) static struct _x{_x();}_x;_x::_x()  /*!   * Useful templated functions and classes that I like to pretend are part of stl @@ -40,7 +51,9 @@ namespace std{      };      #define ASSERT_THROW(_x) if (not (_x)) { \ -        throw std::assert_error("Assertion Failed: " + std::string(#_x)); \ +        throw std::assert_error(str(boost::format( \ +            "Assertion Failed:\n  %s:%d\n  %s\n  ---> %s <---" \ +        ) % __FILE__ % __LINE__ % BOOST_CURRENT_FUNCTION % std::string(#_x))); \      }      template<class T, class InputIterator, class Function> @@ -52,14 +65,19 @@ namespace std{          return tmp;      } +    template<class T, class Iterable, class Function> +    T reduce(Iterable iterable, Function fcn, T init = 0){ +        return reduce(iterable.begin(), iterable.end(), fcn, init); +    } +      template<class T, class InputIterator>      bool has(InputIterator first, InputIterator last, const T &elem){          return last != std::find(first, last, elem);      } -    template<class T> -    T sum(const T &a, const T &b){ -        return a + b; +    template<class T, class Iterable> +    bool has(const Iterable &iterable, const T &elem){ +        return has(iterable.begin(), iterable.end(), elem);      }      template<typename T> T signum(T n){ @@ -70,52 +88,43 @@ namespace std{  }//namespace std -/*namespace uhd{ - -inline void tune( -    freq_t target_freq, -    freq_t lo_offset, -    wax::obj subdev_freq_proxy, -    bool subdev_quadrature, -    bool subdev_spectrum_inverted, -    bool subdev_is_tx, -    wax::obj dsp_freq_proxy, -    freq_t dsp_sample_rate -){ -    // Ask the d'board to tune as closely as it can to target_freq+lo_offset -    subdev_freq_proxy = target_freq + lo_offset; -    freq_t inter_freq = wax::cast<freq_t>(subdev_freq_proxy); - -    // Calculate the DDC setting that will downconvert the baseband from the -    // daughterboard to our target frequency. -    freq_t delta_freq = target_freq - inter_freq; -    freq_t delta_sign = std::signum(delta_freq); -    delta_freq *= delta_sign; -    delta_freq = fmod(delta_freq, dsp_sample_rate); -    bool inverted = delta_freq > dsp_sample_rate/2.0; -    freq_t dxc_freq = inverted? (delta_freq - dsp_sample_rate) : (-delta_freq); -    dxc_freq *= delta_sign; - -    // If the spectrum is inverted, and the daughterboard doesn't do -    // quadrature downconversion, we can fix the inversion by flipping the -    // sign of the dxc_freq...  (This only happens using the basic_rx board) -    if (subdev_spectrum_inverted){ -        inverted = not inverted; -    } -    if (inverted and not subdev_quadrature){ -        dxc_freq = -dxc_freq; -        inverted = not inverted; -    } -    if (subdev_is_tx){ -        dxc_freq = -dxc_freq;	// down conversion versus up conversion +#include <boost/format.hpp> +#include <boost/foreach.hpp> +#include <boost/lexical_cast.hpp> + +namespace uhd{ + +    /*! +     * Check that an element is found in a container. +     * If not, throw a meaningful assertion error. +     * The "what" in the error will show what is +     * being set and a list of known good values. +     * +     * \param iterable a list of possible settings +     * \param elem an element that may be in the list +     * \param what a description of what is being set +     * \throw assertion_error when elem not in list +     */ +    template<class T, class Iterable> void assert_has( +        const Iterable &iterable, +        const T &elem, +        const std::string &what = "unknown" +    ){ +        if (std::has(iterable, elem)) return; +        std::string possible_values = ""; +        BOOST_FOREACH(T e, iterable){ +            if (e != iterable.begin()[0]) possible_values += ", "; +            possible_values += boost::lexical_cast<std::string>(e); +        } +        throw std::assert_error(str(boost::format( +                "Error: %s is not a valid %s. " +                "Possible values are: [%s]." +            ) +            % boost::lexical_cast<std::string>(elem) +            % what % possible_values +        ));      } -    dsp_freq_proxy = dxc_freq; -    //freq_t actual_dxc_freq = wax::cast<freq_t>(dsp_freq_proxy); - -    //return some kind of tune result tuple/struct -} - -} //namespace uhd*/ +}//namespace uhd  #endif /* INCLUDED_UHD_UTILS_HPP */ diff --git a/host/include/uhd/wax.hpp b/host/include/uhd/wax.hpp index 1d5054351..30645f491 100644 --- a/host/include/uhd/wax.hpp +++ b/host/include/uhd/wax.hpp @@ -18,33 +18,37 @@  #ifndef INCLUDED_WAX_HPP  #define INCLUDED_WAX_HPP +#include <uhd/config.hpp>  #include <boost/any.hpp> -#include <iostream>  /*!   * WAX - it's a metaphor!   * - * The WAX framework allows object to have generic/anyobj properties. + * The WAX framework allows an object to have generic/anyobj properties.   * These properties can be addressed through generic/anyobj identifiers. - * A property of a WAX object may even be another WAX object.   * - * When a property is a WAX object, the returned value must be an obj pointer. - * A WAX object provides two objs of pointers: obj::ptr and obj::sptr. - * The choice of pointer vs smart pointer depends on the owner of the memory. + * The WAX object itself is an anytype container much like boost::any. + * To retrieve the value of the appropriate type, use my_obj.as<type>().   *   * Proprties may be referenced though the [] overloaded operator.   * The [] operator returns a special proxy that allows for assigment.   * Also, the [] operators may be chained as in the folowing examples: - *   my_obj[prop1][prop2][prop3] = value - *   value = my_obj[prop1][prop2][prop3] + *   my_obj[prop1][prop2][prop3] = value; + *   value = my_obj[prop1][prop2][prop3].as<type>();   * - * Any value returned from an access operation is of wax::obj. - * To use this value, it must be cast with wax::cast<new_obj>(value). + * Property nesting occurs when a WAX object gets another object's link. + * This special link is obtained through a call to my_obj.get_link().   */  namespace wax{      /*! +     * The wax::bad cast will be thrown when +     * cast is called with the wrong typeid. +     */ +    typedef boost::bad_any_cast bad_cast; + +    /*!       * WAX object base class:       *       * A wax obj has two major purposes: @@ -56,7 +60,7 @@ namespace wax{       * For property nesting, wax obj subclasses return special links       * to other wax obj subclasses, and the api handles the magic.       */ -    class obj{ +    class UHD_API obj{      public:          /*! @@ -124,6 +128,17 @@ namespace wax{           */          const std::type_info & type(void) const; +        /*! +         * Cast this obj into the desired type. +         * Usage: myobj.as<type>() +         * +         * \return an object of the desired type +         * \throw wax::bad_cast when the cast fails +         */ +        template<class T> T as(void) const{ +            return boost::any_cast<T>(resolve()); +        } +      private:          //private interface (override in subclasses)          virtual void get(const obj &, obj &); @@ -137,31 +152,12 @@ namespace wax{           * \return a boost any type with contents           */          boost::any resolve(void) const; -        template<class T> friend T cast(const obj &);          //private contents of this obj          boost::any _contents;      }; -    /*! -     * The wax::bad cast will be thrown when -     * cast is called with the wrong typeid. -     */ -    typedef boost::bad_any_cast bad_cast; - -    /*! -     * Cast a wax::obj into the desired obj. -     * Usage wax::cast<new_obj>(my_value). -     * -     * \param val the obj to cast -     * \return an object of the desired type -     * \throw wax::bad_cast when the cast fails -     */ -    template<class T> T cast(const obj &val){ -        return boost::any_cast<T>(val.resolve()); -    } -  } //namespace wax  #endif /* INCLUDED_WAX_HPP */ diff --git a/host/lib/CMakeLists.txt b/host/lib/CMakeLists.txt index 5cf334678..46cce729e 100644 --- a/host/lib/CMakeLists.txt +++ b/host/lib/CMakeLists.txt @@ -15,29 +15,100 @@  # along with this program.  If not, see <http://www.gnu.org/licenses/>.  # - +######################################################################## +# Create a list of libuhd sources +########################################################################  SET(libuhd_sources      device.cpp      device_addr.cpp      gain_handler.cpp -    shared_iovec.cpp -    uhd.cpp +    load_modules.cpp +    metadata.cpp +    simple_device.cpp +    time_spec.cpp +    types.cpp      wax.cpp -    transport/udp.cpp +    transport/udp_simple.cpp +    transport/vrt.cpp      usrp/dboard/basic.cpp      usrp/dboard_base.cpp -    usrp/dboard_id.cpp      usrp/dboard_interface.cpp      usrp/dboard_manager.cpp      usrp/usrp2/dboard_impl.cpp      usrp/usrp2/dboard_interface.cpp      usrp/usrp2/dsp_impl.cpp +    usrp/usrp2/io_impl.cpp      usrp/usrp2/mboard_impl.cpp      usrp/usrp2/usrp2_impl.cpp  ) +######################################################################## +# Conditionally add the udp sources +######################################################################## +LIST(APPEND libuhd_sources +    transport/udp_zero_copy_asio.cpp +) + +######################################################################## +# Conditionally add the usrp1e sources +######################################################################## +INCLUDE(CheckIncludeFiles) +SET(usrp1e_required_headers +    linux/ioctl.h +    linux/spi/spidev.h +    linux/usrp1_e.h +) +CHECK_INCLUDE_FILES( +    "${usrp1e_required_headers}" +    HAS_USRP1E_REQUIRED_HEADERS +) + +IF(HAS_USRP1E_REQUIRED_HEADERS) +    MESSAGE(STATUS "Building usrp1e support...") +    LIST(APPEND libuhd_sources +        usrp/usrp1e/dboard_impl.cpp +        usrp/usrp1e/dboard_interface.cpp +        usrp/usrp1e/dsp_impl.cpp +        usrp/usrp1e/fpga-downloader.cc +        usrp/usrp1e/mboard_impl.cpp +        usrp/usrp1e/usrp1e_impl.cpp +    ) +ELSE(HAS_USRP1E_REQUIRED_HEADERS) +    MESSAGE(STATUS "Skipping usrp1e support...") +    LIST(APPEND libuhd_sources +        usrp/usrp1e/usrp1e_none.cpp +    ) +ENDIF(HAS_USRP1E_REQUIRED_HEADERS) + +######################################################################## +# Setup defines for module loading +######################################################################## +INCLUDE(CheckIncludeFileCXX) + +CHECK_INCLUDE_FILE_CXX(dlfcn.h HAVE_DLFCN_H) +CHECK_INCLUDE_FILE_CXX(Winbase.h HAVE_WINBASE_H) + +IF(HAVE_DLFCN_H) +    MESSAGE(STATUS "Module loading supported through dlopen...") +    ADD_DEFINITIONS(-DHAVE_DLFCN_H) +ELSEIF(HAVE_WINBASE_H) +    MESSAGE(STATUS "Module loading supported through LoadLibrary...") +    ADD_DEFINITIONS(-DHAVE_WINBASE_H) +ELSE(HAVE_DLFCN_H) +    MESSAGE(STATUS "Module loading not supported...") +ENDIF(HAVE_DLFCN_H) + +######################################################################## +# Setup libuhd library +########################################################################  ADD_LIBRARY(uhd SHARED ${libuhd_sources}) -TARGET_LINK_LIBRARIES(uhd ${Boost_LIBRARIES}) +TARGET_LINK_LIBRARIES(uhd ${Boost_LIBRARIES} ${CMAKE_DL_LIBS}) + +SET_TARGET_PROPERTIES(uhd PROPERTIES DEFINE_SYMBOL "UHD_DLL_EXPORTS") -INSTALL(TARGETS uhd LIBRARY DESTINATION ${LIBRARY_DIR}) +INSTALL(TARGETS uhd +    LIBRARY DESTINATION ${LIBRARY_DIR} # .so file +    ARCHIVE DESTINATION ${LIBRARY_DIR} # .lib file +    RUNTIME DESTINATION ${LIBRARY_DIR} # .dll file +) diff --git a/host/lib/device.cpp b/host/lib/device.cpp index e376a5c50..cd8a01ab4 100644 --- a/host/lib/device.cpp +++ b/host/lib/device.cpp @@ -12,57 +12,133 @@  // GNU General Public License for more details.  //  // You should have received a copy of the GNU General Public License -// along with this program.  If not, see <http://www.gnu.org/licenses/>. +// asize_t with this program.  If not, see <http://www.gnu.org/licenses/>.  // -#include <uhd/usrp/usrp2.hpp>  #include <uhd/device.hpp> +#include <uhd/dict.hpp> +#include <uhd/utils.hpp> +#include <boost/foreach.hpp>  #include <boost/format.hpp> +#include <boost/weak_ptr.hpp> +#include <boost/functional/hash.hpp> +#include <boost/tuple/tuple.hpp>  #include <stdexcept> +#include <algorithm>  using namespace uhd; +/*********************************************************************** + * Helper Functions + **********************************************************************/ +/*! + * Make a device hash that maps 1 to 1 with a device address. + * The hash will be used to identify created devices. + * \param dev_addr the device address + * \return the hash number + */ +static size_t hash_device_addr( +    const device_addr_t &dev_addr +){ +    //sort the keys of the device address +    std::vector<std::string> keys = dev_addr.get_keys(); +    std::sort(keys.begin(), keys.end()); + +    //combine the hashes of sorted keys/value pairs +    size_t hash = 0; +    BOOST_FOREACH(std::string key, keys){ +        boost::hash_combine(hash, key); +        boost::hash_combine(hash, dev_addr[key]); +    } +    return hash; +} + +/*********************************************************************** + * Registration + **********************************************************************/ +typedef boost::tuple<device::discover_t, device::make_t> dev_fcn_reg_t; + +// instantiate the device function registry container +STATIC_INSTANCE(std::vector<dev_fcn_reg_t>, get_dev_fcn_regs) + +void device::register_device( +    const discover_t &discover, +    const make_t &make +){ +    //std::cout << "registering device" << std::endl; +    get_dev_fcn_regs().push_back(dev_fcn_reg_t(discover, make)); +} + +/*********************************************************************** + * Discover + **********************************************************************/  device_addrs_t device::discover(const device_addr_t &hint){      device_addrs_t device_addrs; -    if (not hint.has_key("type")){ -        //TODO call discover for others and append results -    } -    else if (hint["type"] == "udp"){ -        std::vector<device_addr_t> usrp2_addrs = usrp::usrp2::discover(hint); -        device_addrs.insert(device_addrs.begin(), usrp2_addrs.begin(), usrp2_addrs.end()); + +    BOOST_FOREACH(dev_fcn_reg_t fcn, get_dev_fcn_regs()){ +        device_addrs_t discovered_addrs = fcn.get<0>()(hint); +        device_addrs.insert( +            device_addrs.begin(), +            discovered_addrs.begin(), +            discovered_addrs.end() +        );      } +      return device_addrs;  } +/*********************************************************************** + * Make + **********************************************************************/  device::sptr device::make(const device_addr_t &hint, size_t which){ -    std::vector<device_addr_t> device_addrs = discover(hint); +    typedef boost::tuple<device_addr_t, make_t> dev_addr_make_t; +    std::vector<dev_addr_make_t> dev_addr_makers; + +    BOOST_FOREACH(dev_fcn_reg_t fcn, get_dev_fcn_regs()){ +        BOOST_FOREACH(device_addr_t dev_addr, fcn.get<0>()(hint)){ +            //copy keys that were in hint but not in dev_addr +            //this way, we can pass additional transport arguments +            BOOST_FOREACH(std::string key, hint.get_keys()){ +                if (not dev_addr.has_key(key)) dev_addr[key] = hint[key]; +            } +            //append the discovered address and its factory function +            dev_addr_makers.push_back(dev_addr_make_t(dev_addr, fcn.get<1>())); +        } +    }      //check that we found any devices -    if (device_addrs.size() == 0){ +    if (dev_addr_makers.size() == 0){          throw std::runtime_error(str( -            boost::format("No devices found for %s") % device_addr_to_string(hint) +            boost::format("No devices found for ----->\n%s") % device_addr::to_string(hint)          ));      }      //check that the which index is valid -    if (device_addrs.size() <= which){ +    if (dev_addr_makers.size() <= which){          throw std::runtime_error(str( -            boost::format("No device at index %d for %s") % which % device_addr_to_string(hint) +            boost::format("No device at index %d for ----->\n%s") % which % device_addr::to_string(hint)          ));      } -    //create the new device with the discovered address -    //TODO only a usrp2 device will be made (until others are supported) -    if (hint.has_key("type") and hint["type"] == "udp"){ -        return usrp::usrp2::make(device_addrs.at(which)); -    } -    throw std::runtime_error("cant make a device"); -} +    //create a unique hash for the device address +    device_addr_t dev_addr; make_t maker; +    boost::tie(dev_addr, maker) = dev_addr_makers.at(which); +    size_t dev_hash = hash_device_addr(dev_addr); +    //std::cout << boost::format("Hash: %u") % dev_hash << std::endl; -device::device(void){ -    /* NOP */ -} +    //map device address hash to created devices +    static uhd::dict<size_t, boost::weak_ptr<device> > hash_to_device; -device::~device(void){ -    /* NOP */ +    //try to find an existing device +    try{ +        ASSERT_THROW(hash_to_device.has_key(dev_hash)); +        ASSERT_THROW(not hash_to_device[dev_hash].expired()); +        return hash_to_device[dev_hash].lock(); +    } +    //create and register a new device +    catch(const std::assert_error &){ +        device::sptr dev = maker(dev_addr); +        hash_to_device[dev_hash] = dev; +        return dev; +    }  } diff --git a/host/lib/device_addr.cpp b/host/lib/device_addr.cpp index ffd511f92..d26bb4b9d 100644 --- a/host/lib/device_addr.cpp +++ b/host/lib/device_addr.cpp @@ -28,7 +28,7 @@ uhd::mac_addr_t::mac_addr_t(const std::string &mac_addr_str_){      std::string mac_addr_str = (mac_addr_str_ == "")? "ff:ff:ff:ff:ff:ff" : mac_addr_str_;      //ether_aton_r(str.c_str(), &mac_addr); -    uint8_t p[6] = {0x00, 0x50, 0xC2, 0x85, 0x30, 0x00}; // Matt's IAB +    boost::uint8_t p[6] = {0x00, 0x50, 0xC2, 0x85, 0x30, 0x00}; // Matt's IAB      try{          //only allow patterns of xx:xx or xx:xx:xx:xx:xx:xx @@ -43,7 +43,7 @@ uhd::mac_addr_t::mac_addr_t(const std::string &mac_addr_str_){              int hex_num;              std::istringstream iss(hex_strs[i]);              iss >> std::hex >> hex_num; -            p[i] = uint8_t(hex_num); +            p[i] = boost::uint8_t(hex_num);          }      } @@ -58,7 +58,7 @@ uhd::mac_addr_t::mac_addr_t(const std::string &mac_addr_str_){  std::string uhd::mac_addr_t::to_string(void) const{      //ether_ntoa_r(&mac_addr, addr_buf); -    const uint8_t *p = reinterpret_cast<const uint8_t *>(&mac_addr); +    const boost::uint8_t *p = reinterpret_cast<const boost::uint8_t *>(&mac_addr);      return str(          boost::format("%02x:%02x:%02x:%02x:%02x:%02x")          % int(p[0]) % int(p[1]) % int(p[2]) @@ -72,7 +72,7 @@ std::ostream& operator<<(std::ostream &os, const uhd::mac_addr_t &x){  }  //----------------------- usrp device_addr_t wrapper -------------------------// -std::string uhd::device_addr_to_string(const uhd::device_addr_t &device_addr){ +std::string uhd::device_addr::to_string(const uhd::device_addr_t &device_addr){      std::stringstream ss;      BOOST_FOREACH(std::string key, device_addr.get_keys()){          ss << boost::format("%s: %s") % key % device_addr[key] << std::endl; @@ -81,6 +81,6 @@ std::string uhd::device_addr_to_string(const uhd::device_addr_t &device_addr){  }  std::ostream& operator<<(std::ostream &os, const uhd::device_addr_t &device_addr){ -    os << uhd::device_addr_to_string(device_addr); +    os << uhd::device_addr::to_string(device_addr);      return os;  } diff --git a/host/lib/gain_handler.cpp b/host/lib/gain_handler.cpp index b03d5bda2..7dd1547cb 100644 --- a/host/lib/gain_handler.cpp +++ b/host/lib/gain_handler.cpp @@ -17,151 +17,161 @@  #include <uhd/gain_handler.hpp>  #include <uhd/utils.hpp> +#include <uhd/types.hpp> +#include <uhd/props.hpp>  #include <boost/assign/list_of.hpp>  #include <boost/foreach.hpp>  #include <boost/format.hpp> +#include <cmath>  #include <vector>  using namespace uhd;  /*********************************************************************** - * Helper functions and macros + * gain handler implementation interface   **********************************************************************/ -#define GET_PROP_NAMES() \ -    wax::cast<prop_names_t>((*_wax_obj_ptr)[_gain_names_prop]) - -/*! - * Helper function to simplify getting a named gain (also min, max, step). - */ -static gain_t get_named_gain(wax::obj *wax_obj_ptr, wax::obj prop, std::string name){ -    return wax::cast<gain_t>((*wax_obj_ptr)[named_prop_t(prop, name)]); +class gain_handler_impl : public gain_handler{ +public: +    gain_handler_impl( +        const wax::obj &link, +        const props_t &props, +        is_equal_t is_equal +    ); +    ~gain_handler_impl(void); +    bool intercept_get(const wax::obj &key, wax::obj &val); +    bool intercept_set(const wax::obj &key, const wax::obj &val); + +private: +    wax::obj     _link; +    props_t _props; +    is_equal_t   _is_equal; + +    prop_names_t get_gain_names(void); +    gain_t get_overall_gain_val(void); +    gain_range_t get_overall_gain_range(void); +    template <class T> T get_named_prop(const wax::obj &prop, const std::string &name){ +        return _link[named_prop_t(prop, name)].as<T>(); +    } +}; + +/*********************************************************************** + * the make function + **********************************************************************/ +gain_handler::sptr gain_handler::make( +    const wax::obj &link, +    const props_t &props, +    is_equal_t is_equal +){ +    return sptr(new gain_handler_impl(link, props, is_equal));  }  /*********************************************************************** - * Class methods of gain handler + * gain handler implementation methods   **********************************************************************/ -gain_handler::~gain_handler(void){ +gain_handler::props_t::props_t(void){      /* NOP */  } -void gain_handler::_check_key(const wax::obj &key_){ -    wax::obj key; std::string name; -    boost::tie(key, name) = extract_named_prop(key_); -     -    try{ -        //only handle non wildcard names -        ASSERT_THROW(name != ""); - -        //only handle these gain props -        ASSERT_THROW( -            _is_equal(key, _gain_prop)     or -            _is_equal(key, _gain_min_prop) or -            _is_equal(key, _gain_max_prop) or -            _is_equal(key, _gain_step_prop) -        ); - -        //check that the name is allowed -        prop_names_t prop_names = GET_PROP_NAMES(); -        ASSERT_THROW(not std::has(prop_names.begin(), prop_names.end(), name)); - -        //if we get here, throw an exception -        throw std::invalid_argument(str( -            boost::format("Unknown gain name %s") % name -        )); -    } -    catch(const std::assert_error &){} +gain_handler_impl::gain_handler_impl( +    const wax::obj &link, +    const props_t &props, +    is_equal_t is_equal +){ +    _link = link; +    _props = props; +    _is_equal = is_equal;  } -static gain_t gain_max(gain_t a, gain_t b){ -    return std::max(a, b); +gain_handler_impl::~gain_handler_impl(void){ +    /* NOP */  } -static gain_t gain_sum(gain_t a, gain_t b){ -    return std::sum(a, b); + +prop_names_t gain_handler_impl::get_gain_names(void){ +    return _link[_props.names].as<prop_names_t>();  } -bool gain_handler::intercept_get(const wax::obj &key, wax::obj &val){ -    _check_key(key); //verify the key - -    std::vector<wax::obj> gain_props = boost::assign::list_of -        (_gain_prop)(_gain_min_prop)(_gain_max_prop)(_gain_step_prop); - -    /*! -     * Handle getting overall gains when a name is not specified. -     * For the gain props below, set the overall value and return true.  -     */ -    BOOST_FOREACH(wax::obj prop_key, gain_props){ -        if (_is_equal(key, prop_key)){ -            //form the gains vector from the props vector -            prop_names_t prop_names = GET_PROP_NAMES(); -            std::vector<gain_t> gains(prop_names.size()); -            std::transform( -                prop_names.begin(), prop_names.end(), gains.begin(), -                boost::bind(get_named_gain, _wax_obj_ptr, key, _1) -            ); - -            //reduce across the gain vector -            if (_is_equal(key, _gain_step_prop)){ -                val = std::reduce<gain_t>(gains.begin(), gains.end(), gain_max); -            } -            else{ -                val = std::reduce<gain_t>(gains.begin(), gains.end(), gain_sum); -            } -            return true; -        } +gain_t gain_handler_impl::get_overall_gain_val(void){ +    gain_t gain_val = 0; +    BOOST_FOREACH(std::string name, get_gain_names()){ +        gain_val += get_named_prop<gain_t>(_props.value, name);      } +    return gain_val; +} -    return false; +gain_range_t gain_handler_impl::get_overall_gain_range(void){ +    gain_t gain_min = 0, gain_max = 0, gain_step = 0; +    BOOST_FOREACH(std::string name, get_gain_names()){ +        gain_range_t gain_tmp = get_named_prop<gain_range_t>(_props.range, name); +        gain_min += gain_tmp.min; +        gain_max += gain_tmp.max; +        gain_step = std::max(gain_step, gain_tmp.step); +    } +    return gain_range_t(gain_min, gain_max, gain_step);  } -bool gain_handler::intercept_set(const wax::obj &key_, const wax::obj &val){ -    _check_key(key_); //verify the key +/*********************************************************************** + * gain handler implementation get method + **********************************************************************/ +bool gain_handler_impl::intercept_get(const wax::obj &key_, wax::obj &val){ +    wax::obj key; std::string name; +    boost::tie(key, name) = extract_named_prop(key_); + +    //not a wildcard... dont handle (but check name) +    if (name != ""){ +        assert_has(get_gain_names(), name, "gain name"); +        return false; +    } + +    if (_is_equal(key, _props.value)){ +        val = get_overall_gain_val(); +        return true; +    } + +    if (_is_equal(key, _props.range)){ +        val = get_overall_gain_range(); +        return true; +    } + +    return false; //not handled +} +/*********************************************************************** + * gain handler implementation set method + **********************************************************************/ +bool gain_handler_impl::intercept_set(const wax::obj &key_, const wax::obj &val){      wax::obj key; std::string name;      boost::tie(key, name) = extract_named_prop(key_); -    /*! -     * Verify that a named gain component is in range. -     */ -    try{ -        //only handle the gain props -        ASSERT_THROW(_is_equal(key, _gain_prop)); - -        //check that the gain is in range -        gain_t gain = wax::cast<gain_t>(val); -        gain_t gain_min = get_named_gain(_wax_obj_ptr, _gain_min_prop,  name); -        gain_t gain_max = get_named_gain(_wax_obj_ptr, _gain_max_prop,  name); -        ASSERT_THROW(gain > gain_max or gain < gain_min); - -        //if we get here, throw an exception -        throw std::range_error(str( -            boost::format("gain %s is out of range of (%f, %f)") % name % gain_min % gain_max +    //not a gain value key... dont handle +    if (not _is_equal(key, _props.value)) return false; + +    gain_t gain_val = val.as<gain_t>(); + +    //not a wildcard... dont handle (but check name and range) +    if (name != ""){ +        assert_has(get_gain_names(), name, "gain name"); +        gain_range_t gain = get_named_prop<gain_range_t>(_props.range, name); +        if (gain_val > gain.max or gain_val < gain.min) throw std::range_error(str( +            boost::format("A value of %f for gain %s is out of range of (%f, %f)") +            % gain_val % name % gain.min % gain.max          )); +        return false;      } -    catch(const std::assert_error &){} - -    /*! -     * Handle setting the overall gain. -     */ -    if (_is_equal(key, _gain_prop) and name == ""){ -        gain_t gain = wax::cast<gain_t>(val); -        prop_names_t prop_names = GET_PROP_NAMES(); -        BOOST_FOREACH(std::string name, prop_names){ -            //get the min, max, step for this gain name -            gain_t gain_min  = get_named_gain(_wax_obj_ptr, _gain_min_prop,  name); -            gain_t gain_max  = get_named_gain(_wax_obj_ptr, _gain_max_prop,  name); -            gain_t gain_step = get_named_gain(_wax_obj_ptr, _gain_step_prop, name); - -            //clip g to be within the allowed range -            gain_t g = std::min(std::max(gain, gain_min), gain_max); -            //set g to be a multiple of the step size -            g -= fmod(g, gain_step); -            //set g to be the new gain -            (*_wax_obj_ptr)[named_prop_t(_gain_prop, name)] = g; -            //subtract g out of the total gain left to apply -            gain -= g; -        } -        return true; + +    //set the overall gain +    BOOST_FOREACH(std::string name, get_gain_names()){ +        //get the min, max, step for this gain name +        gain_range_t gain = get_named_prop<gain_range_t>(_props.range, name); + +        //clip g to be within the allowed range +        gain_t g = std::min(std::max(gain_val, gain.min), gain.max); +        //set g to be a multiple of the step size +        g -= std::fmod(g, gain.step); +        //set g to be the new gain +        _link[named_prop_t(_props.value, name)] = g; +        //subtract g out of the total gain left to apply +        gain_val -= g;      } -    return false; +    return true;  } diff --git a/host/lib/load_modules.cpp b/host/lib/load_modules.cpp new file mode 100644 index 000000000..700afcd3f --- /dev/null +++ b/host/lib/load_modules.cpp @@ -0,0 +1,117 @@ +// +// Copyright 2010 Ettus Research LLC +// +// This program 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. +// +// This program 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 this program.  If not, see <http://www.gnu.org/licenses/>. +// + +#include <uhd/utils.hpp> +#include <boost/format.hpp> +#include <boost/foreach.hpp> +#include <boost/algorithm/string.hpp> +#include <boost/filesystem.hpp> +#include <iostream> +#include <stdexcept> +#include <cstdlib> + +namespace fs = boost::filesystem; + +/*********************************************************************** + * Module Load Function + **********************************************************************/ +#ifdef HAVE_DLFCN_H +#include <dlfcn.h> + +static void load_module(const std::string &file_name){ +    if (dlopen(file_name.c_str(), RTLD_LAZY) == NULL){ +        throw std::runtime_error(str( +            boost::format("dlopen failed to load \"%s\"") % file_name +        )); +    } +} + +#elif HAVE_WINBASE_H +#include <Winbase.h> + +static void load_module(const std::string &file_name){ +    if (LoadLibrary(file_name.c_str()) == NULL){ +        throw std::runtime_error(str( +            boost::format("LoadLibrary failed to load \"%s\"") % file_name +        )); +    } +} + +#else + +static void load_module(const std::string &file_name){ +    throw std::runtime_error(str( +        boost::format("Module loading not supported: Cannot load \"%s\"") % file_name +    )); +} + +#endif + +/*********************************************************************** + * Load Modules + **********************************************************************/ +/*! + * Load all modules in a given path. + * This will recurse into sub-directories. + * Does not throw, prints to std error. + * \param path the filesystem path + */ +static void load_path(const fs::path &path){ +    if (not fs::exists(path)){ +        std::cerr << boost::format("Module path \"%s\" not found.") % path.file_string() << std::endl; +        return; +    } + +    //try to load the files in this path +    if (fs::is_directory(path)){ +        for( +            fs::directory_iterator dir_itr(path); +            dir_itr != fs::directory_iterator(); +            ++dir_itr +        ){ +            load_path(dir_itr->path()); +        } +        return; +    } + +    //its not a directory, try to load it +    try{ +        load_module(path.file_string()); +    } +    catch(const std::exception &err){ +        std::cerr << boost::format("Error: %s") % err.what() << std::endl; +    } +} + +/*! + * Load all the modules given by the module path enviroment variable. + * The path variable may be several paths split by path separators. + */ +STATIC_BLOCK(load_modules){ +    //get the environment variable module path +    char *env_module_path = std::getenv("UHD_MODULE_PATH"); +    if (env_module_path == NULL) return; + +    //split the path at the path separators +    std::vector<std::string> module_paths; +    boost::split(module_paths, env_module_path, boost::is_any_of(":;")); + +    //load modules in each path +    BOOST_FOREACH(const std::string &module_path, module_paths){ +        load_path(fs::system_complete(fs::path(module_path))); +    } +} diff --git a/host/include/uhd.hpp b/host/lib/metadata.cpp index ee8c13dfe..40fdb7c73 100644 --- a/host/include/uhd.hpp +++ b/host/lib/metadata.cpp @@ -15,10 +15,23 @@  // along with this program.  If not, see <http://www.gnu.org/licenses/>.  // -#ifndef INCLUDED_UHD_HPP -#define INCLUDED_UHD_HPP +#include <uhd/metadata.hpp> -//include convenience headers -#include <uhd/device.hpp> +using namespace uhd; -#endif /* INCLUDED_UHD_HPP */ +rx_metadata_t::rx_metadata_t(void){ +    stream_id = 0; +    has_stream_id = false; +    time_spec = time_spec_t(); +    has_time_spec = false; +    is_fragment = false; +} + +tx_metadata_t::tx_metadata_t(void){ +    stream_id = 0; +    has_stream_id = false; +    time_spec = time_spec_t(); +    has_time_spec = false; +    start_of_burst = false; +    end_of_burst = false; +} diff --git a/host/lib/simple_device.cpp b/host/lib/simple_device.cpp new file mode 100644 index 000000000..ba1966e0d --- /dev/null +++ b/host/lib/simple_device.cpp @@ -0,0 +1,293 @@ +// +// Copyright 2010 Ettus Research LLC +// +// This program 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. +// +// This program 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 this program.  If not, see <http://www.gnu.org/licenses/>. +// + +#include <uhd/simple_device.hpp> +#include <uhd/device.hpp> +#include <uhd/utils.hpp> +#include <uhd/props.hpp> +#include <uhd/types.hpp> +#include <boost/algorithm/string.hpp> +#include <boost/foreach.hpp> +#include <boost/format.hpp> +#include <stdexcept> + +using namespace uhd; + +/*********************************************************************** + * Tune Helper Function + **********************************************************************/ +static tune_result_t tune( +    double target_freq, +    double lo_offset, +    wax::obj subdev, +    wax::obj dxc, +    bool is_tx +){ +    wax::obj subdev_freq_proxy = subdev[SUBDEV_PROP_FREQ]; +    bool subdev_quadrature = subdev[SUBDEV_PROP_QUADRATURE].as<bool>(); +    bool subdev_spectrum_inverted = subdev[SUBDEV_PROP_SPECTRUM_INVERTED].as<bool>(); +    wax::obj dxc_freq_proxy = dxc[std::string("freq")]; +    double dxc_sample_rate = dxc[std::string("rate")].as<double>(); + +    // Ask the d'board to tune as closely as it can to target_freq+lo_offset +    double target_inter_freq = target_freq + lo_offset; +    subdev_freq_proxy = target_inter_freq; +    double actual_inter_freq = subdev_freq_proxy.as<double>(); + +    // Calculate the DDC setting that will downconvert the baseband from the +    // daughterboard to our target frequency. +    double delta_freq = target_freq - actual_inter_freq; +    double delta_sign = std::signum(delta_freq); +    delta_freq *= delta_sign; +    delta_freq = fmod(delta_freq, dxc_sample_rate); +    bool inverted = delta_freq > dxc_sample_rate/2.0; +    double target_dxc_freq = inverted? (delta_freq - dxc_sample_rate) : (-delta_freq); +    target_dxc_freq *= delta_sign; + +    // If the spectrum is inverted, and the daughterboard doesn't do +    // quadrature downconversion, we can fix the inversion by flipping the +    // sign of the dxc_freq...  (This only happens using the basic_rx board) +    if (subdev_spectrum_inverted){ +        inverted = not inverted; +    } +    if (inverted and not subdev_quadrature){ +        target_dxc_freq *= -1.0; +        inverted = not inverted; +    } +    // down conversion versus up conversion, fight! +    // your mother is ugly and your going down... +    target_dxc_freq *= (is_tx)? -1.0 : +1.0; + +    dxc_freq_proxy = target_dxc_freq; +    double actual_dxc_freq = dxc_freq_proxy.as<double>(); + +    //return some kind of tune result tuple/struct +    tune_result_t tune_result; +    tune_result.target_inter_freq = target_inter_freq; +    tune_result.actual_inter_freq = actual_inter_freq; +    tune_result.target_dxc_freq = target_dxc_freq; +    tune_result.actual_dxc_freq = actual_dxc_freq; +    tune_result.spectrum_inverted = inverted; +    return tune_result; +} + +/*********************************************************************** + * Helper Functions + **********************************************************************/ +static std::string trim(const std::string &in){ +    return boost::algorithm::trim_copy(in); +} + +device_addr_t args_to_device_addr(const std::string &args){ +    device_addr_t addr; + +    //split the args at the semi-colons +    std::vector<std::string> pairs; +    boost::split(pairs, args, boost::is_any_of(";")); +    BOOST_FOREACH(std::string pair, pairs){ +        if (trim(pair) == "") continue; + +        //split the key value pairs at the equals +        std::vector<std::string> key_val; +        boost::split(key_val, pair, boost::is_any_of("=")); +        if (key_val.size() != 2) throw std::runtime_error("invalid args string: "+args); +        addr[trim(key_val[0])] = trim(key_val[1]); +    } + +    return addr; +} + +static std::vector<double> get_xx_rates(wax::obj decerps, wax::obj rate){ +    std::vector<double> rates; +    BOOST_FOREACH(size_t decerp, decerps.as<std::vector<size_t> >()){ +        rates.push_back(rate.as<double>()/decerp); +    } +    return rates; +} + +/*********************************************************************** + * Simple Device Implementation + **********************************************************************/ +class simple_device_impl : public simple_device{ +public: +    simple_device_impl(const device_addr_t &addr){ +        _dev = device::make(addr); +        _mboard = (*_dev)[DEVICE_PROP_MBOARD]; +        _rx_ddc = _mboard[named_prop_t(MBOARD_PROP_RX_DSP, "ddc0")]; +        _tx_duc = _mboard[named_prop_t(MBOARD_PROP_TX_DSP, "duc0")]; + +        //extract rx subdevice +        wax::obj rx_dboard = _mboard[MBOARD_PROP_RX_DBOARD]; +        std::string rx_subdev_in_use = rx_dboard[DBOARD_PROP_USED_SUBDEVS].as<prop_names_t>().at(0); +        _rx_subdev = rx_dboard[named_prop_t(DBOARD_PROP_SUBDEV, rx_subdev_in_use)]; + +        //extract tx subdevice +        wax::obj tx_dboard = _mboard[MBOARD_PROP_TX_DBOARD]; +        std::string tx_subdev_in_use = tx_dboard[DBOARD_PROP_USED_SUBDEVS].as<prop_names_t>().at(0); +        _tx_subdev = tx_dboard[named_prop_t(DBOARD_PROP_SUBDEV, tx_subdev_in_use)]; +    } + +    ~simple_device_impl(void){ +        /* NOP */ +    } + +    device::sptr get_device(void){ +        return _dev; +    } + +    std::string get_name(void){ +        return _mboard[MBOARD_PROP_NAME].as<std::string>(); +    } + +    /******************************************************************* +     * Streaming +     ******************************************************************/ +    void set_streaming(bool enb){ +        _rx_ddc[std::string("enabled")] = enb; +    } + +    bool get_streaming(void){ +        return _rx_ddc[std::string("enabled")].as<bool>(); +    } + +    /******************************************************************* +     * RX methods +     ******************************************************************/ +    void set_rx_rate(double rate){ +        double samp_rate = _rx_ddc[std::string("rate")].as<double>(); +        assert_has(get_rx_rates(), rate, "simple device rx rate"); +        _rx_ddc[std::string("decim")] = size_t(samp_rate/rate); +    } + +    double get_rx_rate(void){ +        double samp_rate = _rx_ddc[std::string("rate")].as<double>(); +        size_t decim = _rx_ddc[std::string("decim")].as<size_t>(); +        return samp_rate/decim; +    } + +    std::vector<double> get_rx_rates(void){ +        return get_xx_rates(_rx_ddc[std::string("decims")], _rx_ddc[std::string("rate")]); +    } + +    tune_result_t set_rx_freq(double target_freq){ +        double lo_offset = 0.0; +        //if the local oscillator will be in the passband, use an offset +        if (_rx_subdev[SUBDEV_PROP_LO_INTERFERES].as<bool>()){ +            lo_offset = get_rx_rate()*2.0; +        } +        return tune(target_freq, lo_offset, _rx_subdev, _rx_ddc, false/* not tx */); +    } + +    freq_range_t get_rx_freq_range(void){ +        return _rx_subdev[SUBDEV_PROP_FREQ_RANGE].as<freq_range_t>(); +    } + +    void set_rx_gain(float gain){ +        _rx_subdev[SUBDEV_PROP_GAIN] = gain; +    } + +    float get_rx_gain(void){ +        return _rx_subdev[SUBDEV_PROP_GAIN].as<gain_t>(); +    } + +    gain_range_t get_rx_gain_range(void){ +        return _rx_subdev[SUBDEV_PROP_GAIN_RANGE].as<gain_range_t>(); +    } + +    void set_rx_antenna(const std::string &ant){ +        _rx_subdev[SUBDEV_PROP_ANTENNA] = ant; +    } + +    std::string get_rx_antenna(void){ +        return _rx_subdev[SUBDEV_PROP_ANTENNA].as<std::string>(); +    } + +    std::vector<std::string> get_rx_antennas(void){ +        return _rx_subdev[SUBDEV_PROP_ANTENNA_NAMES].as<std::vector<std::string> >(); +    } + +    /******************************************************************* +     * TX methods +     ******************************************************************/ +    void set_tx_rate(double rate){ +        double samp_rate = _tx_duc[std::string("rate")].as<double>(); +        assert_has(get_tx_rates(), rate, "simple device tx rate"); +        _tx_duc[std::string("interp")] = size_t(samp_rate/rate); +    } + +    double get_tx_rate(void){ +        double samp_rate = _tx_duc[std::string("rate")].as<double>(); +        size_t interp = _tx_duc[std::string("interp")].as<size_t>(); +        return samp_rate/interp; +    } + +    std::vector<double> get_tx_rates(void){ +        return get_xx_rates(_tx_duc[std::string("interps")], _tx_duc[std::string("rate")]); +    } + +    tune_result_t set_tx_freq(double target_freq){ +        double lo_offset = 0.0; +        //if the local oscillator will be in the passband, use an offset +        if (_tx_subdev[SUBDEV_PROP_LO_INTERFERES].as<bool>()){ +            lo_offset = get_tx_rate()*2.0; +        } +        return tune(target_freq, lo_offset, _tx_subdev, _tx_duc, true/* is tx */); +    } + +    freq_range_t get_tx_freq_range(void){ +        return _tx_subdev[SUBDEV_PROP_FREQ_RANGE].as<freq_range_t>(); +    } + +    void set_tx_gain(float gain){ +        _tx_subdev[SUBDEV_PROP_GAIN] = gain; +    } + +    float get_tx_gain(void){ +        return _tx_subdev[SUBDEV_PROP_GAIN].as<gain_t>(); +    } + +    gain_range_t get_tx_gain_range(void){ +        return _tx_subdev[SUBDEV_PROP_GAIN_RANGE].as<gain_range_t>(); +    } + +    void set_tx_antenna(const std::string &ant){ +        _tx_subdev[SUBDEV_PROP_ANTENNA] = ant; +    } + +    std::string get_tx_antenna(void){ +        return _tx_subdev[SUBDEV_PROP_ANTENNA].as<std::string>(); +    } + +    std::vector<std::string> get_tx_antennas(void){ +        return _tx_subdev[SUBDEV_PROP_ANTENNA_NAMES].as<std::vector<std::string> >(); +    } + +private: +    device::sptr _dev; +    wax::obj _mboard; +    wax::obj _rx_ddc; +    wax::obj _tx_duc; +    wax::obj _rx_subdev; +    wax::obj _tx_subdev; +}; + +/*********************************************************************** + * The Make Function + **********************************************************************/ +simple_device::sptr simple_device::make(const std::string &args){ +    return sptr(new simple_device_impl(args_to_device_addr(args))); +} diff --git a/host/lib/time_spec.cpp b/host/lib/time_spec.cpp new file mode 100644 index 000000000..210010394 --- /dev/null +++ b/host/lib/time_spec.cpp @@ -0,0 +1,40 @@ +// +// Copyright 2010 Ettus Research LLC +// +// This program 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. +// +// This program 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 this program.  If not, see <http://www.gnu.org/licenses/>. +// + +#include <uhd/time_spec.hpp> + +using namespace uhd; + +time_spec_t::time_spec_t(void){ +    secs = ~0; +    ticks = ~0; +} + +time_spec_t::time_spec_t(boost::uint32_t new_secs, boost::uint32_t new_ticks){ +    secs = new_secs; +    ticks = new_ticks; +} + +static const boost::posix_time::ptime epoch(boost::gregorian::date(1970,1,1)); +static double time_tick_rate = double(boost::posix_time::time_duration::ticks_per_second()); + +time_spec_t::time_spec_t(boost::posix_time::ptime time, double tick_rate){ +    boost::posix_time::time_duration td = time - epoch; +    secs = boost::uint32_t(td.total_seconds()); +    double time_ticks_per_device_ticks = time_tick_rate/tick_rate; +    ticks = boost::uint32_t(td.fractional_seconds()/time_ticks_per_device_ticks); +} diff --git a/host/lib/transport/udp.cpp b/host/lib/transport/udp.cpp deleted file mode 100644 index 06defb107..000000000 --- a/host/lib/transport/udp.cpp +++ /dev/null @@ -1,67 +0,0 @@ -// -// Copyright 2010 Ettus Research LLC -// -// This program 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. -// -// This program 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 this program.  If not, see <http://www.gnu.org/licenses/>. -// - -#include <uhd/transport/udp.hpp> -#include <boost/format.hpp> -#include <boost/assign/list_of.hpp> -#include <iostream> - -uhd::transport::udp::udp(const std::string &addr, const std::string &port, bool bcast){ -    //std::cout << boost::format("Creating udp transport for %s %s") % addr % port << std::endl; - -    // resolve the address -    boost::asio::ip::udp::resolver resolver(_io_service); -    boost::asio::ip::udp::resolver::query query(boost::asio::ip::udp::v4(), addr, port); -    _receiver_endpoint = *resolver.resolve(query); - -    // Create and open the socket -    _socket = new boost::asio::ip::udp::socket(_io_service); -    _socket->open(boost::asio::ip::udp::v4()); - -    if (bcast){ -        // Allow broadcasting -        boost::asio::socket_base::broadcast option(true); -        _socket->set_option(option); -    } - -} - -uhd::transport::udp::~udp(void){ -    delete _socket; -} - -void uhd::transport::udp::send(const std::vector<boost::asio::const_buffer> &buffs){ -    _socket->send_to(buffs, _receiver_endpoint); -} - -void uhd::transport::udp::send(const boost::asio::const_buffer &buff){ -    std::vector<boost::asio::const_buffer> buffs = boost::assign::list_of(buff); -    send(buffs); -} - -uhd::shared_iovec uhd::transport::udp::recv(void){ -    //allocate a buffer for the number of bytes available (could be zero) -    uhd::shared_iovec iov(_socket->available()); -    //call recv only if data is available -    if (iov.len != 0){ -        _socket->receive_from( -            boost::asio::buffer(iov.base, iov.len), -            _sender_endpoint -        ); -    } -    return iov; -} diff --git a/host/lib/transport/udp_simple.cpp b/host/lib/transport/udp_simple.cpp new file mode 100644 index 000000000..3c8ecb70d --- /dev/null +++ b/host/lib/transport/udp_simple.cpp @@ -0,0 +1,158 @@ +// +// Copyright 2010 Ettus Research LLC +// +// This program 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. +// +// This program 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 this program.  If not, see <http://www.gnu.org/licenses/>. +// + +#include <uhd/transport/udp_simple.hpp> +#include <boost/thread.hpp> +#include <boost/format.hpp> +#include <iostream> + +using namespace uhd::transport; + +/*********************************************************************** + * Helper Functions + **********************************************************************/ +/*! + * A receive timeout for a socket: + * + * It seems that asio cannot have timeouts with synchronous io. + * However, we can implement a polling loop that will timeout. + * This is okay bacause this is the slow-path implementation. + * + * \param socket the asio socket + */ +static void reasonable_recv_timeout( +    boost::asio::ip::udp::socket &socket +){ +    boost::asio::deadline_timer timer(socket.get_io_service()); +    timer.expires_from_now(boost::posix_time::milliseconds(100)); +    while (not (socket.available() or timer.expires_from_now().is_negative())){ +        boost::this_thread::sleep(boost::posix_time::milliseconds(1)); +    } +} + +/*********************************************************************** + * UDP connected implementation class + **********************************************************************/ +class udp_connected_impl : public udp_simple{ +public: +    //structors +    udp_connected_impl(const std::string &addr, const std::string &port); +    ~udp_connected_impl(void); + +    //send/recv +    size_t send(const boost::asio::const_buffer &buff); +    size_t recv(const boost::asio::mutable_buffer &buff); + +private: +    boost::asio::ip::udp::socket   *_socket; +    boost::asio::io_service        _io_service; +}; + +udp_connected_impl::udp_connected_impl(const std::string &addr, const std::string &port){ +    //std::cout << boost::format("Creating udp transport for %s %s") % addr % port << std::endl; + +    // resolve the address +    boost::asio::ip::udp::resolver resolver(_io_service); +    boost::asio::ip::udp::resolver::query query(boost::asio::ip::udp::v4(), addr, port); +    boost::asio::ip::udp::endpoint receiver_endpoint = *resolver.resolve(query); + +    // Create, open, and connect the socket +    _socket = new boost::asio::ip::udp::socket(_io_service); +    _socket->open(boost::asio::ip::udp::v4()); +    _socket->connect(receiver_endpoint); +} + +udp_connected_impl::~udp_connected_impl(void){ +    delete _socket; +} + +size_t udp_connected_impl::send(const boost::asio::const_buffer &buff){ +    return _socket->send(boost::asio::buffer(buff)); +} + +size_t udp_connected_impl::recv(const boost::asio::mutable_buffer &buff){ +    reasonable_recv_timeout(*_socket); +    if (not _socket->available()) return 0; +    return _socket->receive(boost::asio::buffer(buff)); +} + +/*********************************************************************** + * UDP broadcast implementation class + **********************************************************************/ +class udp_broadcast_impl : public udp_simple{ +public: +    //structors +    udp_broadcast_impl(const std::string &addr, const std::string &port); +    ~udp_broadcast_impl(void); + +    //send/recv +    size_t send(const boost::asio::const_buffer &buff); +    size_t recv(const boost::asio::mutable_buffer &buff); + +private: +    boost::asio::ip::udp::socket   *_socket; +    boost::asio::ip::udp::endpoint _receiver_endpoint; +    boost::asio::io_service        _io_service; +}; + +udp_broadcast_impl::udp_broadcast_impl(const std::string &addr, const std::string &port){ +    //std::cout << boost::format("Creating udp transport for %s %s") % addr % port << std::endl; + +    // resolve the address +    boost::asio::ip::udp::resolver resolver(_io_service); +    boost::asio::ip::udp::resolver::query query(boost::asio::ip::udp::v4(), addr, port); +    _receiver_endpoint = *resolver.resolve(query); + +    // Create and open the socket +    _socket = new boost::asio::ip::udp::socket(_io_service); +    _socket->open(boost::asio::ip::udp::v4()); + +    // Allow broadcasting +    boost::asio::socket_base::broadcast option(true); +    _socket->set_option(option); + +} + +udp_broadcast_impl::~udp_broadcast_impl(void){ +    delete _socket; +} + +size_t udp_broadcast_impl::send(const boost::asio::const_buffer &buff){ +    return _socket->send_to(boost::asio::buffer(buff), _receiver_endpoint); +} + +size_t udp_broadcast_impl::recv(const boost::asio::mutable_buffer &buff){ +    reasonable_recv_timeout(*_socket); +    if (not _socket->available()) return 0; +    boost::asio::ip::udp::endpoint sender_endpoint; +    return _socket->receive_from(boost::asio::buffer(buff), sender_endpoint); +} + +/*********************************************************************** + * UDP public make functions + **********************************************************************/ +udp_simple::sptr udp_simple::make_connected( +    const std::string &addr, const std::string &port +){ +    return sptr(new udp_connected_impl(addr, port)); +} + +udp_simple::sptr udp_simple::make_broadcast( +    const std::string &addr, const std::string &port +){ +    return sptr(new udp_broadcast_impl(addr, port)); +} diff --git a/host/lib/transport/udp_zero_copy_asio.cpp b/host/lib/transport/udp_zero_copy_asio.cpp new file mode 100644 index 000000000..219ae8720 --- /dev/null +++ b/host/lib/transport/udp_zero_copy_asio.cpp @@ -0,0 +1,154 @@ +// +// Copyright 2010 Ettus Research LLC +// +// This program 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. +// +// This program 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 this program.  If not, see <http://www.gnu.org/licenses/>. +// + +#include <uhd/transport/udp_zero_copy.hpp> +#include <boost/cstdint.hpp> +#include <boost/thread.hpp> +#include <boost/format.hpp> +#include <iostream> + +using namespace uhd::transport; + +/*********************************************************************** + * Smart buffer implementation for udp zerocopy none + * + * This smart buffer implemention houses a const buffer. + * When the smart buffer is deleted, the buffer is freed. + * The memory in the const buffer is allocated with new [], + * and so the destructor frees the buffer with delete []. + **********************************************************************/ +class smart_buffer_impl : public smart_buffer{ +public: +    smart_buffer_impl(const boost::asio::const_buffer &buff){ +        _buff = buff; +    } + +    ~smart_buffer_impl(void){ +        delete [] boost::asio::buffer_cast<const boost::uint32_t *>(_buff); +    } + +    const boost::asio::const_buffer &get(void) const{ +        return _buff; +    } + +private: +    boost::asio::const_buffer _buff; +}; + +/*********************************************************************** + * UDP zero copy implementation class + * + * This is the portable zero copy implementation for systems + * where a faster, platform specific solution is not available. + * + * It uses boost asio udp sockets and the standard recv() class, + * and in-fact, is not actually doing a zero-copy implementation. + **********************************************************************/ +class udp_zero_copy_impl : public udp_zero_copy{ +public: +    //structors +    udp_zero_copy_impl(const std::string &addr, const std::string &port); +    ~udp_zero_copy_impl(void); + +    //send/recv +    size_t send(const boost::asio::const_buffer &buff); +    smart_buffer::sptr recv(void); + +private: +    boost::asio::ip::udp::socket   *_socket; +    boost::asio::io_service        _io_service; + +    size_t get_recv_buff_size(void); +    void set_recv_buff_size(size_t); +}; + +udp_zero_copy_impl::udp_zero_copy_impl(const std::string &addr, const std::string &port){ +    //std::cout << boost::format("Creating udp transport for %s %s") % addr % port << std::endl; + +    // resolve the address +    boost::asio::ip::udp::resolver resolver(_io_service); +    boost::asio::ip::udp::resolver::query query(boost::asio::ip::udp::v4(), addr, port); +    boost::asio::ip::udp::endpoint receiver_endpoint = *resolver.resolve(query); + +    // Create, open, and connect the socket +    _socket = new boost::asio::ip::udp::socket(_io_service); +    _socket->open(boost::asio::ip::udp::v4()); +    _socket->connect(receiver_endpoint); + +    // set the rx socket buffer size: +    // pick a huge size, and deal with whatever we get +    set_recv_buff_size(size_t(54321e3)); //some big number! +    size_t current_buff_size = get_recv_buff_size(); +    std::cout << boost::format( +        "Current rx socket buffer size: %d\n" +    ) % current_buff_size; +    if (current_buff_size < size_t(.1e6)) std::cout << boost::format( +        "Adjust max rx socket buffer size (linux only):\n" +        "  sysctl -w net.core.rmem_max=VALUE\n" +    ); +} + +udp_zero_copy_impl::~udp_zero_copy_impl(void){ +    delete _socket; +} + +size_t udp_zero_copy_impl::send(const boost::asio::const_buffer &buff){ +    return _socket->send(boost::asio::buffer(buff)); +} + +smart_buffer::sptr udp_zero_copy_impl::recv(void){ +    size_t available = 0; + +    //implement timeout through polling and sleeping +    boost::asio::deadline_timer timer(_socket->get_io_service()); +    timer.expires_from_now(boost::posix_time::milliseconds(50)); +    while (not ((available = _socket->available()) or timer.expires_from_now().is_negative())){ +        boost::this_thread::sleep(boost::posix_time::milliseconds(1)); +    } + +    //allocate memory and create buffer +    boost::uint32_t *buff_mem = new boost::uint32_t[available/sizeof(boost::uint32_t)]; +    boost::asio::mutable_buffer buff(buff_mem, available); + +    //receive only if data is available +    if (available){ +        _socket->receive(boost::asio::buffer(buff)); +    } + +    //create a new smart buffer to house the data +    return smart_buffer::sptr(new smart_buffer_impl(buff)); +} + +size_t udp_zero_copy_impl::get_recv_buff_size(void){ +    boost::asio::socket_base::receive_buffer_size option; +    _socket->get_option(option); +    return option.value(); +} + +void udp_zero_copy_impl::set_recv_buff_size(size_t new_size){ +    boost::asio::socket_base::receive_buffer_size option(new_size); +    _socket->set_option(option); +} + +/*********************************************************************** + * UDP zero copy make function + **********************************************************************/ +udp_zero_copy::sptr udp_zero_copy::make( +    const std::string &addr, const std::string &port +){ +    return sptr(new udp_zero_copy_impl(addr, port)); +} diff --git a/host/lib/transport/vrt.cpp b/host/lib/transport/vrt.cpp new file mode 100644 index 000000000..a06b5bf21 --- /dev/null +++ b/host/lib/transport/vrt.cpp @@ -0,0 +1,109 @@ +// +// Copyright 2010 Ettus Research LLC +// +// This program 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. +// +// This program 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 this program.  If not, see <http://www.gnu.org/licenses/>. +// + +#include <uhd/transport/vrt.hpp> +#include <boost/asio.hpp> //endianness conversion +#include <stdexcept> + +using namespace uhd; +using namespace uhd::transport; + +void vrt::pack( +    const tx_metadata_t &metadata, //input +    boost::uint32_t *header_buff,  //output +    size_t &num_header_words32,    //output +    size_t num_payload_words32,    //input +    size_t &num_packet_words32,    //output +    size_t packet_count            //input +){ +    boost::uint32_t vrt_hdr_flags = 0; +    num_header_words32 = 1; + +    //load the vrt header and flags +    if(metadata.has_stream_id){ +        vrt_hdr_flags |= (0x1 << 28); //IF Data packet with Stream Identifier +        header_buff[num_header_words32++] = htonl(metadata.stream_id); +    } + +    if(metadata.has_time_spec){ +        vrt_hdr_flags |= (0x3 << 22) | (0x1 << 20); //TSI: Other, TSF: Sample Count Timestamp +        header_buff[num_header_words32++] = htonl(metadata.time_spec.secs); +        header_buff[num_header_words32++] = htonl(metadata.time_spec.ticks); +        header_buff[num_header_words32++] = 0; //unused part of fractional seconds +    } + +    vrt_hdr_flags |= (metadata.start_of_burst)? (0x1 << 25) : 0; +    vrt_hdr_flags |= (metadata.end_of_burst)?   (0x1 << 24) : 0; + +    num_packet_words32 = num_header_words32 + num_payload_words32; + +    //fill in complete header word +    header_buff[0] = htonl(vrt_hdr_flags | +        ((packet_count & 0xf) << 16) | +        (num_packet_words32 & 0xffff) +    ); +} + +void vrt::unpack( +    rx_metadata_t &metadata,            //output +    const boost::uint32_t *header_buff, //input +    size_t &num_header_words32,         //output +    size_t &num_payload_words32,        //output +    size_t num_packet_words32,          //input +    size_t &packet_count                //output +){ +    //clear the metadata +    metadata = rx_metadata_t(); + +    //extract vrt header +    boost::uint32_t vrt_hdr_word = ntohl(header_buff[0]); +    size_t packet_words32 = vrt_hdr_word & 0xffff; +    packet_count = (vrt_hdr_word >> 16) & 0xf; + +    //failure cases +    if (packet_words32 == 0 or num_packet_words32 < packet_words32) +        throw std::runtime_error("bad vrt header or packet fragment"); +    if (vrt_hdr_word & (0x7 << 29)) +        throw std::runtime_error("unsupported vrt packet type"); + +    //parse the header flags +    num_header_words32 = 1; + +    if (vrt_hdr_word & (0x1 << 28)){ //stream id +        metadata.has_stream_id = true; +        metadata.stream_id = ntohl(header_buff[num_header_words32++]); +    } + +    if (vrt_hdr_word & (0x1 << 27)){ //class id (we dont use) +        num_header_words32 += 2; +    } + +    if (vrt_hdr_word & (0x3 << 22)){ //integer time +        metadata.has_time_spec = true; +        metadata.time_spec.secs = ntohl(header_buff[num_header_words32++]); +    } + +    if (vrt_hdr_word & (0x3 << 20)){ //fractional time +        metadata.has_time_spec = true; +        metadata.time_spec.ticks = ntohl(header_buff[num_header_words32++]); +        num_header_words32++; //unused part of fractional seconds +    } + +    size_t num_trailer_words32 = (vrt_hdr_word & (0x1 << 26))? 1 : 0; + +    num_payload_words32 = packet_words32 - num_header_words32 - num_trailer_words32; +} diff --git a/host/lib/types.cpp b/host/lib/types.cpp new file mode 100644 index 000000000..f8a9a9b36 --- /dev/null +++ b/host/lib/types.cpp @@ -0,0 +1,57 @@ +// +// Copyright 2010 Ettus Research LLC +// +// This program 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. +// +// This program 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 this program.  If not, see <http://www.gnu.org/licenses/>. +// + +#include <uhd/types.hpp> + +using namespace uhd; + +/*********************************************************************** + * gain range + **********************************************************************/ +gain_range_t::gain_range_t(float min_, float max_, float step_){ +    min = min_; +    max = max_; +    step = step_; +} + +/*********************************************************************** + * freq range + **********************************************************************/ +freq_range_t::freq_range_t(double min_, double max_){ +    min = min_; +    max = max_; +} + +/*********************************************************************** + * tune result + **********************************************************************/ +tune_result_t::tune_result_t(void){ +    target_inter_freq = 0.0; +    actual_inter_freq = 0.0; +    target_dxc_freq = 0.0; +    actual_dxc_freq = 0.0; +    spectrum_inverted = false; +} + +/*********************************************************************** + * clock config + **********************************************************************/ +clock_config_t::clock_config_t(void){ +    ref_source = ""; //not a valid setting +    pps_source = ""; //not a valid setting +    pps_polarity = POLARITY_NEG; +} diff --git a/host/lib/uhd.cpp b/host/lib/uhd.cpp deleted file mode 100644 index 5e250c76f..000000000 --- a/host/lib/uhd.cpp +++ /dev/null @@ -1,20 +0,0 @@ -// -// Copyright 2010 Ettus Research LLC -// -// This program 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. -// -// This program 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 this program.  If not, see <http://www.gnu.org/licenses/>. -// - -#include <uhd.hpp> - -//nothing here, just includes the header so the compiler can check diff --git a/host/lib/usrp/dboard/basic.cpp b/host/lib/usrp/dboard/basic.cpp index f39ebff2f..82485ae6a 100644 --- a/host/lib/usrp/dboard/basic.cpp +++ b/host/lib/usrp/dboard/basic.cpp @@ -15,42 +15,287 @@  // along with this program.  If not, see <http://www.gnu.org/licenses/>.  // -#include "dboards.hpp" +#include <uhd/utils.hpp> +#include <uhd/props.hpp> +#include <uhd/types.hpp> +#include <uhd/usrp/dboard_base.hpp> +#include <uhd/usrp/dboard_manager.hpp> +#include <boost/assign/list_of.hpp> +#include <boost/format.hpp> + +using namespace uhd; +using namespace uhd::usrp; +using namespace boost::assign;  /*********************************************************************** - * Basic RX dboard + * The basic and lf boards: + *   They share a common class because only the frequency bounds differ.   **********************************************************************/ -basic_rx::basic_rx(ctor_args_t const& args) : rx_dboard_base(args){ -    /* NOP */ +class basic_rx : public rx_dboard_base{ +public: +    basic_rx(ctor_args_t const& args, freq_t max_freq); +    ~basic_rx(void); + +    void rx_get(const wax::obj &key, wax::obj &val); +    void rx_set(const wax::obj &key, const wax::obj &val); + +private: +    freq_t _max_freq; +}; + +class basic_tx : public tx_dboard_base{ +public: +    basic_tx(ctor_args_t const& args, freq_t max_freq); +    ~basic_tx(void); + +    void tx_get(const wax::obj &key, wax::obj &val); +    void tx_set(const wax::obj &key, const wax::obj &val); + +private: +    freq_t _max_freq; +}; + +/*********************************************************************** + * Register the basic and LF dboards + **********************************************************************/ +static dboard_base::sptr make_basic_rx(dboard_base::ctor_args_t const& args){ +    return dboard_base::sptr(new basic_rx(args, 90e9)); +} + +static dboard_base::sptr make_basic_tx(dboard_base::ctor_args_t const& args){ +    return dboard_base::sptr(new basic_tx(args, 90e9)); +} + +static dboard_base::sptr make_lf_rx(dboard_base::ctor_args_t const& args){ +    return dboard_base::sptr(new basic_rx(args, 32e6)); +} + +static dboard_base::sptr make_lf_tx(dboard_base::ctor_args_t const& args){ +    return dboard_base::sptr(new basic_tx(args, 32e6)); +} + +STATIC_BLOCK(reg_dboards){ +    dboard_manager::register_dboard(0x0000, &make_basic_tx, "Basic TX", list_of("")); +    dboard_manager::register_dboard(0x0001, &make_basic_rx, "Basic RX", list_of("ab")("a")("b")); +    dboard_manager::register_dboard(0x000e, &make_lf_tx,    "LF TX",    list_of("")); +    dboard_manager::register_dboard(0x000f, &make_lf_rx,    "LF RX",    list_of("ab")("a")("b")); +} + +/*********************************************************************** + * Basic and LF RX dboard + **********************************************************************/ +basic_rx::basic_rx(ctor_args_t const& args, freq_t max_freq) : rx_dboard_base(args){ +    _max_freq = max_freq; +    // set the gpios to safe values (all inputs) +    get_interface()->set_gpio_ddr(dboard_interface::GPIO_RX_BANK, 0x0000, 0xffff);  }  basic_rx::~basic_rx(void){      /* NOP */  } -void basic_rx::rx_get(const wax::obj &, wax::obj &){ -    /* TODO */ +void basic_rx::rx_get(const wax::obj &key_, wax::obj &val){ +    wax::obj key; std::string name; +    boost::tie(key, name) = extract_named_prop(key_); + +    //handle the get request conditioned on the key +    switch(key.as<subdev_prop_t>()){ +    case SUBDEV_PROP_NAME: +        val = std::string(str(boost::format("%s - %s") +            % dboard_id::to_string(get_rx_id()) +            % get_subdev_name() +        )); +        return; + +    case SUBDEV_PROP_OTHERS: +        val = prop_names_t(); //empty +        return; + +    case SUBDEV_PROP_GAIN: +        val = gain_t(0); +        return; + +    case SUBDEV_PROP_GAIN_RANGE: +        val = gain_range_t(0, 0, 0); +        return; + +    case SUBDEV_PROP_GAIN_NAMES: +        val = prop_names_t(); //empty +        return; + +    case SUBDEV_PROP_FREQ: +        val = freq_t(0); +        return; + +    case SUBDEV_PROP_FREQ_RANGE: +        val = freq_range_t(+_max_freq, -_max_freq); +        return; + +    case SUBDEV_PROP_ANTENNA: +        val = std::string(""); +        return; + +    case SUBDEV_PROP_ANTENNA_NAMES: +        val = prop_names_t(1, ""); //vector of 1 empty string +        return; + +    case SUBDEV_PROP_ENABLED: +        val = true; //always enabled +        return; + +    case SUBDEV_PROP_QUADRATURE: +        val = (get_subdev_name() == "ab"); //only quadrature in ab mode +        return; + +    case SUBDEV_PROP_IQ_SWAPPED: +    case SUBDEV_PROP_SPECTRUM_INVERTED: +    case SUBDEV_PROP_LO_INTERFERES: +        val = false; +        return; +    }  } -void basic_rx::rx_set(const wax::obj &, const wax::obj &){ -    /* TODO */ +void basic_rx::rx_set(const wax::obj &key_, const wax::obj &val){ +    wax::obj key; std::string name; +    boost::tie(key, name) = extract_named_prop(key_); + +    //handle the get request conditioned on the key +    switch(key.as<subdev_prop_t>()){ + +    case SUBDEV_PROP_GAIN: +        ASSERT_THROW(val.as<gain_t>() == gain_t(0)); +        return; + +    case SUBDEV_PROP_ANTENNA: +        ASSERT_THROW(val.as<std::string>() == std::string("")); +        return; + +    case SUBDEV_PROP_ENABLED: +        return; // it wont do you much good, but you can set it + +    case SUBDEV_PROP_FREQ: +        return; // it wont do you much good, but you can set it + +    case SUBDEV_PROP_NAME: +    case SUBDEV_PROP_OTHERS: +    case SUBDEV_PROP_GAIN_RANGE: +    case SUBDEV_PROP_GAIN_NAMES: +    case SUBDEV_PROP_FREQ_RANGE: +    case SUBDEV_PROP_ANTENNA_NAMES: +    case SUBDEV_PROP_QUADRATURE: +    case SUBDEV_PROP_IQ_SWAPPED: +    case SUBDEV_PROP_SPECTRUM_INVERTED: +    case SUBDEV_PROP_LO_INTERFERES: +        throw std::runtime_error(str(boost::format( +            "Error: trying to set read-only property on %s subdev" +        ) % dboard_id::to_string(get_rx_id()))); +    }  }  /*********************************************************************** - * Basic TX dboard + * Basic and LF TX dboard   **********************************************************************/ -basic_tx::basic_tx(ctor_args_t const& args) : tx_dboard_base(args){ -    /* NOP */ +basic_tx::basic_tx(ctor_args_t const& args, freq_t max_freq) : tx_dboard_base(args){ +    _max_freq = max_freq; +    // set the gpios to safe values (all inputs) +    get_interface()->set_gpio_ddr(dboard_interface::GPIO_TX_BANK, 0x0000, 0xffff);  }  basic_tx::~basic_tx(void){      /* NOP */  } -void basic_tx::tx_get(const wax::obj &, wax::obj &){ -    /* TODO */ +void basic_tx::tx_get(const wax::obj &key_, wax::obj &val){ +    wax::obj key; std::string name; +    boost::tie(key, name) = extract_named_prop(key_); + +    //handle the get request conditioned on the key +    switch(key.as<subdev_prop_t>()){ +    case SUBDEV_PROP_NAME: +        val = dboard_id::to_string(get_tx_id()); +        return; + +    case SUBDEV_PROP_OTHERS: +        val = prop_names_t(); //empty +        return; + +    case SUBDEV_PROP_GAIN: +        val = gain_t(0); +        return; + +    case SUBDEV_PROP_GAIN_RANGE: +        val = gain_range_t(0, 0, 0); +        return; + +    case SUBDEV_PROP_GAIN_NAMES: +        val = prop_names_t(); //empty +        return; + +    case SUBDEV_PROP_FREQ: +        val = freq_t(0); +        return; + +    case SUBDEV_PROP_FREQ_RANGE: +        val = freq_range_t(+_max_freq, -_max_freq); +        return; + +    case SUBDEV_PROP_ANTENNA: +        val = std::string(""); +        return; + +    case SUBDEV_PROP_ANTENNA_NAMES: +        val = prop_names_t(1, ""); //vector of 1 empty string +        return; + +    case SUBDEV_PROP_ENABLED: +        val = true; //always enabled +        return; + +    case SUBDEV_PROP_QUADRATURE: +        val = true; +        return; + +    case SUBDEV_PROP_IQ_SWAPPED: +    case SUBDEV_PROP_SPECTRUM_INVERTED: +    case SUBDEV_PROP_LO_INTERFERES: +        val = false; +        return; +    }  } -void basic_tx::tx_set(const wax::obj &, const wax::obj &){ -    /* TODO */ +void basic_tx::tx_set(const wax::obj &key_, const wax::obj &val){ +    wax::obj key; std::string name; +    boost::tie(key, name) = extract_named_prop(key_); + +    //handle the get request conditioned on the key +    switch(key.as<subdev_prop_t>()){ + +    case SUBDEV_PROP_GAIN: +        ASSERT_THROW(val.as<gain_t>() == gain_t(0)); +        return; + +    case SUBDEV_PROP_ANTENNA: +        ASSERT_THROW(val.as<std::string>() == std::string("")); +        return; + +    case SUBDEV_PROP_ENABLED: +        return; // it wont do you much good, but you can set it + +    case SUBDEV_PROP_FREQ: +        return; // it wont do you much good, but you can set it + +    case SUBDEV_PROP_NAME: +    case SUBDEV_PROP_OTHERS: +    case SUBDEV_PROP_GAIN_RANGE: +    case SUBDEV_PROP_GAIN_NAMES: +    case SUBDEV_PROP_FREQ_RANGE: +    case SUBDEV_PROP_ANTENNA_NAMES: +    case SUBDEV_PROP_QUADRATURE: +    case SUBDEV_PROP_IQ_SWAPPED: +    case SUBDEV_PROP_SPECTRUM_INVERTED: +    case SUBDEV_PROP_LO_INTERFERES: +        throw std::runtime_error(str(boost::format( +            "Error: trying to set read-only property on %s subdev" +        ) % dboard_id::to_string(get_tx_id()))); +    }  } diff --git a/host/lib/usrp/dboard_manager.cpp b/host/lib/usrp/dboard_manager.cpp index f0846db25..6ca15e98c 100644 --- a/host/lib/usrp/dboard_manager.cpp +++ b/host/lib/usrp/dboard_manager.cpp @@ -16,85 +16,49 @@  //  #include <uhd/usrp/dboard_manager.hpp> +#include <uhd/gain_handler.hpp>  #include <uhd/utils.hpp>  #include <uhd/dict.hpp> -#include <boost/assign/list_of.hpp>  #include <boost/tuple/tuple.hpp>  #include <boost/format.hpp> +#include <boost/bind.hpp>  #include <boost/foreach.hpp> -#include "dboard/dboards.hpp"  using namespace uhd;  using namespace uhd::usrp; -using namespace boost::assign; - -/*********************************************************************** - * register internal dboards - * - * Register internal/known dboards located in this build tree. - * Each board should have entries below mapping an id to a constructor. - * The xcvr type boards should register both rx and tx sides. - * - * This function will be called before new boards are registered. - * This allows for internal boards to be externally overridden. - * This function will also be called when creating a new dboard_manager - * to ensure that the maps are filled with the entries below. - **********************************************************************/ -static void register_internal_dboards(void){ -    //ensure that this function can only be called once per instance -    static bool called = false; -    if (called) return; called = true; -    //register the known dboards (dboard id, constructor, subdev names) -    dboard_manager::register_subdevs(ID_BASIC_TX, &basic_tx::make, list_of("")); -    dboard_manager::register_subdevs(ID_BASIC_RX, &basic_rx::make, list_of("a")("b")("ab")); -}  /***********************************************************************   * storage and registering for dboards   **********************************************************************/ -typedef boost::tuple<dboard_manager::dboard_ctor_t, prop_names_t> args_t; +//dboard registry tuple: dboard constructor, canonical name, subdev names +typedef boost::tuple<dboard_manager::dboard_ctor_t, std::string, prop_names_t> args_t;  //map a dboard id to a dboard constructor -static uhd::dict<dboard_id_t, args_t> id_to_args_map; +typedef uhd::dict<dboard_id_t, args_t> id_to_args_map_t; +STATIC_INSTANCE(id_to_args_map_t, get_id_to_args_map) -void dboard_manager::register_subdevs( +void dboard_manager::register_dboard(      dboard_id_t dboard_id,      dboard_ctor_t dboard_ctor, +    const std::string &name,      const prop_names_t &subdev_names  ){ -    register_internal_dboards(); //always call first -    id_to_args_map[dboard_id] = args_t(dboard_ctor, subdev_names); +    //std::cout << "registering: " << name << std::endl; +    if (get_id_to_args_map().has_key(dboard_id)){ +        throw std::runtime_error(str(boost::format( +            "The dboard id 0x%04x is already registered to %s." +        ) % dboard_id % dboard_id::to_string(dboard_id))); +    } +    get_id_to_args_map()[dboard_id] = args_t(dboard_ctor, name, subdev_names);  } -/*********************************************************************** - * dboard manager implementation class - **********************************************************************/ -class dboard_manager_impl : public dboard_manager{ - -public: -    dboard_manager_impl( -        dboard_id_t rx_dboard_id, -        dboard_id_t tx_dboard_id, -        dboard_interface::sptr interface -    ); -    ~dboard_manager_impl(void); - -    //dboard_interface -    prop_names_t get_rx_subdev_names(void); -    prop_names_t get_tx_subdev_names(void); -    wax::obj get_rx_subdev(const std::string &subdev_name); -    wax::obj get_tx_subdev(const std::string &subdev_name); - -private: -    //list of rx and tx dboards in this dboard_manager -    //each dboard here is actually a subdevice proxy -    //the subdevice proxy is internal to the cpp file -    uhd::dict<std::string, wax::obj> _rx_dboards; -    uhd::dict<std::string, wax::obj> _tx_dboards; -}; +std::string dboard_id::to_string(const dboard_id_t &id){ +    std::string name = (get_id_to_args_map().has_key(id))? get_id_to_args_map()[id].get<1>() : "unknown"; +    return str(boost::format("%s (0x%04x)") % name % id); +}  /*********************************************************************** - * internal helper classes + * internal helper classe   **********************************************************************/  /*!   * A special wax proxy object that forwards calls to a subdev. @@ -108,7 +72,17 @@ public:      //structors      subdev_proxy(dboard_base::sptr subdev, type_t type)      : _subdev(subdev), _type(type){ -        /* NOP */ +        //initialize gain props struct +        gain_handler::props_t gain_props; +        gain_props.value = SUBDEV_PROP_GAIN; +        gain_props.range = SUBDEV_PROP_GAIN_RANGE; +        gain_props.names = SUBDEV_PROP_GAIN_NAMES; + +        //make a new gain handler +        _gain_handler = gain_handler::make( +            this->get_link(), gain_props, +            boost::bind(&gain_handler::is_equal<subdev_prop_t>, _1, _2) +        );      }      ~subdev_proxy(void){ @@ -116,11 +90,13 @@ public:      }  private: +    gain_handler::sptr   _gain_handler;      dboard_base::sptr   _subdev;      type_t              _type;      //forward the get calls to the rx or tx      void get(const wax::obj &key, wax::obj &val){ +        if (_gain_handler->intercept_get(key, val)) return;          switch(_type){          case RX_TYPE: return _subdev->rx_get(key, val);          case TX_TYPE: return _subdev->tx_get(key, val); @@ -129,6 +105,7 @@ private:      //forward the set calls to the rx or tx      void set(const wax::obj &key, const wax::obj &val){ +        if (_gain_handler->intercept_set(key, val)) return;          switch(_type){          case RX_TYPE: return _subdev->rx_set(key, val);          case TX_TYPE: return _subdev->tx_set(key, val); @@ -137,6 +114,35 @@ private:  };  /*********************************************************************** + * dboard manager implementation class + **********************************************************************/ +class dboard_manager_impl : public dboard_manager{ + +public: +    dboard_manager_impl( +        dboard_id_t rx_dboard_id, +        dboard_id_t tx_dboard_id, +        dboard_interface::sptr interface +    ); +    ~dboard_manager_impl(void); + +    //dboard_interface +    prop_names_t get_rx_subdev_names(void); +    prop_names_t get_tx_subdev_names(void); +    wax::obj get_rx_subdev(const std::string &subdev_name); +    wax::obj get_tx_subdev(const std::string &subdev_name); + +private: +    //list of rx and tx dboards in this dboard_manager +    //each dboard here is actually a subdevice proxy +    //the subdevice proxy is internal to the cpp file +    uhd::dict<std::string, subdev_proxy::sptr> _rx_dboards; +    uhd::dict<std::string, subdev_proxy::sptr> _tx_dboards; +    dboard_interface::sptr _interface; +    void set_nice_gpio_pins(void); +}; + +/***********************************************************************   * make routine for dboard manager   **********************************************************************/  dboard_manager::sptr dboard_manager::make( @@ -158,16 +164,16 @@ static args_t get_dboard_args(  ){      //special case, its rx and the none id (0xffff)      if (xx_type == "rx" and dboard_id == ID_NONE){ -        return args_t(&basic_rx::make, list_of("ab")); +        return get_dboard_args(0x0001, xx_type);      }      //special case, its tx and the none id (0xffff)      if (xx_type == "tx" and dboard_id == ID_NONE){ -        return args_t(&basic_tx::make, list_of("")); +        return get_dboard_args(0x0000, xx_type);      }      //verify that there is a registered constructor for this id -    if (not id_to_args_map.has_key(dboard_id)){ +    if (not get_id_to_args_map().has_key(dboard_id)){          throw std::runtime_error(str(              boost::format("Unregistered %s dboard id: %s")              % xx_type % dboard_id::to_string(dboard_id) @@ -175,7 +181,7 @@ static args_t get_dboard_args(      }      //return the dboard args for this id -    return id_to_args_map[dboard_id]; +    return get_id_to_args_map()[dboard_id];  }  dboard_manager_impl::dboard_manager_impl( @@ -183,37 +189,30 @@ dboard_manager_impl::dboard_manager_impl(      dboard_id_t tx_dboard_id,      dboard_interface::sptr interface  ){ -    register_internal_dboards(); //always call first +    _interface = interface; -    dboard_ctor_t rx_dboard_ctor; prop_names_t rx_subdevs; -    boost::tie(rx_dboard_ctor, rx_subdevs) = get_dboard_args(rx_dboard_id, "rx"); +    dboard_ctor_t rx_dboard_ctor; std::string rx_name; prop_names_t rx_subdevs; +    boost::tie(rx_dboard_ctor, rx_name, rx_subdevs) = get_dboard_args(rx_dboard_id, "rx"); -    dboard_ctor_t tx_dboard_ctor; prop_names_t tx_subdevs; -    boost::tie(tx_dboard_ctor, tx_subdevs) = get_dboard_args(tx_dboard_id, "tx"); +    dboard_ctor_t tx_dboard_ctor; std::string tx_name; prop_names_t tx_subdevs; +    boost::tie(tx_dboard_ctor, tx_name, tx_subdevs) = get_dboard_args(tx_dboard_id, "tx");      //initialize the gpio pins before creating subdevs -    interface->set_gpio_ddr(dboard_interface::GPIO_RX_BANK, 0x0000, 0xffff); //all inputs -    interface->set_gpio_ddr(dboard_interface::GPIO_TX_BANK, 0x0000, 0xffff); - -    interface->write_gpio(dboard_interface::GPIO_RX_BANK, 0x0000, 0xffff); //all zeros -    interface->write_gpio(dboard_interface::GPIO_TX_BANK, 0x0000, 0xffff); - -    interface->set_atr_reg(dboard_interface::GPIO_RX_BANK, 0x0000, 0x0000, 0x0000); //software controlled -    interface->set_atr_reg(dboard_interface::GPIO_TX_BANK, 0x0000, 0x0000, 0x0000); +    set_nice_gpio_pins();      //make xcvr subdevs (make one subdev for both rx and tx dboards)      if (rx_dboard_ctor == tx_dboard_ctor){          ASSERT_THROW(rx_subdevs == tx_subdevs); -        BOOST_FOREACH(std::string name, rx_subdevs){ +        BOOST_FOREACH(std::string subdev, rx_subdevs){              dboard_base::sptr xcvr_dboard = rx_dboard_ctor( -                dboard_base::ctor_args_t(name, interface, rx_dboard_id, tx_dboard_id) +                dboard_base::ctor_args_t(subdev, interface, rx_dboard_id, tx_dboard_id)              );              //create a rx proxy for this xcvr board -            _rx_dboards[name] = subdev_proxy::sptr( +            _rx_dboards[subdev] = subdev_proxy::sptr(                  new subdev_proxy(xcvr_dboard, subdev_proxy::RX_TYPE)              );              //create a tx proxy for this xcvr board -            _tx_dboards[name] = subdev_proxy::sptr( +            _tx_dboards[subdev] = subdev_proxy::sptr(                  new subdev_proxy(xcvr_dboard, subdev_proxy::TX_TYPE)              );          } @@ -222,22 +221,22 @@ dboard_manager_impl::dboard_manager_impl(      //make tx and rx subdevs (separate subdevs for rx and tx dboards)      else{          //make the rx subdevs -        BOOST_FOREACH(std::string name, rx_subdevs){ +        BOOST_FOREACH(std::string subdev, rx_subdevs){              dboard_base::sptr rx_dboard = rx_dboard_ctor( -                dboard_base::ctor_args_t(name, interface, rx_dboard_id, ID_NONE) +                dboard_base::ctor_args_t(subdev, interface, rx_dboard_id, ID_NONE)              );              //create a rx proxy for this rx board -            _rx_dboards[name] = subdev_proxy::sptr( +            _rx_dboards[subdev] = subdev_proxy::sptr(                  new subdev_proxy(rx_dboard, subdev_proxy::RX_TYPE)              );          }          //make the tx subdevs -        BOOST_FOREACH(std::string name, tx_subdevs){ +        BOOST_FOREACH(std::string subdev, tx_subdevs){              dboard_base::sptr tx_dboard = tx_dboard_ctor( -                dboard_base::ctor_args_t(name, interface, ID_NONE, tx_dboard_id) +                dboard_base::ctor_args_t(subdev, interface, ID_NONE, tx_dboard_id)              );              //create a tx proxy for this tx board -            _tx_dboards[name] = subdev_proxy::sptr( +            _tx_dboards[subdev] = subdev_proxy::sptr(                  new subdev_proxy(tx_dboard, subdev_proxy::TX_TYPE)              );          } @@ -245,7 +244,7 @@ dboard_manager_impl::dboard_manager_impl(  }  dboard_manager_impl::~dboard_manager_impl(void){ -    /* NOP */ +    set_nice_gpio_pins();  }  prop_names_t dboard_manager_impl::get_rx_subdev_names(void){ @@ -261,7 +260,7 @@ wax::obj dboard_manager_impl::get_rx_subdev(const std::string &subdev_name){          str(boost::format("Unknown rx subdev name %s") % subdev_name)      );      //get a link to the rx subdev proxy -    return wax::cast<subdev_proxy::sptr>(_rx_dboards[subdev_name])->get_link(); +    return _rx_dboards[subdev_name]->get_link();  }  wax::obj dboard_manager_impl::get_tx_subdev(const std::string &subdev_name){ @@ -269,5 +268,18 @@ wax::obj dboard_manager_impl::get_tx_subdev(const std::string &subdev_name){          str(boost::format("Unknown tx subdev name %s") % subdev_name)      );      //get a link to the tx subdev proxy -    return wax::cast<subdev_proxy::sptr>(_tx_dboards[subdev_name])->get_link(); +    return _tx_dboards[subdev_name]->get_link(); +} + +void dboard_manager_impl::set_nice_gpio_pins(void){ +    //std::cout << "Set nice GPIO pins" << std::endl; + +    _interface->set_gpio_ddr(dboard_interface::GPIO_RX_BANK, 0x0000, 0xffff); //all inputs +    _interface->set_gpio_ddr(dboard_interface::GPIO_TX_BANK, 0x0000, 0xffff); + +    _interface->write_gpio(dboard_interface::GPIO_RX_BANK, 0x0000, 0xffff); //all zeros +    _interface->write_gpio(dboard_interface::GPIO_TX_BANK, 0x0000, 0xffff); + +    _interface->set_atr_reg(dboard_interface::GPIO_RX_BANK, 0x0000, 0x0000, 0x0000); //software controlled +    _interface->set_atr_reg(dboard_interface::GPIO_TX_BANK, 0x0000, 0x0000, 0x0000);  } diff --git a/host/lib/usrp/usrp1e/dboard_impl.cpp b/host/lib/usrp/usrp1e/dboard_impl.cpp new file mode 100644 index 000000000..a2798dce3 --- /dev/null +++ b/host/lib/usrp/usrp1e/dboard_impl.cpp @@ -0,0 +1,76 @@ +// +// Copyright 2010 Ettus Research LLC +// +// This program 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. +// +// This program 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 this program.  If not, see <http://www.gnu.org/licenses/>. +// + +#include <boost/bind.hpp> +#include <uhd/utils.hpp> +#include "usrp1e_impl.hpp" + +using namespace uhd::usrp; + +/*********************************************************************** + * Dboard Initialization + **********************************************************************/ +void usrp1e_impl::dboard_init(void){ +    dboard_id_t rx_dboard_id = dboard_id::NONE; //TODO get these from the eeprom +    dboard_id_t tx_dboard_id = dboard_id::NONE; + +    //create a new dboard interface and manager +    dboard_interface::sptr dboard_interface( +        make_usrp1e_dboard_interface(this) +    ); +    _dboard_manager = dboard_manager::make( +        rx_dboard_id, tx_dboard_id, dboard_interface +    ); + +    //setup the dboard proxies +    _rx_dboard_proxy = wax_obj_proxy::make( +        boost::bind(&usrp1e_impl::rx_dboard_get, this, _1, _2), +        boost::bind(&usrp1e_impl::rx_dboard_set, this, _1, _2) +    ); +    _tx_dboard_proxy = wax_obj_proxy::make( +        boost::bind(&usrp1e_impl::tx_dboard_get, this, _1, _2), +        boost::bind(&usrp1e_impl::tx_dboard_set, this, _1, _2) +    ); +} + +/*********************************************************************** + * RX Dboard Get + **********************************************************************/ +void usrp1e_impl::rx_dboard_get(const wax::obj &, wax::obj &){ +     +} + +/*********************************************************************** + * RX Dboard Set + **********************************************************************/ +void usrp1e_impl::rx_dboard_set(const wax::obj &, const wax::obj &){ +     +} + +/*********************************************************************** + * TX Dboard Get + **********************************************************************/ +void usrp1e_impl::tx_dboard_get(const wax::obj &, wax::obj &){ +     +} + +/*********************************************************************** + * TX Dboard Set + **********************************************************************/ +void usrp1e_impl::tx_dboard_set(const wax::obj &, const wax::obj &){ +     +} diff --git a/host/lib/usrp/usrp1e/dboard_interface.cpp b/host/lib/usrp/usrp1e/dboard_interface.cpp new file mode 100644 index 000000000..ef91014ac --- /dev/null +++ b/host/lib/usrp/usrp1e/dboard_interface.cpp @@ -0,0 +1,189 @@ +// +// Copyright 2010 Ettus Research LLC +// +// This program 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. +// +// This program 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 this program.  If not, see <http://www.gnu.org/licenses/>. +// + +#include <uhd/utils.hpp> +#include <algorithm> //std::copy +#include "usrp1e_impl.hpp" +#include <linux/usrp1_e.h> + +using namespace uhd::usrp; + +class usrp1e_dboard_interface : public dboard_interface{ +public: +    usrp1e_dboard_interface(usrp1e_impl *impl); +    ~usrp1e_dboard_interface(void); + +    void write_aux_dac(unit_type_t, int, int); +    int read_aux_adc(unit_type_t, int); + +    void set_atr_reg(gpio_bank_t, boost::uint16_t, boost::uint16_t, boost::uint16_t); +    void set_gpio_ddr(gpio_bank_t, boost::uint16_t, boost::uint16_t); +    void write_gpio(gpio_bank_t, boost::uint16_t, boost::uint16_t); +    boost::uint16_t read_gpio(gpio_bank_t); + +    void write_i2c(int, const byte_vector_t &); +    byte_vector_t read_i2c(int, size_t); + +    double get_rx_clock_rate(void); +    double get_tx_clock_rate(void); + +private: +    byte_vector_t transact_spi( +        spi_dev_t dev, +        spi_latch_t latch, +        spi_push_t push, +        const byte_vector_t &buf, +        bool readback +    ); + +    usrp1e_impl *_impl; +}; + +/*********************************************************************** + * Make Function + **********************************************************************/ +dboard_interface::sptr make_usrp1e_dboard_interface(usrp1e_impl *impl){ +    return dboard_interface::sptr(new usrp1e_dboard_interface(impl)); +} + +/*********************************************************************** + * Structors + **********************************************************************/ +usrp1e_dboard_interface::usrp1e_dboard_interface(usrp1e_impl *impl){ +    _impl = impl; +} + +usrp1e_dboard_interface::~usrp1e_dboard_interface(void){ +    /* NOP */ +} + +/*********************************************************************** + * Clock Rates + **********************************************************************/ +double usrp1e_dboard_interface::get_rx_clock_rate(void){ +    throw std::runtime_error("not implemented"); +} + +double usrp1e_dboard_interface::get_tx_clock_rate(void){ +    throw std::runtime_error("not implemented"); +} + +/*********************************************************************** + * GPIO + **********************************************************************/ +void usrp1e_dboard_interface::set_gpio_ddr(gpio_bank_t bank, boost::uint16_t value, boost::uint16_t mask){ +    throw std::runtime_error("not implemented"); +} + +void usrp1e_dboard_interface::write_gpio(gpio_bank_t bank, boost::uint16_t value, boost::uint16_t mask){ +    throw std::runtime_error("not implemented"); +} + +boost::uint16_t usrp1e_dboard_interface::read_gpio(gpio_bank_t bank){ +    throw std::runtime_error("not implemented"); +} + +void usrp1e_dboard_interface::set_atr_reg(gpio_bank_t bank, boost::uint16_t tx_value, boost::uint16_t rx_value, boost::uint16_t mask){ +    throw std::runtime_error("not implemented"); +} + +/*********************************************************************** + * SPI + **********************************************************************/ +dboard_interface::byte_vector_t usrp1e_dboard_interface::transact_spi( +    spi_dev_t dev, +    spi_latch_t latch, +    spi_push_t push, +    const byte_vector_t &buf, +    bool readback +){ +    //load data struct +    usrp_e_spi data; +    data.readback = (readback)? UE_SPI_TXRX : UE_SPI_TXONLY; +    data.slave = (dev == SPI_RX_DEV)? UE_SPI_CTRL_RXNEG : UE_SPI_CTRL_TXNEG; +    data.length = buf.size() * 8; //bytes to bits +    boost::uint8_t *data_bytes = reinterpret_cast<boost::uint8_t*>(&data.data); + +    //load the data +    ASSERT_THROW(buf.size() <= sizeof(data.data)); +    std::copy(buf.begin(), buf.end(), data_bytes); + +    //load the flags +    data.flags = 0; +    data.flags |= (latch == SPI_LATCH_RISE)? UE_SPI_LATCH_RISE : UE_SPI_LATCH_FALL; +    data.flags |= (push ==  SPI_PUSH_RISE)?  UE_SPI_PUSH_RISE  : UE_SPI_PUSH_FALL; + +    //call the spi ioctl +    _impl->ioctl(USRP_E_SPI, &data); + +    //unload the data +    byte_vector_t ret(data.length/8); //bits to bytes +    ASSERT_THROW(ret.size() <= sizeof(data.data)); +    std::copy(data_bytes, data_bytes+ret.size(), ret.begin()); +    return ret; +} + +/*********************************************************************** + * I2C + **********************************************************************/ +static const size_t max_i2c_data_bytes = 10; + +void usrp1e_dboard_interface::write_i2c(int i2c_addr, const byte_vector_t &buf){ +    //allocate some memory for this transaction +    ASSERT_THROW(buf.size() <= max_i2c_data_bytes); +    boost::uint8_t mem[sizeof(usrp_e_i2c) + max_i2c_data_bytes]; + +    //load the data struct +    usrp_e_i2c &data = reinterpret_cast<usrp_e_i2c&>(mem); +    data.addr = i2c_addr; +    data.len = buf.size(); +    std::copy(buf.begin(), buf.end(), data.data); + +    //call the spi ioctl +    _impl->ioctl(USRP_E_I2C_WRITE, &data); +} + +dboard_interface::byte_vector_t usrp1e_dboard_interface::read_i2c(int i2c_addr, size_t num_bytes){ +    //allocate some memory for this transaction +    ASSERT_THROW(num_bytes <= max_i2c_data_bytes); +    boost::uint8_t mem[sizeof(usrp_e_i2c) + max_i2c_data_bytes]; + +    //load the data struct +    usrp_e_i2c &data = reinterpret_cast<usrp_e_i2c&>(mem); +    data.addr = i2c_addr; +    data.len = num_bytes; + +    //call the spi ioctl +    _impl->ioctl(USRP_E_I2C_READ, &data); + +    //unload the data +    byte_vector_t ret(data.len); +    ASSERT_THROW(ret.size() == num_bytes); +    std::copy(data.data, data.data+ret.size(), ret.begin()); +    return ret; +} + +/*********************************************************************** + * Aux DAX/ADC + **********************************************************************/ +void usrp1e_dboard_interface::write_aux_dac(dboard_interface::unit_type_t unit, int which, int value){ +    throw std::runtime_error("not implemented"); +} + +int usrp1e_dboard_interface::read_aux_adc(dboard_interface::unit_type_t unit, int which){ +    throw std::runtime_error("not implemented"); +} diff --git a/host/lib/usrp/usrp1e/dsp_impl.cpp b/host/lib/usrp/usrp1e/dsp_impl.cpp new file mode 100644 index 000000000..862b89184 --- /dev/null +++ b/host/lib/usrp/usrp1e/dsp_impl.cpp @@ -0,0 +1,70 @@ +// +// Copyright 2010 Ettus Research LLC +// +// This program 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. +// +// This program 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 this program.  If not, see <http://www.gnu.org/licenses/>. +// + +#include <boost/bind.hpp> +#include <uhd/utils.hpp> +#include "usrp1e_impl.hpp" + +using namespace uhd::usrp; + +/*********************************************************************** + * RX DDC Initialization + **********************************************************************/ +void usrp1e_impl::rx_ddc_init(void){ +    _rx_ddc_proxy = wax_obj_proxy::make( +        boost::bind(&usrp1e_impl::rx_ddc_get, this, _1, _2), +        boost::bind(&usrp1e_impl::rx_ddc_set, this, _1, _2) +    ); +} + +/*********************************************************************** + * RX DDC Get + **********************************************************************/ +void usrp1e_impl::rx_ddc_get(const wax::obj &, wax::obj &){ +     +} + +/*********************************************************************** + * RX DDC Set + **********************************************************************/ +void usrp1e_impl::rx_ddc_set(const wax::obj &, const wax::obj &){ +     +} + +/*********************************************************************** + * TX DUC Initialization + **********************************************************************/ +void usrp1e_impl::tx_duc_init(void){ +    _tx_duc_proxy = wax_obj_proxy::make( +        boost::bind(&usrp1e_impl::tx_duc_get, this, _1, _2), +        boost::bind(&usrp1e_impl::tx_duc_set, this, _1, _2) +    ); +} + +/*********************************************************************** + * TX DUC Get + **********************************************************************/ +void usrp1e_impl::tx_duc_get(const wax::obj &, wax::obj &){ +     +} + +/*********************************************************************** + * TX DUC Set + **********************************************************************/ +void usrp1e_impl::tx_duc_set(const wax::obj &, const wax::obj &){ +     +} diff --git a/host/lib/usrp/usrp1e/fpga-downloader.cc b/host/lib/usrp/usrp1e/fpga-downloader.cc new file mode 100644 index 000000000..f7c78b875 --- /dev/null +++ b/host/lib/usrp/usrp1e/fpga-downloader.cc @@ -0,0 +1,262 @@ +// +// Copyright 2010 Ettus Research LLC +// +// This program 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. +// +// This program 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 this program.  If not, see <http://www.gnu.org/licenses/>. +// + +#include <iostream> +#include <sstream> +#include <fstream> +#include <string> +#include <cstdlib> + +#include <fcntl.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <sys/ioctl.h> + +#include <linux/spi/spidev.h> + +/* + * Configuration connections + * + * CCK    - MCSPI1_CLK + * DIN    - MCSPI1_MOSI + * PROG_B - GPIO_175     - output (change mux) + * DONE   - GPIO_173     - input  (change mux) + * INIT_B - GPIO_114     - input  (change mux) + * +*/ + +const unsigned int PROG_B = 175; +const unsigned int DONE   = 173; +const unsigned int INIT_B = 114; + +//static std::string bit_file = "safe_u1e.bin"; + +const int BUF_SIZE = 4096; + +enum gpio_direction {IN, OUT}; + +class gpio { +	public: + +	gpio(unsigned int gpio_num, gpio_direction pin_direction); + +	bool get_value(); +	void set_value(bool state); + +	private: + +	std::stringstream base_path; +	std::fstream value_file;	 +}; + +class spidev { +	public: + +	spidev(std::string dev_name); +	~spidev(); + +	void send(char *wbuf, char *rbuf, unsigned int nbytes); + +	private: + +	int fd; + +}; + +gpio::gpio(unsigned int gpio_num, gpio_direction pin_direction) +{ +	std::fstream export_file; + +	export_file.open("/sys/class/gpio/export", std::ios::out); +	if (!export_file.is_open())  ///\todo Poor error handling +		std::cout << "Failed to open gpio export file." << std::endl; + +	export_file << gpio_num << std::endl; + +	base_path << "/sys/class/gpio/gpio" << gpio_num << std::flush; + +	std::fstream direction_file; +	std::string direction_file_name; + +	direction_file_name = base_path.str() + "/direction"; + +	direction_file.open(direction_file_name.c_str());  +	if (!direction_file.is_open()) +		std::cout << "Failed to open direction file." << std::endl; +	if (pin_direction == OUT) +		direction_file << "out" << std::endl; +	else +		direction_file << "in" << std::endl; + +	std::string value_file_name; + +	value_file_name = base_path.str() + "/value"; + +	value_file.open(value_file_name.c_str(), std::ios_base::in | std::ios_base::out); +	if (!value_file.is_open()) +		std::cout << "Failed to open value file." << std::endl; +} + +bool gpio::get_value() +{ + +	std::string val; + +	std::getline(value_file, val); +	value_file.seekg(0); + +	if (val == "0") +		return false; +	else if (val == "1") +		return true; +	else +		std::cout << "Data read from value file|" << val << "|" << std::endl; + +	return false; +} + +void gpio::set_value(bool state) +{ + +	if (state) +		value_file << "1" << std::endl; +	else +		value_file << "0" << std::endl; +} + +static void prepare_fpga_for_configuration(gpio &prog, gpio &)//init) +{ + +	prog.set_value(true); +	prog.set_value(false); +	prog.set_value(true); + +#if 0 +	bool ready_to_program(false); +	unsigned int count(0); +	do { +		ready_to_program = init.get_value(); +		count++; + +		sleep(1); +	} while (count < 10 && !ready_to_program); + +	if (count == 10) { +		std::cout << "FPGA not ready for programming." << std::endl; +		exit(-1); +	} +#endif +} + +spidev::spidev(std::string fname) +{ +	int ret; +	int mode = 0; +	int speed = 12000000; +	int bits = 8; + +	fd = open(fname.c_str(), O_RDWR); + +	ret = ioctl(fd, SPI_IOC_WR_MODE, &mode); +	ret = ioctl(fd, SPI_IOC_WR_MAX_SPEED_HZ, &speed); +	ret = ioctl(fd, SPI_IOC_WR_BITS_PER_WORD, &bits); +} +	 + +spidev::~spidev() +{ +	close(fd); +} + +void spidev::send(char *buf, char *rbuf, unsigned int nbytes) +{ +	int ret; + +	struct spi_ioc_transfer tr; +	tr.tx_buf = (unsigned long) buf; +	tr.rx_buf = (unsigned long) rbuf; +	tr.len = nbytes; +	tr.delay_usecs = 0; +	tr.speed_hz = 48000000; +	tr.bits_per_word = 8; + +	ret = ioctl(fd, SPI_IOC_MESSAGE(1), &tr);	 + +} + +static void send_file_to_fpga(const std::string &file_name, gpio &error, gpio &done) +{ +	std::ifstream bitstream; + +	std::cout << "File name - " << file_name.c_str() << std::endl; + +	bitstream.open(file_name.c_str(), std::ios::binary); +	if (!bitstream.is_open()) +		std::cout << "File " << file_name << " not opened succesfully." << std::endl; + +	spidev spi("/dev/spidev1.0"); +	char buf[BUF_SIZE]; +	char rbuf[BUF_SIZE]; + +	do { +		bitstream.read(buf, BUF_SIZE); +		spi.send(buf, rbuf, bitstream.gcount()); + +		if (error.get_value()) +			std::cout << "INIT_B went high, error occured." << std::endl; + +		if (!done.get_value()) +			std::cout << "Configuration complete." << std::endl; + +	} while (bitstream.gcount() == BUF_SIZE); +} + +/* +int main(int argc, char *argv[]) +{ + +	gpio gpio_prog_b(PROG_B, OUT); +	gpio gpio_init_b(INIT_B, IN); +	gpio gpio_done  (DONE,   IN); + +	if (argc == 2) +		bit_file = argv[1]; + +	std::cout << "FPGA config file: " << bit_file << std::endl; + +	prepare_fpga_for_configuration(gpio_prog_b, gpio_init_b); + +	std::cout << "Done = " << gpio_done.get_value() << std::endl; + +	send_file_to_fpga(bit_file, gpio_init_b, gpio_done); +} +*/ + +#include <uhd/usrp/usrp1e.hpp> +void uhd::usrp::usrp1e::load_fpga(const std::string &bin_file){ +	gpio gpio_prog_b(PROG_B, OUT); +	gpio gpio_init_b(INIT_B, IN); +	gpio gpio_done  (DONE,   IN); + +	std::cout << "FPGA config file: " << bin_file << std::endl; + +	prepare_fpga_for_configuration(gpio_prog_b, gpio_init_b); + +	std::cout << "Done = " << gpio_done.get_value() << std::endl; + +	send_file_to_fpga(bin_file, gpio_init_b, gpio_done); +} diff --git a/host/lib/usrp/dboard/dboards.hpp b/host/lib/usrp/usrp1e/mboard_impl.cpp index 79b90d593..b480f7616 100644 --- a/host/lib/usrp/dboard/dboards.hpp +++ b/host/lib/usrp/usrp1e/mboard_impl.cpp @@ -15,39 +15,32 @@  // along with this program.  If not, see <http://www.gnu.org/licenses/>.  // -#ifndef INCLUDED_LOCAL_DBOARDS_HPP -#define INCLUDED_LOCAL_DBOARDS_HPP - -#include <uhd/usrp/dboard_base.hpp> +#include <boost/bind.hpp> +#include <uhd/utils.hpp> +#include "usrp1e_impl.hpp"  using namespace uhd::usrp;  /*********************************************************************** - * The basic boards: + * Mboard Initialization   **********************************************************************/ -class basic_rx : public rx_dboard_base{ -public: -    static dboard_base::sptr make(ctor_args_t const& args){ -        return dboard_base::sptr(new basic_rx(args)); -    } -    basic_rx(ctor_args_t const& args); -    ~basic_rx(void); - -    void rx_get(const wax::obj &key, wax::obj &val); -    void rx_set(const wax::obj &key, const wax::obj &val); -}; - -class basic_tx : public tx_dboard_base{ -public: -    static dboard_base::sptr make(ctor_args_t const& args){ -        return dboard_base::sptr(new basic_tx(args)); -    } -    basic_tx(ctor_args_t const& args); -    ~basic_tx(void); - -    void tx_get(const wax::obj &key, wax::obj &val); -    void tx_set(const wax::obj &key, const wax::obj &val); +void usrp1e_impl::mboard_init(void){ +    _mboard_proxy = wax_obj_proxy::make( +        boost::bind(&usrp1e_impl::mboard_get, this, _1, _2), +        boost::bind(&usrp1e_impl::mboard_set, this, _1, _2) +    ); +} -}; +/*********************************************************************** + * Mboard Get + **********************************************************************/ +void usrp1e_impl::mboard_get(const wax::obj &, wax::obj &){ +     +} -#endif /* INCLUDED_LOCAL_DBOARDS_HPP */ +/*********************************************************************** + * Mboard Set + **********************************************************************/ +void usrp1e_impl::mboard_set(const wax::obj &, const wax::obj &){ +     +} diff --git a/host/lib/usrp/usrp1e/usrp1e_impl.cpp b/host/lib/usrp/usrp1e/usrp1e_impl.cpp new file mode 100644 index 000000000..8230cc8e4 --- /dev/null +++ b/host/lib/usrp/usrp1e/usrp1e_impl.cpp @@ -0,0 +1,178 @@ +// +// Copyright 2010 Ettus Research LLC +// +// This program 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. +// +// This program 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 this program.  If not, see <http://www.gnu.org/licenses/>. +// + +#include <uhd/utils.hpp> +#include <boost/format.hpp> +#include <boost/filesystem.hpp> +#include "usrp1e_impl.hpp" +#include <fcntl.h> //open +#include <sys/ioctl.h> //ioctl + +using namespace uhd; +using namespace uhd::usrp; +namespace fs = boost::filesystem; + +STATIC_BLOCK(register_usrp1e_device){ +    device::register_device(&usrp1e::discover, &usrp1e::make); +} + +/*********************************************************************** + * Helper Functions + **********************************************************************/ +static std::string abs_path(const std::string &file_path){ +    return fs::system_complete(fs::path(file_path)).file_string(); +} + +/*********************************************************************** + * Discovery + **********************************************************************/ +device_addrs_t usrp1e::discover(const device_addr_t &device_addr){ +    device_addrs_t usrp1e_addrs; + +    //if a node was provided, use it and only it +    if (device_addr.has_key("node")){ +        if (not fs::exists(device_addr["node"])) return usrp1e_addrs; +        device_addr_t new_addr; +        new_addr["name"] = "USRP1E"; +        new_addr["node"] = abs_path(device_addr["node"]); +        usrp1e_addrs.push_back(new_addr); +    } + +    //otherwise look for a few nodes at small indexes +    else{ +        for(size_t i = 0; i < 5; i++){ +            std::string node = str(boost::format("/dev/usrp1_e%d") % i); +            if (not fs::exists(node)) continue; +            device_addr_t new_addr; +            new_addr["name"] = "USRP1E"; +            new_addr["node"] = abs_path(node); +            usrp1e_addrs.push_back(new_addr); +        } +    } + +    return usrp1e_addrs; +} + +/*********************************************************************** + * Make + **********************************************************************/ +device::sptr usrp1e::make(const device_addr_t &device_addr){ +    return sptr(new usrp1e_impl(device_addr["node"])); +} + +/*********************************************************************** + * Structors + **********************************************************************/ +usrp1e_impl::usrp1e_impl(const std::string &node){ +    //open the device node and check file descriptor +    if ((_node_fd = ::open(node.c_str(), O_RDWR)) < 0){ +        throw std::runtime_error(str( +            boost::format("Failed to open %s") % node +        )); +    } + +    //initialize the mboard +    mboard_init(); + +    //initialize the dboards +    dboard_init(); + +    //initialize the dsps +    rx_ddc_init(); +    tx_duc_init(); +} + +usrp1e_impl::~usrp1e_impl(void){ +    //close the device node file descriptor +    ::close(_node_fd); +} + +/*********************************************************************** + * Misc Methods + **********************************************************************/ +void usrp1e_impl::ioctl(int request, void *mem){ +    if (::ioctl(_node_fd, request, mem) < 0){ +        throw std::runtime_error(str( +            boost::format("ioctl failed with request %d") % request +        )); +    } +} + +/*********************************************************************** + * Device Get + **********************************************************************/ +void usrp1e_impl::get(const wax::obj &key_, wax::obj &val){ +    wax::obj key; std::string name; +    boost::tie(key, name) = extract_named_prop(key_); + +    //handle the get request conditioned on the key +    switch(key.as<device_prop_t>()){ +    case DEVICE_PROP_NAME: +        val = std::string("usrp1e device"); +        return; + +    case DEVICE_PROP_MBOARD: +        ASSERT_THROW(name == ""); +        val = _mboard_proxy->get_link(); +        return; + +    case DEVICE_PROP_MBOARD_NAMES: +        val = prop_names_t(1, ""); //vector of size 1 with empty string +        return; + +    case DEVICE_PROP_MAX_RX_SAMPLES: +        val = size_t(_max_num_samples); +        return; + +    case DEVICE_PROP_MAX_TX_SAMPLES: +        val = size_t(_max_num_samples); +        return; + +    } +} + +/*********************************************************************** + * Device Set + **********************************************************************/ +void usrp1e_impl::set(const wax::obj &, const wax::obj &){ +    throw std::runtime_error("Cannot set in usrp1e device"); +} + +/*********************************************************************** + * Device IO (TODO) + **********************************************************************/ +size_t usrp1e_impl::send( +    const boost::asio::const_buffer &, +    const uhd::tx_metadata_t &, +    const std::string &type +){ +    if (type != "16sc"){ +        throw std::runtime_error(str(boost::format("usrp1e send: cannot handle type \"%s\"") % type)); +    } +    return 0; +} + +size_t usrp1e_impl::recv( +    const boost::asio::mutable_buffer &, +    uhd::rx_metadata_t &, +    const std::string &type +){ +    if (type != "16sc"){ +        throw std::runtime_error(str(boost::format("usrp1e recv: cannot handle type \"%s\"") % type)); +    } +    return 0; +} diff --git a/host/lib/usrp/usrp1e/usrp1e_impl.hpp b/host/lib/usrp/usrp1e/usrp1e_impl.hpp new file mode 100644 index 000000000..c199a0465 --- /dev/null +++ b/host/lib/usrp/usrp1e/usrp1e_impl.hpp @@ -0,0 +1,135 @@ +// +// Copyright 2010 Ettus Research LLC +// +// This program 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. +// +// This program 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 this program.  If not, see <http://www.gnu.org/licenses/>. +// + +#include <uhd/usrp/usrp1e.hpp> +#include <uhd/usrp/dboard_manager.hpp> + +#ifndef INCLUDED_USRP1E_IMPL_HPP +#define INCLUDED_USRP1E_IMPL_HPP + +class usrp1e_impl; // dummy class declaration + +/*! + * Make a usrp1e dboard interface. + * \param impl a pointer to the usrp1e impl object + * \return a sptr to a new dboard interface + */ +uhd::usrp::dboard_interface::sptr make_usrp1e_dboard_interface(usrp1e_impl *impl); + +/*! + * Simple wax obj proxy class: + * Provides a wax obj interface for a set and a get function. + * This allows us to create nested properties structures + * while maintaining flattened code within the implementation. + */ +class wax_obj_proxy : public wax::obj{ +public: +    typedef boost::function<void(const wax::obj &, wax::obj &)>       get_t; +    typedef boost::function<void(const wax::obj &, const wax::obj &)> set_t; +    typedef boost::shared_ptr<wax_obj_proxy> sptr; + +    static sptr make(const get_t &get, const set_t &set){ +        return sptr(new wax_obj_proxy(get, set)); +    } + +    ~wax_obj_proxy(void){ +        /* NOP */ +    } + +private: +    get_t _get; +    set_t _set; + +    wax_obj_proxy(const get_t &get, const set_t &set){ +        _get = get; +        _set = set; +    }; + +    void get(const wax::obj &key, wax::obj &val){ +        return _get(key, val); +    } + +    void set(const wax::obj &key, const wax::obj &val){ +        return _set(key, val); +    } +}; + +/*! + * USRP1E implementation guts: + * The implementation details are encapsulated here. + * Handles properties on the mboard, dboard, dsps... + */ +class usrp1e_impl : public uhd::device{ +public: +    //structors +    usrp1e_impl(const std::string &node); +    ~usrp1e_impl(void); + +    //the io interface +    size_t send(const boost::asio::const_buffer &, const uhd::tx_metadata_t &, const std::string &); +    size_t recv(const boost::asio::mutable_buffer &, uhd::rx_metadata_t &, const std::string &); + +    /*! +     * Perform an ioctl call on the device node file descriptor. +     * This will throw when the internal ioctl call fails. +     * \param request the control word +     * \param mem pointer to some memory +     */ +    void ioctl(int request, void *mem); + +private: +    static const size_t _max_num_samples = 2048/sizeof(boost::uint32_t); +    int _node_fd; + +    //device functions and settings +    void get(const wax::obj &, wax::obj &); +    void set(const wax::obj &, const wax::obj &); + +    //mboard functions and settings +    void mboard_init(void); +    void mboard_get(const wax::obj &, wax::obj &); +    void mboard_set(const wax::obj &, const wax::obj &); +    wax_obj_proxy::sptr _mboard_proxy; + +    //xx dboard functions and settings +    void dboard_init(void); +    uhd::usrp::dboard_manager::sptr _dboard_manager; + +    //rx dboard functions and settings +    void rx_dboard_get(const wax::obj &, wax::obj &); +    void rx_dboard_set(const wax::obj &, const wax::obj &); +    wax_obj_proxy::sptr _rx_dboard_proxy; + +    //tx dboard functions and settings +    void tx_dboard_get(const wax::obj &, wax::obj &); +    void tx_dboard_set(const wax::obj &, const wax::obj &); +    wax_obj_proxy::sptr _tx_dboard_proxy; + +    //rx ddc functions and settings +    void rx_ddc_init(void); +    void rx_ddc_get(const wax::obj &, wax::obj &); +    void rx_ddc_set(const wax::obj &, const wax::obj &); +    wax_obj_proxy::sptr _rx_ddc_proxy; + +    //tx duc functions and settings +    void tx_duc_init(void); +    void tx_duc_get(const wax::obj &, wax::obj &); +    void tx_duc_set(const wax::obj &, const wax::obj &); +    wax_obj_proxy::sptr _tx_duc_proxy; +}; + +#endif /* INCLUDED_USRP1E_IMPL_HPP */ diff --git a/host/lib/usrp/dboard_id.cpp b/host/lib/usrp/usrp1e/usrp1e_none.cpp index d2ef7cd7d..ac0b12a75 100644 --- a/host/lib/usrp/dboard_id.cpp +++ b/host/lib/usrp/usrp1e/usrp1e_none.cpp @@ -15,20 +15,24 @@  // along with this program.  If not, see <http://www.gnu.org/licenses/>.  // -#include <uhd/usrp/dboard_id.hpp> -#include <boost/format.hpp> -#include <uhd/dict.hpp> +#include <uhd/usrp/usrp1e.hpp> +using namespace uhd;  using namespace uhd::usrp; -std::string dboard_id::to_string(const dboard_id_t &id){ -    //map the dboard ids to string representations -    uhd::dict<dboard_id_t, std::string> id_to_str; -    id_to_str[ID_NONE]     = "none"; -    id_to_str[ID_BASIC_TX] = "basic tx"; -    id_to_str[ID_BASIC_RX] = "basic rx"; +/*! + * This file defines the usrp1e discover and make functions + * when the required kernel module headers are not present. + */ -    //get the string representation -    std::string name = (id_to_str.has_key(id))? id_to_str[id] : "unknown"; -    return str(boost::format("%s (0x%.4x)") % name % id); +device_addrs_t usrp1e::discover(const device_addr_t &){ +    return device_addrs_t(); //return empty list +} + +device::sptr usrp1e::make(const device_addr_t &){ +    throw std::runtime_error("this build has no usrp1e support"); +} + +void usrp1e::load_fpga(const std::string &){ +    throw std::runtime_error("this build has no usrp1e support");  } diff --git a/host/lib/usrp/usrp2/dboard_impl.cpp b/host/lib/usrp/usrp2/dboard_impl.cpp index 32c64f541..66e02d469 100644 --- a/host/lib/usrp/usrp2/dboard_impl.cpp +++ b/host/lib/usrp/usrp2/dboard_impl.cpp @@ -16,8 +16,8 @@  //  #include <uhd/utils.hpp> +#include <boost/format.hpp>  #include "usrp2_impl.hpp" -#include "dboard_interface.hpp"  using namespace uhd;  using namespace uhd::usrp; @@ -31,35 +31,68 @@ void usrp2_impl::dboard_init(void){      out_data.id = htonl(USRP2_CTRL_ID_GIVE_ME_YOUR_DBOARD_IDS_BRO);      usrp2_ctrl_data_t in_data = ctrl_send_and_recv(out_data);      ASSERT_THROW(htonl(in_data.id) == USRP2_CTRL_ID_THESE_ARE_MY_DBOARD_IDS_DUDE); -    std::cout << boost::format("rx id 0x%.2x, tx id 0x%.2x") -        % ntohs(in_data.data.dboard_ids.rx_id) -        % ntohs(in_data.data.dboard_ids.tx_id) << std::endl; -    //extract the dboard ids an convert them to enums -    dboard_id_t rx_dboard_id = static_cast<dboard_id_t>( -        ntohs(in_data.data.dboard_ids.rx_id) -    ); -    dboard_id_t tx_dboard_id = static_cast<dboard_id_t>( -        ntohs(in_data.data.dboard_ids.tx_id) -    ); +    //extract the dboard ids an convert them +    dboard_id_t rx_dboard_id = ntohs(in_data.data.dboard_ids.rx_id); +    dboard_id_t tx_dboard_id = ntohs(in_data.data.dboard_ids.tx_id);      //create a new dboard interface and manager      dboard_interface::sptr _dboard_interface( -        new usrp2_dboard_interface(this) +        make_usrp2_dboard_interface(this)      ); -    dboard_manager::sptr _dboard_manager = dboard_manager::make( +    _dboard_manager = dboard_manager::make(          rx_dboard_id, tx_dboard_id, _dboard_interface      );      //load dboards -    _rx_dboards[""] = wax_obj_proxy( +    _rx_dboards[""] = wax_obj_proxy::make(          boost::bind(&usrp2_impl::rx_dboard_get, this, _1, _2),          boost::bind(&usrp2_impl::rx_dboard_set, this, _1, _2)      ); -    _tx_dboards[""] = wax_obj_proxy( +    _tx_dboards[""] = wax_obj_proxy::make(          boost::bind(&usrp2_impl::tx_dboard_get, this, _1, _2),          boost::bind(&usrp2_impl::tx_dboard_set, this, _1, _2)      ); + +    //init the subdevs in use (use the first subdevice) +    _rx_subdevs_in_use = prop_names_t(1, _dboard_manager->get_rx_subdev_names().at(0)); +    _tx_subdevs_in_use = prop_names_t(1, _dboard_manager->get_tx_subdev_names().at(0)); +    update_mux_config(); +} + +void usrp2_impl::update_mux_config(void){ +    //calculate the rx mux +    boost::uint32_t rx_mux = 0; +    ASSERT_THROW(_rx_subdevs_in_use.size() == 1); +    wax::obj rx_subdev = _dboard_manager->get_rx_subdev(_rx_subdevs_in_use.at(0)); +    std::cout << "Using: " << rx_subdev[SUBDEV_PROP_NAME].as<std::string>() << std::endl; +    if (rx_subdev[SUBDEV_PROP_QUADRATURE].as<bool>()){ +        rx_mux = (0x01 << 2) | (0x00 << 0); //Q=ADC1, I=ADC0 +    }else{ +        rx_mux = 0x00; //ADC0 +    } +    if (rx_subdev[SUBDEV_PROP_IQ_SWAPPED].as<bool>()){ +        rx_mux = (((rx_mux >> 0) & 0x3) << 2) | (((rx_mux >> 2) & 0x3) << 0); +    } + +    //calculate the tx mux +    boost::uint32_t tx_mux = 0x10; +    ASSERT_THROW(_tx_subdevs_in_use.size() == 1); +    wax::obj tx_subdev = _dboard_manager->get_tx_subdev(_tx_subdevs_in_use.at(0)); +    std::cout << "Using: " << tx_subdev[SUBDEV_PROP_NAME].as<std::string>() << std::endl; +    if (tx_subdev[SUBDEV_PROP_IQ_SWAPPED].as<bool>()){ +        tx_mux = (((tx_mux >> 0) & 0x1) << 1) | (((tx_mux >> 1) & 0x1) << 0); +    } + +    //setup the out data +    usrp2_ctrl_data_t out_data; +    out_data.id = htonl(USRP2_CTRL_ID_UPDATE_THOSE_MUX_SETTINGS_BRO); +    out_data.data.mux_args.rx_mux = htonl(rx_mux); +    out_data.data.mux_args.tx_mux = htonl(tx_mux); + +    //send and recv +    usrp2_ctrl_data_t in_data = ctrl_send_and_recv(out_data); +    ASSERT_THROW(htonl(in_data.id) == USRP2_CTRL_ID_UPDATED_THE_MUX_SETTINGS_DUDE);  }  /*********************************************************************** @@ -70,7 +103,7 @@ void usrp2_impl::rx_dboard_get(const wax::obj &key_, wax::obj &val){      boost::tie(key, name) = extract_named_prop(key_);      //handle the get request conditioned on the key -    switch(wax::cast<dboard_prop_t>(key)){ +    switch(key.as<dboard_prop_t>()){      case DBOARD_PROP_NAME:          val = std::string("usrp2 dboard (rx unit)");          return; @@ -83,12 +116,22 @@ void usrp2_impl::rx_dboard_get(const wax::obj &key_, wax::obj &val){          val = _dboard_manager->get_rx_subdev_names();          return; -    case DBOARD_PROP_CODEC: -        throw std::runtime_error("unhandled prop in usrp2 dboard"); +    case DBOARD_PROP_USED_SUBDEVS: +        val = _rx_subdevs_in_use; +        return; + +    //case DBOARD_PROP_CODEC: +    //    throw std::runtime_error("unhandled prop in usrp2 dboard");      }  } -void usrp2_impl::rx_dboard_set(const wax::obj &, const wax::obj &){ +void usrp2_impl::rx_dboard_set(const wax::obj &key, const wax::obj &val){ +    if (key.as<dboard_prop_t>() == DBOARD_PROP_USED_SUBDEVS){ +        _rx_subdevs_in_use = val.as<prop_names_t>(); +        update_mux_config(); //if the val is bad, this will throw +        return; +    } +      throw std::runtime_error("Cannot set on usrp2 dboard");  } @@ -100,7 +143,7 @@ void usrp2_impl::tx_dboard_get(const wax::obj &key_, wax::obj &val){      boost::tie(key, name) = extract_named_prop(key_);      //handle the get request conditioned on the key -    switch(wax::cast<dboard_prop_t>(key)){ +    switch(key.as<dboard_prop_t>()){      case DBOARD_PROP_NAME:          val = std::string("usrp2 dboard (tx unit)");          return; @@ -113,11 +156,21 @@ void usrp2_impl::tx_dboard_get(const wax::obj &key_, wax::obj &val){          val = _dboard_manager->get_tx_subdev_names();          return; -    case DBOARD_PROP_CODEC: -        throw std::runtime_error("unhandled prop in usrp2 dboard"); +    case DBOARD_PROP_USED_SUBDEVS: +        val = _tx_subdevs_in_use; +        return; + +    //case DBOARD_PROP_CODEC: +    //    throw std::runtime_error("unhandled prop in usrp2 dboard");      }  } -void usrp2_impl::tx_dboard_set(const wax::obj &, const wax::obj &){ +void usrp2_impl::tx_dboard_set(const wax::obj &key, const wax::obj &val){ +    if (key.as<dboard_prop_t>() == DBOARD_PROP_USED_SUBDEVS){ +        _tx_subdevs_in_use = val.as<prop_names_t>(); +        update_mux_config(); //if the val is bad, this will throw +        return; +    } +      throw std::runtime_error("Cannot set on usrp2 dboard");  } diff --git a/host/lib/usrp/usrp2/dboard_interface.cpp b/host/lib/usrp/usrp2/dboard_interface.cpp index f12b101f3..d20465147 100644 --- a/host/lib/usrp/usrp2/dboard_interface.cpp +++ b/host/lib/usrp/usrp2/dboard_interface.cpp @@ -16,11 +16,48 @@  //  #include <uhd/utils.hpp> -#include "dboard_interface.hpp"  #include "usrp2_impl.hpp"  using namespace uhd::usrp; +class usrp2_dboard_interface : public dboard_interface{ +public: +    usrp2_dboard_interface(usrp2_impl *impl); +    ~usrp2_dboard_interface(void); + +    void write_aux_dac(unit_type_t, int, int); +    int read_aux_adc(unit_type_t, int); + +    void set_atr_reg(gpio_bank_t, boost::uint16_t, boost::uint16_t, boost::uint16_t); +    void set_gpio_ddr(gpio_bank_t, boost::uint16_t, boost::uint16_t); +    void write_gpio(gpio_bank_t, boost::uint16_t, boost::uint16_t); +    boost::uint16_t read_gpio(gpio_bank_t); + +    void write_i2c(int, const byte_vector_t &); +    byte_vector_t read_i2c(int, size_t); + +    double get_rx_clock_rate(void); +    double get_tx_clock_rate(void); + +private: +    byte_vector_t transact_spi( +        spi_dev_t dev, +        spi_latch_t latch, +        spi_push_t push, +        const byte_vector_t &buf, +        bool readback +    ); + +    usrp2_impl *_impl; +}; + +/*********************************************************************** + * Make Function + **********************************************************************/ +dboard_interface::sptr make_usrp2_dboard_interface(usrp2_impl *impl){ +    return dboard_interface::sptr(new usrp2_dboard_interface(impl)); +} +  /***********************************************************************   * Structors   **********************************************************************/ @@ -52,7 +89,7 @@ double usrp2_dboard_interface::get_tx_clock_rate(void){   * \param bank the dboard interface gpio bank enum   * \return an over the wire representation   */ -static uint8_t gpio_bank_to_otw(dboard_interface::gpio_bank_t bank){ +static boost::uint8_t gpio_bank_to_otw(dboard_interface::gpio_bank_t bank){      switch(bank){      case uhd::usrp::dboard_interface::GPIO_TX_BANK: return USRP2_DIR_TX;      case uhd::usrp::dboard_interface::GPIO_RX_BANK: return USRP2_DIR_RX; @@ -60,7 +97,7 @@ static uint8_t gpio_bank_to_otw(dboard_interface::gpio_bank_t bank){      throw std::invalid_argument("unknown gpio bank type");  } -void usrp2_dboard_interface::set_gpio_ddr(gpio_bank_t bank, uint16_t value, uint16_t mask){ +void usrp2_dboard_interface::set_gpio_ddr(gpio_bank_t bank, boost::uint16_t value, boost::uint16_t mask){      //setup the out data      usrp2_ctrl_data_t out_data;      out_data.id = htonl(USRP2_CTRL_ID_USE_THESE_GPIO_DDR_SETTINGS_BRO); @@ -73,7 +110,7 @@ void usrp2_dboard_interface::set_gpio_ddr(gpio_bank_t bank, uint16_t value, uint      ASSERT_THROW(htonl(in_data.id) == USRP2_CTRL_ID_GOT_THE_GPIO_DDR_SETTINGS_DUDE);  } -void usrp2_dboard_interface::write_gpio(gpio_bank_t bank, uint16_t value, uint16_t mask){ +void usrp2_dboard_interface::write_gpio(gpio_bank_t bank, boost::uint16_t value, boost::uint16_t mask){      //setup the out data      usrp2_ctrl_data_t out_data;      out_data.id = htonl(USRP2_CTRL_ID_SET_YOUR_GPIO_PIN_OUTS_BRO); @@ -86,7 +123,7 @@ void usrp2_dboard_interface::write_gpio(gpio_bank_t bank, uint16_t value, uint16      ASSERT_THROW(htonl(in_data.id) == USRP2_CTRL_ID_I_SET_THE_GPIO_PIN_OUTS_DUDE);  } -uint16_t usrp2_dboard_interface::read_gpio(gpio_bank_t bank){ +boost::uint16_t usrp2_dboard_interface::read_gpio(gpio_bank_t bank){      //setup the out data      usrp2_ctrl_data_t out_data;      out_data.id = htonl(USRP2_CTRL_ID_GIVE_ME_YOUR_GPIO_PIN_VALS_BRO); @@ -98,7 +135,7 @@ uint16_t usrp2_dboard_interface::read_gpio(gpio_bank_t bank){      return ntohs(in_data.data.gpio_config.value);  } -void usrp2_dboard_interface::set_atr_reg(gpio_bank_t bank, uint16_t tx_value, uint16_t rx_value, uint16_t mask){ +void usrp2_dboard_interface::set_atr_reg(gpio_bank_t bank, boost::uint16_t tx_value, boost::uint16_t rx_value, boost::uint16_t mask){      //setup the out data      usrp2_ctrl_data_t out_data;      out_data.id = htonl(USRP2_CTRL_ID_USE_THESE_ATR_SETTINGS_BRO); @@ -121,7 +158,7 @@ void usrp2_dboard_interface::set_atr_reg(gpio_bank_t bank, uint16_t tx_value, ui   * \param dev the dboard interface spi dev enum   * \return an over the wire representation   */ -static uint8_t spi_dev_to_otw(dboard_interface::spi_dev_t dev){ +static boost::uint8_t spi_dev_to_otw(dboard_interface::spi_dev_t dev){      switch(dev){      case uhd::usrp::dboard_interface::SPI_TX_DEV: return USRP2_DIR_TX;      case uhd::usrp::dboard_interface::SPI_RX_DEV: return USRP2_DIR_RX; @@ -135,7 +172,7 @@ static uint8_t spi_dev_to_otw(dboard_interface::spi_dev_t dev){   * \param latch the dboard interface spi latch enum   * \return an over the wire representation   */ -static uint8_t spi_latch_to_otw(dboard_interface::spi_latch_t latch){ +static boost::uint8_t spi_latch_to_otw(dboard_interface::spi_latch_t latch){      switch(latch){      case uhd::usrp::dboard_interface::SPI_LATCH_RISE: return USRP2_CLK_EDGE_RISE;      case uhd::usrp::dboard_interface::SPI_LATCH_FALL: return USRP2_CLK_EDGE_FALL; @@ -149,7 +186,7 @@ static uint8_t spi_latch_to_otw(dboard_interface::spi_latch_t latch){   * \param push the dboard interface spi push enum   * \return an over the wire representation   */ -static uint8_t spi_push_to_otw(dboard_interface::spi_push_t push){ +static boost::uint8_t spi_push_to_otw(dboard_interface::spi_push_t push){      switch(push){      case uhd::usrp::dboard_interface::SPI_PUSH_RISE: return USRP2_CLK_EDGE_RISE;      case uhd::usrp::dboard_interface::SPI_PUSH_FALL: return USRP2_CLK_EDGE_FALL; @@ -249,7 +286,7 @@ dboard_interface::byte_vector_t usrp2_dboard_interface::read_i2c(int i2c_addr, s   * \param unit the dboard interface unit type enum   * \return an over the wire representation   */ -static uint8_t spi_dev_to_otw(dboard_interface::unit_type_t unit){ +static boost::uint8_t spi_dev_to_otw(dboard_interface::unit_type_t unit){      switch(unit){      case uhd::usrp::dboard_interface::UNIT_TYPE_TX: return USRP2_DIR_TX;      case uhd::usrp::dboard_interface::UNIT_TYPE_RX: return USRP2_DIR_RX; @@ -263,7 +300,7 @@ void usrp2_dboard_interface::write_aux_dac(dboard_interface::unit_type_t unit, i      out_data.id = htonl(USRP2_CTRL_ID_WRITE_THIS_TO_THE_AUX_DAC_BRO);      out_data.data.aux_args.dir = spi_dev_to_otw(unit);      out_data.data.aux_args.which = which; -    out_data.data.aux_args.dir = htonl(value); +    out_data.data.aux_args.value = htonl(value);      //send and recv      usrp2_ctrl_data_t in_data = _impl->ctrl_send_and_recv(out_data); diff --git a/host/lib/usrp/usrp2/dboard_interface.hpp b/host/lib/usrp/usrp2/dboard_interface.hpp deleted file mode 100644 index a06359e5e..000000000 --- a/host/lib/usrp/usrp2/dboard_interface.hpp +++ /dev/null @@ -1,63 +0,0 @@ -// -// Copyright 2010 Ettus Research LLC -// -// This program 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. -// -// This program 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 this program.  If not, see <http://www.gnu.org/licenses/>. -// - -#include <uhd/usrp/dboard_interface.hpp> - -#ifndef INCLUDED_DBOARD_INTERFACE_HPP -#define INCLUDED_DBOARD_INTERFACE_HPP - -class usrp2_impl; //dummy class declaration - -class usrp2_dboard_interface : public uhd::usrp::dboard_interface{ -public: -    usrp2_dboard_interface(usrp2_impl *impl); - -    ~usrp2_dboard_interface(void); - -    void write_aux_dac(unit_type_t, int, int); - -    int read_aux_adc(unit_type_t, int); - -    void set_atr_reg(gpio_bank_t, uint16_t, uint16_t, uint16_t); - -    void set_gpio_ddr(gpio_bank_t, uint16_t, uint16_t); - -    void write_gpio(gpio_bank_t, uint16_t, uint16_t); - -    uint16_t read_gpio(gpio_bank_t); - -    void write_i2c(int, const byte_vector_t &); - -    byte_vector_t read_i2c(int, size_t); - -    double get_rx_clock_rate(void); - -    double get_tx_clock_rate(void); - -private: -    byte_vector_t transact_spi( -        spi_dev_t dev, -        spi_latch_t latch, -        spi_push_t push, -        const byte_vector_t &buf, -        bool readback -    ); - -    usrp2_impl *_impl; -}; - -#endif /* INCLUDED_DBOARD_INTERFACE_HPP */ diff --git a/host/lib/usrp/usrp2/dsp_impl.cpp b/host/lib/usrp/usrp2/dsp_impl.cpp index 22c00d99a..34cce0afb 100644 --- a/host/lib/usrp/usrp2/dsp_impl.cpp +++ b/host/lib/usrp/usrp2/dsp_impl.cpp @@ -16,19 +16,30 @@  //  #include <uhd/utils.hpp> +#include <boost/format.hpp>  #include <boost/assign/list_of.hpp> +#include <boost/math/special_functions/round.hpp>  #include "usrp2_impl.hpp"  using namespace uhd; +static const size_t default_decim = 16; +static const size_t default_interp = 16; + +#define rint boost::math::iround + +template <class T> T log2(T num){ +    return std::log(num)/std::log(T(2)); +} +  /***********************************************************************   * DDC Helper Methods   **********************************************************************/ -static uint32_t calculate_freq_word_and_update_actual_freq(freq_t &freq, freq_t clock_freq){ -    double scale_factor = pow(2.0, 32); +static boost::uint32_t calculate_freq_word_and_update_actual_freq(freq_t &freq, freq_t clock_freq){ +    double scale_factor = std::pow(2.0, 32);      //calculate the freq register word -    uint32_t freq_word = rint((freq / clock_freq) * scale_factor); +    boost::uint32_t freq_word = rint((freq / clock_freq) * scale_factor);      //update the actual frequency      freq = (double(freq_word) / scale_factor) * clock_freq; @@ -36,15 +47,19 @@ static uint32_t calculate_freq_word_and_update_actual_freq(freq_t &freq, freq_t      return freq_word;  } +static boost::uint32_t calculate_iq_scale_word(boost::int16_t i, boost::int16_t q){ +    return (boost::uint16_t(i) << 16) | (boost::uint16_t(q) << 0); +} +  void usrp2_impl::init_ddc_config(void){      //create the ddc in the rx dsp dict -    _rx_dsps["ddc0"] = wax_obj_proxy( +    _rx_dsps["ddc0"] = wax_obj_proxy::make(          boost::bind(&usrp2_impl::ddc_get, this, _1, _2),          boost::bind(&usrp2_impl::ddc_set, this, _1, _2)      );      //initial config and update -    _ddc_decim = 16; +    _ddc_decim = default_decim;      _ddc_freq = 0;      update_ddc_config(); @@ -61,6 +76,10 @@ void usrp2_impl::update_ddc_config(void){          calculate_freq_word_and_update_actual_freq(_ddc_freq, get_master_clock_freq())      );      out_data.data.ddc_args.decim = htonl(_ddc_decim); +    static const boost::int16_t default_rx_scale_iq = 1024; +    out_data.data.ddc_args.scale_iq = htonl( +        calculate_iq_scale_word(default_rx_scale_iq, default_rx_scale_iq) +    );      //send and recv      usrp2_ctrl_data_t in_data = ctrl_send_and_recv(out_data); @@ -74,6 +93,7 @@ void usrp2_impl::update_ddc_enabled(void){      out_data.data.streaming.enabled = (_ddc_enabled)? 1 : 0;      out_data.data.streaming.secs =  htonl(_ddc_stream_at.secs);      out_data.data.streaming.ticks = htonl(_ddc_stream_at.ticks); +    out_data.data.streaming.samples = htonl(_max_rx_samples_per_packet);      //send and recv      usrp2_ctrl_data_t in_data = ctrl_send_and_recv(out_data); @@ -89,7 +109,7 @@ void usrp2_impl::update_ddc_enabled(void){  void usrp2_impl::ddc_get(const wax::obj &key, wax::obj &val){      //handle the case where the key is an expected dsp property      if (key.type() == typeid(dsp_prop_t)){ -        switch(wax::cast<dsp_prop_t>(key)){ +        switch(key.as<dsp_prop_t>()){          case DSP_PROP_NAME:              val = std::string("usrp2 ddc0");              return; @@ -98,7 +118,7 @@ void usrp2_impl::ddc_get(const wax::obj &key, wax::obj &val){                  prop_names_t others = boost::assign::list_of                      ("rate")                      ("decim") -                    ("decim_rates") +                    ("decims")                      ("freq")                      ("enabled")                      ("stream_at") @@ -110,7 +130,7 @@ void usrp2_impl::ddc_get(const wax::obj &key, wax::obj &val){      }      //handle string-based properties specific to this dsp -    std::string key_name = wax::cast<std::string>(key); +    std::string key_name = key.as<std::string>();      if (key_name == "rate"){          val = get_master_clock_freq();          return; @@ -119,7 +139,7 @@ void usrp2_impl::ddc_get(const wax::obj &key, wax::obj &val){          val = _ddc_decim;          return;      } -    else if (key_name == "decim_rates"){ +    else if (key_name == "decims"){          val = _allowed_decim_and_interp_rates;          return;      } @@ -139,20 +159,19 @@ void usrp2_impl::ddc_get(const wax::obj &key, wax::obj &val){  void usrp2_impl::ddc_set(const wax::obj &key, const wax::obj &val){      //handle string-based properties specific to this dsp -    std::string key_name = wax::cast<std::string>(key); +    std::string key_name = key.as<std::string>();      if (key_name == "decim"){ -        size_t new_decim = wax::cast<size_t>(val); -        ASSERT_THROW(std::has( -            _allowed_decim_and_interp_rates.begin(), -            _allowed_decim_and_interp_rates.end(), -            new_decim -        )); +        size_t new_decim = val.as<size_t>(); +        assert_has( +            _allowed_decim_and_interp_rates, +            new_decim, "usrp2 decimation" +        );          _ddc_decim = new_decim; //shadow          update_ddc_config();          return;      }      else if (key_name == "freq"){ -        freq_t new_freq = wax::cast<freq_t>(val); +        freq_t new_freq = val.as<freq_t>();          ASSERT_THROW(new_freq <= get_master_clock_freq()/2.0);          ASSERT_THROW(new_freq >= -get_master_clock_freq()/2.0);          _ddc_freq = new_freq; //shadow @@ -160,13 +179,13 @@ void usrp2_impl::ddc_set(const wax::obj &key, const wax::obj &val){          return;      }      else if (key_name == "enabled"){ -        bool new_enabled = wax::cast<bool>(val); +        bool new_enabled = val.as<bool>();          _ddc_enabled = new_enabled; //shadow          update_ddc_enabled();          return;      }      else if (key_name == "stream_at"){ -        time_spec_t new_stream_at = wax::cast<time_spec_t>(val); +        time_spec_t new_stream_at = val.as<time_spec_t>();          _ddc_stream_at = new_stream_at; //shadow          //update_ddc_enabled(); //dont update from here          return; @@ -182,13 +201,13 @@ void usrp2_impl::ddc_set(const wax::obj &key, const wax::obj &val){   **********************************************************************/  void usrp2_impl::init_duc_config(void){      //create the duc in the tx dsp dict -    _tx_dsps["duc0"] = wax_obj_proxy( +    _tx_dsps["duc0"] = wax_obj_proxy::make(          boost::bind(&usrp2_impl::duc_get, this, _1, _2),          boost::bind(&usrp2_impl::duc_set, this, _1, _2)      );      //initial config and update -    _duc_interp = 16; +    _duc_interp = default_interp;      _duc_freq = 0;      update_duc_config();  } @@ -199,8 +218,8 @@ void usrp2_impl::update_duc_config(void){      while(tmp_interp > 128) tmp_interp /= 2;      // Calculate closest multiplier constant to reverse gain absent scale multipliers -    size_t interp_cubed = pow(tmp_interp, 3); -    size_t scale = rint((4096*pow(2, ceil(log2(interp_cubed))))/(1.65*interp_cubed)); +    double interp_cubed = std::pow(double(tmp_interp), 3); +    boost::int16_t scale = rint((4096*std::pow(2, ceil(log2(interp_cubed))))/(1.65*interp_cubed));      //setup the out data      usrp2_ctrl_data_t out_data; @@ -209,7 +228,9 @@ void usrp2_impl::update_duc_config(void){          calculate_freq_word_and_update_actual_freq(_duc_freq, get_master_clock_freq())      );      out_data.data.duc_args.interp = htonl(_duc_interp); -    out_data.data.duc_args.scale_iq = htonl(scale); +    out_data.data.duc_args.scale_iq = htonl( +        calculate_iq_scale_word(scale, scale) +    );      //send and recv      usrp2_ctrl_data_t in_data = ctrl_send_and_recv(out_data); @@ -222,7 +243,7 @@ void usrp2_impl::update_duc_config(void){  void usrp2_impl::duc_get(const wax::obj &key, wax::obj &val){      //handle the case where the key is an expected dsp property      if (key.type() == typeid(dsp_prop_t)){ -        switch(wax::cast<dsp_prop_t>(key)){ +        switch(key.as<dsp_prop_t>()){          case DSP_PROP_NAME:              val = std::string("usrp2 duc0");              return; @@ -231,7 +252,7 @@ void usrp2_impl::duc_get(const wax::obj &key, wax::obj &val){                  prop_names_t others = boost::assign::list_of                      ("rate")                      ("interp") -                    ("interp_rates") +                    ("interps")                      ("freq")                  ;                  val = others; @@ -241,7 +262,7 @@ void usrp2_impl::duc_get(const wax::obj &key, wax::obj &val){      }      //handle string-based properties specific to this dsp -    std::string key_name = wax::cast<std::string>(key); +    std::string key_name = key.as<std::string>();      if (key_name == "rate"){          val = get_master_clock_freq();          return; @@ -250,7 +271,7 @@ void usrp2_impl::duc_get(const wax::obj &key, wax::obj &val){          val = _duc_interp;          return;      } -    else if (key_name == "interp_rates"){ +    else if (key_name == "interps"){          val = _allowed_decim_and_interp_rates;          return;      } @@ -266,20 +287,19 @@ void usrp2_impl::duc_get(const wax::obj &key, wax::obj &val){  void usrp2_impl::duc_set(const wax::obj &key, const wax::obj &val){      //handle string-based properties specific to this dsp -    std::string key_name = wax::cast<std::string>(key); +    std::string key_name = key.as<std::string>();      if (key_name == "interp"){ -        size_t new_interp = wax::cast<size_t>(val); -        ASSERT_THROW(std::has( -            _allowed_decim_and_interp_rates.begin(), -            _allowed_decim_and_interp_rates.end(), -            new_interp -        )); +        size_t new_interp = val.as<size_t>(); +        assert_has( +            _allowed_decim_and_interp_rates, +            new_interp, "usrp2 interpolation" +        );          _duc_interp = new_interp; //shadow          update_duc_config();          return;      }      else if (key_name == "freq"){ -        freq_t new_freq = wax::cast<freq_t>(val); +        freq_t new_freq = val.as<freq_t>();          ASSERT_THROW(new_freq <= get_master_clock_freq()/2.0);          ASSERT_THROW(new_freq >= -get_master_clock_freq()/2.0);          _duc_freq = new_freq; //shadow diff --git a/host/lib/usrp/usrp2/fw_common.h b/host/lib/usrp/usrp2/fw_common.h index 3def8ddaa..7fcae6fb2 100644 --- a/host/lib/usrp/usrp2/fw_common.h +++ b/host/lib/usrp/usrp2/fw_common.h @@ -24,9 +24,18 @@   * Therefore, this header may only contain valid C code.   */  #ifdef __cplusplus +#include <boost/cstdint.hpp> +#define _SINS_ boost:://stdint namespace when in c++  extern "C" { +#else +#include <stdint.h> +#define _SINS_  #endif +// size of the vrt header and trailer to the host +#define USRP2_HOST_RX_VRT_HEADER_WORDS32 5 +#define USRP2_HOST_RX_VRT_TRAILER_WORDS32 1 //FIXME fpga sets wrong header size when no trailer present +  // udp ports for the usrp2 communication  // Dynamic and/or private ports: 49152-65535  #define USRP2_UDP_CTRL_PORT 49152 @@ -87,6 +96,12 @@ typedef enum{      USRP2_CTRL_ID_SETUP_THIS_DUC_FOR_ME_BRO,      USRP2_CTRL_ID_TOTALLY_SETUP_THE_DUC_DUDE, +    USRP2_CTRL_ID_GOT_A_NEW_TIME_FOR_YOU_BRO, +    USRP2_CTRL_ID_SWEET_I_GOT_THAT_TIME_DUDE, + +    USRP2_CTRL_ID_UPDATE_THOSE_MUX_SETTINGS_BRO, +    USRP2_CTRL_ID_UPDATED_THE_MUX_SETTINGS_DUDE, +      USRP2_CTRL_ID_PEACE_OUT  } usrp2_ctrl_id_t; @@ -118,68 +133,79 @@ typedef enum{  } usrp2_clk_edge_t;  typedef struct{ -    uint32_t id; -    uint32_t seq; +    _SINS_ uint32_t id; +    _SINS_ uint32_t seq;      union{ -        uint32_t ip_addr; -        uint8_t mac_addr[6]; +        _SINS_ uint32_t ip_addr; +        _SINS_ uint8_t mac_addr[6];          struct { -            uint16_t rx_id; -            uint16_t tx_id; +            _SINS_ uint16_t rx_id; +            _SINS_ uint16_t tx_id;          } dboard_ids;          struct { -            uint8_t pps_source; -            uint8_t pps_polarity; -            uint8_t ref_source; -            uint8_t _pad; +            _SINS_ uint8_t pps_source; +            _SINS_ uint8_t pps_polarity; +            _SINS_ uint8_t ref_source; +            _SINS_ uint8_t _pad;          } clock_config;          struct { -            uint8_t bank; -            uint8_t _pad[3]; -            uint16_t value; -            uint16_t mask; +            _SINS_ uint8_t bank; +            _SINS_ uint8_t _pad[3]; +            _SINS_ uint16_t value; +            _SINS_ uint16_t mask;          } gpio_config;          struct { -            uint8_t bank; -            uint8_t _pad[3]; -            uint16_t tx_value; -            uint16_t rx_value; -            uint16_t mask; +            _SINS_ uint8_t bank; +            _SINS_ uint8_t _pad[3]; +            _SINS_ uint16_t tx_value; +            _SINS_ uint16_t rx_value; +            _SINS_ uint16_t mask;          } atr_config;          struct { -            uint8_t dev; -            uint8_t latch; -            uint8_t push; -            uint8_t readback; -            uint8_t bytes; -            uint8_t data[sizeof(uint32_t)]; +            _SINS_ uint8_t dev; +            _SINS_ uint8_t latch; +            _SINS_ uint8_t push; +            _SINS_ uint8_t readback; +            _SINS_ uint8_t bytes; +            _SINS_ uint8_t data[sizeof(_SINS_ uint32_t)];          } spi_args;          struct { -            uint8_t addr; -            uint8_t bytes; -            uint8_t data[sizeof(uint32_t)]; +            _SINS_ uint8_t addr; +            _SINS_ uint8_t bytes; +            _SINS_ uint8_t data[sizeof(_SINS_ uint32_t)];          } i2c_args;          struct { -            uint8_t dir; -            uint8_t which; -            uint8_t _pad[2]; -            uint32_t value; +            _SINS_ uint8_t dir; +            _SINS_ uint8_t which; +            _SINS_ uint8_t _pad[2]; +            _SINS_ uint32_t value;          } aux_args;          struct { -            uint32_t freq_word; -            uint32_t decim; +            _SINS_ uint32_t freq_word; +            _SINS_ uint32_t decim; +            _SINS_ uint32_t scale_iq;          } ddc_args;          struct { -            uint8_t enabled; -            uint8_t _pad[3]; -            uint32_t secs; -            uint32_t ticks; +            _SINS_ uint8_t enabled; +            _SINS_ uint8_t _pad[3]; +            _SINS_ uint32_t secs; +            _SINS_ uint32_t ticks; +            _SINS_ uint32_t samples;          } streaming;          struct { -            uint32_t freq_word; -            uint32_t interp; -            uint32_t scale_iq; +            _SINS_ uint32_t freq_word; +            _SINS_ uint32_t interp; +            _SINS_ uint32_t scale_iq;          } duc_args; +        struct { +            _SINS_ uint32_t secs; +            _SINS_ uint32_t ticks; +            _SINS_ uint8_t now; +        } time_args; +        struct { +            _SINS_ uint32_t rx_mux; +            _SINS_ uint32_t tx_mux; +        } mux_args;      } data;  } usrp2_ctrl_data_t; diff --git a/host/lib/usrp/usrp2/io_impl.cpp b/host/lib/usrp/usrp2/io_impl.cpp new file mode 100644 index 000000000..dc8eea243 --- /dev/null +++ b/host/lib/usrp/usrp2/io_impl.cpp @@ -0,0 +1,258 @@ +// +// Copyright 2010 Ettus Research LLC +// +// This program 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. +// +// This program 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 this program.  If not, see <http://www.gnu.org/licenses/>. +// + +#include <complex> +#include <algorithm> +#include <boost/format.hpp> +#include "usrp2_impl.hpp" + +using namespace uhd; +using namespace uhd::usrp; +using namespace uhd::transport; +namespace asio = boost::asio; + +/*********************************************************************** + * Constants + **********************************************************************/ +typedef std::complex<float>          fc32_t; +typedef std::complex<boost::int16_t> sc16_t; + +static const float shorts_per_float = float(1 << 15); +static const float floats_per_short = float(1.0/shorts_per_float); + +/*********************************************************************** + * Helper Functions + **********************************************************************/ +void usrp2_impl::io_init(void){ +    //initially empty copy buffer +    _rx_copy_buff = asio::buffer("", 0); + +    //send a small data packet so the usrp2 knows the udp source port +    boost::uint32_t zero_data = 0; +    _data_transport->send(asio::buffer(&zero_data, sizeof(zero_data))); +} + +#define unrolled_loop(__i, __len, __inst) {\ +    size_t __i = 0; \ +    while(__i < (__len & ~0x7)){ \ +        __inst; __i++; __inst; __i++; \ +        __inst; __i++; __inst; __i++; \ +        __inst; __i++; __inst; __i++; \ +        __inst; __i++; __inst; __i++; \ +    } \ +    while(__i < __len){ \ +        __inst; __i++;\ +    } \ +} + +// set a boolean flag that indicates the endianess +#ifdef HAVE_BIG_ENDIAN +static const bool is_big_endian = true; +#else +static const bool is_big_endian = false; +#endif + +static inline void host_floats_to_usrp2_items( +    boost::uint32_t *usrp2_items, +    const fc32_t *host_floats, +    size_t num_samps +){ +    unrolled_loop(i, num_samps,{ +        boost::uint16_t real = boost::int16_t(host_floats[i].real()*shorts_per_float); +        boost::uint16_t imag = boost::int16_t(host_floats[i].imag()*shorts_per_float); +        usrp2_items[i] = htonl((real << 16) | (imag << 0)); +    }); +} + +static inline void usrp2_items_to_host_floats( +    fc32_t *host_floats, +    const boost::uint32_t *usrp2_items, +    size_t num_samps +){ +    unrolled_loop(i, num_samps,{ +        boost::uint32_t item = ntohl(usrp2_items[i]); +        boost::int16_t real = boost::uint16_t(item >> 16); +        boost::int16_t imag = boost::uint16_t(item >> 0); +        host_floats[i] = fc32_t(float(real*floats_per_short), float(imag*floats_per_short)); +    }); +} + +static inline void host_items_to_usrp2_items( +    boost::uint32_t *usrp2_items, +    const boost::uint32_t *host_items, +    size_t num_samps +){ +    if (is_big_endian){ +        std::memcpy(usrp2_items, host_items, num_samps*sizeof(boost::uint32_t)); +    } +    else{ +        unrolled_loop(i, num_samps, usrp2_items[i] = htonl(host_items[i])); +    } +} + +static inline void usrp2_items_to_host_items( +    boost::uint32_t *host_items, +    const boost::uint32_t *usrp2_items, +    size_t num_samps +){ +    if (is_big_endian){ +        std::memcpy(host_items, usrp2_items, num_samps*sizeof(boost::uint32_t)); +    } +    else{ +        unrolled_loop(i, num_samps, host_items[i] = ntohl(usrp2_items[i])); +    } +} + +/*********************************************************************** + * Receive Raw Data + **********************************************************************/ +void usrp2_impl::recv_raw(rx_metadata_t &metadata){ +    //do a receive +    _rx_smart_buff = _data_transport->recv(); + +    //unpack the vrt header +    size_t num_packet_words32 = asio::buffer_size(_rx_smart_buff->get())/sizeof(boost::uint32_t); +    if (num_packet_words32 == 0){ +        _rx_copy_buff = boost::asio::buffer("", 0); +        return; //must exit here after setting the buffer +    } +    const boost::uint32_t *vrt_hdr = asio::buffer_cast<const boost::uint32_t *>(_rx_smart_buff->get()); +    size_t num_header_words32_out, num_payload_words32_out, packet_count_out; +    try{ +        vrt::unpack( +            metadata,                //output +            vrt_hdr,                 //input +            num_header_words32_out,  //output +            num_payload_words32_out, //output +            num_packet_words32,      //input +            packet_count_out         //output +        ); +    }catch(const std::exception &e){ +        std::cerr << "bad vrt header: " << e.what() << std::endl; +        _rx_copy_buff = boost::asio::buffer("", 0); +        return; //must exit here after setting the buffer +    } + +    //handle the packet count / sequence number +    size_t expected_packet_count = _rx_stream_id_to_packet_seq[metadata.stream_id]; +    if (packet_count_out != expected_packet_count){ +        std::cerr << "S" << (packet_count_out - expected_packet_count)%16; +    } +    _rx_stream_id_to_packet_seq[metadata.stream_id] = (packet_count_out+1)%16; + +    //setup the rx buffer to point to the data +    _rx_copy_buff = asio::buffer( +        vrt_hdr + num_header_words32_out, +        num_payload_words32_out*sizeof(boost::uint32_t) +    ); +} + +/*********************************************************************** + * Send Data + **********************************************************************/ +size_t usrp2_impl::send( +    const asio::const_buffer &buff, +    const tx_metadata_t &metadata, +    const std::string &type +){ +    boost::uint32_t tx_mem[_mtu/sizeof(boost::uint32_t)]; +    boost::uint32_t *items = tx_mem + vrt::max_header_words32; //offset for data +    size_t num_samps = _max_tx_samples_per_packet; + +    //calculate the number of samples to be copied +    //and copy the samples into the send buffer +    if (type == "32fc"){ +        num_samps = std::min(asio::buffer_size(buff)/sizeof(fc32_t), num_samps); +        host_floats_to_usrp2_items(items, asio::buffer_cast<const fc32_t*>(buff), num_samps); +    } +    else if (type == "16sc"){ +        num_samps = std::min(asio::buffer_size(buff)/sizeof(sc16_t), num_samps); +        host_items_to_usrp2_items(items, asio::buffer_cast<const boost::uint32_t*>(buff), num_samps); +    } +    else{ +        throw std::runtime_error(str(boost::format("usrp2 send: cannot handle type \"%s\"") % type)); +    } + +    boost::uint32_t vrt_hdr[vrt::max_header_words32]; +    size_t num_header_words32, num_packet_words32; +    size_t packet_count = _tx_stream_id_to_packet_seq[metadata.stream_id]++; + +    //pack metadata into a vrt header +    vrt::pack( +        metadata,            //input +        vrt_hdr,             //output +        num_header_words32,  //output +        num_samps,           //input +        num_packet_words32,  //output +        packet_count         //input +    ); + +    //copy in the vrt header (yes we left space) +    items -= num_header_words32; +    std::memcpy(items, vrt_hdr, num_header_words32*sizeof(boost::uint32_t)); + +    //send and return number of samples +    _data_transport->send(asio::buffer(items, num_packet_words32*sizeof(boost::uint32_t))); +    return num_samps; +} + +/*********************************************************************** + * Receive Data + **********************************************************************/ +size_t usrp2_impl::recv( +    const asio::mutable_buffer &buff, +    rx_metadata_t &metadata, +    const std::string &type +){ +    //perform a receive if no rx data is waiting to be copied +    if (asio::buffer_size(_rx_copy_buff) == 0){ +        recv_raw(metadata); +    } +    //otherwise flag the metadata to show that is is a fragment +    else{ +        metadata = rx_metadata_t(); +        metadata.is_fragment = true; +    } + +    //extract the number of samples available to copy +    //and a pointer into the usrp2 received items memory +    size_t bytes_to_copy = asio::buffer_size(_rx_copy_buff); +    if (bytes_to_copy == 0) return 0; //nothing to receive +    size_t num_samps = bytes_to_copy/sizeof(boost::uint32_t); +    const boost::uint32_t *items = asio::buffer_cast<const boost::uint32_t*>(_rx_copy_buff); + +    //calculate the number of samples to be copied +    //and copy the samples from the recv buffer +    if (type == "32fc"){ +        num_samps = std::min(asio::buffer_size(buff)/sizeof(fc32_t), num_samps); +        usrp2_items_to_host_floats(asio::buffer_cast<fc32_t*>(buff), items, num_samps); +    } +    else if (type == "16sc"){ +        num_samps = std::min(asio::buffer_size(buff)/sizeof(sc16_t), num_samps); +        usrp2_items_to_host_items(asio::buffer_cast<boost::uint32_t*>(buff), items, num_samps); +    } +    else{ +        throw std::runtime_error(str(boost::format("usrp2 recv: cannot handle type \"%s\"") % type)); +    } + +    //update the rx copy buffer to reflect the bytes copied +    _rx_copy_buff = asio::buffer( +        items + num_samps, bytes_to_copy - num_samps*sizeof(boost::uint32_t) +    ); + +    return num_samps; +} diff --git a/host/lib/usrp/usrp2/mboard_impl.cpp b/host/lib/usrp/usrp2/mboard_impl.cpp index cc73b229c..cbca8eec7 100644 --- a/host/lib/usrp/usrp2/mboard_impl.cpp +++ b/host/lib/usrp/usrp2/mboard_impl.cpp @@ -24,28 +24,38 @@ using namespace uhd;   * Helper Methods   **********************************************************************/  void usrp2_impl::mboard_init(void){ -    _mboards[""] = wax_obj_proxy( +    _mboards[""] = wax_obj_proxy::make(          boost::bind(&usrp2_impl::mboard_get, this, _1, _2),          boost::bind(&usrp2_impl::mboard_set, this, _1, _2)      ); + +    //set the time on the usrp2 as close as possible to the system utc time +    boost::posix_time::ptime now(boost::posix_time::microsec_clock::universal_time()); +    set_time_spec(time_spec_t(now, get_master_clock_freq()), true);  }  void usrp2_impl::init_clock_config(void){ +    //init the ref source clock config +    _ref_source_dict = boost::assign::map_list_of +        ("int", USRP2_REF_SOURCE_INT) +        ("sma", USRP2_REF_SOURCE_SMA) +        ("mimo", USRP2_REF_SOURCE_MIMO) +    ; +    _clock_config.ref_source = "int"; +      //init the pps source clock config -    _pps_source_dict["sma"]  = USRP2_PPS_SOURCE_SMA; -    _pps_source_dict["mimo"] = USRP2_PPS_SOURCE_MIMO; -    _pps_source = "sma"; +    _pps_source_dict = boost::assign::map_list_of +        ("sma", USRP2_PPS_SOURCE_SMA) +        ("mimo", USRP2_PPS_SOURCE_MIMO) +    ; +    _clock_config.pps_source = "sma";      //init the pps polarity clock config -    _pps_polarity_dict["pos"] = USRP2_PPS_POLARITY_POS; -    _pps_polarity_dict["neg"] = USRP2_PPS_POLARITY_NEG; -    _pps_polarity = "neg"; - -    //init the ref source clock config -    _ref_source_dict["int"]  = USRP2_REF_SOURCE_INT; -    _ref_source_dict["sma"]  = USRP2_REF_SOURCE_SMA; -    _ref_source_dict["mimo"] = USRP2_REF_SOURCE_MIMO; -    _ref_source = "int"; +    _pps_polarity_dict = boost::assign::map_list_of +        (clock_config_t::POLARITY_POS, USRP2_PPS_POLARITY_POS) +        (clock_config_t::POLARITY_NEG, USRP2_PPS_POLARITY_NEG) +    ; +    _clock_config.pps_polarity = clock_config_t::POLARITY_NEG;      //update the clock config (sends a control packet)      update_clock_config(); @@ -55,15 +65,28 @@ void usrp2_impl::update_clock_config(void){      //setup the out data      usrp2_ctrl_data_t out_data;      out_data.id = htonl(USRP2_CTRL_ID_HERES_A_NEW_CLOCK_CONFIG_BRO); -    out_data.data.clock_config.pps_source   = _pps_source_dict  [_pps_source]; -    out_data.data.clock_config.pps_polarity = _pps_polarity_dict[_pps_polarity]; -    out_data.data.clock_config.ref_source   = _ref_source_dict  [_ref_source]; +    out_data.data.clock_config.ref_source   = _ref_source_dict  [_clock_config.ref_source]; +    out_data.data.clock_config.pps_source   = _pps_source_dict  [_clock_config.pps_source]; +    out_data.data.clock_config.pps_polarity = _pps_polarity_dict[_clock_config.pps_polarity];      //send and recv      usrp2_ctrl_data_t in_data = ctrl_send_and_recv(out_data);      ASSERT_THROW(htonl(in_data.id) == USRP2_CTRL_ID_GOT_THE_NEW_CLOCK_CONFIG_DUDE);  } +void usrp2_impl::set_time_spec(const time_spec_t &time_spec, bool now){ +    //setup the out data +    usrp2_ctrl_data_t out_data; +    out_data.id = htonl(USRP2_CTRL_ID_GOT_A_NEW_TIME_FOR_YOU_BRO); +    out_data.data.time_args.secs  = htonl(time_spec.secs); +    out_data.data.time_args.ticks = htonl(time_spec.ticks); +    out_data.data.time_args.now   = (now)? 1 : 0; + +    //send and recv +    usrp2_ctrl_data_t in_data = ctrl_send_and_recv(out_data); +    ASSERT_THROW(htonl(in_data.id) == USRP2_CTRL_ID_SWEET_I_GOT_THAT_TIME_DUDE); +} +  /***********************************************************************   * MBoard Get Properties   **********************************************************************/ @@ -71,18 +94,55 @@ void usrp2_impl::mboard_get(const wax::obj &key_, wax::obj &val){      wax::obj key; std::string name;      boost::tie(key, name) = extract_named_prop(key_); +    //handle the other props +    if (key.type() == typeid(std::string)){ +        if (key.as<std::string>() == "mac-addr"){ +            //setup the out data +            usrp2_ctrl_data_t out_data; +            out_data.id = htonl(USRP2_CTRL_ID_GIVE_ME_YOUR_MAC_ADDR_BRO); + +            //send and recv +            usrp2_ctrl_data_t in_data = ctrl_send_and_recv(out_data); +            ASSERT_THROW(htonl(in_data.id) == USRP2_CTRL_ID_THIS_IS_MY_MAC_ADDR_DUDE); + +            //extract the address +            val = reinterpret_cast<mac_addr_t*>(in_data.data.mac_addr)->to_string(); +            return; +        } + +        if (key.as<std::string>() == "ip-addr"){ +            //setup the out data +            usrp2_ctrl_data_t out_data; +            out_data.id = htonl(USRP2_CTRL_ID_GIVE_ME_YOUR_IP_ADDR_BRO); + +            //send and recv +            usrp2_ctrl_data_t in_data = ctrl_send_and_recv(out_data); +            ASSERT_THROW(htonl(in_data.id) == USRP2_CTRL_ID_THIS_IS_MY_IP_ADDR_DUDE); + +            //extract the address +            val = boost::asio::ip::address_v4(ntohl(in_data.data.ip_addr)).to_string(); +            return; +        } +    } +      //handle the get request conditioned on the key -    switch(wax::cast<mboard_prop_t>(key)){ +    switch(key.as<mboard_prop_t>()){      case MBOARD_PROP_NAME:          val = std::string("usrp2 mboard");          return; -    case MBOARD_PROP_OTHERS: -        val = prop_names_t(); //empty other props +    case MBOARD_PROP_OTHERS:{ +            prop_names_t others = boost::assign::list_of +                ("mac-addr") +                ("ip-addr") +            ; +            val = others; +        }          return;      case MBOARD_PROP_RX_DBOARD: -        val = _rx_dboards[name].get_link(); +        ASSERT_THROW(_rx_dboards.has_key(name)); +        val = _rx_dboards[name]->get_link();          return;      case MBOARD_PROP_RX_DBOARD_NAMES: @@ -90,25 +150,21 @@ void usrp2_impl::mboard_get(const wax::obj &key_, wax::obj &val){          return;      case MBOARD_PROP_TX_DBOARD: -        val = _tx_dboards[name].get_link(); +        ASSERT_THROW(_tx_dboards.has_key(name)); +        val = _tx_dboards[name]->get_link();          return;      case MBOARD_PROP_TX_DBOARD_NAMES:          val = prop_names_t(_tx_dboards.get_keys());          return; -    case MBOARD_PROP_MTU: -        // FIXME we dont know the real MTU... -        // give them something to fragment about -        val = size_t(1500); -        return; -      case MBOARD_PROP_CLOCK_RATE:          val = freq_t(get_master_clock_freq());          return;      case MBOARD_PROP_RX_DSP: -        val = _rx_dsps[name].get_link(); +        ASSERT_THROW(_rx_dsps.has_key(name)); +        val = _rx_dsps[name]->get_link();          return;      case MBOARD_PROP_RX_DSP_NAMES: @@ -116,29 +172,22 @@ void usrp2_impl::mboard_get(const wax::obj &key_, wax::obj &val){          return;      case MBOARD_PROP_TX_DSP: -        val = _tx_dsps[name].get_link(); +        ASSERT_THROW(_tx_dsps.has_key(name)); +        val = _tx_dsps[name]->get_link();          return;      case MBOARD_PROP_TX_DSP_NAMES:          val = prop_names_t(_tx_dsps.get_keys());          return; -    case MBOARD_PROP_PPS_SOURCE: -        val = _pps_source; +    case MBOARD_PROP_CLOCK_CONFIG: +        val = _clock_config;          return;      case MBOARD_PROP_PPS_SOURCE_NAMES:          val = prop_names_t(_pps_source_dict.get_keys());          return; -    case MBOARD_PROP_PPS_POLARITY: -        val = _pps_polarity; -        return; - -    case MBOARD_PROP_REF_SOURCE: -        val = _ref_source; -        return; -      case MBOARD_PROP_REF_SOURCE_NAMES:          val = prop_names_t(_ref_source_dict.get_keys());          return; @@ -154,36 +203,58 @@ void usrp2_impl::mboard_get(const wax::obj &key_, wax::obj &val){   * MBoard Set Properties   **********************************************************************/  void usrp2_impl::mboard_set(const wax::obj &key, const wax::obj &val){ +    //handle the other props +    if (key.type() == typeid(std::string)){ +        if (key.as<std::string>() == "mac-addr"){ +            //setup the out data +            usrp2_ctrl_data_t out_data; +            out_data.id = htonl(USRP2_CTRL_ID_HERE_IS_A_NEW_MAC_ADDR_BRO); +            mac_addr_t mac_addr(val.as<std::string>()); +            std::memcpy(out_data.data.mac_addr, &mac_addr, sizeof(mac_addr_t)); + +            //send and recv +            usrp2_ctrl_data_t in_data = ctrl_send_and_recv(out_data); +            ASSERT_THROW(htonl(in_data.id) == USRP2_CTRL_ID_THIS_IS_MY_MAC_ADDR_DUDE); +            return; +        } + +        if (key.as<std::string>() == "ip-addr"){ +            //setup the out data +            usrp2_ctrl_data_t out_data; +            out_data.id = htonl(USRP2_CTRL_ID_HERE_IS_A_NEW_IP_ADDR_BRO); +            out_data.data.ip_addr = htonl(boost::asio::ip::address_v4::from_string(val.as<std::string>()).to_ulong()); + +            //send and recv +            usrp2_ctrl_data_t in_data = ctrl_send_and_recv(out_data); +            ASSERT_THROW(htonl(in_data.id) == USRP2_CTRL_ID_THIS_IS_MY_IP_ADDR_DUDE); +            return; +        } +    } +      //handle the get request conditioned on the key -    switch(wax::cast<mboard_prop_t>(key)){ +    switch(key.as<mboard_prop_t>()){ -    case MBOARD_PROP_PPS_SOURCE:{ -            std::string name = wax::cast<std::string>(val); -            ASSERT_THROW(_pps_source_dict.has_key(name)); -            _pps_source = name; //shadow +    case MBOARD_PROP_CLOCK_CONFIG:{ +            clock_config_t clock_config = val.as<clock_config_t>(); +            assert_has(_pps_source_dict.get_keys(), clock_config.pps_source, "usrp2 pps source"); +            assert_has(_ref_source_dict.get_keys(), clock_config.ref_source, "usrp2 ref source"); +            _clock_config = clock_config; //shadow              update_clock_config();          }          return; -    case MBOARD_PROP_PPS_POLARITY:{ -            std::string name = wax::cast<std::string>(val); -            ASSERT_THROW(_pps_polarity_dict.has_key(name)); -            _pps_polarity = name; //shadow -            update_clock_config(); -        } +    case MBOARD_PROP_TIME_NOW:{ +        set_time_spec(val.as<time_spec_t>(), true);          return; +    } -    case MBOARD_PROP_REF_SOURCE:{ -            std::string name = wax::cast<std::string>(val); -            ASSERT_THROW(_ref_source_dict.has_key(name)); -            _ref_source = name; //shadow -            update_clock_config(); -        } +    case MBOARD_PROP_TIME_NEXT_PPS:{ +        set_time_spec(val.as<time_spec_t>(), false);          return; +    }      case MBOARD_PROP_NAME:      case MBOARD_PROP_OTHERS: -    case MBOARD_PROP_MTU:      case MBOARD_PROP_CLOCK_RATE:      case MBOARD_PROP_RX_DSP:      case MBOARD_PROP_RX_DSP_NAMES: @@ -195,8 +266,6 @@ void usrp2_impl::mboard_set(const wax::obj &key, const wax::obj &val){      case MBOARD_PROP_TX_DBOARD_NAMES:      case MBOARD_PROP_PPS_SOURCE_NAMES:      case MBOARD_PROP_REF_SOURCE_NAMES: -    case MBOARD_PROP_TIME_NOW: -    case MBOARD_PROP_TIME_NEXT_PPS:          throw std::runtime_error("Error: trying to set read-only property on usrp2 mboard");      } diff --git a/host/lib/usrp/usrp2/usrp2_impl.cpp b/host/lib/usrp/usrp2/usrp2_impl.cpp index 2b4e8fe39..85d73e83a 100644 --- a/host/lib/usrp/usrp2/usrp2_impl.cpp +++ b/host/lib/usrp/usrp2/usrp2_impl.cpp @@ -23,6 +23,12 @@  using namespace uhd;  using namespace uhd::usrp; +using namespace uhd::transport; +namespace asio = boost::asio; + +STATIC_BLOCK(register_usrp2_device){ +    device::register_device(&usrp2::discover, &usrp2::make); +}  /***********************************************************************   * Discovery over the udp transport @@ -30,40 +36,41 @@ using namespace uhd::usrp;  uhd::device_addrs_t usrp2::discover(const device_addr_t &hint){      device_addrs_t usrp2_addrs; +    if (not hint.has_key("addr")) return usrp2_addrs; +      //create a udp transport to communicate      //TODO if an addr is not provided, search all interfaces?      std::string ctrl_port = boost::lexical_cast<std::string>(USRP2_UDP_CTRL_PORT); -    uhd::transport::udp udp_transport(hint["addr"], ctrl_port, true); +    udp_simple::sptr udp_transport = udp_simple::make_broadcast( +        hint["addr"], ctrl_port +    );      //send a hello control packet      usrp2_ctrl_data_t ctrl_data_out;      ctrl_data_out.id = htonl(USRP2_CTRL_ID_GIVE_ME_YOUR_IP_ADDR_BRO); -    udp_transport.send(boost::asio::buffer(&ctrl_data_out, sizeof(ctrl_data_out))); +    udp_transport->send(boost::asio::buffer(&ctrl_data_out, sizeof(ctrl_data_out))); -    //loop and recieve until the time is up -    size_t num_timeouts = 0; +    //loop and recieve until the timeout      while(true){ -        uhd::shared_iovec iov = udp_transport.recv(); -        //std::cout << boost::asio::buffer_size(buff) << "\n"; -        if (iov.len < sizeof(usrp2_ctrl_data_t)){ -            //sleep a little so we dont burn cpu -            if (num_timeouts++ > 50) break; -            boost::this_thread::sleep(boost::posix_time::milliseconds(1)); -        }else{ +        usrp2_ctrl_data_t ctrl_data_in; +        size_t len = udp_transport->recv(asio::buffer(&ctrl_data_in, sizeof(ctrl_data_in))); +        //std::cout << len << "\n"; +        if (len >= sizeof(usrp2_ctrl_data_t)){              //handle the received data -            const usrp2_ctrl_data_t *ctrl_data_in = reinterpret_cast<const usrp2_ctrl_data_t *>(iov.base); -            switch(ntohl(ctrl_data_in->id)){ +            switch(ntohl(ctrl_data_in.id)){              case USRP2_CTRL_ID_THIS_IS_MY_IP_ADDR_DUDE:                  //make a boost asio ipv4 with the raw addr in host byte order -                boost::asio::ip::address_v4 ip_addr(ntohl(ctrl_data_in->data.ip_addr)); +                boost::asio::ip::address_v4 ip_addr(ntohl(ctrl_data_in.data.ip_addr));                  device_addr_t new_addr;                  new_addr["name"] = "USRP2"; -                new_addr["type"] = "udp"; +                new_addr["transport"] = "udp";                  new_addr["addr"] = ip_addr.to_string();                  usrp2_addrs.push_back(new_addr); -                break; +                //dont break here, it will exit the while loop +                //just continue on to the next loop iteration              }          } +        if (len == 0) break; //timeout      }      return usrp2_addrs; @@ -72,21 +79,19 @@ uhd::device_addrs_t usrp2::discover(const device_addr_t &hint){  /***********************************************************************   * Make   **********************************************************************/ +template <class T> std::string num2str(T num){ +    return boost::lexical_cast<std::string>(num); +} +  device::sptr usrp2::make(const device_addr_t &device_addr){      //create a control transport -    uhd::transport::udp::sptr ctrl_transport( -        new uhd::transport::udp( -            device_addr["addr"], -            boost::lexical_cast<std::string>(USRP2_UDP_CTRL_PORT) -        ) +    udp_simple::sptr ctrl_transport = udp_simple::make_connected( +        device_addr["addr"], num2str(USRP2_UDP_CTRL_PORT)      );      //create a data transport -    uhd::transport::udp::sptr data_transport( -        new uhd::transport::udp( -            device_addr["addr"], -            boost::lexical_cast<std::string>(USRP2_UDP_DATA_PORT) -        ) +    udp_zero_copy::sptr data_transport = udp_zero_copy::make( +        device_addr["addr"], num2str(USRP2_UDP_DATA_PORT)      );      //create the usrp2 implementation guts @@ -99,8 +104,8 @@ device::sptr usrp2::make(const device_addr_t &device_addr){   * Structors   **********************************************************************/  usrp2_impl::usrp2_impl( -    uhd::transport::udp::sptr ctrl_transport, -    uhd::transport::udp::sptr data_transport +    udp_simple::sptr ctrl_transport, +    udp_zero_copy::sptr data_transport  ){      _ctrl_transport = ctrl_transport;      _data_transport = data_transport; @@ -121,9 +126,6 @@ usrp2_impl::usrp2_impl(      //init the mboard      mboard_init(); -    //init the tx and rx dboards -    dboard_init(); -      //init the ddc      init_ddc_config(); @@ -132,6 +134,13 @@ usrp2_impl::usrp2_impl(      //initialize the clock configuration      init_clock_config(); + +    //init the tx and rx dboards (do last) +    dboard_init(); + +    //init the send and recv io +    io_init(); +  }  usrp2_impl::~usrp2_impl(void){ @@ -156,22 +165,15 @@ usrp2_ctrl_data_t usrp2_impl::ctrl_send_and_recv(const usrp2_ctrl_data_t &out_da      out_copy.seq = htonl(++_ctrl_seq_num);      _ctrl_transport->send(boost::asio::buffer(&out_copy, sizeof(usrp2_ctrl_data_t))); -    //loop and recieve until the time is up -    size_t num_timeouts = 0; +    //loop until we get the packet or timeout      while(true){ -        uhd::shared_iovec iov = _ctrl_transport->recv(); -        if (iov.len < sizeof(usrp2_ctrl_data_t)){ -            //sleep a little so we dont burn cpu -            if (num_timeouts++ > 50) break; -            boost::this_thread::sleep(boost::posix_time::milliseconds(1)); -        }else{ -            //handle the received data -            usrp2_ctrl_data_t in_data = *reinterpret_cast<const usrp2_ctrl_data_t *>(iov.base); -            if (ntohl(in_data.seq) == _ctrl_seq_num){ -                return in_data; -            } -            //didnt get seq, continue on... +        usrp2_ctrl_data_t in_data; +        size_t len = _ctrl_transport->recv(asio::buffer(&in_data, sizeof(in_data))); +        if (len >= sizeof(usrp2_ctrl_data_t) and ntohl(in_data.seq) == _ctrl_seq_num){ +            return in_data;          } +        if (len == 0) break; //timeout +        //didnt get seq or bad packet, continue looking...      }      throw std::runtime_error("usrp2 no control response");  } @@ -184,32 +186,31 @@ void usrp2_impl::get(const wax::obj &key_, wax::obj &val){      boost::tie(key, name) = extract_named_prop(key_);      //handle the get request conditioned on the key -    switch(wax::cast<device_prop_t>(key)){ +    switch(key.as<device_prop_t>()){      case DEVICE_PROP_NAME:          val = std::string("usrp2 device");          return;      case DEVICE_PROP_MBOARD: -        val = _mboards[name].get_link(); +        ASSERT_THROW(_mboards.has_key(name)); +        val = _mboards[name]->get_link();          return;      case DEVICE_PROP_MBOARD_NAMES:          val = prop_names_t(_mboards.get_keys());          return; + +    case DEVICE_PROP_MAX_RX_SAMPLES: +        val = size_t(_max_rx_samples_per_packet); +        return; + +    case DEVICE_PROP_MAX_TX_SAMPLES: +        val = size_t(_max_tx_samples_per_packet); +        return; +      }  }  void usrp2_impl::set(const wax::obj &, const wax::obj &){      throw std::runtime_error("Cannot set in usrp2 device");  } - -/*********************************************************************** - * IO Interface - **********************************************************************/ -void usrp2_impl::send_raw(const std::vector<boost::asio::const_buffer> &){ -    return; -} - -uhd::shared_iovec usrp2_impl::recv_raw(void){ -    throw std::runtime_error("not implemented"); -} diff --git a/host/lib/usrp/usrp2/usrp2_impl.hpp b/host/lib/usrp/usrp2/usrp2_impl.hpp index 2545efd58..55ac0b192 100644 --- a/host/lib/usrp/usrp2/usrp2_impl.hpp +++ b/host/lib/usrp/usrp2/usrp2_impl.hpp @@ -15,19 +15,31 @@  // along with this program.  If not, see <http://www.gnu.org/licenses/>.  // +#ifndef INCLUDED_USRP2_IMPL_HPP +#define INCLUDED_USRP2_IMPL_HPP +  #include <uhd/usrp/usrp2.hpp>  #include <uhd/dict.hpp> -#include <uhd/props.hpp> +#include <uhd/types.hpp>  #include <uhd/time_spec.hpp>  #include <boost/thread.hpp>  #include <boost/shared_ptr.hpp>  #include <boost/function.hpp> -#include <uhd/transport/udp.hpp> +#include <boost/assign/list_of.hpp> +#include <uhd/transport/vrt.hpp> +#include <uhd/transport/udp_simple.hpp> +#include <uhd/transport/udp_zero_copy.hpp>  #include <uhd/usrp/dboard_manager.hpp>  #include "fw_common.h" -#ifndef INCLUDED_USRP2_IMPL_HPP -#define INCLUDED_USRP2_IMPL_HPP +class usrp2_impl; //dummy class declaration + +/*! + * Make a usrp2 dboard interface. + * \param impl a pointer to the usrp2 impl object + * \return a sptr to a new dboard interface + */ +uhd::usrp::dboard_interface::sptr make_usrp2_dboard_interface(usrp2_impl *impl);  /*!   * Simple wax obj proxy class: @@ -39,20 +51,25 @@ class wax_obj_proxy : public wax::obj{  public:      typedef boost::function<void(const wax::obj &, wax::obj &)>       get_t;      typedef boost::function<void(const wax::obj &, const wax::obj &)> set_t; +    typedef boost::shared_ptr<wax_obj_proxy> sptr; -    wax_obj_proxy(void){ +    static sptr make(const get_t &get, const set_t &set){ +        return sptr(new wax_obj_proxy(get, set)); +    } + +    ~wax_obj_proxy(void){          /* NOP */      } +private: +    get_t _get; +    set_t _set; +      wax_obj_proxy(const get_t &get, const set_t &set){          _get = get;          _set = set;      }; -    ~wax_obj_proxy(void){ -        /* NOP */ -    } -      void get(const wax::obj &key, wax::obj &val){          return _get(key, val);      } @@ -60,10 +77,6 @@ public:      void set(const wax::obj &key, const wax::obj &val){          return _set(key, val);      } - -private: -    get_t _get; -    set_t _set;  };  /*! @@ -73,24 +86,18 @@ private:   */  class usrp2_impl : public uhd::device{  public: -    typedef boost::shared_ptr<usrp2_impl> sptr; -      /*!       * Create a new usrp2 impl base.       * \param ctrl_transport the udp transport for control       * \param data_transport the udp transport for data       */      usrp2_impl( -        uhd::transport::udp::sptr ctrl_transport, -        uhd::transport::udp::sptr data_transport +        uhd::transport::udp_simple::sptr ctrl_transport, +        uhd::transport::udp_zero_copy::sptr data_transport      );      ~usrp2_impl(void); -    //properties interface -    void get(const wax::obj &, wax::obj &); -    void set(const wax::obj &, const wax::obj &); -      //performs a control transaction      usrp2_ctrl_data_t ctrl_send_and_recv(const usrp2_ctrl_data_t &); @@ -98,27 +105,51 @@ public:      double get_master_clock_freq(void);      //the io interface -    void send_raw(const std::vector<boost::asio::const_buffer> &); -    uhd::shared_iovec recv_raw(void); +    size_t send(const boost::asio::const_buffer &, const uhd::tx_metadata_t &, const std::string &); +    size_t recv(const boost::asio::mutable_buffer &, uhd::rx_metadata_t &, const std::string &);  private: +    //device properties interface +    void get(const wax::obj &, wax::obj &); +    void set(const wax::obj &, const wax::obj &); + +    //the raw io interface (samples are in the usrp2 native format) +    void recv_raw(uhd::rx_metadata_t &); +    uhd::dict<boost::uint32_t, size_t> _tx_stream_id_to_packet_seq; +    uhd::dict<boost::uint32_t, size_t> _rx_stream_id_to_packet_seq; +    static const size_t _mtu = 1500; //FIXME we have no idea +    static const size_t _hdrs = (2 + 14 + 20 + 8); //size of headers (pad, eth, ip, udp) +    static const size_t _max_rx_samples_per_packet = +        (_mtu - _hdrs)/sizeof(boost::uint32_t) - +        USRP2_HOST_RX_VRT_HEADER_WORDS32 - +        USRP2_HOST_RX_VRT_TRAILER_WORDS32 +    ; +    static const size_t _max_tx_samples_per_packet = +        (_mtu - _hdrs)/sizeof(boost::uint32_t) - +        uhd::transport::vrt::max_header_words32 +    ; +    uhd::transport::smart_buffer::sptr _rx_smart_buff; +    boost::asio::const_buffer _rx_copy_buff; +    void io_init(void); +      //udp transports for control and data -    uhd::transport::udp::sptr _ctrl_transport; -    uhd::transport::udp::sptr _data_transport; +    uhd::transport::udp_simple::sptr _ctrl_transport; +    uhd::transport::udp_zero_copy::sptr _data_transport;      //private vars for dealing with send/recv control -    uint32_t _ctrl_seq_num; +    boost::uint32_t _ctrl_seq_num;      boost::mutex _ctrl_mutex;      //methods and shadows for clock configuration -    std::string _pps_source, _pps_polarity, _ref_source; +    uhd::clock_config_t _clock_config;      void init_clock_config(void);      void update_clock_config(void); +    void set_time_spec(const uhd::time_spec_t &time_spec, bool now);      //mappings from clock config strings to over the wire enums -    uhd::dict<std::string, usrp2_pps_source_t>   _pps_source_dict; -    uhd::dict<std::string, usrp2_pps_polarity_t> _pps_polarity_dict; -    uhd::dict<std::string, usrp2_ref_source_t>   _ref_source_dict; +    uhd::dict<std::string, usrp2_ref_source_t> _ref_source_dict; +    uhd::dict<std::string, usrp2_pps_source_t> _pps_source_dict; +    uhd::dict<uhd::clock_config_t::polarity_t, usrp2_pps_polarity_t> _pps_polarity_dict;      //rx and tx dboard methods and objects      uhd::usrp::dboard_manager::sptr _dboard_manager; @@ -128,17 +159,20 @@ private:      void mboard_init(void);      void mboard_get(const wax::obj &, wax::obj &);      void mboard_set(const wax::obj &, const wax::obj &); -    uhd::dict<std::string, wax_obj_proxy> _mboards; +    uhd::dict<std::string, wax_obj_proxy::sptr> _mboards;      //properties interface for rx dboard      void rx_dboard_get(const wax::obj &, wax::obj &);      void rx_dboard_set(const wax::obj &, const wax::obj &); -    uhd::dict<std::string, wax_obj_proxy> _rx_dboards; +    uhd::dict<std::string, wax_obj_proxy::sptr> _rx_dboards; +    uhd::prop_names_t _rx_subdevs_in_use;      //properties interface for tx dboard      void tx_dboard_get(const wax::obj &, wax::obj &);      void tx_dboard_set(const wax::obj &, const wax::obj &); -    uhd::dict<std::string, wax_obj_proxy> _tx_dboards; +    uhd::dict<std::string, wax_obj_proxy::sptr> _tx_dboards; +    uhd::prop_names_t _tx_subdevs_in_use; +    void update_mux_config(void);      //methods and shadows for the ddc dsp      std::vector<size_t> _allowed_decim_and_interp_rates; @@ -159,12 +193,12 @@ private:      //properties interface for ddc      void ddc_get(const wax::obj &, wax::obj &);      void ddc_set(const wax::obj &, const wax::obj &); -    uhd::dict<std::string, wax_obj_proxy> _rx_dsps; +    uhd::dict<std::string, wax_obj_proxy::sptr> _rx_dsps;      //properties interface for duc      void duc_get(const wax::obj &, wax::obj &);      void duc_set(const wax::obj &, const wax::obj &); -    uhd::dict<std::string, wax_obj_proxy> _tx_dsps; +    uhd::dict<std::string, wax_obj_proxy::sptr> _tx_dsps;  }; diff --git a/host/lib/wax.cpp b/host/lib/wax.cpp index c08398c50..0e2e82a3a 100644 --- a/host/lib/wax.cpp +++ b/host/lib/wax.cpp @@ -36,7 +36,11 @@ public:      link_args_t(const wax::obj *obj_ptr) : _obj_ptr(obj_ptr){          /* NOP */      } -    wax::obj & operator()(void){ +    wax::obj & operator()(void) const{ +        //recursively resolve link args to get at original pointer +        if (_obj_ptr->type() == typeid(link_args_t)){ +            return _obj_ptr->as<link_args_t>()(); +        }          return *const_cast<wax::obj *>(_obj_ptr);      }  private: @@ -56,10 +60,10 @@ public:      proxy_args_t(const wax::obj *obj_ptr, const wax::obj &key) : _key(key){          _obj_link = obj_ptr->get_link();      } -    wax::obj & operator()(void){ -        return wax::cast<link_args_t>(_obj_link)(); +    wax::obj & operator()(void) const{ +        return _obj_link.as<link_args_t>()();      } -    const wax::obj & key(void){ +    const wax::obj & key(void) const{          return _key;      }  private: @@ -90,7 +94,7 @@ wax::obj wax::obj::operator[](const obj &key){          obj val = resolve();          //check if its a special link and call          if (val.type() == typeid(link_args_t)){ -            return cast<link_args_t>(val)()[key]; +            return val.as<link_args_t>()()[key];          }          //unknown obj          throw std::runtime_error("cannot use [] on non wax::obj link"); diff --git a/host/test/CMakeLists.txt b/host/test/CMakeLists.txt index 234b6f92c..1791d9082 100644 --- a/host/test/CMakeLists.txt +++ b/host/test/CMakeLists.txt @@ -15,14 +15,20 @@  # along with this program.  If not, see <http://www.gnu.org/licenses/>.  # - +######################################################################## +# unit test suite +########################################################################  ADD_EXECUTABLE(main_test      main_test.cpp      addr_test.cpp      gain_handler_test.cpp +    vrt_test.cpp      wax_test.cpp  ) -  TARGET_LINK_LIBRARIES(main_test uhd) -  ADD_TEST(test main_test) + +######################################################################## +# demo of a loadable module +######################################################################## +ADD_LIBRARY(module_test MODULE module_test.cpp) diff --git a/host/test/gain_handler_test.cpp b/host/test/gain_handler_test.cpp index c81221aac..47acb30f0 100644 --- a/host/test/gain_handler_test.cpp +++ b/host/test/gain_handler_test.cpp @@ -17,33 +17,37 @@  #include <boost/test/unit_test.hpp>  #include <uhd/gain_handler.hpp> +#include <uhd/types.hpp> +#include <uhd/props.hpp>  #include <uhd/dict.hpp> +#include <boost/bind.hpp>  #include <iostream>  using namespace uhd;  enum prop_t{ -    PROP_GAIN, -    PROP_GAIN_MIN, -    PROP_GAIN_MAX, -    PROP_GAIN_STEP, +    PROP_GAIN_VALUE, +    PROP_GAIN_RANGE,      PROP_GAIN_NAMES  };  class gainful_obj : public wax::obj{  public:      gainful_obj(void){ -        _gain_handler = gain_handler::sptr(new gain_handler( -            this, PROP_GAIN, PROP_GAIN_MIN, PROP_GAIN_MAX, PROP_GAIN_STEP, PROP_GAIN_NAMES -        )); -        _gains["g0"] = 0; -        _gains["g1"] = 0; -        _gains_min["g0"] = -10; -        _gains_min["g1"] = 0; -        _gains_max["g0"] = 0; -        _gains_max["g1"] = 100; -        _gains_step["g0"] = .1; -        _gains_step["g1"] = 1.5; +        //initialize gain props struct +        gain_handler::props_t gain_props; +        gain_props.value = PROP_GAIN_VALUE; +        gain_props.range = PROP_GAIN_RANGE; +        gain_props.names = PROP_GAIN_NAMES; +        //make a new gain handler +        _gain_handler = gain_handler::make( +            this->get_link(), gain_props, +            boost::bind(&gain_handler::is_equal<prop_t>, _1, _2) +        ); +        _gain_values["g0"] = 0; +        _gain_values["g1"] = 0; +        _gain_ranges["g0"] = gain_range_t(-10, 0, .1); +        _gain_ranges["g1"] = gain_range_t(0, 100, 1.5);      }      ~gainful_obj(void){} @@ -56,25 +60,17 @@ private:          boost::tie(key, name) = extract_named_prop(key_);          //handle the get request conditioned on the key -        switch(wax::cast<prop_t>(key)){ -        case PROP_GAIN: -            val = _gains[name]; +        switch(key.as<prop_t>()){ +        case PROP_GAIN_VALUE: +            val = _gain_values[name];              return; -        case PROP_GAIN_MIN: -            val = _gains_min[name]; -            return; - -        case PROP_GAIN_MAX: -            val = _gains_max[name]; -            return; - -        case PROP_GAIN_STEP: -            val = _gains_step[name]; +        case PROP_GAIN_RANGE: +            val = _gain_ranges[name];              return;          case PROP_GAIN_NAMES: -            val = prop_names_t(_gains.get_keys()); +            val = _gain_values.get_keys();              return;          }      } @@ -86,24 +82,20 @@ private:          boost::tie(key, name) = extract_named_prop(key_);          //handle the get request conditioned on the key -        switch(wax::cast<prop_t>(key)){ -        case PROP_GAIN: -            _gains[name] = wax::cast<gain_t>(val); +        switch(key.as<prop_t>()){ +        case PROP_GAIN_VALUE: +            _gain_values[name] = val.as<gain_t>();              return; -        case PROP_GAIN_MIN: -        case PROP_GAIN_MAX: -        case PROP_GAIN_STEP: +        case PROP_GAIN_RANGE:          case PROP_GAIN_NAMES:              throw std::runtime_error("cannot set this property");          }      }      gain_handler::sptr _gain_handler; -    uhd::dict<std::string, gain_t> _gains; -    uhd::dict<std::string, gain_t> _gains_min; -    uhd::dict<std::string, gain_t> _gains_max; -    uhd::dict<std::string, gain_t> _gains_step; +    uhd::dict<std::string, gain_t> _gain_values; +    uhd::dict<std::string, gain_range_t> _gain_ranges;  }; @@ -112,17 +104,18 @@ BOOST_AUTO_TEST_CASE(test_gain_handler){      gainful_obj go0;      BOOST_CHECK_THROW( -        wax::cast<gain_t>(go0[named_prop_t(PROP_GAIN, "fail")]), -        std::invalid_argument +        go0[named_prop_t(PROP_GAIN_VALUE, "fail")].as<gain_t>(), +        std::exception      );      std::cout << "verifying the overall min, max, step" << std::endl; -    BOOST_CHECK_EQUAL(wax::cast<gain_t>(go0[PROP_GAIN_MIN]), gain_t(-10)); -    BOOST_CHECK_EQUAL(wax::cast<gain_t>(go0[PROP_GAIN_MAX]), gain_t(100)); -    BOOST_CHECK_EQUAL(wax::cast<gain_t>(go0[PROP_GAIN_STEP]), gain_t(1.5)); +    gain_range_t gain = go0[PROP_GAIN_RANGE].as<gain_range_t>(); +    BOOST_CHECK_EQUAL(gain.min, gain_t(-10)); +    BOOST_CHECK_EQUAL(gain.max, gain_t(100)); +    BOOST_CHECK_EQUAL(gain.step, gain_t(1.5));      std::cout << "verifying the overall gain" << std::endl; -    go0[named_prop_t(PROP_GAIN, "g0")] = gain_t(-5); -    go0[named_prop_t(PROP_GAIN, "g1")] = gain_t(30); -    BOOST_CHECK_EQUAL(wax::cast<gain_t>(go0[PROP_GAIN]), gain_t(25)); +    go0[named_prop_t(PROP_GAIN_VALUE, "g0")] = gain_t(-5); +    go0[named_prop_t(PROP_GAIN_VALUE, "g1")] = gain_t(30); +    BOOST_CHECK_EQUAL(go0[PROP_GAIN_VALUE].as<gain_t>(), gain_t(25));  } diff --git a/host/lib/shared_iovec.cpp b/host/test/module_test.cpp index 60062fbf0..71721ef90 100644 --- a/host/lib/shared_iovec.cpp +++ b/host/test/module_test.cpp @@ -15,14 +15,12 @@  // along with this program.  If not, see <http://www.gnu.org/licenses/>.  // -#include <uhd/shared_iovec.hpp> +#include <uhd/utils.hpp> +#include <iostream> -uhd::shared_iovec::shared_iovec(size_t len_){ -    _shared_array = boost::shared_array<uint8_t>(new uint8_t[len_]); -    base = _shared_array.get(); -    len = len_; -} - -uhd::shared_iovec::~shared_iovec(void){ -    /* NOP */ +STATIC_BLOCK(module_test){ +    std::cout << "---------------------------------------" << std::endl; +    std::cout << "-- Good news, everyone!" << std::endl; +    std::cout << "-- The test module has been loaded." << std::endl; +    std::cout << "---------------------------------------" << std::endl;  } diff --git a/host/test/vrt_test.cpp b/host/test/vrt_test.cpp new file mode 100644 index 000000000..40116e110 --- /dev/null +++ b/host/test/vrt_test.cpp @@ -0,0 +1,100 @@ +// +// Copyright 2010 Ettus Research LLC +// +// This program 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. +// +// This program 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 this program.  If not, see <http://www.gnu.org/licenses/>. +// + +#include <boost/test/unit_test.hpp> +#include <uhd/transport/vrt.hpp> + +using namespace uhd::transport; + +static void pack_and_unpack( +    const uhd::tx_metadata_t &metadata, +    size_t num_payload_words32, +    size_t packet_count +){ +    boost::uint32_t header_buff[vrt::max_header_words32]; +    size_t num_header_words32; +    size_t num_packet_words32; + +    //pack metadata into a vrt header +    vrt::pack( +        metadata,            //input +        header_buff,         //output +        num_header_words32,  //output +        num_payload_words32, //input +        num_packet_words32,  //output +        packet_count         //input +    ); + +    uhd::rx_metadata_t metadata_out; +    size_t num_header_words32_out; +    size_t num_payload_words32_out; +    size_t packet_count_out; + +    //unpack the vrt header back into metadata +    vrt::unpack( +        metadata_out,            //output +        header_buff,             //input +        num_header_words32_out,  //output +        num_payload_words32_out, //output +        num_packet_words32,      //input +        packet_count_out         //output +    ); + +    //check the the unpacked metadata is the same +    BOOST_CHECK_EQUAL(packet_count, packet_count_out); +    BOOST_CHECK_EQUAL(num_header_words32, num_header_words32_out); +    BOOST_CHECK_EQUAL(num_payload_words32, num_payload_words32_out); +    BOOST_CHECK_EQUAL(metadata.has_stream_id, metadata_out.has_stream_id); +    if (metadata.has_stream_id and metadata_out.has_stream_id){ +        BOOST_CHECK_EQUAL(metadata.stream_id, metadata_out.stream_id); +    } +    BOOST_CHECK_EQUAL(metadata.has_time_spec, metadata_out.has_time_spec); +    if (metadata.has_time_spec and metadata_out.has_time_spec){ +        BOOST_CHECK_EQUAL(metadata.time_spec.secs, metadata_out.time_spec.secs); +        BOOST_CHECK_EQUAL(metadata.time_spec.ticks, metadata_out.time_spec.ticks); +    } +} + +BOOST_AUTO_TEST_CASE(test_with_none){ +    uhd::tx_metadata_t metadata; +    pack_and_unpack(metadata, 300, 1); +} + +BOOST_AUTO_TEST_CASE(test_with_sid){ +    uhd::tx_metadata_t metadata; +    metadata.has_stream_id = true; +    metadata.stream_id = 6; +    pack_and_unpack(metadata, 400, 2); +} + +BOOST_AUTO_TEST_CASE(test_with_time_spec){ +    uhd::tx_metadata_t metadata; +    metadata.has_time_spec = true; +    metadata.time_spec.secs = 7; +    metadata.time_spec.ticks = 2000; +    pack_and_unpack(metadata, 500, 3); +} + +BOOST_AUTO_TEST_CASE(test_with_sid_and_time_spec){ +    uhd::tx_metadata_t metadata; +    metadata.has_stream_id = true; +    metadata.stream_id = 2; +    metadata.has_time_spec = true; +    metadata.time_spec.secs = 5; +    metadata.time_spec.ticks = 1000; +    pack_and_unpack(metadata, 600, 4); +} diff --git a/host/test/wax_test.cpp b/host/test/wax_test.cpp index e5e1adc25..cb3b12052 100644 --- a/host/test/wax_test.cpp +++ b/host/test/wax_test.cpp @@ -17,14 +17,15 @@  #include <boost/test/unit_test.hpp>  #include <uhd/wax.hpp> +#include <iostream>  enum opt_a_t{OPTION_A_0, OPTION_A_1};  enum opt_b_t{OPTION_B_0, OPTION_B_1};  BOOST_AUTO_TEST_CASE(test_enums){      wax::obj opta = OPTION_A_0; -    BOOST_CHECK_THROW(wax::cast<opt_b_t>(opta), wax::bad_cast); -    BOOST_CHECK_EQUAL(wax::cast<opt_a_t>(opta), OPTION_A_0); +    BOOST_CHECK_THROW(opta.as<opt_b_t>(), wax::bad_cast); +    BOOST_CHECK_EQUAL(opta.as<opt_a_t>(), OPTION_A_0);  }  /*********************************************************************** @@ -48,14 +49,14 @@ public:      }      void get(const wax::obj &key, wax::obj &value){          if (d_subs.size() == 0){ -            value = d_nums[wax::cast<size_t>(key)]; +            value = d_nums[key.as<size_t>()];          }else{ -            value = d_subs[wax::cast<size_t>(key)].get_link(); +            value = d_subs[key.as<size_t>()].get_link();          }      }      void set(const wax::obj &key, const wax::obj &value){          if (d_subs.size() == 0){ -            d_nums[wax::cast<size_t>(key)] = wax::cast<float>(value); +            d_nums[key.as<size_t>()] = value.as<float>();          }else{              throw std::runtime_error("cant set to a wax demo with sub demos");          } @@ -78,10 +79,10 @@ BOOST_AUTO_TEST_CASE(test_set_get){      for (size_t i = 0; i < 10; i++){          for (size_t j = 0; j < 10; j++){              for (size_t k = 0; k < 10; k++){ -                float val = i * j * k + i + j + k; +                float val = float(i * j * k + i + j + k);                  //std::cout << i << " " << j << " " << k << std::endl;                  wd[i][j][k] = val; -                BOOST_CHECK_EQUAL(val, wax::cast<float>(wd[i][j][k])); +                BOOST_CHECK_EQUAL(val, wd[i][j][k].as<float>());              }          }      } @@ -94,5 +95,5 @@ BOOST_AUTO_TEST_CASE(test_proxy){      std::cout << "assign proxy" << std::endl;      wax::obj a = p[size_t(0)]; -    BOOST_CHECK_EQUAL(wax::cast<float>(a), float(5)); +    BOOST_CHECK_EQUAL(a.as<float>(), float(5));  } | 
