Skip to content

Chapter 3: IPC Framework

Chapter 2 covered AIPC for middleware-to-middleware communication. Now let's look at the SDK-level IPC that connects the main processes.

While AIPC uses shared memory between ACE middleware services, the IPC framework here is built on NNG (nanomsg-next-gen) and uses Unix domain sockets to connect the SDK's own processes — Agent, CDMB, MqttProxy, and Provisioner. Think of it as the nervous system of the SDK: every command, every event, every MQTT message flows through these IPC channels.

How It's Organized

The IPC framework has three layers:

Layer Header Role
IPC Manager (client) iotshd_ipc_mgr.h Sends requests, subscribes to events
IPC Service (server) iotshd_ipc_svc_ipc.h Listens for requests, publishes events
IPC Adapter iotshd_ipc_adpt.h Platform-specific glue layer

Each process runs either a Manager (client), a Service (server), or both.

Socket URLs: The Addresses

Every IPC channel has a Unix domain socket URL. The CDMB service defines its endpoints in include/ipc/iotshd_ipc_cdmb.h:

#define IOTMI_CDMB_SVC_ASYNC_URL "ipc:///tmp/iotmi_cdmb_svc_async"
#define IOTMI_CDMB_SVC_SYNC_URL  "ipc:///tmp/iotmi_cdmb_svc_sync"
#define IOTMI_CDMB_SVC_PUB_URL   "ipc:///tmp/iotmi_cdmb_svc_pub"

Three URLs, three channels — each serves a different communication pattern:

  • async — fire-and-forget requests (up to 64 in parallel)
  • sync — blocking request/response
  • pub — broadcast events to all subscribers

The URL structure IotmiIpcMgr_Urls in iotshd_ipc_common_types.h bundles these together so a Manager knows where to connect.

Sync vs Async: Two Ways to Talk

Synchronous (Blocking)

The caller sends a request and waits for a response. Defined in iotshd_ipc_mgr_ipc.h:

iotmi_statusCode_t IotmiIpcMgr_IpcSync(
    IotmiIpc_Command cmd,
    const uint8_t *buf, uint32_t buf_len,
    uint8_t **resp_buf, uint32_t *resp_buf_len,
    IotmiIpcMgr_InternalContext *ipc_ctx);

Use this when you need an answer right now — like sending an MQTT message through the proxy and confirming it was accepted.

Asynchronous (Non-Blocking)

The caller sends a request and provides a callback. The framework calls you back when the response arrives:

iotmi_statusCode_t IotmiIpcMgr_IpcAsync(
    IotmiIpc_Command cmd,
    const uint8_t *buf, uint32_t buf_len,
    iotmiIpcMgr_ipcFuncCallback_t cb, void *client_ctx,
    IotmiIpcMgr_InternalContext *ipc_ctx);

Use this for operations that might take a while — like sending a command from Agent to CDMB (IOTMI_IPC_SVC_SEND_REQ_TO_CDMB).

Sync Request/Response Flow

Here's what happens when the Agent sends a synchronous request to the CDMB service:

sequenceDiagram
    participant Agent
    participant IPC Manager
    participant NNG Socket
    participant IPC Service
    participant CDMB

    Agent->>IPC Manager: IotmiIpcMgr_IpcSync(cmd, data)
    IPC Manager->>NNG Socket: Send via sync URL
    NNG Socket->>IPC Service: Deliver message
    IPC Service->>CDMB: Route to command handler
    CDMB-->>Agent: Response via same socket

The message format is compact — defined as IotmiIpc_IpcMessage in iotshd_ipc_common_types.h:

typedef struct {
  uint8_t  cmd;      // which command (IotmiIpc_Command enum)
  uint8_t  status;   // response status code
  uint8_t  pad1;     // reserved
  uint8_t  pad2;     // reserved
  uint32_t buf_len;  // payload length (max 1MB)
  // payload bytes follow immediately
} IotmiIpc_IpcMessage;

Commands: What Can You Send?

Commands are defined in the IotmiIpc_Command enum (iotshd_ipc_common_types.h). They fall into two groups:

Range Type Examples
0–29 Sync SYNC_SEND_MQTT_MSG_TO_PROXY, SYNC_SEND_MQTT_SUB_TO_PROXY
30–63 Async SEND_REQ_TO_CDMB, SEND_MQTT_MSG_FROM_AGENT

On the server side, each command maps to a handler registered with a simple macro in iotshd_ipc_svc_internal.h:

IOTMI_IPC_SVC_REGISTER_CMD(cmd, handler_func)

Pub/Sub: Event Broadcasting

Not everything is request/response. When the CDMB has a new device report, it needs to tell everyone at once. That's what the pub/sub channel is for.

