Choosing a Pattern

The Driver SDK provides four I/O patterns. Each models a different relationship between reads and writes. Choosing the right one is the most important design decision for your driver.

The quick answer

You need…PatternFramework
Bidirectional byte pipeP1: Pure StreamStreamDriver
Request in, response outP2: Write-then-ReadWriteReadDriver
Push events to readersP3: Event StreamEventStreamDriver
Config phase + streaming outputP4: Configured StreamConfiguredStreamDriver
Full FUSE control / diagnosticsL1: RawBentosDriver

If that table answered your question, go build. If not, read on.

The key question: how do read and write relate?

The four patterns exist because there are four fundamentally different relationships between the read and write sides of a device:

P1: Read and write are independent

The write side and read side are separate streams. Writing does not cause reading. Reading does not depend on writing. Both can happen concurrently.

Think: serial port, PTY, chat connection, bidirectional pipe.

Poll behavior: POLLIN and POLLOUT can both be set simultaneously. They’re independent.

Choose P1 when: your device is a bidirectional channel where each direction operates independently.

P2: Write causes read

Writing submits a request. Reading retrieves the response. You must write before you can read. The causal link between write and read is the defining characteristic.

Think: HTTP request/response (but as a device), database query, RPC call.

Poll behavior: POLLIN and POLLOUT are mutually exclusive. When you can write (accepting a request), you can’t read (no response yet). When you can read (response ready), you can’t write (still delivering the response). This mutual exclusivity IS the pattern.

Choose P2 when: your device processes a complete request and produces a complete response.

P3: Only read (events pushed to you)

The device produces events. Readers consume them. Writing is not supported (returns EACCES). The driver pushes events; the framework broadcasts them to all listeners.

Think: input device (/dev/input/event*), sensor, notification stream, log tail.

Poll behavior: POLLIN when events are queued. POLLOUT is not applicable.

Choose P3 when: your device is a read-only event source with potentially multiple listeners.

P4: Configure, then stream

Heavy upfront configuration (via ioctl or initial writes), then submit input, then stream output. A 6-state machine manages the lifecycle. This is the most complex pattern – and the one that inference drivers use.

Think: ALSA audio (/dev/snd/pcm*), video encoder, LLM inference. Set parameters, submit input, stream output tokens.

Poll behavior: Changes with state. POLLOUT during config/input. POLLIN during streaming output. Mutually exclusive across phases.

Choose P4 when: your device needs configuration before processing, and produces streaming output from submitted input.

Real-world examples

DevicePatternWhy
/dev/echoP1Write bytes in, read them back – independent streams
/dev/kvP2Write a key query, read the value – request/response
/dev/tickerP3Emits periodic events – read-only event source
/dev/synthP4Configure transformation, write input, stream output
/dev/llm/anthropic/claude-sonnet-4P4Configure model params, write prompt, stream tokens
/dev/im/telegram/botP1Send messages (write) and receive messages (read) independently
/dev/input/keyboardP3Keyboard emits key events – read-only

When nothing fits: Layer 1

If your device genuinely doesn’t match any pattern, use BentosDriver directly (Layer 1). You get raw FUSE op callbacks with full control. But this means you handle session management, poll readiness, state machines, and error translation yourself.

The playground_driver example is a good starting point – it’s an instrumented workbench that logs every FUSE op.

Complexity comparison

PatternOps callbacksState machineTypical driver LOC
P1: Pure Stream2-4None~15
P2: Write-then-Read1-34 states (framework-managed)~20
P3: Event Stream1-3None (framework tracks listeners)~15
P4: Configured Stream4-86 states (framework-managed)~45
L1: Raw1-8You manage itvaries

The pattern frameworks earn their complexity budget. P4 manages a 6-state machine, buffering, ioctl routing, and cancellation – so you don’t have to.