src/devices/p2p/p2p-net-device.cc
changeset 378 32bd402ea5ea
parent 371 d3cd20dfb425
child 379 ae74e8a7bb44
--- a/src/devices/p2p/p2p-net-device.cc	Mon Mar 26 06:50:33 2007 -0700
+++ b/src/devices/p2p/p2p-net-device.cc	Tue Mar 27 13:04:11 2007 -0700
@@ -23,18 +23,21 @@
 #include <cassert>
 #include "ns3/debug.h"
 #include "ns3/queue.h"
+#include "ns3/simulator.h"
 #include "ns3/composite-trace-resolver.h"
 #include "p2p-net-device.h"
 #include "p2p-channel.h"
-#include "p2p-phy.h"
 
 NS_DEBUG_COMPONENT_DEFINE ("PointToPointNetDevice");
 
 namespace ns3 {
 
-
-PointToPointNetDevice::PointToPointNetDevice(Node* node) : 
-  NetDevice(node, MacAddress("00:00:00:00:00:00")), m_phy(0), m_channel(0), 
+PointToPointNetDevice::PointToPointNetDevice(Node* node) 
+: 
+  NetDevice(node, MacAddress("00:00:00:00:00:00")), 
+  m_txMachineState(READY),
+  m_bps (DataRate(0xffffffff)),
+  m_channel(0), 
   m_queue(0)
 {
   NS_DEBUG ("PointToPointNetDevice::PointToPointNetDevice (" << node << ")");
@@ -45,21 +48,30 @@
   EnableBroadcast (MacAddress ("ff:ff:ff:ff:ff:ff"));
   EnableMulticast();
   EnablePointToPoint();
-
-  m_phy = new PointToPointPhy(node, this);
 }
 
 PointToPointNetDevice::~PointToPointNetDevice()
 {
   NS_DEBUG ("PointToPointNetDevice::~PointToPointNetDevice ()");
-  delete m_phy;
+}
+
+  void 
+PointToPointNetDevice::SetDataRate(DataRate bps)
+{
+  m_bps = bps;
 }
 
+  void 
+PointToPointNetDevice::SetInterframeGap(Time t)
+{
+  m_tInterframeGap = t;
+}
 
-bool
+  bool
 PointToPointNetDevice::SendTo (Packet& p, const MacAddress& dest)
 {
   NS_DEBUG ("PointToPointNetDevice::SendTo (" << &p << ", " << &dest << ")");
+  NS_DEBUG ("PointToPointNetDevice::SendTo (): UID is " << p.GetUid () << ")");
 
   assert (IsLinkUp ());
 
@@ -68,12 +80,142 @@
     tag.address = address;
     p.AddTag (tag);
 #endif
-    if (m_queue->Enqueue(p) )
+
+//
+// This class simulates a point to point device.  In the case of a serial
+// link, this means that we're simulating something like a UART.  This is
+// not a requirement for a point-to-point link, but it's a typical model for
+// the device.  
+//
+// Generally, a real device will have a list of pending packets to transmit.  
+// An on-device CPU frees the main CPU(s) of the details of what is happening
+// in the device and feeds the USART.  The main CPU basically just sees the 
+// list of packets -- it puts packets into the list, and the device frees the
+// packets when they are transmitted.
+//
+// In the case of our virtual device here, the queue pointed to by m_queue
+// corresponds to this list.  The main CPU adds packets to the list by 
+// calling this method and when the device completes a send, the packets are
+// freed in an "interrupt" service routine.
+//
+// We're going to do the same thing here.  So first of all, the incoming packet
+// goes onto our queue if possible.  If the queue can't handle it, there's
+// nothing to be done.
+//
+    if (m_queue->Enqueue(p) == false )
+      {
+        return false;
+      }
+//
+// If there's a transmission in progress, the "interrupt" will keep the
+// transmission process going.  If the device is idle, we need to start a
+// transmission.
+//
+// In the real world, the USART runs until it finishes sending bits, and then
+// pulls on the device's transmit complete interrupt wire.  At the same time,
+// the electrons from the last wiggle of the wire are busy propagating down
+// the wire.  In the case of a long speed-of-light delay in the wire, we could
+// conceivably start transmitting the next packet before the end of the 
+// previously sent data has even reached the end of the wire.  This situation
+// is usually avoided (like the plague) and an "interframe gap" is introduced.
+// This is usually the round-trip delay on the channel plus some hard-to-
+// quantify receiver turn-around time (the time required for the receiver
+// to process the last frame and prepare for reception of the next).
+//
+// So, if the transmit machine is ready, we need to schedule a transmit 
+// complete event (at which time we tell the channel we're no longer sending
+// bits).  A separate transmit ready event (at which time the transmitter 
+// becomes ready to start sending bits again is scheduled there).  Finally, 
+// we tell the channel (via TransmitStart ()) that we've started wiggling the 
+// wire and bits are coming out.
+//
+// If the transmit machine is not ready, we just leave and the transmit ready
+// event we know is coming will kick-start the transmit process.
+//
+    if (m_txMachineState == READY) 
       {
-        NotifyDataAvailable ();
-        return true;
+        return TransmitStart (p);
       }
-    return false;
+    return true;
+}
+
+  bool
+PointToPointNetDevice::TransmitStart (Packet &p)
+{
+  NS_DEBUG ("PointToPointNetDevice::TransmitStart (" << &p << ")");
+  NS_DEBUG ("PointToPointNetDevice::TransmitStart (): UID is " << p.GetUid () << ")");
+//
+// This function is called to start the process of transmitting a packet.
+// We need to tell the channel that we've started wiggling the wire and
+// schedule an event that will be executed when it's time to tell the 
+// channel that we're done wiggling the wire.
+//
+  NS_ASSERT(m_txMachineState == READY && "Must be READY to transmit");
+  m_txMachineState = BUSY;
+  Time tEvent = Seconds (m_bps.CalculateTxTime(p.GetSize()));
+
+  NS_DEBUG ("PointToPointNetDevice::TransmitStart (): " <<
+    "Schedule TransmitCompleteEvent in " << 
+    tEvent.GetSeconds () << "sec");
+
+  Simulator::Schedule (tEvent, 
+                       &PointToPointNetDevice::TransmitCompleteEvent, 
+                       this);
+  return m_channel->TransmitStart (p, this); 
+}
+
+  void
+PointToPointNetDevice::TransmitCompleteEvent (void)
+{
+  NS_DEBUG ("PointToPointNetDevice::TransmitCompleteEvent ()");
+//
+// This function is called to finish the  process of transmitting a packet.
+// We need to tell the channel that we've stopped wiggling the wire and
+// schedule an event that will be executed when it's time to re-enable
+// the transmitter after the interframe gap.
+//
+  NS_ASSERT(m_txMachineState == BUSY && "Must be BUSY if transmitting");
+  m_txMachineState = GAP;
+  Packet p;
+  bool found = m_queue->Dequeue (p);
+  NS_ASSERT(found && "Packet must be on queue if transmitted");
+  NS_DEBUG ("PointToPointNetDevice::TransmitCompleteEvent (): Pkt UID is " << 
+            p.GetUid () << ")");
+  m_channel->TransmitEnd (p, this); 
+
+  NS_DEBUG (
+    "PointToPointNetDevice::TransmitCompleteEvent (): " <<
+    "Schedule TransmitReadyEvent in "
+    << m_tInterframeGap.GetSeconds () << "sec");
+
+  Simulator::Schedule (m_tInterframeGap, 
+                       &PointToPointNetDevice::TransmitReadyEvent, 
+                       this);
+}
+
+  void
+PointToPointNetDevice::TransmitReadyEvent (void)
+{
+  NS_DEBUG ("PointToPointNetDevice::TransmitReadyEvent ()");
+//
+// This function is called to enable the transmitter after the interframe
+// gap has passed.  If there are pending transmissions, we use this opportunity
+// to start the next transmit.
+//
+  NS_ASSERT(m_txMachineState == GAP && "Must be in interframe gap");
+  m_txMachineState = READY;
+
+  if (m_queue->IsEmpty())
+    {
+      return;
+    }
+  else
+    {
+      Packet p;
+      bool found = m_queue->Peek (p);
+      NS_ASSERT(found && "IsEmpty false but no Packet on queue?");
+      TransmitStart (p);
+    }
 }
 
 TraceResolver *
@@ -95,7 +237,11 @@
   NS_DEBUG ("PointToPointNetDevice::Attach (" << &ch << ")");
 
   m_channel = ch;
-  m_phy->Attach (m_channel);
+
+  m_channel->Attach(this);
+  m_bps = m_channel->GetDataRate ();
+  m_tInterframeGap = m_channel->GetDelay ();
+
   /* 
    * For now, this device is up whenever a channel is attached to it.
    * In fact, it should become up only when the second device
@@ -126,25 +272,6 @@
   ForwardUp (p);
 }
 
-void
-PointToPointNetDevice::NotifyDataAvailable(void)
-{
-  NS_DEBUG ("PointToPointNetDevice::NotifyDataAvailable ()");
-
-  Packet p;
-  bool found = GetQueue ()->Dequeue (p);
-  if (found)
-    {
-#ifdef NOTYET
-      struct NetDevicePacketDestAddress tag;
-      p.PeekTag (tag);
-      // send packet to address tag.address
-#endif
-      NS_DEBUG ("PointToPointNetDevice::NotifyDataAvailable (): Dequeued");
-      m_phy->Send(p);
-    }
-}
-
 Queue* 
 PointToPointNetDevice::GetQueue(void) const 
 {