Server Side — Publishing Events

The service broadcasts events via iotshd_ipc_svc_ipc.h:

IotmiIpcSvc_IpcPublishEvent(
    event_id, data, data_len, pub_sock_idx);

Client Side — Subscribing to Events

A client registers a callback, then subscribes to specific event IDs. From iotshd_ipc_mgr.h:

// 1. Register your callback
IotmiIpcMgr_RegisterSubscriber(&handle, my_cb, ctx, &mgr);
// 2. Subscribe to a specific event
IotmiIpcMgr_SubscribeEvent(&handle, IOTMI_IPC_EVENT_CDMB_REPORT, &mgr);

When that event fires, your callback receives an IotmiIpcMgr_EventParam with the event ID, payload data, and length. Up to IOTMI_IPC_MAX_SUBSCRIBER (5) subscribers can register per Manager context.

Key Event IDs

Some events you'll see frequently (from IotmiIpc_EventId in iotshd_ipc_common_types.h):

Event Purpose
EVENT_CDMB_REPORT Device state report from CDMB → Agent
EVENT_MQTT_CONTROL_TOPIC MQTT downstream control message
EVENT_DEVICE_CAP_MSG_READY Device capabilities ready after onboarding
EVENT_PROVISIONING_TASK_TO_SDK_CLIENT Provisioning task from LPW → SDK Client
EVENT_CUSTOM_PLUGIN Incoming task for custom plugins

The IPC Adapter

The adapter layer (iotshd_ipc_adpt.h) abstracts platform-specific details. It provides four functions:

iotmiIpcAdpt_init();                    // platform setup
iotmiIpcAdpt_registerEventHandler(cb);  // wire up event routing
iotmiIpcAdpt_subscribeEvent(event);     // subscribe at platform level
iotmiIpcAdpt_deinit();                  // cleanup

If you're porting the SDK to a new platform, this is the file you'd implement. The rest of the IPC framework stays the same.

Lifecycle: Putting It All Together

Starting a Client (Manager)

IotmiIpcMgr_InitContext(&mgr, &urls, parallel, timeout_ms);
IotmiIpcMgr_Init(&mgr);
// ... use sync/async calls, subscribe to events ...
IotmiIpcMgr_Deinit(&mgr);

Starting a Server (Service)

IotmiIpcSvc_IpcInit(my_command_handler, async_parallel);
IotmiIpcSvc_IpcStart(use_main_thread);
// ... server runs, dispatching commands ...
IotmiIpcSvc_IpcDeinit();

The service can run on the main thread (blocking) or spawn its own background thread.

Size Limits

A few constants worth knowing from the CDMB configuration (iotshd_ipc_cdmb.h):

Constant Value Meaning
IOTMI_IPC_MAX_MSG_SIZE 1 MB Maximum IPC message payload
IOTMI_CDMB_REQ_MAX_LENGTH_BYTES 8 KB Max CDMB request size
IOTMI_CDMB_RSP_MAX_LENGTH_BYTES 8 KB Max CDMB response size
IOTMI_CDMB_EVENT_MAX_LENGTH_BYTES 1 MB Max event payload
IOTMI_CDMB_SVC_ASYNC_PARALLEL 64 Concurrent async workers

Quick Reference: Key Files

File What's Inside
commonUtils/IoTSmartHomeDevice-IPC/include/iotshd_ipc_mgr.h Client-side Manager API
commonUtils/IoTSmartHomeDevice-IPC/include/iotshd_ipc_mgr_ipc.h Sync/async call implementations
commonUtils/IoTSmartHomeDevice-IPC/include/iotshd_ipc_svc_ipc.h Server-side Service API
commonUtils/IoTSmartHomeDevice-IPC/include/iotshd_ipc_svc_internal.h Command handler registration
commonUtils/IoTSmartHomeDevice-IPC/include/iotshd_ipc_adpt.h Platform adapter interface
commonUtils/IoTSmartHomeDevice-IPC/include/iotshd_ipc_common_types.h Commands, events, message structs
include/ipc/iotshd_ipc_cdmb.h CDMB socket URLs and buffer sizes

Conclusion

The IPC framework gives the SDK a clean, layered communication system: sync calls for immediate answers, async calls for background work, and pub/sub for broadcasting events. Every process — Agent, CDMB, MqttProxy, Provisioner — plugs into this same infrastructure through well-defined socket URLs and command enums.

In Chapter 4: Middleware Protocol Services & HAL, we'll see how the SDK bridges from this IPC layer down to the actual hardware — translating high-level commands into protocol-specific operations through the Hardware Abstraction Layer.