mirror of
https://github.com/alfishe/amiga-bootcamp.git
synced 2026-06-13 00:26:28 +00:00
docs(amiga): complete AmigaOS 3.1/3.2 developer reference — 172 files across 17 sections
Comprehensive technical documentation covering: - Hardware: OCS/ECS/AGA custom chip registers, Copper & Blitter deep dives - Boot sequence: cold boot through startup-sequence - Binary format: HUNK executable spec, relocation, debug info - Linking & ABI: .fd files, LVO tables, register calling conventions - Exec kernel: tasks, interrupts, memory, signals, semaphores - AmigaDOS: file I/O, FFS/OFS layout, CLI/Shell scripting - Graphics: planar bitmaps, Copper programming, HAM/EHB modes - Intuition: screens, windows, IDCMP, BOOPSI - Devices: trackdisk, SCSI, serial, timer, audio, keyboard - Libraries: utility, expansion, IFFParse, locale, ARexx - Networking: bsdsocket API, SANA-II, TCP/IP stack comparison - Toolchain: GCC, vasm/vlink, SAS/C, NDK, debugging - Reverse engineering: IDA/Ghidra setup, compiler fingerprints, case studies - CPU & MMU: 68040/060 emulation libs, PMMU, cache management - Driver development: SANA-II, Picasso96/RTG, AHI audio All files include breadcrumb navigation. No local paths or proprietary content.
This commit is contained in:
parent
f07a368bf1
commit
21751c0025
172 changed files with 19701 additions and 0 deletions
12
16_driver_development/README.md
Normal file
12
16_driver_development/README.md
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
[← Home](../README.md)
|
||||
|
||||
# Driver Development — Overview
|
||||
|
||||
## Section Index
|
||||
|
||||
| File | Description |
|
||||
|---|---|
|
||||
| [sana2_driver.md](sana2_driver.md) | Writing a SANA-II network device driver |
|
||||
| [rtg_driver.md](rtg_driver.md) | Writing Picasso96/RTG display card drivers |
|
||||
| [ahi_driver.md](ahi_driver.md) | Writing AHI audio drivers |
|
||||
| [device_driver_basics.md](device_driver_basics.md) | exec.device framework — how Amiga devices work |
|
||||
185
16_driver_development/ahi_driver.md
Normal file
185
16_driver_development/ahi_driver.md
Normal file
|
|
@ -0,0 +1,185 @@
|
|||
[← Home](../README.md) · [Driver Development](README.md)
|
||||
|
||||
# Writing AHI Audio Drivers
|
||||
|
||||
## Overview
|
||||
|
||||
**AHI** (Audio Hardware Interface) is the standard retargetable audio system for AmigaOS. It abstracts audio hardware behind a uniform API, similar to how RTG abstracts graphics cards. AHI drivers are shared libraries that implement the AHI driver protocol.
|
||||
|
||||
---
|
||||
|
||||
## Architecture
|
||||
|
||||
```
|
||||
Application
|
||||
↓ ahi.device (OpenDevice, CMD_WRITE, etc.)
|
||||
↓
|
||||
AHI System
|
||||
↓ Calls driver functions via function table
|
||||
↓
|
||||
AHI Audio Driver (.audio file)
|
||||
↓ Programs hardware / DMA / codec
|
||||
↓
|
||||
Audio Hardware (Paula, sound card, FPGA audio, codec)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Driver File Location
|
||||
|
||||
```
|
||||
DEVS:AHI/myaudio.audio ; the driver library
|
||||
DEVS:AudioModes/myaudio ; mode file (preferences)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Required Driver Functions
|
||||
|
||||
| Function | LVO | Description |
|
||||
|---|---|---|
|
||||
| `AHIsub_AllocAudio` | −30 | Allocate hardware resources |
|
||||
| `AHIsub_FreeAudio` | −36 | Free hardware resources |
|
||||
| `AHIsub_Disable` | −42 | Disable audio interrupts |
|
||||
| `AHIsub_Enable` | −48 | Enable audio interrupts |
|
||||
| `AHIsub_Start` | −54 | Start playback/recording |
|
||||
| `AHIsub_Update` | −60 | Update playback parameters |
|
||||
| `AHIsub_Stop` | −66 | Stop playback/recording |
|
||||
| `AHIsub_SetVol` | −72 | Set channel volume/panning |
|
||||
| `AHIsub_SetFreq` | −78 | Set channel frequency |
|
||||
| `AHIsub_SetSound` | −84 | Set channel sound data |
|
||||
| `AHIsub_SetEffect` | −90 | Set audio effects |
|
||||
| `AHIsub_LoadSound` | −96 | Load a sound into hardware |
|
||||
| `AHIsub_UnloadSound` | −102 | Unload a sound |
|
||||
| `AHIsub_GetAttr` | −108 | Query driver capabilities |
|
||||
| `AHIsub_HardwareControl` | −114 | Hardware-specific control |
|
||||
|
||||
---
|
||||
|
||||
## AHIsub_AllocAudio
|
||||
|
||||
```c
|
||||
ULONG AHIsub_AllocAudio(struct AHIAudioCtrlDrv *audioctrl,
|
||||
struct TagItem *tags)
|
||||
{
|
||||
/* Allocate hardware resources */
|
||||
struct MyDriverData *dd = AllocMem(sizeof(*dd), MEMF_PUBLIC|MEMF_CLEAR);
|
||||
audioctrl->ahiac_DriverData = dd;
|
||||
|
||||
/* Set up interrupt for mixing: */
|
||||
dd->dd_Interrupt.is_Node.ln_Type = NT_INTERRUPT;
|
||||
dd->dd_Interrupt.is_Node.ln_Pri = 0;
|
||||
dd->dd_Interrupt.is_Node.ln_Name = "myaudio";
|
||||
dd->dd_Interrupt.is_Code = (APTR)PlaybackInterrupt;
|
||||
dd->dd_Interrupt.is_Data = audioctrl;
|
||||
|
||||
/* Report capabilities: */
|
||||
return AHISF_CANRECORD | /* can record */
|
||||
AHISF_KNOWSTEREO | /* knows stereo */
|
||||
AHISF_MIXING; /* uses AHI's software mixing */
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## AHIsub_Start — Begin Playback
|
||||
|
||||
```c
|
||||
ULONG AHIsub_Start(ULONG flags, struct AHIAudioCtrlDrv *audioctrl)
|
||||
{
|
||||
struct MyDriverData *dd = audioctrl->ahiac_DriverData;
|
||||
|
||||
if (flags & AHISF_PLAY) {
|
||||
/* Allocate DMA buffer: */
|
||||
dd->dd_MixBuffer = AllocMem(audioctrl->ahiac_BuffSamples * 4,
|
||||
MEMF_CHIP | MEMF_CLEAR);
|
||||
|
||||
/* Set up timer interrupt for mixing callback: */
|
||||
/* Timer fires at: audioctrl->ahiac_PlayerFreq */
|
||||
dd->dd_TimerReq->tr_time.tv_micro =
|
||||
1000000 / audioctrl->ahiac_PlayerFreq;
|
||||
|
||||
/* Start the interrupt-driven playback loop: */
|
||||
AddIntServer(INTB_VERTB, &dd->dd_Interrupt);
|
||||
}
|
||||
return AHIE_OK;
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Playback Interrupt — The Mixing Callback
|
||||
|
||||
```c
|
||||
LONG __saveds PlaybackInterrupt(struct AHIAudioCtrlDrv *audioctrl __asm("a1"))
|
||||
{
|
||||
struct MyDriverData *dd = audioctrl->ahiac_DriverData;
|
||||
|
||||
/* Call AHI's software mixer to fill our buffer: */
|
||||
CallHookPkt(audioctrl->ahiac_PlayerFunc, audioctrl, NULL);
|
||||
CallHookPkt(audioctrl->ahiac_MixerFunc, audioctrl, dd->dd_MixBuffer);
|
||||
|
||||
/* dd->dd_MixBuffer now contains mixed PCM data */
|
||||
/* Feed it to hardware DMA / codec / FPGA: */
|
||||
HW_SetDMAPointer(dd->dd_HWBase, dd->dd_MixBuffer);
|
||||
HW_SetDMALength(dd->dd_HWBase, audioctrl->ahiac_BuffSamples);
|
||||
|
||||
return 0;
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## AHIsub_GetAttr — Report Capabilities
|
||||
|
||||
```c
|
||||
LONG AHIsub_GetAttr(ULONG attribute, LONG argument, LONG default_val,
|
||||
struct TagItem *tags, struct AHIBase *AHIBase)
|
||||
{
|
||||
switch (attribute) {
|
||||
case AHIDB_Bits: return 16; /* 16-bit audio */
|
||||
case AHIDB_MaxChannels: return 2; /* stereo */
|
||||
case AHIDB_Frequencies: return 4; /* number of supported rates */
|
||||
case AHIDB_Frequency:
|
||||
switch (argument) {
|
||||
case 0: return 22050;
|
||||
case 1: return 44100;
|
||||
case 2: return 48000;
|
||||
case 3: return 96000;
|
||||
}
|
||||
break;
|
||||
case AHIDB_Author: return (LONG)"My Name";
|
||||
case AHIDB_Copyright: return (LONG)"(C) 2026";
|
||||
case AHIDB_Version: return (LONG)"myaudio 1.0";
|
||||
case AHIDB_Annotation: return (LONG)"FPGA audio driver";
|
||||
case AHIDB_Record: return TRUE;
|
||||
case AHIDB_FullDuplex: return TRUE;
|
||||
case AHIDB_MinMixFreq: return 8000;
|
||||
case AHIDB_MaxMixFreq: return 96000;
|
||||
}
|
||||
return default_val;
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Audio Mode Registration
|
||||
|
||||
Create `DEVS:AudioModes/myaudio`:
|
||||
```
|
||||
;; AHI audio mode file
|
||||
BEGIN
|
||||
Name "MyAudio:HiFi 16 bit stereo++"
|
||||
Driver "DEVS:AHI/myaudio.audio"
|
||||
Flags 0
|
||||
Frequency 44100 48000 96000
|
||||
END
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## References
|
||||
|
||||
- AHI Developer's Guide (Martin Blom)
|
||||
- AHI SDK: Aminet `dev/misc/ahidev.lha`
|
||||
- Example: `paula.audio` driver source (AHI distribution)
|
||||
174
16_driver_development/device_driver_basics.md
Normal file
174
16_driver_development/device_driver_basics.md
Normal file
|
|
@ -0,0 +1,174 @@
|
|||
[← Home](../README.md) · [Driver Development](README.md)
|
||||
|
||||
# Exec Device Driver Framework
|
||||
|
||||
## Overview
|
||||
|
||||
An Amiga **device** is a shared library with additional I/O semantics. Devices handle hardware or protocol communication via `IORequest` messages. Understanding this framework is essential before writing any specific driver (network, graphics, audio).
|
||||
|
||||
---
|
||||
|
||||
## Device vs Library
|
||||
|
||||
| Feature | Library | Device |
|
||||
|---|---|---|
|
||||
| Base structure | `struct Library` | `struct Device` (extends Library) |
|
||||
| Entry points | `Open`, `Close`, `Expunge`, LVO functions | `Open`, `Close`, `Expunge` + `BeginIO`, `AbortIO` |
|
||||
| Communication | Direct function calls | `IORequest` messages via `DoIO`/`SendIO` |
|
||||
| Concurrency | Caller's task context | Can have own task/interrupt context |
|
||||
|
||||
---
|
||||
|
||||
## Minimal Device Structure
|
||||
|
||||
```c
|
||||
/* mydevice.h */
|
||||
struct MyDevBase {
|
||||
struct Device md_Device; /* MUST be first */
|
||||
struct Library *md_SysBase;
|
||||
struct Library *md_DOSBase;
|
||||
/* ... device-specific state ... */
|
||||
};
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Device Function Table
|
||||
|
||||
Every device must provide exactly these entry points in its function vector:
|
||||
|
||||
```c
|
||||
/* Function table (same as library, plus BeginIO/AbortIO): */
|
||||
static const APTR funcTable[] = {
|
||||
(APTR) DevOpen, /* -6 (standard library Open) */
|
||||
(APTR) DevClose, /* -12 (standard library Close) */
|
||||
(APTR) DevExpunge, /* -18 (standard library Expunge) */
|
||||
(APTR) DevReserved, /* -24 (reserved, must return 0) */
|
||||
(APTR) DevBeginIO, /* -30 (start I/O operation) */
|
||||
(APTR) DevAbortIO, /* -36 (abort pending I/O) */
|
||||
(APTR) -1 /* end marker */
|
||||
};
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## DevOpen
|
||||
|
||||
```c
|
||||
LONG DevOpen(struct IORequest *ioreq, ULONG unit, ULONG flags,
|
||||
struct MyDevBase *base)
|
||||
{
|
||||
/* Initialise per-unit state if needed */
|
||||
struct MyUnit *u = &base->md_Units[unit];
|
||||
|
||||
ioreq->io_Device = (struct Device *)base;
|
||||
ioreq->io_Unit = (struct Unit *)u;
|
||||
ioreq->io_Error = 0;
|
||||
|
||||
base->md_Device.dd_Library.lib_OpenCnt++;
|
||||
u->mu_OpenCnt++;
|
||||
|
||||
return 0; /* success */
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## DevBeginIO — The Core
|
||||
|
||||
```c
|
||||
void DevBeginIO(struct IORequest *ioreq, struct MyDevBase *base)
|
||||
{
|
||||
struct IOStdReq *ios = (struct IOStdReq *)ioreq;
|
||||
|
||||
ios->io_Error = 0;
|
||||
|
||||
switch (ios->io_Command) {
|
||||
case CMD_READ:
|
||||
/* Handle synchronously or queue for async */
|
||||
if (!(ios->io_Flags & IOF_QUICK)) {
|
||||
/* Queue request — reply later via ReplyMsg */
|
||||
AddTail(&unit->mu_ReadQueue, &ios->io_Message.mn_Node);
|
||||
ios->io_Flags &= ~IOF_QUICK;
|
||||
return; /* do NOT ReplyMsg yet */
|
||||
}
|
||||
/* ... do sync read ... */
|
||||
break;
|
||||
|
||||
case CMD_WRITE:
|
||||
/* ... */
|
||||
break;
|
||||
|
||||
default:
|
||||
ios->io_Error = IOERR_NOCMD;
|
||||
break;
|
||||
}
|
||||
|
||||
/* Complete the request: */
|
||||
if (!(ios->io_Flags & IOF_QUICK)) {
|
||||
ReplyMsg(&ios->io_Message);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## DevAbortIO
|
||||
|
||||
```c
|
||||
LONG DevAbortIO(struct IORequest *ioreq, struct MyDevBase *base)
|
||||
{
|
||||
/* Remove from pending queue if found */
|
||||
Forbid();
|
||||
Remove(&ioreq->io_Message.mn_Node);
|
||||
Permit();
|
||||
|
||||
ioreq->io_Error = IOERR_ABORTED;
|
||||
ReplyMsg(&ioreq->io_Message);
|
||||
return 0;
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## IOF_QUICK — Synchronous Fast Path
|
||||
|
||||
The `IOF_QUICK` flag is critical:
|
||||
- Caller sets `IOF_QUICK` in `ioreq->io_Flags`
|
||||
- If device completes immediately, it leaves `IOF_QUICK` set → caller knows it's done (no `WaitIO` needed)
|
||||
- If device queues the request, it **clears** `IOF_QUICK` → caller must `WaitIO`
|
||||
|
||||
```c
|
||||
/* Caller pattern (DoIO does this internally): */
|
||||
ioreq->io_Flags |= IOF_QUICK;
|
||||
BeginIO(ioreq);
|
||||
if (!(ioreq->io_Flags & IOF_QUICK))
|
||||
WaitIO(ioreq);
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## RomTag for Device
|
||||
|
||||
```c
|
||||
static struct Resident romtag = {
|
||||
RTC_MATCHWORD,
|
||||
&romtag,
|
||||
&endskip,
|
||||
RTF_AUTOINIT,
|
||||
1, /* version */
|
||||
NT_DEVICE, /* ← not NT_LIBRARY */
|
||||
0, /* priority */
|
||||
"mydevice.device",
|
||||
"mydevice 1.0",
|
||||
&initTable
|
||||
};
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## References
|
||||
|
||||
- NDK39: `exec/devices.h`, `exec/io.h`
|
||||
- ADCD 2.1: `DoIO`, `SendIO`, `WaitIO`, `AbortIO`
|
||||
- RKRM: *Amiga ROM Kernel Reference Manual: Devices* — device driver chapter
|
||||
194
16_driver_development/rtg_driver.md
Normal file
194
16_driver_development/rtg_driver.md
Normal file
|
|
@ -0,0 +1,194 @@
|
|||
[← Home](../README.md) · [Driver Development](README.md)
|
||||
|
||||
# Writing Picasso96/RTG Display Drivers
|
||||
|
||||
## Overview
|
||||
|
||||
**RTG** (Retargetable Graphics) allows the Amiga to use non-native display hardware (graphics cards). The two major RTG systems are **Picasso96** and **CyberGraphX**. Both use a board driver model where a `.card` file provides hardware-specific acceleration.
|
||||
|
||||
---
|
||||
|
||||
## Architecture
|
||||
|
||||
```
|
||||
Application
|
||||
↓ graphics.library / cybergraphics.library / Picasso96API
|
||||
↓
|
||||
RTG System (P96 or CGX)
|
||||
↓ Calls board driver functions
|
||||
↓
|
||||
Board Driver (.card file)
|
||||
↓ Programs hardware registers
|
||||
↓
|
||||
Graphics Card Hardware (Cirrus, S3, Permedia, FPGA framebuffer...)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Picasso96 Board Driver Interface
|
||||
|
||||
A P96 board driver is a shared library exposing specific functions:
|
||||
|
||||
### Required Functions
|
||||
|
||||
| Function | Description |
|
||||
|---|---|
|
||||
| `FindCard(boardInfo)` | Detect and identify the graphics card |
|
||||
| `InitCard(boardInfo)` | Initialise card, set up memory map |
|
||||
| `SetSwitch(boardInfo, state)` | Switch between Amiga native and RTG display |
|
||||
| `SetColorArray(boardInfo, start, count)` | Set palette entries |
|
||||
| `SetDAC(boardInfo, type)` | Configure DAC mode |
|
||||
| `SetGC(boardInfo, modeInfo, border)` | Set graphics context (resolution, depth) |
|
||||
| `SetPanning(boardInfo, addr, w, x, y)` | Set display start address (scrolling) |
|
||||
| `SetDisplay(boardInfo, state)` | Enable/disable display output |
|
||||
|
||||
### Optional Acceleration Functions
|
||||
|
||||
| Function | Description |
|
||||
|---|---|
|
||||
| `BlitRect(boardInfo, ri, x1, y1, x2, y2, w, h, mask, mode)` | Accelerated rectangle blit |
|
||||
| `FillRect(boardInfo, ri, x, y, w, h, pen, mask, mode)` | Accelerated rectangle fill |
|
||||
| `InvertRect(boardInfo, ri, x, y, w, h, mask, mode)` | Accelerated rectangle invert |
|
||||
| `BlitRectNoMaskComplete(...)` | Blit without mask |
|
||||
| `BlitTemplate(...)` | Text/pattern blit |
|
||||
| `DrawLine(...)` | Accelerated line drawing |
|
||||
| `SetSprite(boardInfo, state)` | Hardware sprite control |
|
||||
| `SetSpritePosition(boardInfo, x, y)` | Move hardware sprite |
|
||||
| `SetSpriteImage(boardInfo, ...)` | Set sprite image data |
|
||||
| `SetSpriteColor(boardInfo, idx, r, g, b)` | Set sprite palette |
|
||||
|
||||
---
|
||||
|
||||
## struct BoardInfo
|
||||
|
||||
```c
|
||||
/* P96 BoardInfo — key fields */
|
||||
struct BoardInfo {
|
||||
UBYTE *MemoryBase; /* card framebuffer base */
|
||||
ULONG MemorySize; /* framebuffer size */
|
||||
UBYTE *RegisterBase; /* MMIO register base */
|
||||
ULONG BoardType; /* board type ID */
|
||||
UBYTE *ChipBase; /* chip register base */
|
||||
|
||||
/* Function pointers (set by driver): */
|
||||
APTR SetSwitch;
|
||||
APTR SetColorArray;
|
||||
APTR SetDAC;
|
||||
APTR SetGC;
|
||||
APTR SetPanning;
|
||||
APTR SetDisplay;
|
||||
APTR WaitVerticalSync;
|
||||
|
||||
/* Acceleration (NULL = software fallback): */
|
||||
APTR BlitRect;
|
||||
APTR FillRect;
|
||||
APTR InvertRect;
|
||||
APTR DrawLine;
|
||||
APTR BlitTemplate;
|
||||
APTR BlitRectNoMaskComplete;
|
||||
|
||||
/* Sprite: */
|
||||
APTR SetSprite;
|
||||
APTR SetSpritePosition;
|
||||
APTR SetSpriteImage;
|
||||
APTR SetSpriteColor;
|
||||
|
||||
/* Display mode database: */
|
||||
struct ModeInfo *ModeInfoList;
|
||||
|
||||
/* Color palette (256 entries): */
|
||||
UBYTE CLUT[256 * 3];
|
||||
|
||||
/* ... many more fields ... */
|
||||
};
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Minimal FindCard Implementation
|
||||
|
||||
```c
|
||||
BOOL FindCard(struct BoardInfo *bi)
|
||||
{
|
||||
struct ConfigDev *cd = NULL;
|
||||
|
||||
/* Scan Zorro bus for our card: */
|
||||
cd = FindConfigDev(cd, MY_MANUFACTURER_ID, MY_PRODUCT_ID);
|
||||
if (!cd) return FALSE;
|
||||
|
||||
bi->MemoryBase = cd->cd_BoardAddr;
|
||||
bi->MemorySize = cd->cd_BoardSize;
|
||||
bi->RegisterBase = cd->cd_BoardAddr + REGISTER_OFFSET;
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Minimal InitCard Implementation
|
||||
|
||||
```c
|
||||
BOOL InitCard(struct BoardInfo *bi)
|
||||
{
|
||||
/* Set up function pointers: */
|
||||
bi->SetSwitch = (APTR)MySetSwitch;
|
||||
bi->SetColorArray = (APTR)MySetColorArray;
|
||||
bi->SetDAC = (APTR)MySetDAC;
|
||||
bi->SetGC = (APTR)MySetGC;
|
||||
bi->SetPanning = (APTR)MySetPanning;
|
||||
bi->SetDisplay = (APTR)MySetDisplay;
|
||||
|
||||
/* Optional acceleration: */
|
||||
bi->FillRect = (APTR)MyFillRect;
|
||||
bi->BlitRect = (APTR)MyBlitRect;
|
||||
|
||||
/* Register display modes: */
|
||||
AddResolution(bi, 640, 480, 8); /* 640x480 256 colours */
|
||||
AddResolution(bi, 800, 600, 16); /* 800x600 16-bit */
|
||||
AddResolution(bi, 1024, 768, 24); /* 1024x768 24-bit */
|
||||
|
||||
/* Reset hardware: */
|
||||
HW_Reset(bi->RegisterBase);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## SetSwitch — Native ↔ RTG Toggle
|
||||
|
||||
```c
|
||||
BOOL MySetSwitch(struct BoardInfo *bi, BOOL state)
|
||||
{
|
||||
if (state) {
|
||||
/* Switch TO RTG display */
|
||||
HW_EnableOutput(bi->RegisterBase);
|
||||
/* Disable Amiga native display if using pass-through: */
|
||||
custom.dmacon = DMAF_RASTER; /* turn off bitplane DMA */
|
||||
} else {
|
||||
/* Switch BACK to Amiga native */
|
||||
HW_DisableOutput(bi->RegisterBase);
|
||||
custom.dmacon = DMAF_SETCLR | DMAF_RASTER;
|
||||
}
|
||||
return state;
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Installation
|
||||
|
||||
```
|
||||
DEVS:Monitors/mycard.card ; the board driver
|
||||
DEVS:Monitors/mycard ; monitor file (text config)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## References
|
||||
|
||||
- Picasso96 SDK (P96 developer documentation)
|
||||
- CyberGraphX SDK
|
||||
- Example: UAE RTG driver (`uaegfx.card` source)
|
||||
220
16_driver_development/sana2_driver.md
Normal file
220
16_driver_development/sana2_driver.md
Normal file
|
|
@ -0,0 +1,220 @@
|
|||
[← Home](../README.md) · [Driver Development](README.md)
|
||||
|
||||
# Writing a SANA-II Network Device Driver
|
||||
|
||||
## Overview
|
||||
|
||||
A SANA-II driver is an Amiga device that implements the SANA-II specification. It provides the bridge between a TCP/IP stack (bsdsocket.library) and network hardware. This guide covers writing a complete SANA-II driver from scratch.
|
||||
|
||||
---
|
||||
|
||||
## Architecture
|
||||
|
||||
```
|
||||
TCP/IP Stack (AmiTCP/Roadshow)
|
||||
↓ OpenDevice("mynet.device", 0, ios2req, 0)
|
||||
↓ CMD_READ / CMD_WRITE / S2_DEVICEQUERY / ...
|
||||
SANA-II Device Driver (mynet.device)
|
||||
↓ Hardware register access / DMA
|
||||
Network Hardware (Ethernet NIC / FPGA bridge)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Required Commands
|
||||
|
||||
Every SANA-II driver **must** implement these commands:
|
||||
|
||||
| Command | Code | Description |
|
||||
|---|---|---|
|
||||
| `CMD_READ` | 2 | Read a packet (async — queue and reply when packet arrives) |
|
||||
| `CMD_WRITE` | 3 | Send a packet |
|
||||
| `CMD_FLUSH` | 8 | Abort all pending I/O |
|
||||
| `S2_DEVICEQUERY` | 9 | Report hardware capabilities |
|
||||
| `S2_GETSTATIONADDRESS` | 10 | Return MAC address |
|
||||
| `S2_CONFIGINTERFACE` | 11 | Set MAC address and bring up |
|
||||
| `S2_ONLINE` | 14 | Bring interface online |
|
||||
| `S2_OFFLINE` | 15 | Take interface offline |
|
||||
| `S2_ADDMULTICASTADDRESS` | 16 | Join multicast group |
|
||||
| `S2_DELMULTICASTADDRESS` | 17 | Leave multicast group |
|
||||
| `S2_GETGLOBALSTATS` | 21 | Return packet statistics |
|
||||
| `S2_ONEVENT` | 22 | Notify on event (link up/down) |
|
||||
| `S2_READORPHAN` | 23 | Read unmatched packet types |
|
||||
|
||||
---
|
||||
|
||||
## struct IOSana2Req
|
||||
|
||||
```c
|
||||
/* devices/sana2.h — NDK39/SANA-II Spec */
|
||||
struct IOSana2Req {
|
||||
struct IORequest ios2_Req;
|
||||
ULONG ios2_WireError; /* wire-level error code */
|
||||
ULONG ios2_PacketType; /* Ethernet type (e.g. 0x0800 = IPv4) */
|
||||
UBYTE ios2_SrcAddr[SANA2_MAX_ADDR_BYTES]; /* source MAC */
|
||||
UBYTE ios2_DstAddr[SANA2_MAX_ADDR_BYTES]; /* destination MAC */
|
||||
ULONG ios2_DataLength; /* data length */
|
||||
APTR ios2_Data; /* packet data (via buffer management) */
|
||||
APTR ios2_StatData; /* statistics data pointer */
|
||||
APTR ios2_BufferManagement; /* buffer mgmt hooks from stack */
|
||||
};
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Buffer Management Hooks
|
||||
|
||||
The TCP/IP stack provides buffer copy functions via tags at OpenDevice time. Your driver **must** use these — never copy data directly:
|
||||
|
||||
```c
|
||||
/* In your DevOpen: */
|
||||
typedef BOOL (*CopyToBuff)(APTR to, APTR from, ULONG len);
|
||||
typedef BOOL (*CopyFromBuff)(APTR to, APTR from, ULONG len);
|
||||
|
||||
struct BufferManagement {
|
||||
CopyToBuff bm_CopyToBuff;
|
||||
CopyFromBuff bm_CopyFromBuff;
|
||||
};
|
||||
|
||||
/* Parse tags from ios2req->ios2_BufferManagement: */
|
||||
struct TagItem *tags = (struct TagItem *)ios2req->ios2_BufferManagement;
|
||||
struct BufferManagement *bm = AllocMem(sizeof(*bm), MEMF_PUBLIC);
|
||||
bm->bm_CopyToBuff = (CopyToBuff)GetTagData(S2_CopyToBuff, 0, tags);
|
||||
bm->bm_CopyFromBuff = (CopyFromBuff)GetTagData(S2_CopyFromBuff, 0, tags);
|
||||
ios2req->ios2_BufferManagement = bm;
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Implementing CMD_READ (Receive Path)
|
||||
|
||||
```c
|
||||
void CmdRead(struct IOSana2Req *ios2, struct MyDevBase *base)
|
||||
{
|
||||
struct MyUnit *unit = (struct MyUnit *)ios2->ios2_Req.io_Unit;
|
||||
|
||||
/* CMD_READ is ALWAYS async — queue the request */
|
||||
ios2->ios2_Req.io_Flags &= ~IOF_QUICK;
|
||||
|
||||
Disable();
|
||||
/* Queue by packet type for fast dispatch on interrupt: */
|
||||
struct ReadQueue *rq = FindReadQueue(unit, ios2->ios2_PacketType);
|
||||
if (!rq) {
|
||||
rq = CreateReadQueue(unit, ios2->ios2_PacketType);
|
||||
}
|
||||
AddTail(&rq->rq_List, &ios2->ios2_Req.io_Message.mn_Node);
|
||||
Enable();
|
||||
|
||||
/* Do NOT ReplyMsg — will be replied when a packet arrives */
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Implementing CMD_WRITE (Transmit Path)
|
||||
|
||||
```c
|
||||
void CmdWrite(struct IOSana2Req *ios2, struct MyDevBase *base)
|
||||
{
|
||||
struct MyUnit *unit = (struct MyUnit *)ios2->ios2_Req.io_Unit;
|
||||
struct BufferManagement *bm = ios2->ios2_BufferManagement;
|
||||
|
||||
UBYTE txbuf[1536]; /* max Ethernet frame */
|
||||
ULONG len = ios2->ios2_DataLength;
|
||||
|
||||
/* Build Ethernet header: */
|
||||
CopyMem(ios2->ios2_DstAddr, &txbuf[0], 6); /* dest MAC */
|
||||
CopyMem(unit->mu_StationAddr, &txbuf[6], 6); /* src MAC */
|
||||
txbuf[12] = (ios2->ios2_PacketType >> 8) & 0xFF;
|
||||
txbuf[13] = ios2->ios2_PacketType & 0xFF;
|
||||
|
||||
/* Copy payload from stack's buffer: */
|
||||
bm->bm_CopyFromBuff(&txbuf[14], ios2->ios2_Data, len);
|
||||
|
||||
/* Send to hardware: */
|
||||
HW_Transmit(unit, txbuf, len + 14);
|
||||
|
||||
ios2->ios2_Req.io_Error = 0;
|
||||
TermIO(ios2);
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Interrupt Handler (Packet Arrival)
|
||||
|
||||
```c
|
||||
/* Called when hardware signals packet received: */
|
||||
LONG __saveds RxInterrupt(struct MyDevBase *base __asm("a1"))
|
||||
{
|
||||
struct MyUnit *unit = base->md_Units[0];
|
||||
UBYTE rxbuf[1536];
|
||||
ULONG len;
|
||||
|
||||
while (HW_HasPacket(unit)) {
|
||||
len = HW_ReceivePacket(unit, rxbuf, sizeof(rxbuf));
|
||||
|
||||
UWORD ptype = (rxbuf[12] << 8) | rxbuf[13];
|
||||
|
||||
/* Find a pending CMD_READ for this packet type: */
|
||||
struct ReadQueue *rq = FindReadQueue(unit, ptype);
|
||||
if (rq && !IsListEmpty(&rq->rq_List)) {
|
||||
struct IOSana2Req *ios2 =
|
||||
(struct IOSana2Req *)RemHead(&rq->rq_List);
|
||||
|
||||
struct BufferManagement *bm = ios2->ios2_BufferManagement;
|
||||
bm->bm_CopyToBuff(ios2->ios2_Data, &rxbuf[14], len - 14);
|
||||
|
||||
CopyMem(&rxbuf[6], ios2->ios2_SrcAddr, 6);
|
||||
ios2->ios2_DataLength = len - 14;
|
||||
ios2->ios2_Req.io_Error = 0;
|
||||
|
||||
ReplyMsg(&ios2->ios2_Req.io_Message);
|
||||
} else {
|
||||
unit->mu_Stats.DroppedPackets++;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## S2_DEVICEQUERY Response
|
||||
|
||||
```c
|
||||
void CmdDeviceQuery(struct IOSana2Req *ios2, struct MyDevBase *base)
|
||||
{
|
||||
struct Sana2DeviceQuery *query = ios2->ios2_StatData;
|
||||
|
||||
query->DevQueryFormat = 0;
|
||||
query->DeviceLevel = 0;
|
||||
query->AddrFieldSize = 48; /* 48-bit MAC */
|
||||
query->MTU = 1500; /* Ethernet MTU */
|
||||
query->BPS = 100000000; /* 100 Mbps */
|
||||
query->HardwareType = S2WireType_Ethernet;
|
||||
|
||||
ios2->ios2_Req.io_Error = 0;
|
||||
TermIO(ios2);
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Building
|
||||
|
||||
```makefile
|
||||
CFLAGS = -noixemul -m68000 -Os -fomit-frame-pointer
|
||||
mynet.device: mynet.o
|
||||
$(CC) $(CFLAGS) -nostartfiles -o $@ $^
|
||||
```
|
||||
|
||||
Install to `DEVS:Networks/mynet.device`.
|
||||
|
||||
---
|
||||
|
||||
## References
|
||||
|
||||
- SANA-II Network Device Driver Specification v2 (Commodore)
|
||||
- NDK39: `devices/sana2.h`
|
||||
- Example drivers: a2065.device source, plipbox driver
|
||||
Loading…
Add table
Add a link
Reference in a new issue