3 * Copyright 2007 Free Software Foundation, Inc.
5 * This file is part of GNU Radio
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)
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.
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.
28 #include <mb_mblock.h>
29 #include <mb_runtime.h>
30 #include <mb_protocol_class.h>
31 #include <mb_exception.h>
32 #include <mb_msg_queue.h>
33 #include <mb_message.h>
34 #include <mb_msg_accepter.h>
35 #include <mb_class_registry.h>
42 #include <symbols_usrp_server_cs.h>
43 #include <symbols_usrp_channel.h>
44 #include <symbols_usrp_low_level_cs.h>
45 #include <symbols_usrp_tx.h>
46 #include <symbols_usrp_rx.h>
48 #include <gmac_symbols.h>
50 static bool verbose = true;
52 gmac::gmac(mb_runtime *rt, const std::string &instance_name, pmt_t user_arg)
53 : mb_mblock(rt, instance_name, user_arg),
54 d_us_rx_chan(PMT_NIL), d_us_tx_chan(PMT_NIL)
57 // When the MAC layer is initialized, we must connect to the USRP and setup
58 // channels. We begin by defining ports to connect to the 'usrp_server' block
59 // and then initialize the USRP by opening it through the 'usrp_server.'
61 // Initialize the ports
64 // Initialize the connection to the USRP
73 // The full functionality of GMAC is based on messages passed back and forth
74 // between the application and a physical layer and/or usrp_server. Each
75 // message triggers additional events, states, and messages to be sent.
76 void gmac::handle_message(mb_message_sptr msg)
79 // The MAC functionality is dispatched based on the event, which is the
80 // driving force of the MAC. The event can be anything from incoming samples
81 // to a message to change the carrier sense threshold.
82 pmt_t event = msg->signal();
83 pmt_t data = msg->data();
84 pmt_t port_id = msg->port_id();
89 std::string error_msg;
93 //---------------------------- INIT ------------------------------------//
94 // In the INIT state, there should be no messages across the ports.
96 error_msg = "no messages should be passed during the INIT state:";
99 //-------------------------- OPENING USRP -------------------------------//
100 // In this state we expect a response from usrp_server over the CS channel
101 // as to whether or not the opening of the USRP was successful. If so, we
102 // switch states to allocating the channels for use.
105 if(pmt_eq(event, s_response_open)
106 && pmt_eq(d_us_cs->port_symbol(), port_id)) {
108 status = pmt_nth(1, data); // PMT_T or PMT_F
110 if(pmt_eq(status, PMT_T)) { // on success, allocate channels!
115 error_msg = "failed to open usrp:";
121 goto unhandled; // all other messages not handled in this state
123 //------------------------ ALLOCATING CHANNELS --------------------------//
124 // When allocating channels, we need to wait for 2 responses from USRP
125 // server: one for TX and one for RX. Both are initialized to NIL so we
126 // know to continue to the next state once both are set.
127 case ALLOCATING_CHANNELS:
129 // ************* TX ALLOCATION RESPONSE ***************** //
130 if(pmt_eq(event, s_response_allocate_channel)
131 && pmt_eq(d_us_tx->port_symbol(), port_id))
133 status = pmt_nth(1, data);
135 if(pmt_eq(status, PMT_T)) { // extract channel on success
136 d_us_tx_chan = pmt_nth(2, data);
139 std::cout << "[GMAC] Received TX allocation"
140 << " on channel " << d_us_tx_chan << std::endl;
142 // If the RX has also been allocated already, we can continue
143 if(!pmt_eqv(d_us_rx_chan, PMT_NIL)) {
150 else { // TX allocation failed
151 error_msg = "failed to allocate TX channel:";
156 // ************* RX ALLOCATION RESPONSE ****************//
157 if(pmt_eq(event, s_response_allocate_channel)
158 && pmt_eq(d_us_rx->port_symbol(), port_id))
160 status = pmt_nth(1, data);
162 if(pmt_eq(status, PMT_T)) {
164 d_us_rx_chan = pmt_nth(2, data);
167 std::cout << "[GMAC] Received RX allocation"
168 << " on channel " << d_us_rx_chan << std::endl;
170 // If the TX has also been allocated already, we can continue
171 if(!pmt_eqv(d_us_tx_chan, PMT_NIL)) {
178 else { // RX allocation failed
179 error_msg = "failed to allocate RX channel:";
186 //----------------------------- INIT GMAC --------------------------------//
187 // In the INIT_GMAC state, now that the USRP is initialized we can do things
188 // like right the carrier sense threshold to the FPGA register.
193 //----------------------------- IDLE ------------------------------------//
194 // In the idle state the MAC is not quite 'idle', it is just not doing
195 // anything specific. It is still being passive with data between the
196 // application and the lower layer.
199 //-------- TX PORT ----------------------------------------------------//
200 if(pmt_eq(d_tx->port_symbol(), port_id)) {
202 //-------- INCOMING PACKET ------------------------------------------//
203 if(pmt_eq(event, s_cmd_tx_pkt)) {
204 handle_cmd_tx_pkt(data);
210 //--------- USRP TX PORT ----------------------------------------------//
211 if(pmt_eq(d_us_tx->port_symbol(), port_id)) {
213 //-------- INCOMING PACKET RESPONSE ---------------------------------//
214 if(pmt_eq(event, s_response_xmit_raw_frame)) {
215 handle_response_xmit_raw_frame(data);
221 //--------- CS PORT ---------------------------------------------------//
222 if(pmt_eq(d_cs->port_symbol(), port_id)) {
224 //------- ENABLE CARRIER SENSE --------------------------------------//
225 if(pmt_eq(event, s_cmd_carrier_sense_enable)) {
226 handle_cmd_carrier_sense_enable(data);
230 //------- CARRIER SENSE THRESHOLD -----------------------------------//
231 if(pmt_eq(event, s_cmd_carrier_sense_threshold)) {
232 handle_cmd_carrier_sense_threshold(data);
236 //------- CARRIER SENSE DEADLINE ------------------------------------//
237 if(pmt_eq(event, s_cmd_carrier_sense_deadline)) {
238 handle_cmd_carrier_sense_deadline(data);
242 //------- DISABLE CARRIER SENSE -------------------------------------//
243 if(pmt_eq(event, s_cmd_carrier_sense_disable)) {
244 handle_cmd_carrier_sense_disable(data);
252 //------------------------ CLOSING CHANNELS -----------------------------//
253 case CLOSING_CHANNELS:
255 if (pmt_eq(event, s_response_deallocate_channel)
256 && pmt_eq(d_us_tx->port_symbol(), port_id))
258 status = pmt_nth(1, data);
260 if(pmt_eq(status, PMT_T)) {
261 d_us_tx_chan = PMT_NIL;
264 std::cout << "[GMAC] Received TX deallocation\n";
266 // If the RX is also deallocated, we can close the USRP
267 if(pmt_eq(d_us_rx_chan, PMT_NIL))
274 error_msg = "failed to deallocate TX channel:";
280 if (pmt_eq(event, s_response_deallocate_channel)
281 && pmt_eq(d_us_rx->port_symbol(), port_id))
283 status = pmt_nth(1, data);
285 // If successful, set the port to NIL
286 if(pmt_eq(status, PMT_T)) {
287 d_us_rx_chan = PMT_NIL;
290 std::cout << "[GMAC] Received RX deallocation\n";
292 // If the TX is also deallocated, we can close the USRP
293 if(pmt_eq(d_us_tx_chan, PMT_NIL))
300 error_msg = "failed to deallocate RX channel:";
308 //-------------------------- CLOSING USRP -------------------------------//
314 // An error occured, print it, and shutdown all m-blocks
316 std::cerr << error_msg << data
317 << "status = " << status << std::endl;
321 // Received an unhandled message for a specific state
323 if(0 && verbose && !pmt_eq(event, pmt_intern("%shutdown")))
324 std::cout << "[GMAC] unhandled msg: " << msg
325 << "in state "<< d_state << std::endl;
328 // The MAC layer connects to 'usrp_server' which has a control/status channel,
329 // a TX, and an RX port. The MAC layer can then relay TX/RX data back and
330 // forth to the application, or a physical layer once available.
331 void gmac::define_ports()
333 // Ports we use to connect to usrp_server
334 d_us_tx = define_port("us-tx0", "usrp-tx", false, mb_port::INTERNAL);
335 d_us_rx = define_port("us-rx0", "usrp-rx", false, mb_port::INTERNAL);
336 d_us_cs = define_port("us-cs", "usrp-server-cs", false, mb_port::INTERNAL);
338 // Ports applications used to connect to us
339 d_tx = define_port("tx0", "gmac-tx", true, mb_port::EXTERNAL);
340 d_rx = define_port("rx0", "gmac-rx", true, mb_port::EXTERNAL);
341 d_cs = define_port("cs", "gmac-cs", true, mb_port::EXTERNAL);
344 // To initialize the USRP we must pass several parameters to 'usrp_server' such
345 // as the RBF to use, and the interpolation/decimation rate. The MAC layer will
346 // then pass these parameters to the block with a message to establish the
347 // connection to the USRP.
348 void gmac::initialize_usrp()
352 std::cout << "[GMAC] Initializing USRP\n";
354 // The initialization parameters are passed to usrp_server via a PMT
356 pmt_t usrp_dict = pmt_make_dict();
358 // Specify the RBF to use
359 pmt_dict_set(usrp_dict,
361 pmt_intern("test2.rbf"));
363 pmt_dict_set(usrp_dict,
364 pmt_intern("interp-tx"),
367 pmt_dict_set(usrp_dict,
368 pmt_intern("decim-rx"),
372 pmt_dict_set(usrp_dict,
373 pmt_intern("rf-freq"),
374 pmt_from_long((long)10e6));
376 // Default is to use USRP considered '0' (incase of multiple)
377 d_which_usrp = pmt_from_long(0);
379 define_component("USRP-SERVER", "usrp_server", usrp_dict);
381 connect("self", "us-tx0", "USRP-SERVER", "tx0");
382 connect("self", "us-rx0", "USRP-SERVER", "rx0");
383 connect("self", "us-cs", "USRP-SERVER", "cs");
385 // Finally, enter the OPENING_USRP state by sending a request to open the
391 // In the initialization state of the MAC layer we set default values for
392 // several functionalities.
393 void gmac::initialize_gmac()
396 // The initial state is the INIT state.
399 // Set carrier sense to enabled by default with the specified threshold and
400 // the deadline to 0 -- which is wait forever.
401 set_carrier_sense(true, 25, 0, PMT_NIL);
403 // Can now notify the application that we are initialized
404 d_cs->send(s_response_gmac_initialized,
405 pmt_list2(PMT_NIL, PMT_T));
407 // The MAC enters an IDLE state where it waits for messages and dispatches
412 // Method for setting the carrier sense and an associated threshold which is
413 // written to a register on the FPGA, which it will read if the CS flag is set
414 // and perform carrier sense based on.
416 // We currently do not wait for the successful response for the write to
417 // register command, we assume it will succeed else the MAC must
418 void gmac::set_carrier_sense(bool toggle, long threshold, long deadline, pmt_t invocation)
420 d_carrier_sense = toggle;
422 // Only waste the bandwidth and processing of a C/S packet if needed
423 if(threshold != d_cs_thresh) {
424 d_us_tx->send(s_cmd_to_control_channel, // C/S packet
425 pmt_list2(invocation, // invoc handle
427 pmt_list2(s_op_write_reg,
429 pmt_from_long(REG_CS_THRESH),
430 pmt_from_long(threshold))))));
431 d_cs_thresh = threshold;
434 std::cout << "[GMAC] Changing CS threshold: " << d_cs_thresh << std::endl;
437 if(deadline != d_cs_deadline) {
438 d_us_tx->send(s_cmd_to_control_channel, // C/S packet
439 pmt_list2(invocation, // invoc handle
441 pmt_list2(s_op_write_reg,
443 pmt_from_long(REG_CS_DEADLINE),
444 pmt_from_long(deadline))))));
445 d_cs_deadline = deadline;
448 std::cout << "[GMAC] Changing CS deadline: " << d_cs_deadline << std::endl;
452 std::cout << "[GMAC] Setting carrier sense to " << toggle << std::endl;
455 // The following sends a command to open the USRP, which will upload the
456 // specified RBF when creating the instance of the USRP server and set all other
457 // relevant parameters.
458 void gmac::open_usrp()
460 d_state = OPENING_USRP;
462 d_us_cs->send(s_cmd_open, pmt_list2(PMT_NIL, d_which_usrp));
465 std::cout << "[GMAC] Opening USRP "
466 << d_which_usrp << std::endl;
469 // Before sending the close to the USRP we wait a couple seconds to let any data
470 // through the USB exit, else a bug in the driver will kick an error and cause
471 // an abnormal termination.
472 void gmac::close_usrp()
474 d_state = CLOSING_USRP;
478 d_us_cs->send(s_cmd_close, pmt_list1(PMT_NIL));
481 // RX and TX channels must be allocated so that the USRP server can
482 // properly share bandwidth across multiple USRPs. No commands will be
483 // successful to the USRP through the USRP server on the TX or RX channels until
484 // a bandwidth allocation has been received.
485 void gmac::allocate_channels()
487 d_state = ALLOCATING_CHANNELS;
490 std::cout << "[GMAC] Sending channel allocation requests\n";
492 long capacity = (long) 16e6;
493 d_us_tx->send(s_cmd_allocate_channel, pmt_list2(PMT_T, pmt_from_long(capacity)));
494 d_us_rx->send(s_cmd_allocate_channel, pmt_list2(PMT_T, pmt_from_long(capacity)));
498 // Before closing the USRP connection, we deallocate our channels so that the
499 // capacity can be reused.
500 void gmac::close_channels()
502 d_state = CLOSING_CHANNELS;
504 d_us_tx->send(s_cmd_deallocate_channel, pmt_list2(PMT_NIL, d_us_tx_chan));
505 d_us_rx->send(s_cmd_deallocate_channel, pmt_list2(PMT_NIL, d_us_rx_chan));
508 std::cout << "[GMAC] Closing channels...\n";
511 // Used to enter the receiving state
512 void gmac::enter_receiving()
514 d_us_rx->send(s_cmd_start_recv_raw_samples,
519 std::cout << "[GMAC] Started RX sample stream\n";
522 // A simple idle state, nothing more to it.
523 void gmac::enter_idle()
528 // Handles the transmission of a pkt from the application. The invocation
529 // handle is passed on but a response is not given back to the application until
530 // the response is passed from usrp_server. This ensures that the MAC passes
531 // back the success or failure. Furthermore, the MAC could decide to retransmit
532 // on a failure based on the result of the packet transmission.
534 // This should eventually be connected to a physically layer rather than
535 // directly to usrp_server. (d_us_tx should be replaced with a different
537 void gmac::handle_cmd_tx_pkt(pmt_t data)
539 pmt_t invocation_handle = pmt_nth(0, data);
540 pmt_t dst = pmt_nth(1, data);
541 pmt_t samples = pmt_nth(2, data);
542 pmt_t pkt_properties = pmt_nth(3, data);
544 pmt_t us_tx_properties = pmt_make_dict();
546 // Set the packet to be carrier sensed?
547 if(carrier_sense_pkt(pkt_properties))
548 pmt_dict_set(us_tx_properties,
549 pmt_intern("carrier-sense"),
552 pmt_t timestamp = pmt_from_long(0xffffffff); // NOW
554 // Construct the proper message for USRP server
555 d_us_tx->send(s_cmd_xmit_raw_frame,
556 pmt_list5(invocation_handle,
563 std::cout << "[GMAC] Transmitted packet\n";
566 // Handles a response from the USRP server about the transmission of a frame,
567 // whether it was successful or failed. This should eventually be replaced with
568 // a response from the PHY layer. This is where a retransmit could be
570 void gmac::handle_response_xmit_raw_frame(pmt_t data)
572 pmt_t invocation_handle = pmt_nth(0, data);
573 pmt_t status = pmt_nth(1, data);
575 d_tx->send(s_response_tx_pkt,
576 pmt_list2(invocation_handle,
580 // This method determines whether carrier sense should be enabled based on two
581 // properties. The first is the MAC setting, which the user can set to carrier
582 // sense packets by default or not. The second is a per packet setting, which
583 // can be used to override the MAC setting for the given packet only.
584 bool gmac::carrier_sense_pkt(pmt_t pkt_properties)
586 // First we extract the per packet properties to check the per packet setting
588 if(pmt_is_dict(pkt_properties)) {
590 if(pmt_t pkt_cs = pmt_dict_ref(pkt_properties,
591 pmt_intern("carrier-sense"),
593 // If the per packet property says true, enable carrier sense regardless
594 // of the MAC setting
595 if(pmt_eqv(pkt_cs, PMT_T))
597 // If the per packet setting says false, disable carrier sense regardless
598 // of the MAC setting
599 else if(pmt_eqv(pkt_cs, PMT_F))
604 // If we've hit this point, the packet properties did not state whether
605 // carrier sense should be used or not, so we use the MAC setting
613 // This method is envoked by an incoming cmd-enable-carrier-sense signal on the
614 // C/S port. It can be used to re-adjust the threshold or simply enabled
615 // carrier sense. When a threshold is not provided, the MAC will use an
616 // averaging algorithm to determine the threshold (in the future).
617 void gmac::handle_cmd_carrier_sense_enable(pmt_t data)
619 pmt_t invocation_handle = pmt_nth(0, data);
620 pmt_t threshold = pmt_nth(1, data);
621 pmt_t deadline = pmt_nth(2, data);
622 long l_threshold, l_deadline;
624 // FIXME: for now, if threshold is NIL, we do not change the threshold.
625 // This should be replaced with an averaging algorithm
626 if(pmt_eqv(threshold, PMT_NIL))
627 l_threshold = d_cs_thresh;
629 l_threshold = pmt_to_long(threshold);
631 // If the deadline is NIL, we do not change the value
632 if(pmt_eqv(threshold, PMT_NIL))
633 l_deadline = d_cs_deadline;
635 l_deadline = pmt_to_long(deadline);
637 set_carrier_sense(true, l_threshold, l_deadline, invocation_handle);
640 // This method is called when an incoming disable carrier sense command is sent
641 // over the control status channel. It so far does not ellicit a response, this
642 // needs to be added correctly. It needs to wait for the response for the C/S
643 // packet from usrp_server.
644 void gmac::handle_cmd_carrier_sense_disable(pmt_t data)
646 pmt_t invocation_handle = pmt_nth(0, data);
648 // We don't change the threshold, we leave it as is because the application
649 // did not request that it changes, only to disable carrier sense.
650 set_carrier_sense(false, d_cs_thresh, d_cs_deadline, invocation_handle);
653 // When the app requests that the threshold changes, the state of the carrier
654 // sense should not change. If it was enabled, it should remain enabled.
655 // Likewise if it was disabled. The deadline value should also remain
657 void gmac::handle_cmd_carrier_sense_threshold(pmt_t data)
659 pmt_t invocation_handle = pmt_nth(0, data);
660 pmt_t threshold = pmt_nth(1, data);
663 // FIXME: for now, if threshold is NIL, we do not change the threshold.
664 // This should be replaced with an averaging algorithm
665 if(pmt_eqv(threshold, PMT_NIL))
666 l_threshold = d_cs_thresh;
668 l_threshold = pmt_to_long(threshold);
670 set_carrier_sense(d_carrier_sense, l_threshold, d_cs_deadline, invocation_handle);
673 // Ability to change the deadline using a C/S packet. The state of all other
674 // carrier sense parameters should not change.
675 void gmac::handle_cmd_carrier_sense_deadline(pmt_t data)
677 pmt_t invocation_handle = pmt_nth(0, data);
678 pmt_t deadline = pmt_nth(1, data);
681 // If the deadline passed is NIL, do *not* change the value.
682 if(pmt_eqv(deadline, PMT_NIL))
683 l_deadline = d_cs_deadline;
685 l_deadline = pmt_to_long(deadline);
687 set_carrier_sense(d_carrier_sense, d_cs_thresh, l_deadline, invocation_handle);
690 REGISTER_MBLOCK_CLASS(gmac);