+class TapEvent : public PollEvent
+{
+ protected:
+ EtherTapBase *tap;
+
+ public:
+ TapEvent(EtherTapBase *_tap, int fd, int e)
+ : PollEvent(fd, e), tap(_tap) {}
+
+ void
+ process(int revent) override
+ {
+ // Ensure that our event queue is active. It may not be since we get
+ // here from the PollQueue whenever a real packet happens to arrive.
+ EventQueue::ScopedMigration migrate(tap->eventQueue());
+
+ tap->recvReal(revent);
+ }
+};
+
+EtherTapBase::EtherTapBase(const Params *p)
+ : SimObject(p), buflen(p->bufsz), dump(p->dump), event(NULL),
+ interface(NULL),
+ txEvent([this]{ retransmit(); }, "EtherTapBase retransmit")
+{
+ buffer = new uint8_t[buflen];
+ interface = new EtherTapInt(name() + ".interface", this);
+}
+
+EtherTapBase::~EtherTapBase()
+{
+ delete buffer;
+ delete event;
+ delete interface;
+}
+
+void
+EtherTapBase::serialize(CheckpointOut &cp) const
+{
+ SERIALIZE_SCALAR(buflen);
+ uint8_t *buffer = (uint8_t *)this->buffer;
+ SERIALIZE_ARRAY(buffer, buflen);
+
+ bool tapevent_present = false;
+ if (event) {
+ tapevent_present = true;
+ SERIALIZE_SCALAR(tapevent_present);
+ event->serialize(cp);
+ } else {
+ SERIALIZE_SCALAR(tapevent_present);
+ }
+}
+
+void
+EtherTapBase::unserialize(CheckpointIn &cp)
+{
+ UNSERIALIZE_SCALAR(buflen);
+ uint8_t *buffer = (uint8_t *)this->buffer;
+ UNSERIALIZE_ARRAY(buffer, buflen);
+
+ bool tapevent_present;
+ UNSERIALIZE_SCALAR(tapevent_present);
+ if (tapevent_present) {
+ event = new TapEvent(this, 0, 0);
+ event->unserialize(cp);
+ if (event->queued())
+ pollQueue.schedule(event);
+ }
+}
+
+
+void
+EtherTapBase::pollFd(int fd)
+{
+ assert(!event);
+ event = new TapEvent(this, fd, POLLIN|POLLERR);
+ pollQueue.schedule(event);
+}
+
+void
+EtherTapBase::stopPolling()
+{
+ assert(event);
+ delete event;
+ event = NULL;
+}
+
+
+Port &
+EtherTapBase::getPort(const std::string &if_name, PortID idx)
+{
+ if (if_name == "tap")
+ return *interface;
+ return SimObject::getPort(if_name, idx);
+}
+
+bool
+EtherTapBase::recvSimulated(EthPacketPtr packet)
+{
+ if (dump)
+ dump->dump(packet);
+
+ DPRINTF(Ethernet, "EtherTap sim->real len=%d\n", packet->length);
+ DDUMP(EthernetData, packet->data, packet->length);
+
+ bool success = sendReal(packet->data, packet->length);
+
+ interface->recvDone();
+
+ return success;
+}
+
+void
+EtherTapBase::sendSimulated(void *data, size_t len)
+{
+ EthPacketPtr packet;
+ packet = make_shared<EthPacketData>(len);
+ packet->length = len;
+ packet->simLength = len;
+ memcpy(packet->data, data, len);
+
+ DPRINTF(Ethernet, "EtherTap real->sim len=%d\n", packet->length);
+ DDUMP(EthernetData, packet->data, packet->length);
+ if (!packetBuffer.empty() || !interface->sendPacket(packet)) {
+ DPRINTF(Ethernet, "bus busy...buffer for retransmission\n");
+ packetBuffer.push(packet);
+ if (!txEvent.scheduled())
+ schedule(txEvent, curTick() + retryTime);
+ } else if (dump) {
+ dump->dump(packet);
+ }
+}
+
+void
+EtherTapBase::retransmit()
+{
+ if (packetBuffer.empty())
+ return;
+
+ EthPacketPtr packet = packetBuffer.front();
+ if (interface->sendPacket(packet)) {
+ if (dump)
+ dump->dump(packet);
+ DPRINTF(Ethernet, "EtherTap retransmit\n");
+ packetBuffer.front() = NULL;
+ packetBuffer.pop();
+ }
+
+ if (!packetBuffer.empty() && !txEvent.scheduled())
+ schedule(txEvent, curTick() + retryTime);
+}
+
+