/* -*- Mode:C++; c-basic-offset:8; tab-width:8; indent-tabs-mode:t -*- */
/*
* mac-802_3.cc
* $Id: mac-802_3.cc,v 1.16 2002/06/14 23:15:03 yuri Exp $
*/
#include <packet.h>
#include <random.h>
#include <arp.h>
#include <ll.h>
#include <mac-802_3.h>
//#define MAC_DEBUG
#ifndef MAC_DEBUG
#define FPRINTF(s, f, t, index, func) do {} while (0)
#else
static double xtime= 0.0;
# define FPRINTF(s, f, t, index, func) \
do { fprintf(s, f, t, index, func); xtime= t; } while (0)
#endif //MAC_DEBUG
#define PRNT_MAC_FUNCS(mac) \
FPRINTF(stderr, "%.15f : %d : %s\n", \
Scheduler::instance().clock(), (mac)->index_, __PRETTY_FUNCTION__)
inline void MacHandler::cancel() {
PRNT_MAC_FUNCS(mac);
Scheduler& s = Scheduler::instance();
assert(busy_);
s.cancel(&intr);
// No need to free the event intr since it's statically allocated.
busy_ = 0;
}
inline void Mac8023HandlerSend::cancel() {
PRNT_MAC_FUNCS(mac);
assert(busy_);
Scheduler &s= Scheduler::instance();
s.cancel(&intr);
busy_= 0;
p_= 0;
}
inline void MacHandlerRecv::cancel() {
PRNT_MAC_FUNCS(mac);
Scheduler& s = Scheduler::instance();
assert(busy_ && p_);
s.cancel(&intr);
busy_ = 0;
Packet::free(p_);
p_= 0;
}
inline void MacHandlerRetx::cancel() {
PRNT_MAC_FUNCS(mac);
Scheduler& s = Scheduler::instance();
assert(busy_ && p_);
s.cancel(&intr);
}
inline void MacHandlerIFS::cancel() {
PRNT_MAC_FUNCS(mac);
//fprintf (stderr, "cancelled dtime= %.15f\n", intr.time_- Scheduler::instance().clock());
MacHandler::cancel();
}
static class Mac802_3Class : public TclClass {
public:
Mac802_3Class() : TclClass("Mac/802_3") {}
TclObject* create(int, const char*const*) {
return (new Mac802_3);
}
} class_mac802_3;
void Mac8023HandlerSend::handle(Event*) {
PRNT_MAC_FUNCS(mac);
assert(p_);
/* Transmission completed successfully */
busy_ = 0;
p_= 0;
mac->mhRetx_.free();
mac->mhRetx_.reset();
mac->mhIFS_.schedule(mac->netif_->txtime(int(IEEE_8023_IFS_BITS/8.0)));
}
void Mac8023HandlerSend::schedule(const Packet *p, double t) {
PRNT_MAC_FUNCS(mac);
Scheduler& s = Scheduler::instance();
assert(!busy_);
s.schedule(this, &intr, t);
busy_ = 1;
p_= p;
}
void MacHandlerRecv::handle(Event* ) {
/* Reception Successful */
PRNT_MAC_FUNCS(mac);
busy_ = 0;
mac->recv_complete(p_);
p_= 0;
}
void MacHandlerRecv::schedule(Packet *p, double t) {
PRNT_MAC_FUNCS(mac);
Scheduler& s = Scheduler::instance();
assert(p && !busy_);
s.schedule(this, &intr, t);
busy_ = 1;
p_ = p;
}
bool MacHandlerRetx::schedule(double delta) {
PRNT_MAC_FUNCS(mac);
Scheduler& s = Scheduler::instance();
assert(p_ && !busy_);
int k, r;
if(try_ < IEEE_8023_ALIMIT) {
k = min(try_, IEEE_8023_BLIMIT);
r = Random::integer(1 << k);
s.schedule(this, &intr, r * mac->netif_->txtime((int)(IEEE_8023_SLOT_BITS/8.0)) + delta);
busy_ = 1;
return true;
}
return false;
}
void MacHandlerRetx::handle(Event *) {
PRNT_MAC_FUNCS(mac);
assert(p_);
busy_= 0;
++try_;
mac->transmit(p_);
}
inline void MacHandlerIFS::schedule(double t) {
PRNT_MAC_FUNCS(mac);
assert(!busy_);
Scheduler &s= Scheduler::instance();
s.schedule(this, &intr, t);
busy_= 1;
}
inline void MacHandlerIFS::handle(Event*) {
PRNT_MAC_FUNCS(mac);
busy_= 0;
mac->resume();
}
Mac802_3::Mac802_3() : trace_(0),
mhRecv_(this), mhRetx_(this), mhIFS_(this), mhSend_(this) {
// Bind mac trace variable
bind_bool("trace_",&trace_);
}
void Mac802_3::sendUp(Packet *p, Handler *) {
PRNT_MAC_FUNCS(this);
/* just received the 1st bit of a packet */
if (state_ != MAC_IDLE && mhIFS_.busy()) {
#define EPS 1.0e-15 /* can be considered controller clock resolution */
if (mhIFS_.expire() - Scheduler::instance().clock() > EPS) {
// This can happen when 3 nodes TX:
// 1 was TX, then IFS, then TX again;
// 2,3 were in RX (from 1), then IFS, then TX, then collision with 1 and IFS
// while 3 in IFS it RX from 2 and vice versa.
// We ignore it and let the ifs timer take care of things.
Packet::free(p);
return;
} else {
// This means that mhIFS_ is about to expire now. We assume that IFS is over
// and resume, because we want to give this guy a chance - otherwise the guy
// who TX before will TX again (fairness); if we have anything to
// TX, this forces a collision.
mhIFS_.cancel();
resume();
}
#undef EPS
}
if(state_ == MAC_IDLE) {
state_ = MAC_RECV;
assert(!mhRecv_.busy());
/* the last bit will arrive in txtime seconds */
mhRecv_.schedule(p, netif_->txtime(p));
} else {
collision(p); //received packet while sending or receiving
}
}
void Mac802_3::sendDown(Packet *p, Handler *h) {
PRNT_MAC_FUNCS(this);
assert(initialized());
assert(h);
assert(netif_->txtime(IEEE_8023_MINFRAME) >
2*netif_->channel()->maxdelay()); /* max prop. delay is limited by specs:
about 25us for 10Mbps
and 2.5us for 100Mbps
*/
int size= (HDR_CMN(p)->size() += ETHER_HDR_LEN); //XXX also preamble?
hdr_mac *mh= HDR_MAC(p);
mh->padding_= 0;
if (size > IEEE_8023_MAXFRAME) {
static bool warnedMAX= false;
if (!warnedMAX) {
fprintf(stderr, "Mac802_3: frame is too big: %d\n", size);
warnedMAX= true;
}
} else if (size < IEEE_8023_MINFRAME) {
// pad it to fit MINFRAME
mh->padding_= IEEE_8023_MINFRAME - size;
HDR_CMN(p)->size() += mh->padding_;
}
callback_ = h;
mhRetx_.packet(p); //packet's buffered by mhRetx in case of retransmissions
transmit(p);
}
void Mac802_3::transmit(Packet *p) {
PRNT_MAC_FUNCS(this);
assert(callback_);
if(mhSend_.packet()) {
fprintf(stderr, "index: %d\n", index_);
fprintf(stderr, "Retx Timer: %d\n", mhRetx_.busy());
fprintf(stderr, "IFS Timer: %d\n", mhIFS_.busy());
fprintf(stderr, "Recv Timer: %d\n", mhRecv_.busy());
fprintf(stderr, "Send Timer: %d\n", mhSend_.busy());
exit(1);
}
/* Perform carrier sense - if we were sending before, never mind state_ */
if (mhIFS_.busy() || (state_ != MAC_IDLE)) {
/* we'll try again when IDLE. It'll happen either when
reception completes, or if collision. Either way,
we call resume() */
return;
}
double txtime = netif_->txtime(p);
/* Schedule transmission of the packet's last bit */
mhSend_.schedule(p, txtime);
// pass the packet to the PHY: need to send a copy,
// because there may be collision and it may be freed
Packet *newp = p->copy();
HDR_CMN(newp)->direction()= hdr_cmn::DOWN; //down
downtarget_->recv(newp);
state_= MAC_SEND;
}
void Mac802_3::collision(Packet *p) {
PRNT_MAC_FUNCS(this);
if (mhIFS_.busy()) mhIFS_.cancel();
double ifstime= netif_->txtime(int((IEEE_8023_JAMSIZE+IEEE_8023_IFS_BITS)/8.0)); //jam time + ifs
mhIFS_.schedule(ifstime);
switch(state_) {
case MAC_SEND:
// If mac trace feature is on generate a collision trace for this packet.
if (trace_)
drop(p);
else
Packet::free(p);
if (mhSend_.busy()) mhSend_.cancel();
if (!mhRetx_.busy()) {
/* schedule retransmissions */
if (!mhRetx_.schedule(ifstime)) {
p= mhRetx_.packet();
hdr_cmn *th = hdr_cmn::access(p);
HDR_CMN(p)->size() -= (ETHER_HDR_LEN + HDR_MAC(p)->padding_);
fprintf(stderr,"BEB limit exceeded:Dropping packet %d\n",th->uid());
fflush(stderr);
drop(p); // drop if backed off far enough
mhRetx_.reset();
}
}
break;
case MAC_RECV:
Packet::free(p);
// more than 2 packets collisions possible
if (mhRecv_.busy()) mhRecv_.cancel();
break;
default:
assert("SHOULD NEVER HAPPEN" == 0);
}
}
void Mac802_3::recv_complete(Packet *p) {
PRNT_MAC_FUNCS(this);
assert(!mhRecv_.busy());
assert(!mhSend_.busy());
hdr_cmn *ch= HDR_CMN(p);
/* Address Filtering */
hdr_mac *mh= HDR_MAC(p);
int dst= mh->macDA();
if ((dst != BCAST_ADDR) && (dst != index_)) {
Packet::free(p);
goto done;
}
/* Strip off the mac header and padding if any */
ch->size() -= (ETHER_HDR_LEN + mh->padding_);
/* xxx FEC here */
if( ch->error() ) {
fprintf(stderr,"\nChecksum error\nDropping packet");
fflush(stderr);
// drop(p);
Packet::free(p);
goto done;
}
/* we could schedule an event to account for mac-delay */
if ((p->ref_count() > 0) /* so the channel is using ref-copying */
&& (dst == BCAST_ADDR)) {
/* make a real copy only if broadcasting, otherwise
* all nodes except the receiver are going to free
* this packet
*/
uptarget_->recv(p->copy(), (Handler*) 0);
Packet::free(p); // this will decrement ref counter
} else {
uptarget_->recv(p, (Handler*) 0);
}
done:
mhIFS_.schedule(netif_->txtime(int(IEEE_8023_IFS_BITS/8.0)));// wait for one IFS, then resume
}
/* we call resume() in these cases:
- successful transmission
- whole packet's received
- collision and backoffLimit's exceeded
- collision while receiving */
void Mac802_3::resume() {
PRNT_MAC_FUNCS(this);
assert(!mhRecv_.busy());
assert(!mhSend_.busy());
assert(!mhIFS_.busy());
state_= MAC_IDLE;
if (mhRetx_.packet()) {
if (!mhRetx_.busy()) {
// we're not backing off and not sensing carrier right now: send
transmit(mhRetx_.packet());
}
} else {
if (callback_ && !mhRetx_.busy()) {
//WARNING: calling callback_->handle may change the value of callback_
Handler* h= callback_;
callback_= 0;
h->handle(0);
}
}
}
syntax highlighted by Code2HTML, v. 0.9.1