--- 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
{