Skip to content

โ† Home ยท Networking

SANA-II โ€” Standard Amiga Network Architecture

Overview

SANA-II (Standard Amiga Networking Architecture, version II) is the standardised exec.device interface between TCP/IP stacks and network hardware drivers. Any SANA-II compliant driver works with any SANA-II compliant stack โ€” providing hardware independence analogous to NDIS on Windows or the Linux kernel network driver model.

See TCP/IP Stacks for how SANA-II fits into the full networking architecture.

flowchart TD
    subgraph "TCP/IP Stacks (any)"
        AT["AmiTCP"]
        RS["Roadshow"]
        MI["Miami"]
    end

    subgraph "SANA-II API (standardised)"
        API["CMD_READ / CMD_WRITE<br/>S2_ONLINE / S2_OFFLINE<br/>S2_DEVICEQUERY<br/>Buffer Management Hooks"]
    end

    subgraph "SANA-II Drivers (any)"
        A2065["a2065.device<br/>(Ethernet)"]
        ARIADNE["ariadne.device<br/>(Ethernet)"]
        PPP["ppp.device<br/>(dial-up)"]
        MISTER["mister_eth.device<br/>(MiSTer virtual)"]
    end

    AT --> API
    RS --> API
    MI --> API
    API --> A2065
    API --> ARIADNE
    API --> PPP
    API --> MISTER

    style API fill:#fff9c4,stroke:#f9a825,color:#333

Opening a SANA-II Device

SANA-II drivers require buffer management hooks โ€” callback functions the driver uses to copy packet data to/from the application's buffers:

#include <devices/sana2.h>

struct MsgPort *port = CreateMsgPort();
struct IOSana2Req *s2req = (struct IOSana2Req *)
    CreateIORequest(port, sizeof(struct IOSana2Req));

/* Buffer management hooks (required): */
static struct TagItem bufferTags[] = {
    { S2_CopyToBuff,   (ULONG)MyCopyToBuff },
    { S2_CopyFromBuff, (ULONG)MyCopyFromBuff },
    { TAG_DONE, 0 }
};
s2req->ios2_BufferManagement = bufferTags;

if (OpenDevice("a2065.device", 0, (struct IORequest *)s2req, 0))
{
    Printf("Cannot open network device\n");
}

Buffer Management Hooks

/* Called by the driver to copy received data into your buffer: */
BOOL __asm MyCopyToBuff(register __a0 APTR to,
                         register __a1 APTR from,
                         register __d0 ULONG length)
{
    CopyMem(from, to, length);
    return TRUE;
}

/* Called by the driver to copy transmit data from your buffer: */
BOOL __asm MyCopyFromBuff(register __a0 APTR to,
                           register __a1 APTR from,
                           register __d0 ULONG length)
{
    CopyMem(from, to, length);
    return TRUE;
}

[!NOTE] Buffer management hooks exist because SANA-II drivers may use DMA or special memory regions. The hooks let the driver control how data is copied โ€” the stack never accesses driver buffers directly.


Commands

Code Constant Direction Description
2 CMD_READ Receive Read a packet (kept outstanding; completed when packet arrives)
3 CMD_WRITE Transmit Send a packet
9 S2_DEVICEQUERY Query Get hardware capabilities (type, MTU, speed)
10 S2_GETSTATIONADDRESS Query Get hardware (MAC) address
11 S2_CONFIGINTERFACE Config Set the hardware address
14 S2_ONLINE Control Bring interface up (start receiving)
15 S2_OFFLINE Control Take interface down
16 S2_ADDMULTICASTADDRESS Config Subscribe to multicast address
17 S2_DELMULTICASTADDRESS Config Unsubscribe from multicast
21 S2_GETGLOBALSTATS Query Get packet/byte counters
22 S2_GETSPECIALSTATS Query Get driver-specific statistics

Sending a Packet

/* Prepare an Ethernet frame: */
s2req->ios2_Req.io_Command = CMD_WRITE;
s2req->ios2_WireError = 0;
s2req->ios2_PacketType = 0x0800;  /* IPv4 EtherType */
s2req->ios2_DataLength = packetLength;

/* Set destination MAC: */
CopyMem(destMAC, s2req->ios2_DstAddr, 6);

/* ios2_Data points to the packet payload (after Ethernet header): */
s2req->ios2_Data = packetData;

DoIO((struct IORequest *)s2req);
if (s2req->ios2_Req.io_Error)
    Printf("Send error: %ld (wire: %ld)\n",
           s2req->ios2_Req.io_Error, s2req->ios2_WireError);

Receiving Packets

The stack posts multiple read requests to keep a pipeline full:

/* Post a read request (non-blocking): */
s2req->ios2_Req.io_Command = CMD_READ;
s2req->ios2_WireError = 0;
s2req->ios2_PacketType = 0x0800;  /* only IPv4 packets */
SendIO((struct IORequest *)s2req);

/* Wait for a packet: */
WaitIO((struct IORequest *)s2req);

Printf("Received %ld bytes from %02x:%02x:%02x:%02x:%02x:%02x\n",
       s2req->ios2_DataLength,
       s2req->ios2_SrcAddr[0], s2req->ios2_SrcAddr[1],
       s2req->ios2_SrcAddr[2], s2req->ios2_SrcAddr[3],
       s2req->ios2_SrcAddr[4], s2req->ios2_SrcAddr[5]);

/* Immediately post another read to keep the pipeline full: */
s2req->ios2_Req.io_Command = CMD_READ;
SendIO((struct IORequest *)s2req);

Querying Device Capabilities

struct Sana2DeviceQuery query;
query.SizeAvailable = sizeof(query);

s2req->ios2_Req.io_Command = S2_DEVICEQUERY;
s2req->ios2_StatData = &query;
DoIO((struct IORequest *)s2req);

Printf("Hardware type: %ld\n", query.HardwareType);  /* 1 = Ethernet */
Printf("MTU: %ld bytes\n", query.MTU);                /* 1500 for Ethernet */
Printf("Speed: %ld bps\n", query.BPS);                /* 10000000 for 10 Mbps */
Printf("Address size: %ld bits\n", query.AddrFieldSize); /* 48 for Ethernet MAC */

Writing a SANA-II Driver

A SANA-II driver is a standard exec.device that implements the SANA-II command set. Key requirements:

Requirement Detail
Must be an exec.device Standard Open/Close/BeginIO/AbortIO entry points
Buffer management Must use the caller's buffer hooks โ€” never copy directly
Multiple openers Must support multiple tasks opening the device simultaneously
Promiscuous mode Should support S2_ONEVENT for link-level monitoring
Error reporting Must set both io_Error and ios2_WireError

References

  • SANA-II Network Device Driver Specification (Commodore, 1992)
  • NDK39: devices/sana2.h
  • Aminet: docs/hard/sana2.lha โ€” specification document
  • See also: tcp_ip_stacks.md โ€” stack architecture
  • See also: bsdsocket.md โ€” socket API above SANA-II