**This is an old revision of the document!** ----
====== Michael Decker: Driver Development ====== ==== Week 4 ==== ---- === 16 June === I added dynamic allocation of RFDs today. * Commit: [[http://git.etherboot.org/?p=people/mdeck/gpxe.git;a=commit;h=e97fd7e347fd280a61e276a81ff21d4b2e4cc125|[Drivers-eepro100] Implemented dynamic allocation of RFDs.]] The ''struct ifec_rfd rfa[RFD_COUNT]'' member of ''struct ifec_private'' was replaced with a ''struct io_buffer *rx_iobs[RFD_COUNT]'' member. Thus, the NIC private data holds an array of pointers to io_buffers. The io_buffers are allocated in ''ifec_net_open()'', and initialized as well. Each io_buffer holds a ''struct ifec_rfd'', which contains both the RFD header and the packet data following contiguously. The immediately following data is a characteristic of "simple mode" and is how the Linux drivers accomplish things. Perhaps experimenting with undocumented methods of separating the packet from the RFD can be an exercise left for a rainy day. Checking the status of our RFDs in ''ifec_net_poll()'' now traverses the ''rx_iobs'' array. When a packet has been received, ''iob_reserve()'' advances the data pointer past the embedded RFD header data, and ''iob_put()'' marks the length of the received data. The io_buffer is then handed directly to ''netdev_rx()''. No packet data is copied; the original buffer is sent up the network stack. Since the driver gave up ownership to that io_buffer, a replacement is allocated for the ''rx_iobs'' array. It is initialized, and linked into the existing chain of RFDs. All the RFDs are linked to allow the hardware to know where to store each following packet. This involves writing a pointer, and modifying the RFD command such that the hardware suspends if it reaches the end of the RFD chain. By suspending, we preserve the received packets waiting to be processed, rather than overwriting them with new data. This is standard to allow forward progress and recovery of lost packets. I ensured the link address to the last RFD was appended first, before updating the command data so only the last RFD suspends the hardware. This prevents race conditions where the device doesn't suspend and tries to access the next linked RFD with an invalid address. That would be a tough one to debug ;-) In ''ifec_net_poll()'', after handing a received packet to the network subsystem, a new io_buffer is allocated. At first, if this allocation failed, it simply returned. However, I decided it may be best to allocate the new io_buffer just before handing the current one to the network subsystem. If that allocation fails, we return leaving the received packet intact to be handled at the next ''ifec_net_poll()'' when memory may be available. Also, since RFDs are initialized both in ''ifec_net_open()'' as well as ''ifec_net_poll()'', I moved the initialization code for RFDs into ''ifec_rfd_init()''. And of course I added code in ''ifec_net_close()'' to free the io_buffers in the ''rx_iobs'' array.