[← Home](../README.md) · [Networking](README.md)
# 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](tcp_ip_stacks.md) for how SANA-II fits into the full networking architecture.
```mermaid
flowchart TD
subgraph "TCP/IP Stacks (any)"
AT["AmiTCP"]
RS["Roadshow"]
MI["Miami"]
end
subgraph "SANA-II API (standardised)"
API["CMD_READ / CMD_WRITE
S2_ONLINE / S2_OFFLINE
S2_DEVICEQUERY
Buffer Management Hooks"]
end
subgraph "SANA-II Drivers (any)"
A2065["a2065.device
(Ethernet)"]
ARIADNE["ariadne.device
(Ethernet)"]
PPP["ppp.device
(dial-up)"]
MISTER["mister_eth.device
(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:
```c
#include
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
```c
/* 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
```c
/* 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:
```c
/* 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
```c
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](tcp_ip_stacks.md) — stack architecture
- See also: [bsdsocket.md](bsdsocket.md) — socket API above SANA-II