Hi Jon,
Are you talking about a tcp/ip stack here or is this some application defined or minimalist stack?
The BD ring is designed so that you can have a number of asynchronous processes going on, that use as many frame buffers as required. The buffer descriptors are only used when a frame is ready to be transmitted. You need to 'allocate' a BD or BDs only when you have a buffer that is ready to go or you end up in the pickle you describe.
So in more detail:
You obviously have to have a MAC address for your target before you can construct a frame to transmit. If you need to ARP the target to get the MAC address then your stack will have to put the outgoing data into a queue until the ARP reply comes in. Normally you would arrange for this to happen like so:
- Allocate a frame buffer and start to construct your Ethernet frame (protocol doesn't matter here, it could be UDP, TCP or something else).
- Discover that your ARP cache doesn't contain the required target MAC address, oh dear.
- Put the frame buffer into a queue of some kind and set a timer to poll the queue. You can set yourself a flag or counter so that you know how many times you've ARPed for this address if you want.
- Allocate another frame buffer and construct an ARP request frame for the target host.
- Disable interrupts or do something else to prevent another process trying to modify the BD ring.
- Get the next buffer descriptor and update the next BD ring pointer.
- Fill in the BD with the ARP frame buffer details and set the R bit in the BD.
- Re-enable interrupts or leave the critical section.
Now everything needs to be asynchronous. Your queue should be polled regularly to find out if any queued packets need to be transmitted. You can use this polling to send more ARPs out if you like or to abandon the send attempt if you don't get an ARP reply within a timeout period.
You will have a receive ISR, and this should identify incoming ARP replies and cache the MAC addresses. If this rx ISR sees an ARP request, it could send a reply using the same tx code that locks access to the BD ring so that the application is blocked. It is more usual for the rx ISR to peek into incoming frames so that unwanted ones can be discarded quickly. The rx ISR will then copy the wanted packets to an rx queue. The stack or application will deal with the received packet, next time the rx queue is polled.
If this makes any sense, you should see that there are two levels going on here. The higher level is a bunch of queues and timers that deal with frame buffers - these are buffers that you allocate yourself and fill in with Ethernet and protocol headers and payload. The lower level is the buffer descriptor ring - this is what you use to tell the FEC where your frame buffers are, but only when they are ready to go - this should be the last thing you do as you say goodbye to the frame, definitely not the first.
So, you do need to change your approach, disabling interrupts for a long time is not going to work. And you need to make sure that your code can deal with all of these asynchronous transmits and receives.
Cheers,
Paul.