/* -*- c++ -*- */ /* * Copyright 2008 Free Software Foundation, Inc. * * 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 . */ #ifdef HAVE_CONFIG_H #include #endif #include #include #include #include #include "ethernet.h" #include "pktfilter.h" #include #include #include #include #define FIND_DEBUG 0 // FIXME move to gruel static struct timeval time_duration_to_timeval(boost::posix_time::time_duration delta) { long total_us = delta.total_microseconds(); if (total_us < 0) throw std::invalid_argument("duration_to_time: delta is negative"); struct timeval tv; tv.tv_sec = total_us / 1000000; tv.tv_usec = total_us % 1000000; return tv; } namespace usrp2 { static props reply_to_props(const op_id_reply_t *r) { const uint8_t *mac = (const uint8_t *)&r->addr; char addr_buf[128]; snprintf(addr_buf, sizeof(addr_buf), "%02x:%02x:%02x:%02x:%02x:%02x", mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]); props p; p.addr = std::string(addr_buf); p.hw_rev = ntohs(r->hw_rev); memcpy(p.fpga_md5sum, r->fpga_md5sum, sizeof(p.fpga_md5sum)); memcpy(p.sw_md5sum, r->sw_md5sum, sizeof(p.sw_md5sum)); return p; } static void read_replies(ethernet *enet, struct timeval timeout, const std::string &target_addr, props_vector_t &result) { struct reply { u2_eth_packet_t h; op_id_reply_t op_id_reply; }; uint8_t pktbuf[ethernet::MAX_PKTLEN]; memset(pktbuf, 0, sizeof(pktbuf)); fd_set read_fds; FD_ZERO(&read_fds); FD_SET(enet->fd(), &read_fds); select(enet->fd()+1, &read_fds, 0, 0, &timeout); while(1) { memset(pktbuf, 0, sizeof(pktbuf)); int len = enet->read_packet_dont_block(pktbuf, sizeof(pktbuf)); if (len < 0){ perror("usrp2_basic: read_packet_dont_block"); return; } if (len == 0) break; reply *rp = (reply *)pktbuf; if (u2p_chan(&rp->h.fixed) != CONTROL_CHAN) // ignore continue; if (rp->op_id_reply.opcode != OP_ID_REPLY) // ignore continue; props p = reply_to_props(&rp->op_id_reply); if (FIND_DEBUG) std::cerr << "usrp2::find: response from " << p.addr << std::endl; if ((target_addr == "") || (target_addr == p.addr)) result.push_back(p); } } props_vector_t find(const std::string &ifc, const std::string &addr) { if (FIND_DEBUG) { std::cerr << "usrp2::find: Searching interface " << ifc << " for " << (addr == "" ? "all USRP2s" : addr) << std::endl; } props_vector_t result; struct command { u2_eth_packet_t h; op_generic_t op_id; }; std::auto_ptr enet(new ethernet()); if (!enet->open(ifc, htons(U2_ETHERTYPE))) return result; std::auto_ptr pf(pktfilter::make_ethertype_inbound(U2_ETHERTYPE, enet->mac())); if (!enet->attach_pktfilter(pf.get())) return result; static u2_mac_addr_t broadcast_mac_addr = {{ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }}; uint8_t pktbuf[ethernet::MAX_PKTLEN]; memset(pktbuf, 0, sizeof(pktbuf)); command *c = (command *)pktbuf; c->h.ehdr.ethertype = htons(U2_ETHERTYPE); c->h.ehdr.dst = broadcast_mac_addr; memcpy(&c->h.ehdr.src, enet->mac(), 6); c->h.thdr.flags = 0; c->h.thdr.seqno = 0; c->h.thdr.ack = 0; u2p_set_word0(&c->h.fixed, 0, CONTROL_CHAN); u2p_set_timestamp(&c->h.fixed, -1); c->op_id.opcode = OP_ID; c->op_id.len = sizeof(c->op_id); int len = std::max((size_t) ethernet::MIN_PKTLEN, sizeof(command)); if (enet->write_packet(c, len) != len) return result; if (FIND_DEBUG) std::cerr << "usrp2::find: broadcast ID command" << std::endl; /* * Gather all responses that occur within 50ms */ boost::posix_time::ptime start(boost::posix_time::microsec_clock::universal_time()); boost::posix_time::ptime limit(start + boost::posix_time::milliseconds(50)); boost::posix_time::ptime now; while (1){ now = boost::posix_time::microsec_clock::universal_time(); if (now >= limit) break; boost::posix_time::time_duration delta(limit - now); struct timeval timeout = time_duration_to_timeval(delta); read_replies(enet.get(), timeout, addr, result); } return result; } } // namespace usrp2