I. Introduction
This is a brief introduction to writing
a U-Net application using the libunet library on a Fast
Ethernet network. Note that libunet is only a way of using
U-Net service. Once a user understands the concept of creating
end-points and channels, he or she should be able to customize
libunet to maximize efficiency.
II. Overview of the U-Net
A machine running U-Net can support
multiple network adapters of different types such as ATM, Fast
Ethernet etc. Each adapter is a device. Multiple endpoints
can be created on a single device. To send a packet from
one endpoint to another, we need to create a channel
between them.
III. Procedure of Sending/Receiving a packet
The following section explains every
imporatant step in the sample program unettest.c to demonstrate
how to use libunet library. Please refer to the sample
program servertest.c when you read the following descriptions.
This test program sends out a packet from the client and
expects the server to reply with the specified number of
packets.
1. Start the Device Driver
To use U-Net service over Fast Ethernet(the TULIP Board), we must first install U-Net device driver and type the followings at command prompt:
c:\>net start unet
c:\>net start tulip
2. Open the Device in Your Program
hDriver = unet_opendev(UNET_DEVID_TULIP, 0, &info)
UNET_DEVID_TULIP | you want to use the TULIP board |
0 | this is the first TULIP board |
info | when unet_opendev returns, info will return information regarding this device |
3. Open an Endpoint
Before opening an endpoint, we need to tell the device driver more about the charateristics of the desired endpoint. A UNET_CREATE_ENDPOINT_REQUEST struct contains such information. The fields are:
devType | specific adapter type such as TULIP |
devIndex | first adapter of devType will have devIndex=0 and so on |
numTxFifoEntries | number of entries in outgoing queue |
numFreeFifoEntries | number of entries in the queue containing pointers pointing to free space in the buffer area |
numRxFifoEntries | number of entries in incoming queue |
bufferAreaSize | set it to 0 (This field is retained for backward compatability.) |
rxBufferSize | the size of each buffer containing an outgoing packet |
endpt = unet_create_endpt(hDriver, &info, &create, TX_FIFO_SIZE);
hDriver | handle of device driver |
info | the infomation returned by unet_opendev() |
create | described above |
TX_FIFO_SIZE | max. number of entries of the outgoing queue |
4. Open a Channel between 2 Endpoints
4.1 Similar to creating an endpoint, we need to tell U-Net which are the 2 endpoints being connected. This information is stored in a UNET_ACTIVATE_CHANNEL_REQUEST struct.
TxAddr | pointer to a special address structure representing the address of this machine
(see str_to_addr() below) |
rxAddr | pointer to a special address structure representing the address of peer machine |
endpointId | endpoint ID of the endpoint you just created (endpt->endponintID) |
chan | a unique channer number you choose; it has only local importance |
4.2 str_to_addr()
str_to_addr() converts a string representing an address of typeto the U-Net internal representation of such address
str_to_addr(UNET_DEVID_TULIP, input,
&(activate.rxAddr));
UNET_DEVID_TULIP | this is a TULIP device |
input | a string of the format
(XX:XX:XX:XX:XX:XX.PP) XX's are in hexadecimal and they represents the Etherent address PP's is the port number in hex port number is similar to TCP port |
&(activate.rxAddr) | the pointer to the pointer to the address structure |
5. Sending a Packet
5.1 Creating a Transmission Buffer Descriptor (txbd)
In order to send a packet, we need to tell U-Net the location and legth of our packet. TXBD contains this information.
Buf | address of the packet |
length | length of the packet |
chan | channel returned by unet_activate_chan() |
5.2 Push txbd into the queue
First, we need to make sure the outgoing queue is not full.
/* outgoing queue is full if this assertion is true */
endpt->tx_next == endpt->tx_first
+ *(endpt->tx_wall)
If unfortunately, the outgoing queue
is full, we must wait until U-Net has serviced at least one of
the previous requests. For 80X86 machines running NT, we need
to call a fast trap to the kernel so that the U-Net device driver
can service requests. Therefore, we keep calling UNET_TRAP()
until the queue is not full.
while (OUT_GOING_IS_FULL)
UNET_TRAP(hDriver, endpt->endpointId);
/* actually push the txbd onto the queue */
*(endpt->tx_next) = txbd;
/* call a fast trap to send out the packet */
UNET_TRAP( );
Note that this TRAP is not necessary,
we can push several descriptors at a time and wait until the queue
is full before we call UNET_TRAP(). Trapping after pushing each
descriptor will indeed lower the efficiency.
5.3 Increment TX_NEXT pointer
Incresing TX_NEXT pointer indicates the next location a new txbd should be pushed. tx_next wraps around if necessary.
if (endpt->tx_next == endpt->tx_last)
endpt->tx_next = endpt->tx_first;
else
endpt->tx_next++;
6. Receiving a Packet
6.1 Receive Buffer Descriptor (rxbd)
When a packet arrives at a U-Net device, U-Net will forward it to the correct endpoint depending on the port number of incoming packet. A descriptor similar to txbd containing the information of the packet is pushed onto the receive queue of the corresponding endpoint.
rxbd -> chan | channel condition {FREE | BUSY | ERROR} |
rxbd -> length | length of incoming packet |
rxbd -> pay_or_buf | contains either i) a packet if the packet is small or ii) an array of pointers pointing to several buffers containing the packet |
6.2 Polling the Receive Queue (rx_next->chan)
If endpt->rx_next->chan ==
UNET_CHANNEL_FREE, no packet has been received. A value of UNET_CHANNEL_BUSY
indicates a packet has been received. A value of UNET_CHANNEL_ERROR
indicates an error in receiving the packet.
6.2 Locating the Packet Data (rx_next->pay_or_buf)
If the whole can fix entirely in rxbd,
rx_next->pay_or_buf[0..(endpt->rx_next->length-1)]
contains the data, otherwise,
rx_next->pay_or_buf[] is an array of pointers each of which points to a buffer that altogether forms the packet. Number of buffer(s) is:
(len/endpt->rxb_buffsize_used)
+ 1
6.3 Reusing the Packet Buffer
After the incoming packet has been
inspected or copied, the buffer(s) can be used to accommodate
a new incoming packet. Therefore, we must push the buffer(s)
back onto the free queue. Note that this is necessary only if
the packet did not fit in rxbd; otherwise, no buffers were used
to store incoming data in the first place.
Of course, the next step is to increment
endpt->free_next and wrap around it if necessary.
6.4 Adjust rx_next & rx_wall
After we have inpected the incoming packet, we should notify U-Net that a spot on the rx queue has been freed. This is done by setting the channel condition to FREE
endpt->next->chan = UNET_CHANNEL_FREE
and incrementing the rx wall pointer.
7. Clean Up and Shut Down
7.1 Shut Down a Channel
deactivate is of type UNET_DEACTIVATE_CHANNEL_REQUEST
deactivate.endpointId | endpt->endpointId; |
deactivate.chan | chan; |
unet_deactivate_chan(hDriver, &deactivate);
7.2 Destroy an Endpoint
It is important to destroy and endpoint so that the U-Net device driver can free up the space for storing rx, tx queues.
unet_destroy_endpt(hDriver, endpt->endpointId);
7.3 Close the Device Driver
CloseHandle(hDriver);