diff options
-rw-r--r-- | firmware/zpu/apps/txrx_uhd.c | 35 | ||||
-rw-r--r-- | firmware/zpu/lib/eth_addrs.c | 132 | ||||
-rw-r--r-- | firmware/zpu/lib/ethernet.h | 21 | ||||
-rw-r--r-- | firmware/zpu/lib/net_common.c | 28 | ||||
-rw-r--r-- | firmware/zpu/lib/net_common.h | 5 | ||||
-rw-r--r-- | firmware/zpu/usrp2p/u2p_init.c | 3 | ||||
-rw-r--r-- | host/docs/usrp2.rst | 49 | ||||
-rw-r--r-- | host/lib/usrp/common/fx2_ctrl.cpp | 39 | ||||
-rw-r--r-- | host/lib/usrp/mboard_eeprom.cpp | 77 | ||||
-rw-r--r-- | host/lib/usrp/usrp2/fw_common.h | 9 | ||||
-rw-r--r-- | host/lib/usrp/usrp2/io_impl.cpp | 57 | ||||
-rw-r--r-- | host/lib/usrp/usrp2/usrp2_impl.hpp | 1 |
12 files changed, 311 insertions, 145 deletions
diff --git a/firmware/zpu/apps/txrx_uhd.c b/firmware/zpu/apps/txrx_uhd.c index 9daf441e2..a4bfce0ec 100644 --- a/firmware/zpu/apps/txrx_uhd.c +++ b/firmware/zpu/apps/txrx_uhd.c @@ -92,8 +92,35 @@ static void handle_udp_data_packet( default: return; } - eth_mac_addr_t eth_mac_host; arp_cache_lookup_mac(&src.addr, ð_mac_host); - setup_framer(eth_mac_host, *ethernet_mac_addr(), src, dst, which); + //assume the packet destination is the packet source + //the arp cache lookup should never fail for this case + const struct socket_address src_addr = dst; + struct socket_address dst_addr = src; + eth_mac_addr_t eth_mac_dst; + arp_cache_lookup_mac(&dst_addr.addr, ð_mac_dst); + + //however, if this control packet has an alternative destination... + if (payload_len >= sizeof(usrp2_stream_ctrl_t)){ + + //parse the destination ip addr and udp port from the payload + const usrp2_stream_ctrl_t *stream_ctrl = (const usrp2_stream_ctrl_t *)payload; + dst_addr.addr.addr = stream_ctrl->ip_addr; + dst_addr.port = (uint16_t)stream_ctrl->udp_port; + struct ip_addr ip_dest = dst_addr.addr; + + //are we in the subnet? if not use the gateway + const uint32_t subnet_mask = get_subnet()->addr; + const bool in_subnet = ((get_ip_addr()->addr & subnet_mask) == (ip_dest.addr & subnet_mask)); + if (!in_subnet) ip_dest = *get_gateway(); + + //lookup the host ip address with ARP (this may fail) + const bool ok = arp_cache_lookup_mac(&ip_dest, ð_mac_dst); + if (!ok) net_common_send_arp_request(&ip_dest); + const uint32_t result = (ok)? 0 : ~0; + send_udp_pkt(dst.port, src, &result, sizeof(result)); + } + + setup_framer(eth_mac_dst, *ethernet_mac_addr(), dst_addr, src_addr, which); } #define OTW_GPIO_BANK_TO_NUM(bank) \ @@ -279,6 +306,7 @@ int main(void) { u2_init(); + arp_cache_init(); #ifdef BOOTLOADER putstr("\nUSRP N210 UDP bootloader\n"); #else @@ -294,8 +322,7 @@ main(void) //load the production FPGA image or firmware if appropriate do_the_bootload_thing(); //if we get here we've fallen through to safe firmware - set_default_mac_addr(); - set_default_ip_addr(); + eth_addrs_set_default(); #endif print_mac_addr(ethernet_mac_addr()); newline(); diff --git a/firmware/zpu/lib/eth_addrs.c b/firmware/zpu/lib/eth_addrs.c index c45ce7559..6d3347cf3 100644 --- a/firmware/zpu/lib/eth_addrs.c +++ b/firmware/zpu/lib/eth_addrs.c @@ -1,5 +1,5 @@ /* - * Copyright 2010-2011 Ettus Research LLC + * Copyright 2010-2012 Ettus Research LLC * Copyright 2007 Free Software Foundation, Inc. * * This program is free software: you can redistribute it and/or modify @@ -20,6 +20,7 @@ #include "memory_map.h" #include "nonstdio.h" #include <stdbool.h> +#include <string.h> #include "i2c.h" #include "usrp2/fw_common.h" @@ -37,104 +38,69 @@ unprogrammed(const void *t, size_t len) return all_ones | all_zeros; } -//////////////////// MAC Addr Stuff /////////////////////// +typedef struct{ + eth_mac_addr_t mac_addr; + struct ip_addr ip_addr; + struct ip_addr gateway; + struct ip_addr subnet; +} eth_addrs_t; -static bool src_mac_addr_initialized = false; +static bool eth_addrs_initialized = false; -static const eth_mac_addr_t default_mac_addr = {{ - 0x00, 0x50, 0xC2, 0x85, 0x3f, 0xff - }}; +static const eth_addrs_t default_eth_addrs = { + .mac_addr = {{0x00, 0x50, 0xC2, 0x85, 0x3f, 0xff}}, + .ip_addr = {(192 << 24 | 168 << 16 | 10 << 8 | 2 << 0)}, + .gateway = {(192 << 24 | 168 << 16 | 10 << 8 | 1 << 0)}, + .subnet = {(255 << 24 | 255 << 16 | 255 << 8 | 0 << 0)}, +}; -static eth_mac_addr_t src_mac_addr = {{ - 0x00, 0x50, 0xC2, 0x85, 0x3f, 0xff - }}; - -void set_default_mac_addr(void) -{ - src_mac_addr_initialized = true; - src_mac_addr = default_mac_addr; -} +static eth_addrs_t current_eth_addrs; -const eth_mac_addr_t * -ethernet_mac_addr(void) -{ - if (!src_mac_addr_initialized){ // fetch from eeprom - src_mac_addr_initialized = true; +static void eth_addrs_init(void){ + if (eth_addrs_initialized) return; + eth_addrs_initialized = true; - // if we're simulating, don't read the EEPROM model, it's REALLY slow - if (hwconfig_simulation_p()) - return &src_mac_addr; - - eth_mac_addr_t tmp; - bool ok = eeprom_read(USRP2_I2C_ADDR_MBOARD, USRP2_EE_MBOARD_MAC_ADDR, &tmp, sizeof(tmp)); - if (!ok || unprogrammed(&tmp, sizeof(tmp))){ - // use the default + #define eth_addrs_init_x(addr, x){ \ + const bool ok = eeprom_read(USRP2_I2C_ADDR_MBOARD, addr, ¤t_eth_addrs.x, sizeof(current_eth_addrs.x)); \ + if (!ok || unprogrammed(¤t_eth_addrs.x, sizeof(current_eth_addrs.x))){ \ + memcpy(¤t_eth_addrs.x, &default_eth_addrs.x, sizeof(current_eth_addrs.x)); \ + } \ } - else - src_mac_addr = tmp; - } - return &src_mac_addr; -} + eth_addrs_init_x(USRP2_EE_MBOARD_MAC_ADDR, mac_addr); + eth_addrs_init_x(USRP2_EE_MBOARD_IP_ADDR, ip_addr); + eth_addrs_init_x(USRP2_EE_MBOARD_GATEWAY, gateway); + eth_addrs_init_x(USRP2_EE_MBOARD_SUBNET, subnet); -bool -ethernet_set_mac_addr(const eth_mac_addr_t *t) -{ - bool ok = eeprom_write(USRP2_I2C_ADDR_MBOARD, USRP2_EE_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 bool src_ip_addr_initialized = false; - -static const struct ip_addr default_ip_addr = { - (192 << 24 | 168 << 16 | 10 << 8 | 2 << 0) -}; - -static struct ip_addr src_ip_addr = { - (192 << 24 | 168 << 16 | 10 << 8 | 2 << 0) -}; - -void set_default_ip_addr(void) -{ - src_ip_addr_initialized = true; - src_ip_addr = default_ip_addr; +const eth_mac_addr_t *ethernet_mac_addr(void){ + eth_addrs_init(); + return ¤t_eth_addrs.mac_addr; } -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; +const struct ip_addr *get_ip_addr(void){ + eth_addrs_init(); + return ¤t_eth_addrs.ip_addr; +} - struct ip_addr tmp; - bool ok = eeprom_read(USRP2_I2C_ADDR_MBOARD, USRP2_EE_MBOARD_IP_ADDR, &tmp, sizeof(tmp)); - if (!ok || unprogrammed(&tmp, sizeof(tmp))){ - // use the default - } - else - src_ip_addr = tmp; - } +const struct ip_addr *get_subnet(void){ + eth_addrs_init(); + return ¤t_eth_addrs.subnet; +} - return &src_ip_addr; +const struct ip_addr *get_gateway(void){ + eth_addrs_init(); + return ¤t_eth_addrs.gateway; } bool set_ip_addr(const struct ip_addr *t){ - bool ok = eeprom_write(USRP2_I2C_ADDR_MBOARD, USRP2_EE_MBOARD_IP_ADDR, t, sizeof(struct ip_addr)); - if (ok){ - src_ip_addr = *t; - src_ip_addr_initialized = true; - } + const bool ok = eeprom_write(USRP2_I2C_ADDR_MBOARD, USRP2_EE_MBOARD_IP_ADDR, t, sizeof(struct ip_addr)); + if (ok) current_eth_addrs.ip_addr = *t; + return ok; +} - return ok; +void eth_addrs_set_default(void){ + eth_addrs_initialized = true; + memcpy(¤t_eth_addrs, &default_eth_addrs, sizeof(default_eth_addrs)); } diff --git a/firmware/zpu/lib/ethernet.h b/firmware/zpu/lib/ethernet.h index 52b297349..b5b08cb8c 100644 --- a/firmware/zpu/lib/ethernet.h +++ b/firmware/zpu/lib/ethernet.h @@ -1,5 +1,5 @@ -/* -*- c -*- */ /* + * Copyright 2010-2012 Ettus Research LLC * Copyright 2007 Free Software Foundation, Inc. * * This program is free software: you can redistribute it and/or modify @@ -44,27 +44,28 @@ void ethernet_register_link_changed_callback(ethernet_link_changed_callback_t cb */ const eth_mac_addr_t *ethernet_mac_addr(void); -/*!set mac addr to default*/ -void set_default_mac_addr(void); - /*! - * \brief write mac address to eeprom and begin using it + * \returns IP address */ -bool ethernet_set_mac_addr(const eth_mac_addr_t *t); +const struct ip_addr *get_ip_addr(void); /*! - * \returns IP address + * \returns gateway address */ -const struct ip_addr *get_ip_addr(void); +const struct ip_addr *get_gateway(void); -/*!set ip addr to default*/ -void set_default_ip_addr(void); +/*! + * \returns subnet address + */ +const struct ip_addr *get_subnet(void); /*! * \brief write ip address to eeprom and begin using it */ bool set_ip_addr(const struct ip_addr *t); +//! Apply default settings to eth addrs +void eth_addrs_set_default(void); /* * \brief read RMON regs and return error mask diff --git a/firmware/zpu/lib/net_common.c b/firmware/zpu/lib/net_common.c index 42e365393..9b75006d3 100644 --- a/firmware/zpu/lib/net_common.c +++ b/firmware/zpu/lib/net_common.c @@ -1,5 +1,5 @@ /* - * Copyright 2009-2011 Ettus Research LLC + * Copyright 2009-2012 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 @@ -374,6 +374,22 @@ send_arp_reply(struct arp_eth_ipv4 *req, eth_mac_addr_t our_mac) send_pkt(t, ETHERTYPE_ARP, &reply, sizeof(reply), 0, 0, 0, 0); } +void net_common_send_arp_request(const struct ip_addr *addr){ + struct arp_eth_ipv4 req _AL4; + req.ar_hrd = ARPHRD_ETHER; + req.ar_pro = ETHERTYPE_IPV4; + req.ar_hln = sizeof(eth_mac_addr_t); + req.ar_pln = sizeof(struct ip_addr); + req.ar_op = ARPOP_REQUEST; + memcpy(req.ar_sha, ethernet_mac_addr(), sizeof(eth_mac_addr_t)); + memcpy(req.ar_sip, get_ip_addr(), sizeof(struct ip_addr)); + memset(req.ar_tha, 0x00, sizeof(eth_mac_addr_t)); + memcpy(req.ar_tip, addr, sizeof(struct ip_addr)); + + //send the request with a broadcast ethernet mac address + send_pkt(BCAST_MAC_ADDR, ETHERTYPE_ARP, &req, sizeof(req), 0, 0, 0, 0); +} + void send_gratuitous_arp(void){ struct arp_eth_ipv4 req _AL4; req.ar_hrd = ARPHRD_ETHER; @@ -415,7 +431,15 @@ handle_arp_packet(struct arp_eth_ipv4 *p, size_t size) || p->ar_hln != 6 || p->ar_pln != 4) return; - + + if (p->ar_op == ARPOP_REPLY){ + struct ip_addr ip_addr; + memcpy(&ip_addr, p->ar_sip, sizeof(ip_addr)); + eth_mac_addr_t mac_addr; + memcpy(&mac_addr, p->ar_sha, sizeof(mac_addr)); + arp_cache_update(&ip_addr, &mac_addr); + } + if (p->ar_op != ARPOP_REQUEST) return; diff --git a/firmware/zpu/lib/net_common.h b/firmware/zpu/lib/net_common.h index 3cbc5c514..5e6daf689 100644 --- a/firmware/zpu/lib/net_common.h +++ b/firmware/zpu/lib/net_common.h @@ -1,5 +1,5 @@ /* - * Copyright 2009-2011 Ettus Research LLC + * Copyright 2009-2012 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 @@ -55,4 +55,7 @@ void handle_eth_packet(uint32_t *p, size_t nlines); void send_gratuitous_arp(void); +//! Send an ARP request for the given IP address +void net_common_send_arp_request(const struct ip_addr *addr); + #endif /* INCLUDED_NET_COMMON_H */ diff --git a/firmware/zpu/usrp2p/u2p_init.c b/firmware/zpu/usrp2p/u2p_init.c index 381987ae6..1890dd726 100644 --- a/firmware/zpu/usrp2p/u2p_init.c +++ b/firmware/zpu/usrp2p/u2p_init.c @@ -24,7 +24,6 @@ void u2p_init(void){ bool safe_fw = find_safe_booted_flag(); set_safe_booted_flag(0); if (safe_fw) { - set_default_ip_addr(); - set_default_mac_addr(); + eth_addrs_set_default(); } } diff --git a/host/docs/usrp2.rst b/host/docs/usrp2.rst index d81440b07..bf42e70b9 100644 --- a/host/docs/usrp2.rst +++ b/host/docs/usrp2.rst @@ -315,6 +315,55 @@ the following clock configuration must be set on the slave device: clock_config.pps_source = uhd::clock_config_t::PPS_MIMO; usrp->set_clock_config(clock_config, slave_index); + +------------------------------------------------------------------------ +Alternative stream destination +------------------------------------------------------------------------ +It is possible to program the USRP to send RX packets to an alternative IP/UDP destination. + +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +Set the subnet and gateway +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +To use an alternative streaming destination, +the device needs to be able to determine if the destination address +is within its subnet, and ARP appropriately. +Therefore, the user should ensure that subnet and gateway addresses +have been programmed into the device's EEPROM. + +Run the following commands: +:: + + cd <install-path>/share/uhd/utils + ./usrp_burn_mb_eeprom --args=<optional device args> --key=subnet --val=255.255.255.0 + ./usrp_burn_mb_eeprom --args=<optional device args> --key=gateway --val=192.168.10.1 + +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +Create a receive streamer +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +Set the stream args "addr" and "port" values to the alternative destination. +Packets will be sent to this destination when the user issues a stream command. + +:: + + //create a receive streamer, host type does not matter + uhd::stream_args_t stream_args("fc32"); + + //resolvable address and port for a remote udp socket + stream_args.args["addr"] = "192.168.10.42"; + stream_args.args["port"] = "12345"; + + //create the streamer + uhd::rx_streamer::sptr rx_stream = usrp->get_rx_stream(stream_args); + + //issue stream command + uhd::stream_cmd_t stream_cmd(uhd::stream_cmd_t::STREAM_MODE_NUM_SAMPS_AND_DONE); + stream_cmd.num_samps = total_num_samps; + stream_cmd.stream_now = true; + usrp->issue_stream_cmd(stream_cmd); + +**Note:** +Calling recv() on this streamer object should yield a timeout. + ------------------------------------------------------------------------ Hardware setup notes ------------------------------------------------------------------------ diff --git a/host/lib/usrp/common/fx2_ctrl.cpp b/host/lib/usrp/common/fx2_ctrl.cpp index 7b8920eb1..5cc701eb0 100644 --- a/host/lib/usrp/common/fx2_ctrl.cpp +++ b/host/lib/usrp/common/fx2_ctrl.cpp @@ -411,6 +411,26 @@ public: return usrp_control_write(request, value, index, 0, 0); } + void write_eeprom( + boost::uint8_t addr, + boost::uint8_t offset, + const byte_vector_t &bytes + ){ + byte_vector_t bytes_with_cmd(bytes.size() + 1); + bytes_with_cmd[0] = offset; + std::copy(bytes.begin(), bytes.end(), &bytes_with_cmd[1]); + this->write_i2c(addr, bytes_with_cmd); + } + + byte_vector_t read_eeprom( + boost::uint8_t addr, + boost::uint8_t offset, + size_t num_bytes + ){ + this->write_i2c(addr, byte_vector_t(1, offset)); + return this->read_i2c(addr, num_bytes); + } + int usrp_i2c_write(boost::uint16_t i2c_addr, unsigned char *buf, boost::uint16_t len) { return usrp_control_write(VRQ_I2C_WRITE, i2c_addr, 0, buf, len); @@ -428,12 +448,7 @@ public: { UHD_ASSERT_THROW(bytes.size() < max_i2c_data_bytes); - unsigned char buff[max_i2c_data_bytes] = {}; - std::copy(bytes.begin(), bytes.end(), buff); - - int ret = this->usrp_i2c_write(addr & 0xff, - buff, - bytes.size()); + int ret = this->usrp_i2c_write(addr, (unsigned char *)&bytes.front(), bytes.size()); if (iface_debug && (ret < 0)) uhd::runtime_error("USRP: failed i2c write"); @@ -443,19 +458,13 @@ public: { UHD_ASSERT_THROW(num_bytes < max_i2c_data_bytes); - unsigned char buff[max_i2c_data_bytes] = {}; - int ret = this->usrp_i2c_read(addr & 0xff, - buff, - num_bytes); + byte_vector_t bytes(num_bytes); + int ret = this->usrp_i2c_read(addr, (unsigned char *)&bytes.front(), num_bytes); if (iface_debug && ((ret < 0) || (unsigned)ret < (num_bytes))) uhd::runtime_error("USRP: failed i2c read"); - byte_vector_t out_bytes; - for (size_t i = 0; i < num_bytes; i++) - out_bytes.push_back(buff[i]); - - return out_bytes; + return bytes; } diff --git a/host/lib/usrp/mboard_eeprom.cpp b/host/lib/usrp/mboard_eeprom.cpp index 785d30296..96a0d36ce 100644 --- a/host/lib/usrp/mboard_eeprom.cpp +++ b/host/lib/usrp/mboard_eeprom.cpp @@ -1,5 +1,5 @@ // -// Copyright 2010-2011 Ettus Research LLC +// Copyright 2010-2012 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 @@ -84,17 +84,20 @@ static std::string uint16_bytes_to_string(const byte_vector_t &bytes){ **********************************************************************/ static const boost::uint8_t N100_EEPROM_ADDR = 0x50; -static const uhd::dict<std::string, boost::uint8_t> USRP_N100_OFFSETS = boost::assign::map_list_of - ("hardware", 0x00) - ("mac-addr", 0x02) - ("ip-addr", 0x0C) - //leave space here for other addresses (perhaps) - ("revision", 0x12) - ("product", 0x14) - ("gpsdo", 0x17) - ("serial", 0x18) - ("name", 0x18 + SERIAL_LEN) -; +struct n100_eeprom_map{ + boost::uint16_t hardware; + boost::uint8_t mac_addr[6]; + boost::uint32_t subnet; + boost::uint32_t ip_addr; + boost::uint16_t _pad0; + boost::uint16_t revision; + boost::uint16_t product; + unsigned char _pad1; + unsigned char gpsdo; + unsigned char serial[SERIAL_LEN]; + unsigned char name[NAME_MAX_LEN]; + boost::uint32_t gateway; +}; enum n200_gpsdo_type{ N200_GPSDO_NONE = 0, @@ -105,30 +108,36 @@ enum n200_gpsdo_type{ static void load_n100(mboard_eeprom_t &mb_eeprom, i2c_iface &iface){ //extract the hardware number mb_eeprom["hardware"] = uint16_bytes_to_string( - iface.read_eeprom(N100_EEPROM_ADDR, USRP_N100_OFFSETS["hardware"], 2) + iface.read_eeprom(N100_EEPROM_ADDR, offsetof(n100_eeprom_map, hardware), 2) ); //extract the revision number mb_eeprom["revision"] = uint16_bytes_to_string( - iface.read_eeprom(N100_EEPROM_ADDR, USRP_N100_OFFSETS["revision"], 2) + iface.read_eeprom(N100_EEPROM_ADDR, offsetof(n100_eeprom_map, revision), 2) ); //extract the product code mb_eeprom["product"] = uint16_bytes_to_string( - iface.read_eeprom(N100_EEPROM_ADDR, USRP_N100_OFFSETS["product"], 2) + iface.read_eeprom(N100_EEPROM_ADDR, offsetof(n100_eeprom_map, product), 2) ); //extract the addresses mb_eeprom["mac-addr"] = mac_addr_t::from_bytes(iface.read_eeprom( - N100_EEPROM_ADDR, USRP_N100_OFFSETS["mac-addr"], 6 + N100_EEPROM_ADDR, offsetof(n100_eeprom_map, mac_addr), 6 )).to_string(); boost::asio::ip::address_v4::bytes_type ip_addr_bytes; - byte_copy(iface.read_eeprom(N100_EEPROM_ADDR, USRP_N100_OFFSETS["ip-addr"], 4), ip_addr_bytes); + byte_copy(iface.read_eeprom(N100_EEPROM_ADDR, offsetof(n100_eeprom_map, ip_addr), 4), ip_addr_bytes); mb_eeprom["ip-addr"] = boost::asio::ip::address_v4(ip_addr_bytes).to_string(); + byte_copy(iface.read_eeprom(N100_EEPROM_ADDR, offsetof(n100_eeprom_map, subnet), 4), ip_addr_bytes); + mb_eeprom["subnet"] = boost::asio::ip::address_v4(ip_addr_bytes).to_string(); + + byte_copy(iface.read_eeprom(N100_EEPROM_ADDR, offsetof(n100_eeprom_map, gateway), 4), ip_addr_bytes); + mb_eeprom["gateway"] = boost::asio::ip::address_v4(ip_addr_bytes).to_string(); + //gpsdo capabilities - boost::uint8_t gpsdo_byte = iface.read_eeprom(N100_EEPROM_ADDR, USRP_N100_OFFSETS["gpsdo"], 1).at(0); + boost::uint8_t gpsdo_byte = iface.read_eeprom(N100_EEPROM_ADDR, offsetof(n100_eeprom_map, gpsdo), 1).at(0); switch(n200_gpsdo_type(gpsdo_byte)){ case N200_GPSDO_INTERNAL: mb_eeprom["gpsdo"] = "internal"; break; case N200_GPSDO_ONBOARD: mb_eeprom["gpsdo"] = "onboard"; break; @@ -137,12 +146,12 @@ static void load_n100(mboard_eeprom_t &mb_eeprom, i2c_iface &iface){ //extract the serial mb_eeprom["serial"] = bytes_to_string(iface.read_eeprom( - N100_EEPROM_ADDR, USRP_N100_OFFSETS["serial"], SERIAL_LEN + N100_EEPROM_ADDR, offsetof(n100_eeprom_map, serial), SERIAL_LEN )); //extract the name mb_eeprom["name"] = bytes_to_string(iface.read_eeprom( - N100_EEPROM_ADDR, USRP_N100_OFFSETS["name"], NAME_MAX_LEN + N100_EEPROM_ADDR, offsetof(n100_eeprom_map, name), NAME_MAX_LEN )); //Empty serial correction: use the mac address to determine serial. @@ -158,32 +167,44 @@ static void load_n100(mboard_eeprom_t &mb_eeprom, i2c_iface &iface){ static void store_n100(const mboard_eeprom_t &mb_eeprom, i2c_iface &iface){ //parse the revision number if (mb_eeprom.has_key("hardware")) iface.write_eeprom( - N100_EEPROM_ADDR, USRP_N100_OFFSETS["hardware"], + N100_EEPROM_ADDR, offsetof(n100_eeprom_map, hardware), string_to_uint16_bytes(mb_eeprom["hardware"]) ); //parse the revision number if (mb_eeprom.has_key("revision")) iface.write_eeprom( - N100_EEPROM_ADDR, USRP_N100_OFFSETS["revision"], + N100_EEPROM_ADDR, offsetof(n100_eeprom_map, revision), string_to_uint16_bytes(mb_eeprom["revision"]) ); //parse the product code if (mb_eeprom.has_key("product")) iface.write_eeprom( - N100_EEPROM_ADDR, USRP_N100_OFFSETS["product"], + N100_EEPROM_ADDR, offsetof(n100_eeprom_map, product), string_to_uint16_bytes(mb_eeprom["product"]) ); //store the addresses if (mb_eeprom.has_key("mac-addr")) iface.write_eeprom( - N100_EEPROM_ADDR, USRP_N100_OFFSETS["mac-addr"], + N100_EEPROM_ADDR, offsetof(n100_eeprom_map, mac_addr), mac_addr_t::from_string(mb_eeprom["mac-addr"]).to_bytes() ); if (mb_eeprom.has_key("ip-addr")){ byte_vector_t ip_addr_bytes(4); byte_copy(boost::asio::ip::address_v4::from_string(mb_eeprom["ip-addr"]).to_bytes(), ip_addr_bytes); - iface.write_eeprom(N100_EEPROM_ADDR, USRP_N100_OFFSETS["ip-addr"], ip_addr_bytes); + iface.write_eeprom(N100_EEPROM_ADDR, offsetof(n100_eeprom_map, ip_addr), ip_addr_bytes); + } + + if (mb_eeprom.has_key("subnet")){ + byte_vector_t ip_addr_bytes(4); + byte_copy(boost::asio::ip::address_v4::from_string(mb_eeprom["subnet"]).to_bytes(), ip_addr_bytes); + iface.write_eeprom(N100_EEPROM_ADDR, offsetof(n100_eeprom_map, subnet), ip_addr_bytes); + } + + if (mb_eeprom.has_key("gateway")){ + byte_vector_t ip_addr_bytes(4); + byte_copy(boost::asio::ip::address_v4::from_string(mb_eeprom["gateway"]).to_bytes(), ip_addr_bytes); + iface.write_eeprom(N100_EEPROM_ADDR, offsetof(n100_eeprom_map, gateway), ip_addr_bytes); } //gpsdo capabilities @@ -191,18 +212,18 @@ static void store_n100(const mboard_eeprom_t &mb_eeprom, i2c_iface &iface){ boost::uint8_t gpsdo_byte = N200_GPSDO_NONE; if (mb_eeprom["gpsdo"] == "internal") gpsdo_byte = N200_GPSDO_INTERNAL; if (mb_eeprom["gpsdo"] == "onboard") gpsdo_byte = N200_GPSDO_ONBOARD; - iface.write_eeprom(N100_EEPROM_ADDR, USRP_N100_OFFSETS["gpsdo"], byte_vector_t(1, gpsdo_byte)); + iface.write_eeprom(N100_EEPROM_ADDR, offsetof(n100_eeprom_map, gpsdo), byte_vector_t(1, gpsdo_byte)); } //store the serial if (mb_eeprom.has_key("serial")) iface.write_eeprom( - N100_EEPROM_ADDR, USRP_N100_OFFSETS["serial"], + N100_EEPROM_ADDR, offsetof(n100_eeprom_map, serial), string_to_bytes(mb_eeprom["serial"], SERIAL_LEN) ); //store the name if (mb_eeprom.has_key("name")) iface.write_eeprom( - N100_EEPROM_ADDR, USRP_N100_OFFSETS["name"], + N100_EEPROM_ADDR, offsetof(n100_eeprom_map, name), string_to_bytes(mb_eeprom["name"], NAME_MAX_LEN) ); } diff --git a/host/lib/usrp/usrp2/fw_common.h b/host/lib/usrp/usrp2/fw_common.h index 0babf7445..2add74c47 100644 --- a/host/lib/usrp/usrp2/fw_common.h +++ b/host/lib/usrp/usrp2/fw_common.h @@ -37,6 +37,13 @@ extern "C" { //used to differentiate control packets over data port #define USRP2_INVALID_VRT_HEADER 0 +typedef struct{ + uint32_t sequence; + uint32_t vrt_hdr; + uint32_t ip_addr; + uint32_t udp_port; +} usrp2_stream_ctrl_t; + // udp ports for the usrp2 communication // Dynamic and/or private ports: 49152-65535 #define USRP2_UDP_CTRL_PORT 49152 @@ -65,6 +72,8 @@ extern "C" { //////////////////////////////////////////////////////////////////////// #define USRP2_EE_MBOARD_REV 0x00 //2 bytes, little-endian (historic, don't blame me) #define USRP2_EE_MBOARD_MAC_ADDR 0x02 //6 bytes +#define USRP2_EE_MBOARD_GATEWAY 0x38 //uint32, big-endian +#define USRP2_EE_MBOARD_SUBNET 0x08 //uint32, big-endian #define USRP2_EE_MBOARD_IP_ADDR 0x0C //uint32, big-endian #define USRP2_EE_MBOARD_BOOTLOADER_FLAGS 0xF7 diff --git a/host/lib/usrp/usrp2/io_impl.cpp b/host/lib/usrp/usrp2/io_impl.cpp index d32ffb62c..ea4aa716c 100644 --- a/host/lib/usrp/usrp2/io_impl.cpp +++ b/host/lib/usrp/usrp2/io_impl.cpp @@ -21,6 +21,7 @@ #include "../../transport/super_send_packet_handler.hpp" #include "usrp2_impl.hpp" #include "usrp2_regs.hpp" +#include "fw_common.h" #include <uhd/utils/log.hpp> #include <uhd/utils/msg.hpp> #include <uhd/utils/tasks.hpp> @@ -31,6 +32,7 @@ #include <boost/thread/thread.hpp> #include <boost/format.hpp> #include <boost/bind.hpp> +#include <boost/asio.hpp> #include <boost/thread/mutex.hpp> #include <boost/make_shared.hpp> #include <iostream> @@ -361,6 +363,60 @@ bool usrp2_impl::recv_async_msg( } /*********************************************************************** + * Stream destination programmer + **********************************************************************/ +void usrp2_impl::program_stream_dest( + zero_copy_if::sptr &xport, const uhd::stream_args_t &args +){ + //perform an initial flush of transport + while (xport->get_recv_buff(0.0)){} + + //program the stream command + usrp2_stream_ctrl_t stream_ctrl = usrp2_stream_ctrl_t(); + stream_ctrl.sequence = uhd::htonx(boost::uint32_t(0 /* don't care seq num */)); + stream_ctrl.vrt_hdr = uhd::htonx(boost::uint32_t(USRP2_INVALID_VRT_HEADER)); + + //user has provided an alternative address and port for destination + if (args.args.has_key("addr") and args.args.has_key("port")){ + UHD_MSG(status) << boost::format( + "Programming streaming destination for custom address.\n" + "IPv4 Address: %s, UDP Port: %s\n" + ) % args.args["addr"] % args.args["port"] << std::endl; + + asio::io_service io_service; + asio::ip::udp::resolver resolver(io_service); + asio::ip::udp::resolver::query query(asio::ip::udp::v4(), args.args["addr"], args.args["port"]); + asio::ip::udp::endpoint endpoint = *resolver.resolve(query); + stream_ctrl.ip_addr = uhd::htonx(boost::uint32_t(endpoint.address().to_v4().to_ulong())); + stream_ctrl.udp_port = uhd::htonx(boost::uint32_t(endpoint.port())); + + for (size_t i = 0; i < 3; i++){ + UHD_MSG(status) << "ARP attempt " << i << std::endl; + managed_send_buffer::sptr send_buff = xport->get_send_buff(); + std::memcpy(send_buff->cast<void *>(), &stream_ctrl, sizeof(stream_ctrl)); + send_buff->commit(sizeof(stream_ctrl)); + boost::this_thread::sleep(boost::posix_time::milliseconds(300)); + managed_recv_buffer::sptr recv_buff = xport->get_recv_buff(0.0); + if (recv_buff and recv_buff->size() >= sizeof(boost::uint32_t)){ + const boost::uint32_t result = uhd::ntohx(recv_buff->cast<const boost::uint32_t *>()[0]); + if (result == 0){ + UHD_MSG(status) << "Success! " << std::endl; + return; + } + } + } + throw uhd::runtime_error("Device failed to ARP when programming alternative streaming destination."); + } + + else{ + //send the partial stream control without destination + managed_send_buffer::sptr send_buff = xport->get_send_buff(); + std::memcpy(send_buff->cast<void *>(), &stream_ctrl, sizeof(stream_ctrl)); + send_buff->commit(sizeof(stream_ctrl)/2); + } +} + +/*********************************************************************** * Receive streamer **********************************************************************/ rx_streamer::sptr usrp2_impl::get_rx_stream(const uhd::stream_args_t &args_){ @@ -406,6 +462,7 @@ rx_streamer::sptr usrp2_impl::get_rx_stream(const uhd::stream_args_t &args_){ const size_t dsp = chan + _mbc[mb].rx_chan_occ - num_chan_so_far; _mbc[mb].rx_dsps[dsp]->set_nsamps_per_packet(spp); //seems to be a good place to set this _mbc[mb].rx_dsps[dsp]->setup(args); + this->program_stream_dest(_mbc[mb].rx_dsp_xports[dsp], args); my_streamer->set_xport_chan_get_buff(chan_i, boost::bind( &zero_copy_if::get_recv_buff, _mbc[mb].rx_dsp_xports[dsp], _1 ), true /*flush*/); diff --git a/host/lib/usrp/usrp2/usrp2_impl.hpp b/host/lib/usrp/usrp2/usrp2_impl.hpp index e5065c02d..6053c3890 100644 --- a/host/lib/usrp/usrp2/usrp2_impl.hpp +++ b/host/lib/usrp/usrp2/usrp2_impl.hpp @@ -129,6 +129,7 @@ private: double set_tx_dsp_freq(const std::string &, const double); uhd::meta_range_t get_tx_dsp_freq_range(const std::string &); void update_clock_source(const std::string &, const std::string &); + void program_stream_dest(uhd::transport::zero_copy_if::sptr &, const uhd::stream_args_t &); }; #endif /* INCLUDED_USRP2_IMPL_HPP */ |