/*  Shrine mentions possibly using two FIFOs
    (in our case, Queues) for pending and
    empty frames. If logical to implement,
    perhaps Zeal NetQueue code should
    do something similar to that idea.

    Each Ethernet Frame will be represented
    as an entry in a CQueue. */

class CNetQueueEntry:CQueue
{
    I64 packet_length;
    U8  frame[ETHERNET_FRAME_SIZE];
};


/*  global variable, holds pointer of Ethernet Queue.
    This acts as the Head of the Queue, Entries act
    as the Tail of the Queue. 
    Upon QueueInit, ->next and ->last are set to
    itself, the Head. */

CQueue *net_queue; // no QueueRemove the Head! only Entries!


/*  PCNet reroutes PCI interrupts to software. See PCNet.
    Net Handler interrupt is generated whenever an entry is
    pushed to the NetQueue. See NetHandler */

#define I_PCNET0        I_USER + 0
#define I_PCNET1        I_USER + 1
#define I_PCNET2        I_USER + 2
#define I_PCNET3        I_USER + 3
#define I_NETHANDLER    I_USER + 4

#define INT_DEST_CPU    0

U0 NetQueueInit()
{
    net_queue = CAlloc(sizeof(CQueue));
    QueueInit(net_queue);
}

CNetQueueEntry *NetQueuePull()
{/* Returns a pointer to a CNetQueueEntry,
    or NULL pointer if Net Queue is empty. */

    CNetQueueEntry *entry;

    if (net_queue->next != net_queue)
    {
        entry = net_queue->next;

        NetLog("NETQUEUE PULL: Removing entry from queue.");
        QueueRemove(entry);
    }
    else // Queue is empty if head->next is head itself.
    {
        entry = NULL;
    }

    return entry;
}

U0 NetQueuePush(U8 *data, I64 length)
{/* Pushes a copy of the packet data and length
    into the Net Queue. The NetQueueEntry is inserted
    after the last entry of net_queue to keep new
    items in the back of the Queue, old in front. */ 

    CNetQueueEntry *entry = CAlloc(sizeof(CNetQueueEntry));

    entry->packet_length = length;

    MemCopy(entry->frame, data, length);

    QueueInsert(entry, net_queue->last);

    // Generate Net Handler interrupt.
    NetLog("NETQUEUE PUSH COPY: Generating NetHandler interrupt.");
    MPInt(I_NETHANDLER, INT_DEST_CPU);
}


NetQueueInit;