[← 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)
{
    /* Initialize 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
