Merged r7478:7608 from michaelld/t186 into trunk. Adds ability to compile GNU Radio...
[debian/gnuradio] / usrp / host / apps-inband / gmac.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
26 #include <gmac.h>
27
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>
36 #include <pmt.h>
37 #include <stdio.h>
38 #include <string.h>
39 #include <iostream>
40 #include <ui_nco.h>
41
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>
47
48 #include <gmac_symbols.h>
49
50 static bool verbose = true;
51
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)
55 {
56
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.'
60
61   // Initialize the ports
62   define_ports();
63
64   // Initialize the connection to the USRP
65   initialize_usrp();
66
67 }
68
69 gmac::~gmac()
70 {
71 }
72
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)
77 {
78
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();
85
86   pmt_t handle = PMT_F;
87   pmt_t status = PMT_F;
88   pmt_t dict = PMT_NIL;
89   std::string error_msg;
90
91   switch(d_state) {
92     
93     //---------------------------- INIT ------------------------------------//
94     // In the INIT state, there should be no messages across the ports. 
95     case INIT:
96       error_msg = "no messages should be passed during the INIT state:"; 
97       goto unhandled;
98
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.
103     case OPENING_USRP:
104
105       if(pmt_eq(event, s_response_open)
106           && pmt_eq(d_us_cs->port_symbol(), port_id)) {
107
108         status = pmt_nth(1, data);          // PMT_T or PMT_F
109
110         if(pmt_eq(status, PMT_T)) {         // on success, allocate channels!
111           allocate_channels();
112           return;
113         }
114         else {
115           error_msg = "failed to open usrp:";
116           goto bail;
117         }
118
119       }
120
121       goto unhandled;   // all other messages not handled in this state
122
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:
128
129       // ************* TX ALLOCATION RESPONSE ***************** //
130       if(pmt_eq(event, s_response_allocate_channel)
131           && pmt_eq(d_us_tx->port_symbol(), port_id)) 
132       {
133         status = pmt_nth(1, data);
134         
135         if(pmt_eq(status, PMT_T)) {   // extract channel on success
136           d_us_tx_chan = pmt_nth(2, data);
137
138           if(verbose)
139             std::cout << "[GMAC] Received TX allocation"
140                       << " on channel " << d_us_tx_chan << std::endl;
141
142           // If the RX has also been allocated already, we can continue
143           if(!pmt_eqv(d_us_rx_chan, PMT_NIL)) {
144             //enter_receiving();
145             initialize_gmac();
146           }
147
148           return;
149         }
150         else {  // TX allocation failed
151           error_msg = "failed to allocate TX channel:";
152           goto bail;
153         }
154       }
155       
156       // ************* RX ALLOCATION RESPONSE ****************//
157       if(pmt_eq(event, s_response_allocate_channel)
158           && pmt_eq(d_us_rx->port_symbol(), port_id)) 
159       {
160         status = pmt_nth(1, data);
161         
162         if(pmt_eq(status, PMT_T)) {
163           
164           d_us_rx_chan = pmt_nth(2, data);
165
166           if(verbose)
167             std::cout << "[GMAC] Received RX allocation"
168                       << " on channel " << d_us_rx_chan << std::endl;
169
170           // If the TX has also been allocated already, we can continue
171           if(!pmt_eqv(d_us_tx_chan, PMT_NIL)) {
172             //enter_receiving();
173             initialize_gmac();
174           }
175
176           return;
177         }
178         else {  // RX allocation failed
179           error_msg = "failed to allocate RX channel:";
180           goto bail;
181         }
182       }
183
184       goto unhandled;
185     
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.
189     case INIT_GMAC:
190       goto unhandled;
191
192     
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.
197     case IDLE:
198       
199       //-------- TX PORT ----------------------------------------------------//
200       if(pmt_eq(d_tx->port_symbol(), port_id)) {
201
202         //-------- INCOMING PACKET ------------------------------------------//
203         if(pmt_eq(event, s_cmd_tx_pkt)) {
204           handle_cmd_tx_pkt(data);
205           return;
206         }
207
208       }
209
210       //--------- USRP TX PORT ----------------------------------------------//
211       if(pmt_eq(d_us_tx->port_symbol(), port_id)) {
212
213         //-------- INCOMING PACKET RESPONSE ---------------------------------//
214         if(pmt_eq(event, s_response_xmit_raw_frame)) {
215           handle_response_xmit_raw_frame(data);
216           return;
217         }
218
219       }
220
221       //--------- CS PORT ---------------------------------------------------//
222       if(pmt_eq(d_cs->port_symbol(), port_id)) {
223         
224         //------- ENABLE CARRIER SENSE --------------------------------------//
225         if(pmt_eq(event, s_cmd_carrier_sense_enable)) {
226           handle_cmd_carrier_sense_enable(data);
227           return;
228         }
229         
230         //------- CARRIER SENSE THRESHOLD -----------------------------------//
231         if(pmt_eq(event, s_cmd_carrier_sense_threshold)) {
232           handle_cmd_carrier_sense_threshold(data);
233           return;
234         }
235
236         //------- CARRIER SENSE DEADLINE ------------------------------------//
237         if(pmt_eq(event, s_cmd_carrier_sense_deadline)) {
238           handle_cmd_carrier_sense_deadline(data);
239           return;
240         }
241
242         //------- DISABLE CARRIER SENSE -------------------------------------//
243         if(pmt_eq(event, s_cmd_carrier_sense_disable)) {
244           handle_cmd_carrier_sense_disable(data);
245           return;
246         }
247
248       }
249
250       goto unhandled;
251
252     //------------------------ CLOSING CHANNELS -----------------------------//
253     case CLOSING_CHANNELS:
254
255       if (pmt_eq(event, s_response_deallocate_channel)
256           && pmt_eq(d_us_tx->port_symbol(), port_id))
257       {
258         status = pmt_nth(1, data);
259
260         if(pmt_eq(status, PMT_T)) {
261           d_us_tx_chan = PMT_NIL;
262
263           if(verbose)
264             std::cout << "[GMAC] Received TX deallocation\n";
265
266           // If the RX is also deallocated, we can close the USRP
267           if(pmt_eq(d_us_rx_chan, PMT_NIL)) 
268             close_usrp();
269
270           return;
271
272         } else {
273
274           error_msg = "failed to deallocate TX channel:";
275           goto bail;
276
277         }
278       }
279
280       if (pmt_eq(event, s_response_deallocate_channel)
281           && pmt_eq(d_us_rx->port_symbol(), port_id))
282       {
283         status = pmt_nth(1, data);
284
285         // If successful, set the port to NIL
286         if(pmt_eq(status, PMT_T)) {
287           d_us_rx_chan = PMT_NIL;
288
289           if(verbose)
290             std::cout << "[GMAC] Received RX deallocation\n";
291
292           // If the TX is also deallocated, we can close the USRP
293           if(pmt_eq(d_us_tx_chan, PMT_NIL)) 
294             close_usrp();
295
296           return;
297
298         } else {
299           
300           error_msg = "failed to deallocate RX channel:";
301           goto bail;
302
303         }
304       }
305
306       goto unhandled;
307
308     //-------------------------- CLOSING USRP -------------------------------//
309     case CLOSING_USRP:
310       goto unhandled;
311       
312   }
313   
314  // An error occured, print it, and shutdown all m-blocks
315  bail:
316   std::cerr << error_msg << data
317             << "status = " << status << std::endl;
318   shutdown_all(PMT_F);
319   return;
320
321  // Received an unhandled message for a specific state
322  unhandled:
323   if(0 && verbose && !pmt_eq(event, pmt_intern("%shutdown")))
324     std::cout << "[GMAC] unhandled msg: " << msg
325               << "in state "<< d_state << std::endl;
326 }
327
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()
332 {
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);
337   
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);
342 }
343
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()
349 {
350
351   if(verbose)
352     std::cout << "[GMAC] Initializing USRP\n";
353
354   // The initialization parameters are passed to usrp_server via a PMT
355   // dictionary.
356   pmt_t usrp_dict = pmt_make_dict();
357
358   // Specify the RBF to use
359   pmt_dict_set(usrp_dict,
360                pmt_intern("rbf"),
361                pmt_intern("test2.rbf"));
362
363   pmt_dict_set(usrp_dict,
364                pmt_intern("interp-tx"),
365                pmt_from_long(128));
366
367   pmt_dict_set(usrp_dict,
368                pmt_intern("decim-rx"),
369                pmt_from_long(16));
370   
371   // Center frequency
372   pmt_dict_set(usrp_dict,
373                pmt_intern("rf-freq"),
374                pmt_from_long((long)10e6));
375
376   // Default is to use USRP considered '0' (incase of multiple)
377   d_which_usrp = pmt_from_long(0);
378   
379   define_component("USRP-SERVER", "usrp_server", usrp_dict);
380   
381   connect("self", "us-tx0", "USRP-SERVER", "tx0");
382   connect("self", "us-rx0", "USRP-SERVER", "rx0");
383   connect("self", "us-cs", "USRP-SERVER", "cs");
384
385   // Finally, enter the OPENING_USRP state by sending a request to open the
386   // USRP.
387   open_usrp();
388
389 }
390
391 // In the initialization state of the MAC layer we set default values for
392 // several functionalities.
393 void gmac::initialize_gmac()
394 {
395
396   // The initial state is the INIT state.
397   d_state = INIT_GMAC;
398
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);
402
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));
406
407   // The MAC enters an IDLE state where it waits for messages and dispatches
408   // based on them
409   enter_idle();
410 }
411
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.
415 //
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)
419 {
420   d_carrier_sense = toggle;
421
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
426                          pmt_list1(
427                               pmt_list2(s_op_write_reg, 
428                                         pmt_list2(
429                                         pmt_from_long(REG_CS_THRESH), 
430                                         pmt_from_long(threshold))))));
431     d_cs_thresh = threshold;
432
433     if(verbose)
434       std::cout << "[GMAC] Changing CS threshold: " << d_cs_thresh << std::endl;
435   }
436
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
440                          pmt_list1(
441                               pmt_list2(s_op_write_reg, 
442                                         pmt_list2(
443                                         pmt_from_long(REG_CS_DEADLINE), 
444                                         pmt_from_long(deadline))))));
445     d_cs_deadline = deadline;
446
447     if(verbose)
448       std::cout << "[GMAC] Changing CS deadline: " << d_cs_deadline << std::endl;
449   }
450
451   if(verbose)
452     std::cout << "[GMAC] Setting carrier sense to " << toggle << std::endl;
453 }
454
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()
459 {
460   d_state = OPENING_USRP;
461
462   d_us_cs->send(s_cmd_open, pmt_list2(PMT_NIL, d_which_usrp));
463   
464   if(verbose)
465     std::cout << "[GMAC] Opening USRP " 
466               << d_which_usrp << std::endl;
467 }
468
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()
473 {
474   d_state = CLOSING_USRP;
475
476   sleep(2);
477
478   d_us_cs->send(s_cmd_close, pmt_list1(PMT_NIL));
479 }
480
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()
486 {
487   d_state = ALLOCATING_CHANNELS;
488   
489   if(verbose)
490     std::cout << "[GMAC] Sending channel allocation requests\n";
491
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)));
495
496 }
497
498 // Before closing the USRP connection, we deallocate our channels so that the
499 // capacity can be reused.
500 void gmac::close_channels()
501 {
502   d_state = CLOSING_CHANNELS;
503
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));
506
507   if(verbose)
508     std::cout << "[GMAC] Closing channels...\n";
509 }
510
511 // Used to enter the receiving state
512 void gmac::enter_receiving()
513 {
514   d_us_rx->send(s_cmd_start_recv_raw_samples,
515              pmt_list2(PMT_F,
516                        d_us_rx_chan));
517
518   if(verbose)
519     std::cout << "[GMAC] Started RX sample stream\n";
520 }
521
522 // A simple idle state, nothing more to it.
523 void gmac::enter_idle()
524 {
525   d_state = IDLE;
526 }
527
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.
533 //
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
536 // connection)
537 void gmac::handle_cmd_tx_pkt(pmt_t data)
538 {
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);
543
544   pmt_t us_tx_properties = pmt_make_dict();
545
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"),
550                  PMT_T);
551
552   pmt_t timestamp = pmt_from_long(0xffffffff);  // NOW
553
554   // Construct the proper message for USRP server
555   d_us_tx->send(s_cmd_xmit_raw_frame,
556                 pmt_list5(invocation_handle,
557                                       d_us_tx_chan,
558                                       samples, 
559                           timestamp,
560                           us_tx_properties));
561
562   if(verbose && 0)
563     std::cout << "[GMAC] Transmitted packet\n";
564 }
565
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
569 // implemented.
570 void gmac::handle_response_xmit_raw_frame(pmt_t data)
571 {
572   pmt_t invocation_handle = pmt_nth(0, data);
573   pmt_t status = pmt_nth(1, data);
574
575   d_tx->send(s_response_tx_pkt,
576              pmt_list2(invocation_handle,
577                        status));
578 }
579
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) 
585 {
586   // First we extract the per packet properties to check the per packet setting
587   // if it exists
588   if(pmt_is_dict(pkt_properties)) {
589
590     if(pmt_t pkt_cs = pmt_dict_ref(pkt_properties,
591                                    pmt_intern("carrier-sense"),
592                                    PMT_NIL)) {
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))
596         return true;
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))
600         return false;
601     }
602   }
603
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
606   if(d_carrier_sense)
607     return true;
608   else
609     return false;
610
611 }
612
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)
618 {
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;
623
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;
628   else
629     l_threshold = pmt_to_long(threshold);
630
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;
634   else
635     l_deadline = pmt_to_long(deadline);
636   
637   set_carrier_sense(true, l_threshold, l_deadline, invocation_handle);
638 }
639
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) 
645 {
646   pmt_t invocation_handle = pmt_nth(0, data);
647   
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);
651 }
652
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
656 // unchanged.
657 void gmac::handle_cmd_carrier_sense_threshold(pmt_t data)
658 {
659   pmt_t invocation_handle = pmt_nth(0, data);
660   pmt_t threshold = pmt_nth(1, data);
661   long l_threshold;
662
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;
667   else
668     l_threshold = pmt_to_long(threshold);
669   
670   set_carrier_sense(d_carrier_sense, l_threshold, d_cs_deadline, invocation_handle);
671 }
672
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)
676 {
677   pmt_t invocation_handle = pmt_nth(0, data);
678   pmt_t deadline = pmt_nth(1, data);
679   long l_deadline;
680
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;
684   else
685     l_deadline = pmt_to_long(deadline);
686   
687   set_carrier_sense(d_carrier_sense, d_cs_thresh, l_deadline, invocation_handle);
688 }
689
690 REGISTER_MBLOCK_CLASS(gmac);