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
|