Skip to Content
We're improving our docs. Share your experience and help shape what comes next.
DocumentationDevice AppExplanationApplication Structure and I/O

Application structure and I/O

Most Ledger OS applications follow a host-driven model: the application on the device does not act autonomously. Instead, it waits for commands from a host process running on a computer or smartphone, performs a secure operation (signing, encryption, derivation), and returns a response. This page explains how that command-and-response cycle works and how the SDK supports it.

The APDU interpretation loop

The command-and-response scheme is modelled on the ISO/IEC 7816-4  smartcard protocol. Each packet is called an APDU (Application Protocol Data Unit). The device application is driven by a never-ending loop that calls io_exchange(...) from the SDK on every iteration:

// Pseudocode for the main APDU loop while (true) { // Send the previous response (if any) and block until the next command arrives. uint8_t *rx_buffer = io_exchange(CHANNEL_APDU, tx_len); // Dispatch to the appropriate handler based on the command byte. handle_apdu(rx_buffer); }

Each call to io_exchange does two things in sequence:

  1. Sends the response APDU that was prepared in the previous iteration (skipped on the very first call).
  2. Blocks until the host sends the next command APDU, then returns a pointer to the received data.

Asynchronous replies

Some operations require the user to confirm an action on the device before the response is sent — for example, approving a transaction before signing it. In this case the application cannot reply immediately, so it uses the IO_ASYNCH_REPLY flag to defer the response:

// In the APDU handler: defer the reply and wait for user input. io_exchange(CHANNEL_APDU | IO_ASYNCH_REPLY, 0); // Later, in the button-press handler, after the user approves: io_exchange(CHANNEL_APDU | IO_RETURN_AFTER_TX, response_len);
  • IO_ASYNCH_REPLY tells the SDK to send no data now and return control to the application.
  • IO_RETURN_AFTER_TX sends the queued response and immediately returns without waiting for the next command — the main loop will call io_exchange again to receive it.

For a worked example, see blue-app-samplesign : the sign-message handler sets IO_ASYNCH_REPLY (line 510), and the approval/denial button handlers send the deferred response using IO_RETURN_AFTER_TX (line 434).

The Event / Commands / Status model

The APDU loop above is a synchronous request-response model. Ledger OS also exposes an Event / Commands / Status (ECS) model that decouples input events (button presses, USB/BLE packets, timer ticks) from the APDU response cycle. ECS is designed to avoid the limitations of the synchronous model and lets developers build custom event-processing loops when the default APDU dispatcher does not fit their use case.

Transport-layer flexibility

The APDU protocol is not implemented by Ledger OS itself — it is entirely the responsibility of the SDK. This means applications can replace or augment it:

  • Alternative protocols: an application can implement a custom protocol on top of USB HID, USB CCID, or BLE instead of APDU.
  • Custom USB enumeration: applications can modify how the device is enumerated as a USB device by the host.

The diagram below shows the common protocol stack used across Ledger OS applications:

Common protocols across Ledger OS applications Common protocols across Ledger OS applications

Summary

ConceptPurpose
io_exchange(CHANNEL_APDU, len)Send a response and block for the next command
IO_ASYNCH_REPLYDefer the response until user confirms on-device
IO_RETURN_AFTER_TXSend the deferred response and return immediately
ECS modelAlternative event loop for non-APDU use cases

Further reading

Last updated on
Ledger
Copyright © Ledger SAS. All rights reserved. Ledger, Ledger Stax, Ledger Flex, Ledger Nano, Ledger Nano S, Ledger OS, Ledger Wallet, [LEDGER] (logo), [L] (logo) are trademarks owned by Ledger SAS.