Merged r11377:11390 from jcorgan/usrp-headers in to trunk.
[debian/gnuradio] / usrp / limbo / inband / usrp_usb_interface.cc
1 /* -*- c++ -*- */
2 /*
3  * Copyright 2007,2008,2009 Free Software Foundation, Inc.
4  * 
5  * This file is part of GNU Radio
6  * 
7  * GNU Radio is free software; you can redistribute it and/or modify
8  * it under the terms of the GNU General Public License as published by
9  * the Free Software Foundation; either version 3, or (at your option)
10  * any later version.
11  * 
12  * GNU Radio is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * GNU General Public License for more details.
16  * 
17  * You should have received a copy of the GNU General Public License along
18  * with this program; if not, write to the Free Software Foundation, Inc.,
19  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
20  */
21
22 #ifdef HAVE_CONFIG_H
23 #include <config.h>
24 #endif
25
26 #include <usrp_usb_interface.h>
27
28 #include <iostream>
29 #include <vector>
30 #include <usb.h>
31 #include <mblock/class_registry.h>
32 #include <usrp_inband_usb_packet.h>
33 #include <fpga_regs_common.h>
34 #include "usrp_rx.h"
35 #include <usrp_rx_stub.h>
36 #include "usrp_tx.h"
37 #include "usrp_standard.h"
38 #include <stdio.h>
39 #include <usrp_dbid.h>
40
41 typedef usrp_inband_usb_packet transport_pkt;
42
43 #include <symbols_usrp_interface_cs.h>
44 #include <symbols_usrp_tx_cs.h>
45 #include <symbols_usrp_rx_cs.h>
46 static pmt_t s_shutdown = pmt_intern("%shutdown");
47
48 static const bool verbose = false;
49
50
51 /*!
52  * \brief Initializes the USB interface m-block.
53  *
54  * The \p user_arg should be a PMT dictionary which can contain optional
55  * arguments for the block, such as the decimatoin and interpolation rate.
56  */
57 usrp_usb_interface::usrp_usb_interface(mb_runtime *rt, const std::string &instance_name, pmt_t user_arg)
58   : mb_mblock(rt, instance_name, user_arg),
59   d_fake_usrp(false),
60   d_rx_reading(false),
61   d_interp_tx(128),
62   d_decim_rx(128),
63   d_rf_freq(-1),
64   d_rbf("inband_tx_rx.rbf")
65 {
66   // Dictionary for arguments to all of the components
67   pmt_t usrp_dict = user_arg;
68   
69   // Default TX/RX interface
70   std::string tx_interface = "usrp_tx";
71   std::string rx_interface = "usrp_rx";
72   
73   if (pmt_is_dict(usrp_dict)) {
74
75     // The 'fake-usrp' key enables the TX and RX stubs if PMT_T
76     if(pmt_t fake_usrp = pmt_dict_ref(usrp_dict, 
77                                       pmt_intern("fake-usrp"), 
78                                       PMT_NIL)) {
79       if(pmt_eqv(fake_usrp, PMT_T)) {
80         tx_interface = "usrp_tx_stub";
81         rx_interface = "usrp_rx_stub";
82         d_fake_usrp=true;
83       }
84     }
85
86     // Read the TX interpolations
87     if(pmt_t interp_tx = pmt_dict_ref(usrp_dict, 
88                                       pmt_intern("interp-tx"), 
89                                       PMT_NIL)) {
90       if(!pmt_eqv(interp_tx, PMT_NIL)) 
91         d_interp_tx = pmt_to_long(interp_tx);
92     }
93     
94     // Read the RX decimation rate
95     if(pmt_t decim_rx = pmt_dict_ref(usrp_dict, 
96                                       pmt_intern("decim-rx"), 
97                                       PMT_NIL)) {
98       if(!pmt_eqv(decim_rx, PMT_NIL)) 
99         d_decim_rx = pmt_to_long(decim_rx);
100     }
101
102     // Read the RBF
103     if(pmt_t rbf = pmt_dict_ref(usrp_dict, 
104                                 pmt_intern("rbf"), 
105                                 PMT_NIL)) {
106       if(!pmt_eqv(rbf, PMT_NIL)) 
107         d_rbf = pmt_symbol_to_string(rbf);
108     }
109
110     // The RF center frequency
111     if(pmt_t rf_freq = pmt_dict_ref(usrp_dict, 
112                                 pmt_intern("rf-freq"), 
113                                 PMT_NIL)) {
114       if(!pmt_eqv(rf_freq, PMT_NIL)) 
115         d_rf_freq = pmt_to_double(rf_freq);
116     }
117   }
118   
119   if (verbose) {
120     std::cout << "[USRP_USB_INTERFACE] Setting USRP RBF to " 
121               << d_rbf << std::endl;
122     
123     std::cout << "[USRP_USB_INTERFACE] Setting TX interpolation to " 
124               << d_interp_tx << std::endl;
125           
126     std::cout << "[USRP_USB_INTERFACE] Setting RX interpolation to " 
127               << d_decim_rx << std::endl;
128
129     std::cout << "[USRP_USB_INTERFACE] Using TX interface: " 
130               << tx_interface << "\n";
131
132     std::cout << "[USRP_USB_INTERFACE] Using RX interface: " 
133               << rx_interface << "\n";
134
135   }
136
137   d_cs = define_port("cs", "usrp-interface-cs", true, mb_port::EXTERNAL);       
138   d_rx_cs = define_port("rx_cs", "usrp-rx-cs", false, mb_port::INTERNAL);       
139   d_tx_cs = define_port("tx_cs", "usrp-tx-cs", false, mb_port::INTERNAL);       
140
141   // Connect to TX and RX
142   define_component("tx", tx_interface, usrp_dict);
143   define_component("rx", rx_interface, usrp_dict);
144   connect("self", "rx_cs", "rx", "cs");
145   connect("self", "tx_cs", "tx", "cs");
146   
147   // FIXME: the code should query the FPGA to retrieve the number of channels and such
148   d_ntx_chan = 2;
149   d_nrx_chan = 2;
150 }
151
152 usrp_usb_interface::~usrp_usb_interface() 
153
154
155 }
156
157 void 
158 usrp_usb_interface::initial_transition()
159 {
160
161 }
162
163 /*!
164  * \brief Handles all incoming signals to the block from the lowest m-blocks
165  * which read/write to the bus, or the higher m-block which is the USRP server.
166  */
167 void
168 usrp_usb_interface::handle_message(mb_message_sptr msg)
169 {
170   pmt_t event = msg->signal();          // the "name" of the message
171   pmt_t port_id = msg->port_id();       // which port it came in on
172   pmt_t data = msg->data();
173   pmt_t invocation_handle;
174
175   if (pmt_eq(event, s_shutdown))        // ignore (for now)
176     return;
177
178   //------------- CONTROL / STATUS -------------//
179   if (pmt_eq(port_id, d_cs->port_symbol())) {   
180
181     //------------ OPEN --------------//
182     if (pmt_eq(event, s_cmd_usrp_open)){
183       handle_cmd_open(data);
184       return;
185     }
186     //----------- CLOSE -------------//
187     else if (pmt_eq(event, s_cmd_usrp_close)) {
188       handle_cmd_close(data);
189       return;
190     }
191     //---------- NTX CHAN ----------//
192     else if (pmt_eq(event, s_cmd_usrp_ntx_chan)) {
193       invocation_handle = pmt_nth(0, data);
194       d_cs->send(s_response_usrp_ntx_chan, 
195                  pmt_list2(invocation_handle, 
196                            pmt_from_long(d_ntx_chan)));
197       return;
198     }
199     //---------- NRX CHAN ----------//
200     else if (pmt_eq(event, s_cmd_usrp_nrx_chan)) {
201       invocation_handle = pmt_nth(0, data);
202       d_cs->send(s_response_usrp_nrx_chan, 
203                  pmt_list2(invocation_handle, 
204                            pmt_from_long(d_nrx_chan)));
205       return;
206     }
207     //------------ WRITE -----------//
208     else if(pmt_eq(event, s_cmd_usrp_write)) {
209       handle_cmd_write(data);
210       return;
211     }
212     //-------- START READING --------//
213     else if(pmt_eq(event, s_cmd_usrp_start_reading)) {
214       handle_cmd_start_reading(data);
215       return;
216     }
217     //-------- STOP READING --------//
218     else if(pmt_eq(event, s_cmd_usrp_stop_reading)) {
219       handle_cmd_stop_reading(data);
220       return;
221     }
222
223     goto unhandled;
224   }
225
226   //---------------- RX ------------------//
227   if (pmt_eq(port_id, d_rx_cs->port_symbol())) {        
228
229     // Relay reads back up
230     if(pmt_eq(event, s_response_usrp_rx_read))  {
231       d_cs->send(s_response_usrp_read, data);
232       return;
233     }
234
235     goto unhandled;
236   }
237   
238   //---------------- TX ------------------//
239   if (pmt_eq(port_id, d_tx_cs->port_symbol())) {        
240
241     if(pmt_eq(event, s_response_usrp_tx_write))  {
242
243       pmt_t invocation_handle = pmt_nth(0, data);
244       pmt_t status = pmt_nth(1, data);
245       pmt_t channel = pmt_nth(2, data);
246
247       d_cs->send(s_response_usrp_write,
248                  pmt_list3(invocation_handle,
249                            status,
250                            channel));
251
252       return;
253     }
254
255     goto unhandled;
256   }
257
258  unhandled:
259   std::cout << "[USRP_USB_INTERFACE] unhandled msg: " << msg << std::endl;
260 }
261
262 /*!
263  * \brief Called by the handle_message() method when the incoming signal is to
264  * open a USB connection to the USRP (cmd-usrp-open).
265  *
266  * The \p data parameter is a PMT list, where the elements are an invocation
267  * handle and the USRP number.
268  */
269 void
270 usrp_usb_interface::handle_cmd_open(pmt_t data)
271 {
272   pmt_t invocation_handle = pmt_nth(0, data);
273   long which_usrp = pmt_to_long(pmt_nth(1, data));
274   pmt_t reply_data;
275  
276   if(d_fake_usrp) {
277     d_cs->send(s_response_usrp_open, pmt_list2(invocation_handle, PMT_T));
278     return;
279   }
280
281   if (verbose)
282     std::cout << "[USRP_USB_INTERFACE] Handling open request for USRP " << which_usrp << "\n";
283
284   // Open up a standard RX and TX for communication with the USRP
285    
286   d_utx = usrp_standard_tx::make(which_usrp,
287                                  d_interp_tx,
288                                  1,                     // 1 channel
289                                  -1,          // mux
290                                  4096,        // USB block size
291                                  16,          // nblocks for async transfers
292                                  d_rbf
293                                  );
294   
295   if(d_utx==0) {
296     if (verbose)
297       std::cout << "[USRP_USB_INTERFACE] Failed to open TX\n";
298     reply_data = pmt_list2(invocation_handle, PMT_F);
299     d_cs->send(s_response_usrp_open, reply_data);
300     return;
301   }
302
303   // Perform TX daughterboard tuning
304   double target_freq;
305   unsigned int mux;
306   int tgain, rgain;
307   float input_rate;
308   bool ok;
309   usrp_tune_result r;
310
311   // Cast to usrp_basic and then detect daughterboards
312   d_ub_tx = d_utx;
313   usrp_subdev_spec tspec = pick_tx_subdevice();
314   db_base_sptr tsubdev = d_ub_tx->selected_subdev(tspec);
315
316   // Set the TX mux value
317   mux = d_utx->determine_tx_mux_value(tspec);
318   d_utx->set_mux(mux);
319   
320   // Set the TX gain and determine rate
321   tgain = tsubdev->gain_max();
322   tsubdev->set_gain(tgain);
323   input_rate = d_ub_tx->converter_rate() / d_utx->interp_rate();
324
325   // Perform the actual tuning, if no frequency specified then pick
326   if(d_rf_freq==-1)
327     target_freq = tsubdev->freq_min()+((tsubdev->freq_max()-tsubdev->freq_min())/2.0);
328   else 
329     target_freq = d_rf_freq;
330   ok = d_utx->tune(tsubdev->which(), tsubdev, target_freq, &r);
331   tsubdev->set_enable(true);
332   
333   if(verbose) {
334     printf("TX Subdevice name is %s\n", tsubdev->name().c_str());
335     printf("TX Subdevice freq range: (%g, %g)\n",
336        tsubdev->freq_min(), tsubdev->freq_max());
337     printf("mux: %#08x\n",  mux);
338     printf("target_freq:     %f\n", target_freq);
339     printf("ok:              %s\n", ok ? "true" : "false");
340     printf("gain:            %d\n", tgain);
341     printf("r.baseband_freq: %f\n", r.baseband_freq);
342     printf("r.dxc_freq:      %f\n", r.dxc_freq);
343     printf("r.residual_freq: %f\n", r.residual_freq);
344     printf("r.inverted:      %d\n", r.inverted);
345   }
346
347   if(!ok) {
348     std::cerr << "[USRP_USB_INTERFACE] Failed to set center frequency on TX\n";
349     reply_data = pmt_list2(invocation_handle, PMT_F);
350     d_cs->send(s_response_usrp_open, reply_data);
351     return;
352   }
353
354   d_utx->start();
355
356   if (verbose)
357     std::cout << "[USRP_USB_INTERFACE] Setup TX channel\n";
358
359   d_urx =
360     usrp_standard_rx::make (which_usrp,
361                             d_decim_rx,         
362                             1,                  // nchan
363                             -1,           // mux
364                             0,            // set blank mode to start
365                             4096,         // USB block size
366                             16,           // number of blocks for async transfers
367           d_rbf);
368
369   if(!d_urx) {
370     if (verbose)
371       std::cout << "[usrp_server] Failed to open RX\n";
372     reply_data = pmt_list2(invocation_handle, PMT_F);
373     d_cs->send(s_response_usrp_open, reply_data);
374     return;
375   }
376   
377   // Cast to usrp_basic and then detect daughterboards
378   d_ub_rx = d_urx;
379   usrp_subdev_spec rspec = pick_rx_subdevice();
380   db_base_sptr rsubdev = d_ub_rx->selected_subdev(rspec);
381
382   // Set the RX mux value
383   mux = d_urx->determine_rx_mux_value(rspec);
384   d_urx->set_mux(mux);
385   
386   // Set the RX gain and determine rate
387   rgain = rsubdev->gain_max()/2.0;
388   rsubdev->set_gain(rgain);
389   input_rate = d_ub_rx->converter_rate() / d_urx->decim_rate();
390
391   ok = d_urx->tune(rsubdev->which(), rsubdev, target_freq, &r);
392   rsubdev->set_enable(true);
393   
394   if(verbose) {
395     printf("RX Subdevice name is %s\n", rsubdev->name().c_str());
396     printf("RX Subdevice freq range: (%g, %g)\n",
397        rsubdev->freq_min(), rsubdev->freq_max());
398     printf("mux: %#08x\n",  mux);
399     printf("target_freq:     %f\n", target_freq);
400     printf("ok:              %s\n", ok ? "true" : "false");
401     printf("gain:            %d\n", rgain);
402     printf("r.baseband_freq: %f\n", r.baseband_freq);
403     printf("r.dxc_freq:      %f\n", r.dxc_freq);
404     printf("r.residual_freq: %f\n", r.residual_freq);
405     printf("r.inverted:      %d\n", r.inverted);
406   }
407   
408   if(!ok) {
409     std::cerr << "[USRP_USB_INTERFACE] Failed to set center frequency on RX\n";
410     reply_data = pmt_list2(invocation_handle, PMT_F);
411     d_cs->send(s_response_usrp_open, reply_data);
412     return;
413   }
414
415   if (verbose)
416     std::cout << "[USRP_USB_INTERFACE] Setup RX channel\n";
417     
418 //  d_utx->_write_fpga_reg(FR_DEBUG_EN,0xf);
419 //  d_utx->_write_oe(0, 0xffff, 0xffff);
420 //  d_urx->_write_oe(0, 0xffff, 0xffff);
421 //  d_utx->_write_oe(1, 0xffff, 0xffff);
422 //  d_urx->_write_oe(1, 0xffff, 0xffff);
423
424   d_cs->send(s_response_usrp_open, pmt_list2(invocation_handle, PMT_T));
425 }
426
427 /*!
428  * \brief Called by the handle_message() method when the incoming signal is to
429  * write data to the USB bus (cmd-usrp-write). 
430  *
431  * The \p data parameter is a PMT list containing 3 mandatory elements in the
432  * following order: an invocation handle, channel, and a uniform vector
433  * representation of the packets.
434  */
435 void
436 usrp_usb_interface::handle_cmd_write(pmt_t data)
437 {
438   pmt_t invocation_handle = pmt_nth(0, data);
439   pmt_t channel = pmt_nth(1, data);
440   pmt_t pkts = pmt_nth(2, data);
441
442   pmt_t tx_handle = pmt_make_any(d_utx);
443
444   d_tx_cs->send(s_cmd_usrp_tx_write, 
445                 pmt_list4(invocation_handle, 
446                           channel,
447                           pkts,
448                           tx_handle));
449 }
450
451 /*!
452  * \brief Called by the handle_message() method when the incoming signal is to
453  * start reading data from the USB bus (cmd-usrp-start-reading).
454  *
455  * The \p data parameter is a PMT list with a single element: an invocation
456  * handle which can be returned with the response.
457  */
458 void
459 usrp_usb_interface::handle_cmd_start_reading(pmt_t data)
460 {
461   pmt_t invocation_handle = pmt_nth(0, data);
462   
463   if(verbose)
464     std::cout << "[USRP_USB_INTERFACE] Starting RX...\n";
465
466   if(!d_fake_usrp)
467     d_urx->start();
468
469   pmt_t rx_handle = pmt_make_any(d_urx);
470
471   d_rx_cs->send(s_cmd_usrp_rx_start_reading, pmt_list2(PMT_NIL, rx_handle));
472
473   d_rx_reading = true;
474
475   return;
476 }
477
478 /*!
479  * \brief Called by the handle_message() method when the incoming signal is to
480  * stop reading data from the USB bus (cmd-usrp-stop-reading).
481  *
482  * The \p data parameter is a PMT list with a single element: an invocation
483  * handle which can be returned with the response.
484  */
485 void
486 usrp_usb_interface::handle_cmd_stop_reading(pmt_t data)
487 {
488   pmt_t invocation_handle = pmt_nth(0, data);
489   
490   if(!d_fake_usrp) {
491     if(verbose)
492       std::cout << "[USRP_USB_INTERFACE] Stopping RX...\n";
493     usrp_rx_stop = true;
494
495     // Used to allow a read() being called by a lower layer to complete before
496     // stopping, else there can be partial data left on the bus and can generate
497     // errors.
498     while(usrp_rx_stop) {usleep(1);}
499     d_urx->stop();
500   }
501   else {
502     if(verbose)
503       std::cout << "[USRP_USB_INTERFACE] Stopping fake RX...\n";
504     usrp_rx_stop_stub = true;  // extern to communicate with stub to wait
505   }
506
507   d_rx_reading = false;
508
509   return;
510 }
511
512 /*!
513  * \brief Called by the handle_message() method when the incoming signal is to
514  * close the USB connection to the USRP.
515  *
516  * The \p data parameter is a PMT list with a single element: an invocation
517  * handle which can be returned with the response.
518  */
519 void
520 usrp_usb_interface::handle_cmd_close(pmt_t data)
521 {
522   pmt_t invocation_handle = pmt_nth(0, data);
523
524   if(d_rx_reading)
525     handle_cmd_stop_reading(PMT_NIL);
526
527   if(d_fake_usrp) {
528     d_cs->send(s_response_usrp_close, pmt_list2(invocation_handle, PMT_T));
529     return;
530   }
531   
532   if (verbose)
533     std::cout << "[USRP_USB_INTERFACE] Handling close request for USRP\n";
534
535   d_utx.reset();
536   d_urx.reset();
537
538   d_cs->send(s_response_usrp_close, pmt_list2(invocation_handle, PMT_T));
539
540   // FIXME This seems like a _very_ strange place to be calling shutdown_all.
541   // That decision should be left to high-level code, not low-level code like this.
542   shutdown_all(PMT_T);
543 }
544
545 usrp_subdev_spec
546 usrp_usb_interface::pick_rx_subdevice()
547 {
548   int dbids[] = {
549     USRP_DBID_FLEX_400_RX,
550     USRP_DBID_FLEX_900_RX,
551     USRP_DBID_FLEX_1200_RX,
552     USRP_DBID_FLEX_2400_RX,
553     USRP_DBID_TV_RX,
554     USRP_DBID_TV_RX_REV_2,
555     USRP_DBID_DBS_RX,
556     USRP_DBID_BASIC_RX
557   };
558
559   std::vector<int> candidates(dbids, dbids+(sizeof(dbids)/sizeof(int)));
560   return pick_subdev(d_ub_rx, candidates);
561 }
562
563 usrp_subdev_spec
564 usrp_usb_interface::pick_tx_subdevice()
565 {
566   int dbids[] = {
567     USRP_DBID_FLEX_400_TX,
568     USRP_DBID_FLEX_900_TX,
569     USRP_DBID_FLEX_1200_TX,
570     USRP_DBID_FLEX_2400_TX,
571     USRP_DBID_BASIC_TX
572   };
573
574   std::vector<int> candidates(dbids, dbids+(sizeof(dbids)/sizeof(int)));
575   return pick_subdev(d_ub_tx, candidates);
576 }
577
578 usrp_subdev_spec
579 usrp_usb_interface::pick_subdev(boost::shared_ptr<usrp_basic> d_usrp_basic, std::vector<int> candidates)
580 {
581   int dbid0 = d_usrp_basic->selected_subdev(usrp_subdev_spec(0, 0))->dbid();
582   int dbid1 = d_usrp_basic->selected_subdev(usrp_subdev_spec(1, 0))->dbid();
583
584   for (int i = 0; i < candidates.size(); i++) {
585     int dbid = candidates[i];
586     if (dbid0 == dbid)
587       return usrp_subdev_spec(0, 0);
588     if (dbid1 == dbid)
589       return usrp_subdev_spec(1, 0);
590   }
591
592   if (dbid0 >= 0)
593     return usrp_subdev_spec(0, 0);
594   if (dbid1 >= 0)
595     return usrp_subdev_spec(1, 0);
596
597   throw std::runtime_error("No suitable daughterboard found!");
598 }
599
600
601 REGISTER_MBLOCK_CLASS(usrp_usb_interface);