This is the device interface guide for U-Net version 2.1, 13-January-1998. Please see the U-Net documentation tree and release notes first.
This document describes the application-visible view of the U-Net device, and describes how to open the device, enable endpoints and channels, send and receive data, manage buffers, and closing the device.
Many of the methods describe here have been encapsulated in the libunet library, which demonstrates use of the U-Net device. While you may find libunet useful, it is by no means the ``standard'' interface, and libunet's behavior is subject to change with each release. It's our hope that the U-Net device interface, described below, will be somewhat stable between releases.
You should look at the libunet code to see how the methods described here can be easily implemented, and use the library as the starting point for your own U-Net communication layer. libunet is by no means suitable for all applications --- it just presents a simplified API which makes certain assumptions, useful for some programs.
This guide assumes that you are familiar with the concepts used by U-Net such as endpoints and channels. See the U-Net documentation tree for information.
The primary definition of the U-Net device interface is the header file unet.h, found in the include subdirectory of the U-Net distribution. This header file defines all aspects of the U-Net device which are not dependent on the particular implementation used.
We have made every effort to make the device interface as generic as possible; however, some implementations (such as that used on the Tulip Fast Ethernet controller) require a kernel trap to force a transmit operation to occur. Therefore, for some implementations you may need to include the device-specific include file, found in the appropriate dev-arch subdirectory of the U-Net distribution. For example, dev-tulip-linux/devtulip.h should be included for programs using the Tulip device.
If including the device-specific include file for FORE Systems ATM boards (such as dev-pca200-linux/devpca200.h), the file unet_cmd.h is included. This is located in the firmware/fore-200 directory, and corresponds to the kernel interface to the i960 firmware.
The primary interface to the U-Net device is the endpoint memory layout which it presents to the application. This memory image is obtained through use of an mmap call on the U-Net device (after opening the device and creating an endpoint; see below). Regions in the endpoint may correspond to host memory or mapped network-interface registers.
The U-Net endpoint layout is as follows:
The Tx wall pointer indicates to the user when the Tx FIFO is full; the Tx FIFO entry pointed to by the wall pointer should always have a channel field of CHAN_FREE. That is, the user process should not attempt to push a Tx descriptor into the entry pointed to by the wall pointer.
The Free wall pointer is used by the user process to determine if the Free FIFO is full.
U-Net device entries in /dev are named after their corresponding physical device; for example, /dev/sba0 or /dev/tulip8. Each entry in /dev corresponds to a single U-Net endpoint. The number of endpoints supported by each physical device is implementation-dependent and can be determined from the device-specific include file (or using the UNET_INFO ioctl; see below).
Your application should select the appropriate /dev entry for the U-Net device and open it with read/write permissions.
To create an endpoint, fill in a Unet_create_endpt structure as described in unet.h, and call the UNET_CREATE_ENDPT ioctl. In this way you indicate the desired sizes for the U-Net FIFOs and buffer regions. If EINVAL is returned, at least one of the requested size values is out of range for the device.
The kernel will fill in the remaining fields in the Unet_create_endpt structure, indiciating byte offsets from the beginning of the endpoint mapping to each of the areas in the endpoint: The three FIFOs and wall pointers, and the buffer area offset. These offsets are determined by the kernel driver and correspond with any alignment restrictions imposed by the device.
To obtain the endpoint memory mapping, the user process should call mmap on the U-Net device with an offset of 0 and a size of Unet_create_endpt.comsegsz. The actual pointer values for the endpoint regions can then be derived from the virtual address of the resulting mapping and the offset values returned by the UNET_CREATE_ENDPT ioctl.
After creating the endpoint, the user process should initialize the chan field of each Rx FIFO entry to CHAN_FREE, and initialize the Rx wall pointer to the byte offset of the last Rx FIFO entry.
To create a point-to-point communications channel for use with an endpoint, the user process should fill in a Unet_activate_chan structure and call the UNET_ACTIVATE_CHAN ioctl on the U-Net device. The Unet_activate_chan has fields for the transmit and receive address, each of which is a union unet_chan_addr. This union is used as different U-Net implementations have different address formats; the devid field of Unet_info structure can be used to determine which to use. For example, struct unet_fe_addr is used for Fast Ethernet MAC addresses, while struct unet_atm_addr is used for ATM VPI/VCI pairs.
The UNET_ACTIVATE_CHAN ioctl will return the channel number which should be used for all subsequent communication through the U-Net endpoint. Each endpoint can support up to info.max_chans channels.
To transmit data, push a Unet_txbd descriptor onto the Tx FIFO in ring order. The Unet_txbd contains the destination channel number, message length, and a byte offset into the transmit/receive buffer region corresponding to the beginning of the message data. The U-Net device will only transmit messages up to info.max_pdu bytes in length, and transmit buffers should be aligned to info.tx_buf_align bytes.
It is important that the entire Unet_txbd be pushed onto the FIFO at once (e.g., using a structure assignment), as the U-Net device may trigger a transmit when just one field of the structure has been pushed onto the ring.
The Tx wall pointer should be used to determine if the Tx FIFO is full; the entry pointed to by the Tx wall pointer should not be pushed into by the user process. If a Tx FIFO entry is free it will have a chan value of CHAN_FREE.
If the Unet_txbd.buf offset has its low-order bit set to one (as indicated by the TX_CONTINUE macro), then the next Tx FIFO will be transmitted as the same network packet. This allows multiple Tx FIFO entries (and multiple Tx buffers) to be chained into a single packet.
Some implementations of U-Net require a kernel trap in order for the Tx FIFO to be serviced. Servicing the Tx FIFO consists of transmitting new entries and marking already-transmitted entries as free. You can use the info.devid field to determine if a trap is required.
Note that even if a trap is required, the Tx FIFO may be updated asynchronously by the U-Net device (for example, as a side-effect of other applications invoking the trap).
The U-Net device will set the channel field of entries in the Tx FIFO to CHAN_FREE once the entry has been transmitted, or to CHAN_ERROR if an error occurred during transmit. The U-Net device will update the Tx wall pointer to correspond to the last Tx FIFO entry which has been transmitted.
As message data is received, the U-Net device will push new entries onto the Rx FIFO. Each entry contains the destination channel tag, length of the message in bytes, and either the message data itself or pointers to buffers containing the message.
If the incoming message is larger than info.rxd_payload_size bytes, a receive buffer contained in the Free FIFO will be allocated. After consuming an entry on the Free FIFO, the U-Net device will set the buf value of the Free FIFO entry to RXB_FREE and increment the Free wall pointer. Buffers on the Free FIFO should be aligned to info.rx_buf_align bytes.
If the message is smaller than (or equal to) info.rxd_payload_size bytes, the pay_or_buf field of the Rx FIFO entry contains the message data itself. Otherwise, the pay_or_buf field contains up to RXD_PAYLOAD_ENTRIES byte offsets into the transmit/receive buffer region, pointing to buffers where the message data is located. Each buffer consumed by the U-Net device stores up to create.rx_buffer_size bytes (as indicated by the user at endpoint creation time).
After consuming an Rx FIFO entry (and its corresponding buffers, if any) the user process should set the chan field of the Rx FIFO entry to CHAN_FREE and update the Rx wall pointer to contain the byte offset (from the beginning of the Rx FIFO) of the last Rx FIFO entry consumed. The U-Net device will not attempt to use the Rx FIFO entry corresponding to the Rx wall; it is used to mark the end of the ring.
When buffers corresponding to incoming messages are consumed they may be pushed back onto the Free FIFO by the user.
If an error occurs during message reception, the U-Net device may set the chan field to CHAN_ERROR. This may happen if, for example, no buffers are available on the Free FIFO, or if a message arrives with a bad CRC. In other cases messages may be silently dropped on reception: Notably in the case where the Rx FIFO itself is full.
The user should call the UNET_DEACTIVATE_CHAN ioctl on the U-Net device with the channel tag as the argument. This will close down the named channel, making it available for re-use.
The user should call the UNET_DESTROY_ENDPT ioctl on the U-Net device. This will implicitly deactivate any channels left activated by the user.