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:
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:
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.