digraph {
// This specifies a diagram using the DOT language. It can be interactively
// viewed with xdot or can generate an image using
// `dot -Tpng alsa_fsm.dot > alsa_fsm.png`

// This was build out of the state transition information from
// linux/sound/core/pcm_native.c and linux/sound/core/pcm_lib.c. Since it is
// intended to help with writing software which uses ALSA's PCM interface, it
// includes every possible transition, even if they are unlikely, rather than
// trying to model a more typical software path.

// snd_pcm_hw_free is called as a result of snd_pcm_close
// snd_pcm_hwsync is called before querying any hardware state such as in
// snd_pcm_avail.

// From the perspective of the state machine, snd_pcm_capture_rewind and
// snd_pcm_playback rewind are the same function so they are not
// distinguished in this diagram. This also holds for the forward
// functions.

// For all states the a SND_PCM_STATE_ prefix has been omitted to clarify
// the state machine.

// For all transitions the snd_pcm_ prefix has been omitted to clarify
// the state machine.

rankdir=BT;

// Bulk processing
ANY [label = "any state"];
ANY -> SUSPENDED [label = suspend];
SUSPENDED -> ANY [label = resume];

// snd_pcm_dev_disconnect is the only kernel-only ALSA function included in
// this diagram because it is the only way to transition into the
// SNDRV_PCM_STATE_DISCONNECTED state and that state should be accounted for
// when designing code which directly interacts with ALSA and might use USB
// audio devices which may be disconnected in use.
ANY -> DISCONNECTED [label = dev_disconnect];
ANY -> ANY [label = "info/hw_refine/unlink/sync_ptr/tstamp"];

NOT_OPEN [label = "any state but OPEN"]
NOT_OPEN -> NOT_OPEN [label = "sw_params/channel_info/link"];

// State-specific transitions.
unitialized [label="", shape=none];
unitialized -> OPEN [label = open];

OPEN -> OPEN [label = hw_params];
OPEN -> SETUP [label = hw_params];

SETUP -> OPEN [label = hw_free];
SETUP -> SETUP [label = "hw_params/drain/drop"];
SETUP -> PREPARED [label = prepare];

PREPARED -> OPEN [label = "hw_free/reset"];
PREPARED -> SETUP [label = "hw_params/drop"];
PREPARED -> PREPARED [label = "prepare/rewind/forward/hwsync/delay"];
PREPARED -> RUNNING [label = "start/writei"];
PREPARED -> DRAINING [label = drain];

RUNNING -> OPEN [label = reset];
RUNNING -> SETUP [label = "drain/drop"];
RUNNING -> RUNNING [label = "rewind/forward/hwsync/delay/writei"];
RUNNING -> XRUN [label = "xrun/hwsync/writei"];
RUNNING -> DRAINING [label = drain];
RUNNING -> PAUSED [label = pause];

// snd_pcm_xrun is called if an xrun is detected by
// snd_pcm_hw_avail_update. It puts the kernel-based ALSA code into the
// SNDRV_PCM_STATE_XRUN state.
XRUN -> SETUP[label = "drain/drop"];
XRUN -> PREPARED [label = prepare];
XRUN -> XRUN [label = xrun];

DRAINING -> SETUP [label = "sw_params/status/drain/drop/delay"];
DRAINING -> PREPARED [label = prepare];
DRAINING -> DRAINING [label = "drain/rewind/forward"];

PAUSED -> OPEN [label = reset];
PAUSED -> SETUP [label = "drain/drop"];
PAUSED -> PREPARED [label = prepare];
PAUSED -> RUNNING [label = pause];
PAUSED -> DRAINING [label = drain];
PAUSED -> PAUSED [label = "forward/writei"];

SUSPENDED -> OPEN [label = reset];
SUSPENDED -> PREPARED [label = prepare];
SUSPENDED -> SUSPENDED [label = "hwsync/delay"];

}
