Writing Network Drivers Under Menuet
====================================

Mike Hibbett,

4th September 2003


Background
==========

The protocol stack is handled in the kernel main loop, os_loop.

All stack code is handled in this loop by a single stack function called 
stack_handler. This function polls the network driver for received data and
calls the IP & TCP processes. 

This release of the stack uses a very simple buffer array for IP packets. The 
array is defined in stack.inc. IPBuffers is an array of NUM_IPBUFFERS entries, 
each one IPBUFFERSIZE big. Each entry looks like this:
 

;    0                   1                   2                   3
;    0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
;
;   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
;   |Version|  IHL  |Type of Service|       Total Length            |
;   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
;   |         Identification        |Flags|      Fragment Offset    |
;   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
;   |  Time to Live |    Protocol   |         Header Checksum       |
;   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
;   |                       Source Address                          |
;   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
;   |                    Destination Address                        |
;   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
;   |      Data                                                     |
;   +-+-+-..........                                               -+


i.e. it is a 'normal' IP packet.

Note: This implementation does not permit the options fields in the IP header
to be populated.

The function ethernet_driver first calls eth_rx, which will poll the lower level
newtwork driver for data. If a packet is available, it is retrieved and stored
in an IP buffer. ethernet_driver then calls eth_tx, which will look through the 
IPBuffers to see if there is a buffer that is marked for transmission. If there
is one, it is passed to the network driver, and immediately transmitted.

Ethernet drivers require a mapping between the IP addresses and the ethernet
hardware address (MAC). This is managed by a protocol called ARP. ARP is only
visible to the network driver; IP does not care about address resolution.


Driver Requirements
===================

Only PCI bus ethernet hardware is supported currently. This can be plug in cards 
or motherboard hosted PCI hardware ( as fitted to most laptops )

Driver requirements are very simple. 

Menuets ethernet drivers are based on the etherboot projects driver code, which 
has been written in C. Get a copy of the source files.

I recommend that you examine the files ns8390.c and eepro100.c, which formed the
basis of rtl8029.inc and i8255x.inc respectively. 

A driver must provide four functions:

drivername_reset
drivername_probe
drivername_poll
drivername_transmit

I recommend that you keep the names of these functions in a similar format to the
other drivers, to make it easier for others to read your code.


_reset function
===============
Input parameters : I/O address in io_addr
                   PCI bus in pci_bus
                   PCI dev # in pci_dev
Output parameters : None

This function should return the card into it's default active state, ready to 
receive data. It is typically called at the end of hardware initialisation. The 
function is not currently called externally, but may be in the future.


_probe function
===============

Input parameters : I/O address in io_addr
                   PCI bus in pci_bus
                   PCI dev # in pci_dev
Output parameters : MAC address writen to node_addr

This function is called once the hardware's I/O address space is known. 
The function will completely initialise the hardware, and extract the MAC 
address. It returns with the hardware activated, awaiting data for transmission 
or reception. If the _probe function completed successfully, the following 
two lines should be performed

   ; Indicate that we have successfully reset the card
   mov      eax, [pci_data]
   mov      [eth_status], eax


_poll function
==============

Input parameters : I/O address in io_addr
Output parameters : Received data written to Ether_buffer
                    Number of bytes received written to eth_rx_data_len

This function examines the hardware to see if a data packet has been received. 
If it has, the data is extracted and stored in the buffer Ether_buffer ( which 
is globally available, and defined in stack.inc ).

First, ensure that the following lines are executed at the beginning

   mov      ax, 0      ; assume no data
   mov      [eth_rx_data_len], ax

eth_rx_data_len is used by the upper layer to determine if data was received.



_transmit function
==================

Input parameters : I/O address in io_addr
                   Pointer to MAC address in edi
                   Type of packet ( IP or ARP ) in bx
                   Size of packet in ecx
                   Pointer to packet in esi
Output parameters : None ( packet assumed transmitted )

Passes a data packet to the hardware, and waits for it's transmission. The 
destination address is specified in the call, so this function does not need 
to worry about address resolution.



Integrating the Driver
======================

Firstly, write you code in a new include file, and give the file a name that 
identifies the driver, eg 3c509.inc

In ethernet.inc, find the lines:


include "RTL8029.INC"
include "I8255X.INC"


which will be near the top. Add your include file hear.


Further down ethernet.inc, find these lines:

PCICards:
dd  0x12098086, I8255x_probe, I8255x_reset, I8255x_poll, I8255x_transmit
dd  0x10298086, I8255x_probe, I8255x_reset, I8255x_poll, I8255x_transmit


Add a new line, and fill in your drivers details. The first dword is the PCI 
bus device and vendor words. These can be found from the etherboot project, 
which give a list of the PCI values for various card types. You need to 
find this value before implementing the driver anyway!

That is it. Re-assemble the kernel, copy it to your boot disk and boot. Now 
when you enable the packet driver, your card will hopefully be detetected 
and used. You can verify this by running the status application


Hints
=====

The etherboot source code relies on some timer functions that are not 
currently available in menuet. These timers are used for generating 
timeouts while waiting for the hardware to complete tasks. I have chosen 
to ignore these for now, and not implement them. So for this has not 
been a problem, and we will address them at a later stage. Generating 
short delays can be a problem, and where there is a real need for generating 
2 - 10 us delays I have used much larger delays with no problem. We are 
working on providing an acuurate, small delay facility within menuet.

My approach has been to simply identify the I/O calls within the original 
source file, and code this in assembler. So far with the two drivers I 
have implemented this has not been an issue. Email me if you have any 
questions on your driver. 

The etherboot project has been an enormous help, as it means that a 
detailed understanding of the hardware is not required. I have not needed 
to refer to the chip specifications on my two implementations.


Notes
=====

The driver implementation is polled rather than interrupt based. This slows 
down transfers but makes driver development very easy. We will address this 
in the future when I can work out how to use interrupts with the hardware!

I should point out that the way in which network drivers are 
implemented will be changing soon in preparation for implementing TCP.
However the principle will remain largely the same, so if you are considering 
writing a driver do it now rather than wait!


Any comments/ questions to mikehibbett [@] oceanfree.net