Updated license from GPL version 2 or later to GPL version 3 or later.
[debian/gnuradio] / mblock / src / lib / qa_disconnect.cc
1 /* -*- c++ -*- */
2 /*
3  * Copyright 2007 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 #include <mb_mblock.h>
26 #include <mb_protocol_class.h>
27 #include <mb_message.h>
28 #include <mb_class_registry.h>
29 #include <iostream>
30 #include <sstream>
31 #include <bitset>
32
33 static pmt_t s_in = pmt_intern("in");
34 static pmt_t s_out = pmt_intern("out");
35 static pmt_t s_data = pmt_intern("data");
36 static pmt_t s_ack = pmt_intern("ack");
37 static pmt_t s_select_pipe = pmt_intern("select-pipe");
38 static pmt_t s_long0 = pmt_from_long(0);
39 static pmt_t s_sys_port = pmt_intern("%sys-port");
40 static pmt_t s_shutdown = pmt_intern("%shutdown");
41
42 class qa_disconnect_mux : public mb_mblock
43 {
44   mb_port_sptr  d_in;
45   mb_port_sptr  d_out;
46   mb_port_sptr  d_cs;
47
48 public:
49   qa_disconnect_mux(mb_runtime *runtime, const std::string &instance_name, pmt_t user_arg);
50   void initial_transition();
51   void handle_message(mb_message_sptr msg);
52 };
53
54 qa_disconnect_mux::qa_disconnect_mux(mb_runtime *runtime,
55                                      const std::string &instance_name,
56                                      pmt_t user_arg)
57   : mb_mblock(runtime, instance_name, user_arg)
58 {
59   d_in  = define_port("in", "qa-bitset", false, mb_port::RELAY);
60   d_out = define_port("out", "qa-bitset", true, mb_port::RELAY);
61   d_cs  = define_port("cs", "qa-disconnect-cs", true, mb_port::EXTERNAL);
62
63   define_component("pipeline0", "qa_bitset8", pmt_from_long(0));
64   define_component("pipeline1", "qa_bitset8", pmt_from_long(8));
65 }
66
67 void
68 qa_disconnect_mux::initial_transition(){}
69
70 void
71 qa_disconnect_mux::handle_message(mb_message_sptr msg)
72 {
73   if (pmt_eq(msg->port_id(), d_cs->port_symbol())       // select-pipe on cs
74       && pmt_eq(msg->signal(), s_select_pipe)){         
75
76     long which_pipe = pmt_to_long(pmt_nth(0, msg->data()));
77
78     disconnect_component("pipeline0");
79     disconnect_component("pipeline1");
80
81     switch(which_pipe){
82
83     case 0:
84       connect("self", "in",  "pipeline0", "in");
85       connect("self", "out", "pipeline0", "out");
86       break;
87
88     case 1:
89       connect("self", "in",  "pipeline1", "in");
90       connect("self", "out", "pipeline1", "out");
91       break;
92     }
93
94     d_cs->send(s_ack, msg->data());
95     return;
96   }
97 }
98
99 REGISTER_MBLOCK_CLASS(qa_disconnect_mux);
100
101 // ------------------------------------------------------------------------
102
103 class qa_disconnect_top : public mb_mblock
104 {
105   enum state_t {
106     UNINITIALIZED,
107     WAIT_FOR_ACK,
108     WAIT_FOR_DATA
109   };
110
111   state_t       d_state;
112   int           d_msg_number;
113   int           d_nmsgs_to_send;
114
115   mb_port_sptr  d_in;
116   mb_port_sptr  d_out;
117   mb_port_sptr  d_cs;
118
119   void check_pipe_send_next_msg();
120   void send_next_msg();
121   void select_pipe(int n);
122
123   // alternate pipes every 128 messages
124   static int  which_pipe(int msg_number) { return (msg_number >> 7) & 0x1; }
125   bool time_to_switch() { return (d_msg_number & 0x7f) == 0; }
126   
127 public:
128   qa_disconnect_top(mb_runtime *runtime, const std::string &instance_name, pmt_t user_arg);
129   void initial_transition();
130   void handle_message(mb_message_sptr msg);
131 };
132
133 qa_disconnect_top::qa_disconnect_top(mb_runtime *runtime,
134                                      const std::string &instance_name,
135                                      pmt_t user_arg)
136   : mb_mblock(runtime, instance_name, user_arg),
137     d_state(UNINITIALIZED), d_msg_number(0)
138 {
139   d_nmsgs_to_send = pmt_to_long(pmt_nth(0, user_arg));
140
141   d_in  = define_port("in", "qa-bitset", false, mb_port::INTERNAL);
142   d_out = define_port("out", "qa-bitset", true, mb_port::INTERNAL);
143   d_cs  = define_port("cs", "qa-disconnect-cs", false, mb_port::INTERNAL);
144
145   define_component("mux", "qa_disconnect_mux", PMT_F);
146
147   connect("self", "cs",  "mux", "cs");
148   connect("self", "out", "mux", "in");
149   connect("self", "in",  "mux", "out");
150 }
151
152 void
153 qa_disconnect_top::initial_transition()
154 {
155   check_pipe_send_next_msg();
156 }
157
158 void
159 qa_disconnect_top::handle_message(mb_message_sptr msg)
160 {
161   if (0)
162     std::cerr << "qa_disconnect_top::handle_msg state = "
163               << d_state << "\n  msg = " << msg << std::endl;
164
165   if (pmt_eq(msg->port_id(), d_cs->port_symbol())       // ack on cs
166       && pmt_eq(msg->signal(), s_ack)
167       && d_state == WAIT_FOR_ACK){
168
169     send_next_msg();
170     return;
171   }
172
173   if (pmt_eq(msg->port_id(), d_in->port_symbol())       // data on in
174       && pmt_eq(msg->signal(), s_data)
175       && d_state == WAIT_FOR_DATA){
176
177     /*  
178      * Confirm that msg passed through the pipe that we expect...
179      */
180     static const long expected_mask[2] = { 0x000000ff, 0x0000ff00 };
181
182     long msg_number = pmt_to_long(pmt_car(msg->data()));
183     long mask = pmt_to_long(pmt_cdr(msg->data()));
184
185     if (mask != expected_mask[which_pipe(msg_number)]){
186       fprintf(stderr, "\nqa_disconnect_top: wrong mask in msg_number = 0x%08lx\n",
187               msg_number);
188       fprintf(stderr, "  expected = 0x%08lx, actual = 0x%08lx\n",
189               expected_mask[which_pipe(msg_number)], mask);
190       shutdown_all(PMT_F);
191       return;
192     }
193
194     if (msg_number == d_nmsgs_to_send - 1){     // we're done (and were successful)
195       shutdown_all(PMT_T);
196       return;
197     }
198
199     check_pipe_send_next_msg();
200     return;
201   }
202
203   if (pmt_eq(msg->port_id(), s_sys_port)        // ignore %shutdown on %sys-port
204       && pmt_eq(msg->signal(), s_shutdown))
205     return;
206
207   std::cerr << "qa_disconnect_top: unhandled msg: state = "
208             << d_state << "\n  msg = " << msg << std::endl;
209 }
210
211 void
212 qa_disconnect_top::select_pipe(int n)
213 {
214   d_cs->send(s_select_pipe, pmt_list1(pmt_from_long(n)));
215   d_state = WAIT_FOR_ACK;
216 }
217
218 void
219 qa_disconnect_top::send_next_msg()
220 {
221   d_state = WAIT_FOR_DATA;
222   if (d_msg_number == d_nmsgs_to_send)  // we've sent all we're supposed to
223     return;
224
225   d_out->send(s_data, pmt_cons(pmt_from_long(d_msg_number), s_long0));
226   d_msg_number++;
227 }
228
229 void
230 qa_disconnect_top::check_pipe_send_next_msg()
231 {
232   if (time_to_switch())
233     select_pipe(which_pipe(d_msg_number));
234   else
235     send_next_msg();
236 }
237
238 REGISTER_MBLOCK_CLASS(qa_disconnect_top);