diff options
Diffstat (limited to 'usrp/host/lib/inband/usrp_server.cc')
-rw-r--r-- | usrp/host/lib/inband/usrp_server.cc | 1342 |
1 files changed, 1156 insertions, 186 deletions
diff --git a/usrp/host/lib/inband/usrp_server.cc b/usrp/host/lib/inband/usrp_server.cc index 800bf0e15..760397dc7 100644 --- a/usrp/host/lib/inband/usrp_server.cc +++ b/usrp/host/lib/inband/usrp_server.cc @@ -27,39 +27,19 @@ #include <usrp_inband_usb_packet.h> #include <mb_class_registry.h> #include <vector> +#include <usrp_usb_interface.h> + +#include <symbols_usrp_server_cs.h> +#include <symbols_usrp_channel.h> +#include <symbols_usrp_tx.h> +#include <symbols_usrp_rx.h> +#include <symbols_usrp_low_level_cs.h> +#include <symbols_usrp_interface_cs.h> +static pmt_t s_shutdown = pmt_intern("%shutdown"); typedef usrp_inband_usb_packet transport_pkt; // makes conversion to gigabit easy -// FIXME We should machine generate these by a simple preprocessor run over this file -// -// These are all the messages that we expect to receive. -// -// We "intern" these here (make them into symbols) so that our -// comparisions below are effectively pointer comparisons. - -static pmt_t s_cmd_allocate_channel = pmt_intern("cmd-allocate-channel"); -static pmt_t s_cmd_close = pmt_intern("cmd-close"); -static pmt_t s_cmd_deallocate_channel = pmt_intern("cmd-deallocate-channel"); -static pmt_t s_cmd_open = pmt_intern("cmd-open"); -static pmt_t s_cmd_start_recv_raw_samples = pmt_intern("cmd-start-recv-raw-samples"); -static pmt_t s_cmd_stop_recv_raw_samples = pmt_intern("cmd-stop-recv-raw-samples"); -static pmt_t s_cmd_to_control_channel = pmt_intern("cmd-to-control-channel"); -static pmt_t s_cmd_xmit_raw_frame = pmt_intern("cmd-xmit-raw-frame"); -static pmt_t s_cmd_max_capacity = pmt_intern("cmd-max-capacity"); -static pmt_t s_cmd_ntx_chan = pmt_intern("cmd-ntx-chan"); -static pmt_t s_cmd_nrx_chan = pmt_intern("cmd-nrx-chan"); -static pmt_t s_cmd_current_capacity_allocation = pmt_intern("cmd-current-capacity-allocation"); -static pmt_t s_response_allocate_channel = pmt_intern("response-allocate-channel"); -static pmt_t s_response_close = pmt_intern("response-close"); -static pmt_t s_response_deallocate_channel = pmt_intern("response-deallocate-channel"); -static pmt_t s_response_from_control_channel = pmt_intern("response-from-control-channel"); -static pmt_t s_response_open = pmt_intern("response-open"); -static pmt_t s_response_recv_raw_samples = pmt_intern("response-recv-raw-samples"); -static pmt_t s_response_xmit_raw_frame = pmt_intern("response-xmit-raw-frame"); -static pmt_t s_response_max_capacity = pmt_intern("response-max-capacity"); -static pmt_t s_response_ntx_chan = pmt_intern("response-ntx-chan"); -static pmt_t s_response_nrx_chan = pmt_intern("response-nrx-chan"); -static pmt_t s_response_current_capacity_allocation = pmt_intern("response-current-capacity-allocation"); +const static bool verbose = false; static std::string str(long x) @@ -70,36 +50,75 @@ str(long x) } usrp_server::usrp_server(mb_runtime *rt, const std::string &instance_name, pmt_t user_arg) - : mb_mblock(rt, instance_name, user_arg) + : mb_mblock(rt, instance_name, user_arg), + d_fake_rx(false) { - // define our ports + // Dictionary for arguments to all of the components + pmt_t usrp_dict = user_arg; + // control & status port d_cs = define_port("cs", "usrp-server-cs", true, mb_port::EXTERNAL); + d_cs_usrp = define_port("cs_usrp", "usrp-interface-cs", false, mb_port::INTERNAL); // ports // // (if/when we do replicated ports, these will be replaced by a // single replicated port) for(int port=0; port < N_PORTS; port++) { - d_tx.push_back(define_port("tx"+str(port), "usrp-tx", true, mb_port::EXTERNAL)); - d_rx.push_back(define_port("rx"+str(port), "usrp-rx", true, mb_port::EXTERNAL)); + + d_tx.push_back(define_port("tx"+str(port), + "usrp-tx", + true, + mb_port::EXTERNAL)); + + d_rx.push_back(define_port("rx"+str(port), + "usrp-rx", + true, + mb_port::EXTERNAL)); } - // FIXME ... initializing to 2 channels on each for now, eventually we should - // query the FPGA to get these values - d_ntx_chan = 2; + define_component("usrp", "usrp_usb_interface", usrp_dict); + connect("self", "cs_usrp", "usrp", "cs"); + + d_defer=false; + d_opened=false; + + // FIXME: needs to be returned from open, if we want to use this d_nrx_chan = 2; + d_ntx_chan = 2; // Initialize capacity on each channel to 0 and to no owner + // Also initialize the USRP standard tx/rx pointers to NULL + for(int chan=0; chan < d_ntx_chan; chan++) + d_chaninfo_tx.push_back(channel_info()); + + for(int chan=0; chan < d_nrx_chan; chan++) + d_chaninfo_rx.push_back(channel_info()); + + d_rx_chan_mask = 0; + + for(int i=0; i < D_MAX_RID; i++) + d_rids.push_back(rid_info()); + + //d_fake_rx=true; +} + +void +usrp_server::reset_channels() +{ + for(int chan=0; chan < d_ntx_chan; chan++) { d_chaninfo_tx[chan].assigned_capacity = 0; d_chaninfo_tx[chan].owner = PMT_NIL; } + for(int chan=0; chan < d_nrx_chan; chan++) { d_chaninfo_rx[chan].assigned_capacity = 0; d_chaninfo_rx[chan].owner = PMT_NIL; } + + d_rx_chan_mask = 0; } usrp_server::~usrp_server() @@ -119,78 +138,328 @@ usrp_server::handle_message(mb_message_sptr msg) pmt_t event = msg->signal(); // the "name" of the message pmt_t port_id = msg->port_id(); // which port it came in on pmt_t data = msg->data(); - pmt_t metadata = msg->metadata(); pmt_t invocation_handle; - pmt_t reply_data; + pmt_t metadata = msg->metadata(); pmt_t status; - if (1){ + long port; + + if (pmt_eq(event, s_shutdown)) // ignore (for now) + return; + + invocation_handle = pmt_nth(0, data); + + if (0){ std::cout << "[USRP_SERVER] event: " << event << std::endl; std::cout << "[USRP_SERVER] port_id: " << port_id << std::endl; } - // It would be nice if this were all table driven, and we could - // compute our state transition as f(current_state, port_id, signal) + // It would be nice if this were all table driven, and we could compute our + // state transition as f(current_state, port_id, signal) + + // A message from the USRP CS, which should *only* be responses + // + // It is important that this set come before checking messages of any other + // components. This is since we always want to listen to the low level USRP + // server, even if we aren't initialized we are waiting for responses to + // become initialized. Likewise, after the usrp_server is "closed", we still + // want to pass responses back from the low level. - if (pmt_eq(port_id, d_cs->port_symbol())){ // message came in on our control/status port + //---------------- USRP RESPONSE ---------------// + if (pmt_eq(port_id, d_cs_usrp->port_symbol())) { + + //-------------- USRP OPEN ------------------// + if(pmt_eq(event, s_response_usrp_open)) { + // pass the response back over the regular CS port + pmt_t status = pmt_nth(1, data); + d_cs->send(s_response_open, pmt_list2(invocation_handle, status)); - if (pmt_eq(event, s_cmd_open)){ - // extract args from data - invocation_handle = pmt_nth(0, data); - long which_usrp = pmt_to_long(pmt_nth(1, data)); // integer usrp id, usually 0 + if(pmt_eqv(status,PMT_T)) { + d_opened = true; + d_defer = false; + recall_defer_queue(); + } + + return; + } + //------------- USRP CLOSE -------------------// + else if (pmt_eq(event, s_response_usrp_close)) { + pmt_t status = pmt_nth(1, data); + d_cs->send(s_response_close, pmt_list2(invocation_handle, status)); + + if(pmt_eqv(status,PMT_T)) { + d_opened = false; + d_defer = false; + reset_channels(); + recall_defer_queue(); + } - // Do the right thing.... - // build a reply - (void) which_usrp; // avoid unused warning + return; + } + //--------------- USRP WRITE --------------// + else if (pmt_eq(event, s_response_usrp_write)) { + + pmt_t status = pmt_nth(1, data); + long channel = pmt_to_long(pmt_nth(2, data)); + long port; + + // Do not report back responses if they were generated from a + // command packet + if(channel == 0x1f) + return; + + // Find the port through the owner of the channel + if((port = tx_port_index(d_chaninfo_tx[channel].owner)) !=-1 ) + d_tx[port]->send(s_response_xmit_raw_frame, + pmt_list2(invocation_handle, status)); + return; + } + //--------------- USRP READ ---------------// + else if (pmt_eq(event, s_response_usrp_read)) { + + pmt_t status = pmt_nth(1, data); + + if(!pmt_eqv(status, PMT_T)) { + std::cerr << "[USRP_SERVER] Error receiving packet\n"; + return; + } + else { + handle_response_usrp_read(data); + return; + } + } + + goto unhandled; + } + + // Checking for defer on all other messages + if(d_defer) { + if (verbose) + std::cout << "[USRP_SERVER] Received msg while deferring (" + << msg->signal() << ")\n"; + d_defer_queue.push(msg); + return; + } + + //--------- CONTROL / STATUS ------------// + if (pmt_eq(port_id, d_cs->port_symbol())){ + + //----------- OPEN -----------// + if (pmt_eq(event, s_cmd_open)){ - // if everything OK - status = PMT_T; - reply_data = pmt_list2(invocation_handle, status); + // Reject if already open + if(d_opened) { + d_cs->send(s_response_open, pmt_list2(invocation_handle, s_err_usrp_already_opened)); + return; + } - // ...and send it - d_cs->send(s_response_open, reply_data); + // the parameters are the same to the low level interface, so we just pass 'data' along + d_cs_usrp->send(s_cmd_usrp_open, data); + + d_defer = true; + return; } + //---------- CLOSE -----------// else if (pmt_eq(event, s_cmd_close)){ - // ... + + if(!d_opened) { + d_cs->send(s_response_close, pmt_list2(invocation_handle, s_err_usrp_already_closed)); + return; + } + + d_defer = true; + d_cs_usrp->send(s_cmd_usrp_close, pmt_list1(invocation_handle)); + + return; } + //---------- MAX CAPACITY ----------// else if (pmt_eq(event, s_cmd_max_capacity)) { - invocation_handle = pmt_nth(0, data); - reply_data = pmt_list2(invocation_handle, pmt_from_long(max_capacity())); - d_cs->send(s_response_max_capacity, reply_data); + + if(!d_opened) { + d_cs->send(s_response_max_capacity, + pmt_list3(invocation_handle, s_err_usrp_not_opened, pmt_from_long(0))); + return; + } + + d_cs->send(s_response_max_capacity, + pmt_list3(invocation_handle, + PMT_T, + pmt_from_long(max_capacity()))); return; } + //---------- NTX CHAN --------------// else if (pmt_eq(event, s_cmd_ntx_chan)) { - invocation_handle = pmt_nth(0, data); - reply_data = pmt_list2(invocation_handle, pmt_from_long(d_ntx_chan)); - d_cs->send(s_response_ntx_chan, reply_data); + + if(!d_opened) { + d_cs->send(s_response_ntx_chan, + pmt_list3(invocation_handle, s_err_usrp_not_opened, pmt_from_long(0))); + return; + } + + d_cs->send(s_response_ntx_chan, + pmt_list3(invocation_handle, + PMT_T, + pmt_from_long(d_ntx_chan))); + return; } + //---------- NRX CHAN -----------// else if (pmt_eq(event, s_cmd_nrx_chan)) { - invocation_handle = pmt_nth(0, data); - reply_data = pmt_list2(invocation_handle, pmt_from_long(d_nrx_chan)); - d_cs->send(s_response_nrx_chan, reply_data); - } + + if(!d_opened) { + d_cs->send(s_response_nrx_chan, + pmt_list3(invocation_handle, s_err_usrp_not_opened, pmt_from_long(0))); + return; + } + + d_cs->send(s_response_nrx_chan, + pmt_list3(invocation_handle, + PMT_T, + pmt_from_long(d_nrx_chan))); + return; + } + //--------- ALLOCATION? -----------// else if (pmt_eq(event, s_cmd_current_capacity_allocation)) { - invocation_handle = pmt_nth(0, data); - reply_data = pmt_list2(invocation_handle, pmt_from_long(current_capacity_allocation())); - d_cs->send(s_response_current_capacity_allocation, reply_data); + + if(!d_opened) { + d_cs->send(s_response_current_capacity_allocation, + pmt_list3(invocation_handle, + s_err_usrp_not_opened, + pmt_from_long(0))); + return; + } + + d_cs->send(s_response_current_capacity_allocation, + pmt_list3(invocation_handle, + PMT_T, + pmt_from_long(current_capacity_allocation()))); + return; } goto unhandled; } + + //-------------- TX ---------------// + if ((port = tx_port_index(port_id)) != -1) { + + //------------ ALLOCATE (TX) ----------------// + if (pmt_eq(event, s_cmd_allocate_channel)){ + + if(!d_opened) { + d_tx[port]->send(s_response_allocate_channel, + pmt_list3(invocation_handle, + s_err_usrp_not_opened, + pmt_from_long(0))); + return; + } + + handle_cmd_allocate_channel(d_tx[port], d_chaninfo_tx, data); + return; + } + + //----------- DEALLOCATE (TX) ---------------// + if (pmt_eq(event, s_cmd_deallocate_channel)) { + + if(!d_opened) { + d_tx[port]->send(s_response_deallocate_channel, + pmt_list3(invocation_handle, + s_err_usrp_not_opened, + pmt_from_long(0))); + return; + } - if (pmt_eq(event, s_cmd_allocate_channel)){ - handle_cmd_allocate_channel(port_id, data); - return; - } + handle_cmd_deallocate_channel(d_tx[port], d_chaninfo_tx, data); + return; + } + + //-------------- XMIT RAW FRAME -----------------/ + if (pmt_eq(event, s_cmd_xmit_raw_frame)){ - if (pmt_eq(event, s_cmd_deallocate_channel)) { - handle_cmd_deallocate_channel(port_id, data); - return; + if(!d_opened) { + d_tx[port]->send(s_response_xmit_raw_frame, + pmt_list2(invocation_handle, s_err_usrp_not_opened)); + return; + } + + handle_cmd_xmit_raw_frame(d_tx[port], d_chaninfo_tx, data); + return; + } + + //-------------- CONTROL PACKET -----------------/ + if (pmt_eq(event, s_cmd_to_control_channel)) { + + if(!d_opened) { + d_tx[port]->send(s_response_xmit_raw_frame, + pmt_list2(invocation_handle, s_err_usrp_not_opened)); + return; + } + + handle_cmd_to_control_channel(d_tx[port], d_chaninfo_tx, data); + return; + + } + + goto unhandled; } + + //-------------- RX ---------------// + if ((port = rx_port_index(port_id)) != -1) { - if (pmt_eq(event, s_cmd_xmit_raw_frame)){ - handle_cmd_xmit_raw_frame(data); - return; + //------------ ALLOCATE (RX) ----------------// + if (pmt_eq(event, s_cmd_allocate_channel)) { + + if(!d_opened) { + d_rx[port]->send(s_response_allocate_channel, + pmt_list3(invocation_handle, + s_err_usrp_not_opened, + pmt_from_long(0))); + return; + } + + handle_cmd_allocate_channel(d_rx[port], d_chaninfo_rx, data); + return; + } + + //----------- DEALLOCATE (RX) ---------------// + if (pmt_eq(event, s_cmd_deallocate_channel)) { + + if(!d_opened) { + d_rx[port]->send(s_response_deallocate_channel, + pmt_list3(invocation_handle, + s_err_usrp_not_opened, + pmt_from_long(0))); + return; + } + + handle_cmd_deallocate_channel(d_rx[port], d_chaninfo_rx, data); + return; + } + + //-------------- START RECV ----------------// + if (pmt_eq(event, s_cmd_start_recv_raw_samples)) { + + if(!d_opened) { + d_rx[port]->send(s_response_recv_raw_samples, + pmt_list2(invocation_handle, s_err_usrp_not_opened)); + return; + } + + handle_cmd_start_recv_raw_samples(d_rx[port], d_chaninfo_rx, data); + return; + } + + //-------------- STOP RECV ----------------// + if (pmt_eq(event, s_cmd_stop_recv_raw_samples)) { + + if(!d_opened) + return; + + // FIX ME : no response for stopping? even if error? (permissions) + handle_cmd_stop_recv_raw_samples(d_rx[port], d_chaninfo_rx, data); + + return; + } + + goto unhandled; } unhandled: @@ -231,128 +500,94 @@ long usrp_server::current_capacity_allocation() { return capacity; } -void usrp_server::handle_cmd_allocate_channel(pmt_t port_id, pmt_t data) { - +void +usrp_server::handle_cmd_allocate_channel( + mb_port_sptr port, + std::vector<struct channel_info> &chan_info, + pmt_t data) +{ pmt_t invocation_handle = pmt_nth(0, data); long rqstd_capacity = pmt_to_long(pmt_nth(1, data)); - long chan, port; - pmt_t reply_data; + long chan; - // If it's a TX port, allocate on a free channel, else check if it's a RX port - // and allocate. - if((port = tx_port_index(port_id)) != -1) { + // Check capacity exists + if((D_USB_CAPACITY - current_capacity_allocation()) < rqstd_capacity) { - // Check capacity exists - if((D_USB_CAPACITY - current_capacity_allocation()) < rqstd_capacity) { - reply_data = pmt_list3(invocation_handle, pmt_from_long(RQSTD_CAPACITY_UNAVAIL), PMT_NIL); // no capacity available - d_tx[port]->send(s_response_allocate_channel, reply_data); - return; - } + // no capacity available + port->send(s_response_allocate_channel, + pmt_list3(invocation_handle, + s_err_requested_capacity_unavailable, + PMT_NIL)); + return; + } - // Find a free channel, assign the capacity and respond - for(chan=0; chan < d_ntx_chan; chan++) { - if(d_chaninfo_tx[chan].owner == PMT_NIL) { - d_chaninfo_tx[chan].owner = port_id; - d_chaninfo_tx[chan].assigned_capacity = rqstd_capacity; - reply_data = pmt_list3(invocation_handle, PMT_T, pmt_from_long(chan)); - d_tx[port]->send(s_response_allocate_channel, reply_data); - return; - } - } + // Find a free channel, assign the capacity and respond + for(chan=0; chan < (long)chan_info.size(); chan++) { - std::cout << "[USRP_SERVER] Couldnt find a TX chan\n"; + if(verbose) + std::cout << "[USRP_SERVER] Checking chan: " << chan + << " owner " << chan_info[chan].owner + << " size " << chan_info.size() + << std::endl; - reply_data = pmt_list3(invocation_handle, pmt_from_long(CHANNEL_UNAVAIL), PMT_NIL); // no free TX chan found - d_tx[port]->send(s_response_allocate_channel, reply_data); - return; - } + if(chan_info[chan].owner == PMT_NIL) { - // Repeat the same process on the RX side if the port was not determined to be TX - if((port = rx_port_index(port_id)) != -1) { - - if((D_USB_CAPACITY - current_capacity_allocation()) < rqstd_capacity) { - reply_data = pmt_list3(invocation_handle, pmt_from_long(RQSTD_CAPACITY_UNAVAIL), PMT_NIL); // no capacity available - d_rx[port]->send(s_response_allocate_channel, reply_data); + chan_info[chan].owner = port->port_symbol(); + chan_info[chan].assigned_capacity = rqstd_capacity; + + port->send(s_response_allocate_channel, + pmt_list3(invocation_handle, + PMT_T, + pmt_from_long(chan))); + + if(verbose) + std::cout << "[USRP_SERVER] Assigning channel: " << chan + << " to " << chan_info[chan].owner + << std::endl; return; } + + } - for(chan=0; chan < d_nrx_chan; chan++) { - if(d_chaninfo_rx[chan].owner == PMT_NIL) { - d_chaninfo_rx[chan].owner = port_id; - d_chaninfo_rx[chan].assigned_capacity = rqstd_capacity; - reply_data = pmt_list3(invocation_handle, PMT_T, pmt_from_long(chan)); - d_rx[port]->send(s_response_allocate_channel, reply_data); - return; - } - } + if (verbose) + std::cout << "[USRP_SERVER] Couldnt find a TX chan\n"; - std::cout << "[USRP_SERVER] Couldnt find a RX chan\n"; - reply_data = pmt_list3(invocation_handle, pmt_from_long(CHANNEL_UNAVAIL), PMT_NIL); // no free RX chan found - d_rx[port]->send(s_response_allocate_channel, reply_data); - return; - } + // no free TX chan found + port->send(s_response_allocate_channel, + pmt_list3(invocation_handle, + s_err_channel_unavailable, + PMT_NIL)); + return; } // Check the port type and deallocate assigned capacity based on this, ensuring -// that the owner of the method invocation is the owner of the port and that -// the channel number is valid. -void usrp_server::handle_cmd_deallocate_channel(pmt_t port_id, pmt_t data) { +// that the owner of the method invocation is the owner of the port and that the +// channel number is valid. +void +usrp_server::handle_cmd_deallocate_channel( + mb_port_sptr port, + std::vector<struct channel_info> &chan_info, + pmt_t data) +{ pmt_t invocation_handle = pmt_nth(0, data); long channel = pmt_to_long(pmt_nth(1, data)); - long port; - pmt_t reply_data; - - // Check that the channel number is valid, and that the calling port is the owner - // of the channel, and if so remove the assigned capacity. - if((port = tx_port_index(port_id)) != -1) { - - if(channel >= d_ntx_chan) { - reply_data = pmt_list2(invocation_handle, pmt_from_long(CHANNEL_INVALID)); // not a legit channel number - d_tx[port]->send(s_response_deallocate_channel, reply_data); - return; - } - if(d_chaninfo_tx[channel].owner != port_id) { - reply_data = pmt_list2(invocation_handle, pmt_from_long(PERMISSION_DENIED)); // not the owner of the port - d_tx[port]->send(s_response_deallocate_channel, reply_data); - return; - } - - d_chaninfo_tx[channel].assigned_capacity = 0; - d_chaninfo_tx[channel].owner = PMT_NIL; - - reply_data = pmt_list2(invocation_handle, PMT_T); - d_tx[port]->send(s_response_deallocate_channel, reply_data); + // Ensure the channel is valid and the caller owns the port + if(!check_valid(port, channel, chan_info, + pmt_list2(s_response_deallocate_channel, invocation_handle))) return; - } - - // Repeated process on the RX side - if((port = rx_port_index(port_id)) != -1) { - if(channel >= d_nrx_chan) { - reply_data = pmt_list2(invocation_handle, pmt_from_long(CHANNEL_INVALID)); // not a legit channel number - d_rx[port]->send(s_response_deallocate_channel, reply_data); - return; - } - - if(d_chaninfo_rx[channel].owner != port_id) { - reply_data = pmt_list2(invocation_handle, pmt_from_long(PERMISSION_DENIED)); // not the owner of the port - d_rx[port]->send(s_response_deallocate_channel, reply_data); - return; - } - - d_chaninfo_rx[channel].assigned_capacity = 0; - d_chaninfo_rx[channel].owner = PMT_NIL; - - reply_data = pmt_list2(invocation_handle, PMT_T); - d_rx[port]->send(s_response_deallocate_channel, reply_data); - return; - } + chan_info[channel].assigned_capacity = 0; + chan_info[channel].owner = PMT_NIL; + port->send(s_response_deallocate_channel, + pmt_list2(invocation_handle, + PMT_T)); + return; } -void usrp_server::handle_cmd_xmit_raw_frame(pmt_t data) { +void usrp_server::handle_cmd_xmit_raw_frame(mb_port_sptr port, std::vector<struct channel_info> &chan_info, pmt_t data) { size_t n_bytes, psize; long max_payload_len = transport_pkt::max_payload(); @@ -361,10 +596,18 @@ void usrp_server::handle_cmd_xmit_raw_frame(pmt_t data) { long channel = pmt_to_long(pmt_nth(1, data)); const void *samples = pmt_uniform_vector_elements(pmt_nth(2, data), n_bytes); long timestamp = pmt_to_long(pmt_nth(3, data)); + + // Ensure the channel is valid and the caller owns the port + if(!check_valid(port, channel, chan_info, + pmt_list2(s_response_xmit_raw_frame, invocation_handle))) + return; + + // Determine the number of packets to allocate contiguous memory for + // bursting over the USB and get a pointer to the memory to be used in + // building the packets + long n_packets = + static_cast<long>(std::ceil(n_bytes / (double)max_payload_len)); - // Determine the number of packets to allocate contiguous memory for bursting over the - // USB and get a pointer to the memory to be used in building the packets - long n_packets = static_cast<long>(std::ceil(n_bytes / (double)max_payload_len)); pmt_t v_packets = pmt_make_u8vector(sizeof(transport_pkt) * n_packets, 0); transport_pkt *pkts = @@ -372,7 +615,8 @@ void usrp_server::handle_cmd_xmit_raw_frame(pmt_t data) { for(int n=0; n < n_packets; n++) { - long payload_len = std::min((long)(n_bytes-(n*max_payload_len)), (long)max_payload_len); + long payload_len = + std::min((long)(n_bytes-(n*max_payload_len)), (long)max_payload_len); if(n == 0) { // first packet gets start of burst flag and timestamp pkts[n].set_header(pkts[n].FL_START_OF_BURST, channel, 0, payload_len); @@ -382,14 +626,740 @@ void usrp_server::handle_cmd_xmit_raw_frame(pmt_t data) { pkts[n].set_timestamp(0xffffffff); } - memcpy(pkts[n].payload(), (uint8_t *)samples+(max_payload_len * n), payload_len); + memcpy(pkts[n].payload(), + (uint8_t *)samples+(max_payload_len * n), + payload_len); + + } + + + pkts[n_packets-1].set_end_of_burst(); // set the last packet's end of burst + + if (verbose) + std::cout << "[USRP_SERVER] Received raw frame invocation: " + << invocation_handle << std::endl; + + // The actual response to the write will be generated by a + // s_response_usrp_write + d_cs_usrp->send(s_cmd_usrp_write, + pmt_list3(invocation_handle, + pmt_from_long(channel), + v_packets)); + + return; +} + +void usrp_server::handle_cmd_to_control_channel(mb_port_sptr port, std::vector<struct channel_info> &chan_info, pmt_t data) +{ + + pmt_t invocation_handle = pmt_nth(0, data); + pmt_t subpackets = pmt_nth(1, data); + + long n_subpkts = pmt_length(subpackets); + long curr_subpkt = 0; + + size_t psize; + long payload_len = 0; + long channel = 0x1f; + + // The design of the following code is optimized for simplicity, not + // performance. To performance optimize this code, the total size in bytes + // needed for all of the CS packets is needed to allocate contiguous memory + // which contains the USB packets for bursting over the bus. However to do + // this the packets subpackets would need to be parsed twice and their sizes + // would need to be determined. + // + // The approach taken is to keep parsing the subpackets and putting them in to + // USB packets. Once the USB packet is full, a write is sent for it and + // another packet is created. + // + // The subpacket creation methods will return false if the subpacket will not + // fit in to the current USB packet. In these cases a new USB packet is + // created and the old is sent. + + new_packet: + // This code needs to become "smart" and only make a new packet when full + pmt_t v_packet = pmt_make_u8vector(sizeof(transport_pkt), 0); + transport_pkt *pkt = (transport_pkt *) pmt_u8vector_writeable_elements(v_packet, psize); + payload_len = 0; + + pkt->set_header(0, channel, 0, payload_len); + pkt->set_timestamp(0xffffffff); + + while(curr_subpkt < n_subpkts) { + + pmt_t subp = pmt_nth(curr_subpkt, subpackets); + pmt_t subp_cmd = pmt_nth(0, subp); + pmt_t subp_data = pmt_nth(1, subp); + + //--------- PING FIXED --------------// + if(pmt_eq(subp_cmd, s_op_ping_fixed)) { + + long urid = pmt_to_long(pmt_nth(0, subp_data)); + long pingval = pmt_to_long(pmt_nth(1, subp_data)); + + // USRP server sets request ID's to keep track of which application gets + // what response back. To allow a full 6-bits for an RID to the user, we + // keep a mapping and replace the RID's as the packets go in and out. If + // there are no RID's available, the command is thrown away silently. + long srid; + if((srid = next_rid()) == -1) + goto subpkt_bail; + + // We use a vector to store the owner of the ping request and will use it + // to send the request on any RX port they own. + d_rids[srid].owner = port->port_symbol(); + d_rids[srid].user_rid = urid; + + // Adds a ping after the previous command in the pkt + if(!pkt->cs_ping(srid, pingval)) + { + d_cs_usrp->send(s_cmd_usrp_write, + pmt_list3(invocation_handle, + pmt_from_long(channel), + v_packet)); + + // Return the RID + d_rids[srid].owner = PMT_NIL; + + goto new_packet; + } + + if(verbose) + std::cout << "[USRP_SERVER] Received ping command request" + << " assigning RID " << srid << std::endl; + + } + + //----------- WRITE REG ---------------// + if(pmt_eq(subp_cmd, s_op_write_reg)) { + + long reg_num = pmt_to_long(pmt_nth(0, subp_data)); + long val = pmt_to_long(pmt_nth(1, subp_data)); + + if(!pkt->cs_write_reg(reg_num, val)) + { + d_cs_usrp->send(s_cmd_usrp_write, + pmt_list3(invocation_handle, + pmt_from_long(channel), + v_packet)); + + goto new_packet; + } + + if(verbose) + std::cout << "[USRP_SERVER] Received write register request " + << "(" + << "Reg: " << reg_num << ", " + << "Val: " << val + << ")\n"; + } + + //------- WRITE REG MASKED ----------// + if(pmt_eq(subp_cmd, s_op_write_reg_masked)) { + + long reg_num = pmt_to_long(pmt_nth(0, subp_data)); + long val = pmt_to_long(pmt_nth(1, subp_data)); + long mask = pmt_to_long(pmt_nth(2, subp_data)); + + if(!pkt->cs_write_reg_masked(reg_num, val, mask)) + { + d_cs_usrp->send(s_cmd_usrp_write, + pmt_list3(invocation_handle, + pmt_from_long(channel), + v_packet)); + + goto new_packet; + } + + if(verbose) + std::cout << "[USRP_SERVER] Received write register masked request\n"; + } + + //------------ READ REG --------------// + if(pmt_eq(subp_cmd, s_op_read_reg)) { + + long urid = pmt_to_long(pmt_nth(0, subp_data)); + long reg_num = pmt_to_long(pmt_nth(1, subp_data)); + + long srid; + if((srid = next_rid()) == -1) + goto subpkt_bail; + + d_rids[srid].owner = port->port_symbol(); + d_rids[srid].user_rid = urid; + + if(!pkt->cs_read_reg(srid, reg_num)) + { + d_cs_usrp->send(s_cmd_usrp_write, + pmt_list3(invocation_handle, + pmt_from_long(channel), + v_packet)); + + // Return the rid + d_rids[srid].owner = PMT_NIL; + + goto new_packet; + } + + if(verbose) + std::cout << "[USRP_SERVER] Received read register request" + << " assigning RID " << srid << std::endl; + } + + //------------ DELAY --------------// + if(pmt_eq(subp_cmd, s_op_delay)) { + + long ticks = pmt_to_long(pmt_nth(0, subp_data)); + + if(!pkt->cs_delay(ticks)) + { + d_cs_usrp->send(s_cmd_usrp_write, + pmt_list3(invocation_handle, + pmt_from_long(channel), + v_packet)); + + goto new_packet; + } + + if(verbose) + std::cout << "[USRP_SERVER] Received delay request of " + << ticks << " ticks\n"; + } + + //--------- I2C WRITE -----------// + // FIXME: could check that byte count does not exceed 2^8 which + // is the max length in the subpacket for # of bytes to read. + if(pmt_eq(subp_cmd, s_op_i2c_write)) { + + long i2c_addr = pmt_to_long(pmt_nth(0, subp_data)); + pmt_t data = pmt_nth(1, subp_data); + + // Get a readable address to the data which also gives us the length + size_t data_len; + uint8_t *i2c_data = (uint8_t *) pmt_u8vector_writeable_elements(data, data_len); + + // Make the USB packet + if(!pkt->cs_i2c_write(i2c_addr, i2c_data, data_len)) + { + d_cs_usrp->send(s_cmd_usrp_write, + pmt_list3(invocation_handle, + pmt_from_long(channel), + v_packet)); + + goto new_packet; + } + + if(verbose) + std::cout << "[USRP_SERVER] Received I2C write\n"; + } + + //----------- I2C Read -------------// + if(pmt_eq(subp_cmd, s_op_i2c_read)) { + + long urid = pmt_to_long(pmt_nth(0, subp_data)); + long i2c_addr = pmt_to_long(pmt_nth(1, subp_data)); + long i2c_bytes = pmt_to_long(pmt_nth(2, subp_data)); + + long srid; + if((srid = next_rid()) == -1) + goto subpkt_bail; + + d_rids[srid].owner = port->port_symbol(); + d_rids[srid].user_rid = urid; + + if(!pkt->cs_i2c_read(srid, i2c_addr, i2c_bytes)) + { + + d_cs_usrp->send(s_cmd_usrp_write, + pmt_list3(invocation_handle, + pmt_from_long(channel), + v_packet)); + + d_rids[srid].owner = PMT_NIL; + + goto new_packet; + } + + if(verbose) + std::cout << "[USRP_SERVER] Received I2C read\n"; + } + + //--------- SPI WRITE -----------// + if(pmt_eq(subp_cmd, s_op_spi_write)) { + + long enables = pmt_to_long(pmt_nth(0, subp_data)); + long format = pmt_to_long(pmt_nth(1, subp_data)); + long opt = pmt_to_long(pmt_nth(2, subp_data)); + pmt_t data = pmt_nth(3, subp_data); + + // Get a readable address to the data which also gives us the length + size_t data_len; + uint8_t *spi_data = (uint8_t *) pmt_u8vector_writeable_elements(data, data_len); + + // Make the USB packet + if(!pkt->cs_spi_write(enables, format, opt, spi_data, data_len)) + { + d_cs_usrp->send(s_cmd_usrp_write, + pmt_list3(invocation_handle, + pmt_from_long(channel), + v_packet)); + + goto new_packet; + } + + if(verbose) + std::cout << "[USRP_SERVER] Received SPI write\n"; + } + + //--------- SPI READ -----------// + if(pmt_eq(subp_cmd, s_op_spi_read)) { + + long urid = pmt_to_long(pmt_nth(0, subp_data)); + long enables = pmt_to_long(pmt_nth(1, subp_data)); + long format = pmt_to_long(pmt_nth(2, subp_data)); + long opt = pmt_to_long(pmt_nth(3, subp_data)); + long n_bytes = pmt_to_long(pmt_nth(4, subp_data)); + + long srid; + if((srid = next_rid()) == -1) + goto subpkt_bail; + + d_rids[srid].owner = port->port_symbol(); + d_rids[srid].user_rid = urid; + + // Make the USB packet + if(!pkt->cs_spi_read(srid, enables, format, opt, n_bytes)) + { + d_cs_usrp->send(s_cmd_usrp_write, + pmt_list3(invocation_handle, + pmt_from_long(channel), + v_packet)); + + // Return the rid + d_rids[srid].owner = PMT_NIL; + + goto new_packet; + } + + if(verbose) + std::cout << "[USRP_SERVER] Received SPI read\n"; + } + + subpkt_bail: + curr_subpkt++; + + } + + + // If the current packets length is > 0, we know there are subpackets that + // need to be sent out still. + if(pkt->payload_len() > 0) + d_cs_usrp->send(s_cmd_usrp_write, + pmt_list3(invocation_handle, + pmt_from_long(channel), + v_packet)); + + return; +} + +void +usrp_server::handle_cmd_start_recv_raw_samples(mb_port_sptr port, std::vector<struct channel_info> &chan_info, pmt_t data) +{ + pmt_t invocation_handle = pmt_nth(0, data); + long channel = pmt_to_long(pmt_nth(1, data)); + + // Ensure the channel is valid and the caller owns the port + if(!check_valid(port, channel, chan_info, + pmt_list2(s_response_xmit_raw_frame, invocation_handle))) + return; + + // Already started receiving samples? (another start before a stop) + // Check the RX channel bitmask. + if(d_rx_chan_mask & (1 << channel)) { + port->send(s_response_recv_raw_samples, + pmt_list5(invocation_handle, + s_err_already_receiving, + PMT_NIL, + PMT_NIL, + PMT_NIL)); + return; + } + + // We only need to generate a 'start reading' command down to the + // low level interface if no other channel is already reading + // + // We carry this over the CS interface because the lower level + // interface does not care about the channel, we only demux it + // at the usrp_server on responses. + if(d_rx_chan_mask == 0) { + + if(verbose) + std::cout << "[USRP_SERVER] Sending read request down to start recv\n"; + + d_cs_usrp->send(s_cmd_usrp_start_reading, pmt_list1(invocation_handle)); + } + + d_rx_chan_mask |= 1<<channel; + + return; +} + +void +usrp_server::handle_cmd_stop_recv_raw_samples( + mb_port_sptr port, + std::vector<struct channel_info> &chan_info, + pmt_t data) +{ + pmt_t invocation_handle = pmt_nth(0, data); + long channel = pmt_to_long(pmt_nth(1, data)); + + // FIX ME : we have no responses to send an error... + // Ensure the channel is valid and the caller owns the port + //if(!check_valid(port, channel, chan_info, + // pmt_list2(s_response_xmit_raw_frame, invocation_handle))) + // return; + + // Remove this hosts bit from the receiver mask + d_rx_chan_mask &= ~(1<<channel); + + // We only need to generate a 'start reading' command down to the + // low level interface if no other channel is already reading + // + // We carry this over the CS interface because the lower level + // interface does not care about the channel, we only demux it + // at the usrp_server on responses. + if(d_rx_chan_mask == 0) { + + if(verbose) + std::cout << "[USRP_SERVER] Sending stop reading request down\n"; + + d_cs_usrp->send(s_cmd_usrp_stop_reading, pmt_list1(invocation_handle)); + } + + return; +} + +// Read the packet header, determine the port by the channel owner +void +usrp_server::handle_response_usrp_read(pmt_t data) +{ + + pmt_t invocation_handle = pmt_nth(0, data); + pmt_t status = pmt_nth(1, data); + pmt_t v_pkt = pmt_nth(2, data); + + size_t n_bytes; + size_t ignore; + + if (d_fake_rx) { + + pmt_t pkt = pmt_nth(2, data); + + d_rx[0]->send(s_response_recv_raw_samples, + pmt_list5(PMT_F, + PMT_T, + pkt, + pmt_from_long(0xffff), + PMT_NIL)); + + return; + } + + // Extract the packet and return appropriately + transport_pkt *pkt = (transport_pkt *) pmt_u8vector_writeable_elements(v_pkt, n_bytes); + + // The channel is used to find the port to pass the samples on + long channel = pkt->chan(); + long payload_len = pkt->payload_len(); + long port; + + // Ignore packets which seem to have incorrect size or size 0 + if(payload_len > pkt->max_payload() || payload_len == 0) + return; + + // If the packet is a C/S packet, parse it separately + if(channel == 0x1f) { + parse_control_pkt(invocation_handle, pkt); + return; + } + + if((port = rx_port_index(d_chaninfo_rx[channel].owner)) == -1) + return; // Don't know where to send the sample... possibility on abrupt close + + pmt_t v_samples = pmt_make_u8vector(payload_len, 0); + uint8_t *samples = pmt_u8vector_writeable_elements(v_samples, ignore); + + memcpy(samples, pkt->payload(), payload_len); + + // Build a properties dictionary to store things such as the RSSI + pmt_t properties = pmt_make_dict(); + + pmt_dict_set(properties, + pmt_intern("rssi"), + pmt_from_long(pkt->rssi())); + + if(pkt->overrun()) + pmt_dict_set(properties, + pmt_intern("overrun"), + PMT_T); + + if(pkt->underrun()) + pmt_dict_set(properties, + pmt_intern("underrun"), + PMT_T); + + d_rx[port]->send(s_response_recv_raw_samples, + pmt_list5(invocation_handle, + status, + v_samples, + pmt_from_long(pkt->timestamp()), + properties)); + return; +} + +void +usrp_server::parse_control_pkt(pmt_t invocation_handle, transport_pkt *pkt) +{ + + long payload_len = pkt->payload_len(); + long curr_payload = 0; + long port; + + // We dispatch based on the control packet type, however we can extract the + // opcode and the length immediately which is consistent in all responses. + // + // Since each control packet can have multiple responses, we keep reading the + // lengths of each subpacket until we reach the payload length. + while(curr_payload < payload_len) { + + pmt_t sub_packet = pkt->read_subpacket(curr_payload); + pmt_t op_symbol = pmt_nth(0, sub_packet); + + int len = pkt->cs_len(curr_payload); + + if(verbose) + std::cout << "[USRP_SERVER] Parsing subpacket " + << op_symbol << " ... length " << len << std::endl; + + //----------------- PING RESPONSE ------------------// + if(pmt_eq(op_symbol, s_op_ping_fixed_reply)) { + + long srid = pmt_to_long(pmt_nth(1, sub_packet)); + pmt_t pingval = pmt_nth(2, sub_packet); + + long urid = d_rids[srid].user_rid; + + if(verbose) + std::cout << "[USRP_SERVER] Found ping response " + << "(" + << "URID: " << urid << ", " + << "SRID: " << srid << ", " + << "VAL: " << pingval + << ")\n"; + + // Do some bounds checking incase of bogus/corrupt responses + if(srid > D_MAX_RID) + return; + + pmt_t owner = d_rids[srid].owner; + + // FIXME: should be 1 response for all subpackets here ? + if((port = tx_port_index(owner)) != -1) + d_tx[port]->send(s_response_from_control_channel, + pmt_list4(invocation_handle, + PMT_T, + pmt_list2(s_op_ping_fixed_reply, // subp + pmt_list2(pmt_from_long(urid), + pingval)), + pmt_from_long(pkt->timestamp()))); + } + + //----------------- READ REG RESPONSE ------------------// + else if(pmt_eq(op_symbol, s_op_read_reg_reply)) { + + long srid = pmt_to_long(pmt_nth(1, sub_packet)); + pmt_t reg_num = pmt_nth(2, sub_packet); + pmt_t reg_val = pmt_nth(3, sub_packet); + + long urid = d_rids[srid].user_rid; + + if(verbose) + std::cout << "[USRP_SERVER] Found read register response " + << "(" + << "URID: " << urid << ", " + << "SRID: " << srid << ", " + << "REG: " << reg_num << ", " + << "VAL: " << reg_val + << ")\n"; + + // Do some bounds checking to avoid seg faults + if(srid > D_MAX_RID) + return; + + pmt_t owner = d_rids[srid].owner; + + // FIXME: should be 1 response for all subpackets here ? + if((port = tx_port_index(owner)) != -1) + d_tx[port]->send(s_response_from_control_channel, + pmt_list4(invocation_handle, + PMT_T, + pmt_list2(s_op_read_reg_reply, // subp + pmt_list3(pmt_from_long(urid), + reg_num, + reg_val)), + pmt_from_long(pkt->timestamp()))); + } + + //------------------ I2C READ REPLY -------------------// + else if(pmt_eq(op_symbol, s_op_i2c_read_reply)) { + + long srid = pmt_to_long(pmt_nth(1, sub_packet)); + pmt_t i2c_addr = pmt_nth(2, sub_packet); + pmt_t i2c_data = pmt_nth(3, sub_packet); + + long urid = d_rids[srid].user_rid; + + if(verbose) + std::cout << "[USRP_SERVER] Found i2c read reply " + << "(" + << "URID: " << urid << ", " + << "SRID: " << srid << ", " + << "Addr: " << i2c_addr << ", " + << "Data: " << i2c_data + << ")\n"; + + // Do some bounds checking to avoid seg faults + if(srid > D_MAX_RID) + return; + + pmt_t owner = d_rids[srid].owner; + + if((port = tx_port_index(owner)) != -1) + d_tx[port]->send(s_response_from_control_channel, + pmt_list4(invocation_handle, + PMT_T, + pmt_list2(s_op_i2c_read_reply, + pmt_list3(pmt_from_long(urid), + i2c_addr, + i2c_data)), + pmt_from_long(pkt->timestamp()))); + } + + //------------------ SPI READ REPLY -------------------// + else if(pmt_eq(op_symbol, s_op_spi_read_reply)) { + + long srid = pmt_to_long(pmt_nth(1, sub_packet)); + pmt_t spi_data = pmt_nth(2, sub_packet); + + long urid = d_rids[srid].user_rid; + + if(verbose) + std::cout << "[USRP_SERVER] Found SPI read reply " + << "(" + << "URID: " << urid << ", " + << "SRID: " << srid << ", " + << "Data: " << spi_data + << ")\n"; + + // Bounds check the RID + if(srid > D_MAX_RID) + return; + + pmt_t owner = d_rids[srid].owner; + + if((port = tx_port_index(owner)) != -1) + d_tx[port]->send(s_response_from_control_channel, + pmt_list4(invocation_handle, + PMT_T, + pmt_list2(s_op_spi_read_reply, + pmt_list2(pmt_from_long(urid), + spi_data)), + pmt_from_long(pkt->timestamp()))); + } + + // Each subpacket has an unaccounted for 2 bytes which is the opcode + // and the length field + curr_payload += len + 2; + + // All subpackets are 32-bit aligned + int align_offset = 4 - (curr_payload % 4); + + if(align_offset != 4) + curr_payload += align_offset; + } +} + +void +usrp_server::recall_defer_queue() +{ + + std::vector<mb_message_sptr> recall; + + while(!d_defer_queue.empty()) { + recall.push_back(d_defer_queue.front()); + d_defer_queue.pop(); } - pkts[n_packets-1].set_end_of_burst(); // set the last packet's end of burst + // Parse the messages that were queued while waiting for an open response + for(int i=0; i < (int)recall.size(); i++) + handle_message(recall[i]); + + return; +} + +bool +usrp_server::check_valid(mb_port_sptr port, + long channel, + std::vector<struct channel_info> &chan_info, + pmt_t signal_info) +{ - // interface with the USRP to send the USB packet, since the memory is - // contiguous, this should be a serious of memory copies to the bus, each being - // USB_PKT_SIZE * MAX_PACKET_BURST bytes worth of data (given a full burst) + pmt_t response_signal = pmt_nth(0, signal_info); + pmt_t invocation_handle = pmt_nth(1, signal_info); + + // not a valid channel number? + if(channel >= (long)chan_info.size() && channel != 0x1f) { + port->send(response_signal, + pmt_list2(invocation_handle, + s_err_channel_invalid)); + + if(verbose) + std::cout << "[USRP_SERVER] Invalid channel number for event " + << response_signal << std::endl; + return false; + } + + // not the owner of the port? + if(chan_info[channel].owner != port->port_symbol()) { + port->send(response_signal, + pmt_list2(invocation_handle, + s_err_channel_permission_denied)); + + if(verbose) + std::cout << "[USRP_SERVER] Invalid permissions" + << " for " << response_signal + << " from " << port->port_symbol() + << " proper owner is " << chan_info[channel].owner + << " on channel " << channel + << " invocation " << invocation_handle + << std::endl; + return false; + } + + return true; +} + +// Goes through the vector of RIDs and retreieves an +// available one for use +long +usrp_server::next_rid() +{ + for(int i = 0; i < D_MAX_RID; i++) + if(pmt_eqv(d_rids[i].owner, PMT_NIL)) + return i; + + return -1; } REGISTER_MBLOCK_CLASS(usrp_server); |