f6c04a58e4d30ed131dd1f1b6fb54ab83e275ed1
[debian/gnuradio] / usrp2 / host / lib / find.cc
1 /* -*- c++ -*- */
2 /*
3  * Copyright 2008 Free Software Foundation, Inc.
4  *
5  * This program is free software: you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation, either version 3 of the License, or
8  * (at your option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
17  */
18
19 #ifdef HAVE_CONFIG_H
20 #include <config.h>
21 #endif
22
23 #include <usrp2_eth_packet.h>
24 #include <usrp2/usrp2.h>
25 #include <boost/scoped_ptr.hpp>
26 #include <boost/date_time/posix_time/posix_time_types.hpp>
27 #include "ethernet.h"
28 #include "pktfilter.h"
29 #include <string.h>
30 #include <iostream>
31 #include <stdexcept>
32
33 #define FIND_DEBUG 0
34
35
36 // FIXME move to gruel
37
38 static struct timeval
39 time_duration_to_timeval(boost::posix_time::time_duration delta)
40 {
41   long total_us = delta.total_microseconds();
42   if (total_us < 0)
43     throw std::invalid_argument("duration_to_time: delta is negative");
44
45   struct timeval tv;
46   tv.tv_sec =  total_us / 1000000;
47   tv.tv_usec = total_us % 1000000;
48   return tv;
49 }
50
51
52 namespace usrp2 {
53
54   static props
55   reply_to_props(const op_id_reply_t *r)
56   {
57     const uint8_t *mac = (const uint8_t *)&r->addr;
58     char addr_buf[128];
59     snprintf(addr_buf, sizeof(addr_buf), "%02x:%02x:%02x:%02x:%02x:%02x",
60              mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
61       
62     props p;
63     p.addr = std::string(addr_buf);  
64     p.hw_rev = ntohs(r->hw_rev);
65     memcpy(p.fpga_md5sum, r->fpga_md5sum, sizeof(p.fpga_md5sum));
66     memcpy(p.sw_md5sum, r->sw_md5sum, sizeof(p.sw_md5sum));
67     return p;
68   }
69
70   static void
71   read_replies(ethernet *enet, struct timeval timeout,
72                const std::string &target_addr, props_vector_t &result)
73   {
74     struct reply {
75       u2_eth_packet_t   h;
76       op_id_reply_t     op_id_reply;
77     };
78     
79     uint8_t pktbuf[ethernet::MAX_PKTLEN];
80     memset(pktbuf, 0, sizeof(pktbuf));
81
82     fd_set read_fds;
83     FD_ZERO(&read_fds);
84     FD_SET(enet->fd(), &read_fds);
85     
86     select(enet->fd()+1, &read_fds, 0, 0, &timeout);
87     while(1) {
88       memset(pktbuf, 0, sizeof(pktbuf));
89       int len = enet->read_packet_dont_block(pktbuf, sizeof(pktbuf));
90       if (len < 0){
91         perror("usrp2_basic: read_packet_dont_block");
92         return;
93       }
94       if (len == 0)
95         break;
96       
97       reply *rp = (reply *)pktbuf;
98       if (u2p_chan(&rp->h.fixed) != CONTROL_CHAN)       // ignore
99         continue;
100       if (rp->op_id_reply.opcode != OP_ID_REPLY)        // ignore
101         continue;
102       
103       props p = reply_to_props(&rp->op_id_reply);
104       if (FIND_DEBUG)
105         std::cerr << "usrp2::find: response from " << p.addr << std::endl;
106       
107       if ((target_addr == "") || (target_addr == p.addr))
108         result.push_back(p);
109     }
110   }
111
112   props_vector_t
113   find(const std::string &ifc, const std::string &addr)
114   {
115     if (FIND_DEBUG) {
116       std::cerr << "usrp2::find: Searching interface " << ifc << " for "
117                 << (addr == "" ? "all USRP2s" : addr)
118                 << std::endl;
119     }
120     
121     props_vector_t result;
122     struct command {
123       u2_eth_packet_t   h;
124       op_generic_t      op_id;
125     };
126     
127     std::auto_ptr<ethernet> enet(new ethernet()); 
128     
129     if (!enet->open(ifc, htons(U2_ETHERTYPE)))
130       return result;
131     
132     std::auto_ptr<pktfilter> pf(pktfilter::make_ethertype_inbound(U2_ETHERTYPE, enet->mac()));
133     if (!enet->attach_pktfilter(pf.get()))
134       return result;
135     
136     static u2_mac_addr_t broadcast_mac_addr =
137       {{ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }};
138     
139     uint8_t pktbuf[ethernet::MAX_PKTLEN];
140     memset(pktbuf, 0, sizeof(pktbuf));
141     
142     command *c = (command *)pktbuf;
143     c->h.ehdr.ethertype = htons(U2_ETHERTYPE);
144     c->h.ehdr.dst = broadcast_mac_addr;
145     memcpy(&c->h.ehdr.src, enet->mac(), 6);
146     c->h.thdr.flags = 0;
147     c->h.thdr.seqno = 0;
148     c->h.thdr.ack = 0;
149     u2p_set_word0(&c->h.fixed, 0, CONTROL_CHAN);
150     u2p_set_timestamp(&c->h.fixed, -1);
151     c->op_id.opcode = OP_ID;
152     c->op_id.len = sizeof(c->op_id);
153     int len = std::max((size_t) ethernet::MIN_PKTLEN, sizeof(command));
154     if (enet->write_packet(c, len) != len)
155       return result;
156     
157     if (FIND_DEBUG)
158       std::cerr << "usrp2::find: broadcast ID command" << std::endl;
159     
160     /*
161      * Gather all responses that occur within 50ms
162      */
163     boost::posix_time::ptime start(boost::posix_time::microsec_clock::universal_time());
164     boost::posix_time::ptime limit(start + boost::posix_time::milliseconds(50));
165     boost::posix_time::ptime now;
166
167     while (1){
168       now = boost::posix_time::microsec_clock::universal_time();
169       if (now >= limit)
170         break;
171
172       boost::posix_time::time_duration delta(limit - now);
173       struct timeval timeout = time_duration_to_timeval(delta);
174
175       read_replies(enet.get(), timeout, addr, result);
176     }
177     return result;
178   }
179   
180 } // namespace usrp2
181