-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathrdkFwupdateMgr_api.c
More file actions
471 lines (425 loc) · 18.7 KB
/
rdkFwupdateMgr_api.c
File metadata and controls
471 lines (425 loc) · 18.7 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
/*
* Copyright 2026 Comcast Cable Communications Management, LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* SPDX-License-Identifier: Apache-2.0
*/
/**
* @file rdkFwupdateMgr_api.c
* @brief Public API implementations: checkForUpdate, downloadFirmware, updateFirmware
*
* ALL THREE APIS USE THE SAME ASYNC PATTERN:
* ===========================================
* All APIs are NON-BLOCKING fire-and-forget calls that return immediately.
* Results are delivered asynchronously via D-Bus signals to registered callbacks.
*
* CHECKFORUPDATE:
* ---------------
* 1. Validate handle and callback
* 2. Register callback in registry (BEFORE D-Bus call to avoid race)
* 3. Fire CheckForUpdate D-Bus method call (fire-and-forget)
* 4. Return CHECK_FOR_UPDATE_SUCCESS immediately
*
* [Later - typically 5-30 seconds]
* Daemon queries XConf server and emits CheckForUpdateComplete signal
* → on_check_complete_signal() fires in background thread
* → dispatch_all_pending() calls registered UpdateEventCallback
* → Callback receives FwInfoData with version info and update details
*
* DOWNLOAD / UPDATE FIRMWARE:
* ============================
* Same pattern but with progress signals:
* - DownloadFirmware → DownloadProgress signals (multiple, 0%-100%)
* - UpdateFirmware → UpdateProgress signals (multiple, 0%-100%)
*
* Callbacks fire repeatedly until COMPLETED or ERROR status.
*/
#include "rdkFwupdateMgr_client.h"
#include "rdkFwupdateMgr_async_internal.h"
#include "rdkFwupdateMgr_log.h"
#include <gio/gio.h>
#include <string.h>
#include <stdlib.h>
#include <inttypes.h>
/* ========================================================================
* checkForUpdate — SYNCHRONOUS implementation
* ======================================================================== */
/**
* @brief Check for firmware update — non-blocking, returns immediately
*
* Sends CheckForUpdate(handle) to the daemon and returns immediately.
* The daemon will query the XConf server in the background (5-30 seconds)
* and emit a CheckForUpdateComplete signal when done.
*
* The callback fires ONCE when the signal arrives with complete firmware info:
* - FwInfoData.status: FIRMWARE_AVAILABLE, FIRMWARE_NOT_AVAILABLE, etc.
* - FwInfoData.CurrFWVersion: Current firmware version
* - FwInfoData.UpdateDetails: Details about available update (if any)
*
* The callback is registered in the async registry before sending the D-Bus call
* to ensure the signal doesn't arrive before we're ready to receive it.
*
* @param handle Valid FirmwareInterfaceHandle from registerProcess()
* @param callback Invoked when CheckForUpdateComplete signal arrives
* @return CHECK_FOR_UPDATE_SUCCESS or CHECK_FOR_UPDATE_FAIL
*/
CheckForUpdateResult checkForUpdate(FirmwareInterfaceHandle handle,
UpdateEventCallback callback)
{
/* [1] Validate */
if (handle == NULL || handle[0] == '\0') {
FWUPMGR_ERROR("checkForUpdate: invalid handle (NULL or empty)\n");
return CHECK_FOR_UPDATE_FAIL;
}
if (callback == NULL) {
FWUPMGR_ERROR("checkForUpdate: callback is NULL\n");
return CHECK_FOR_UPDATE_FAIL;
}
FWUPMGR_INFO("checkForUpdate: handle='%s'\n", handle);
/* [2] Connect to D-Bus FIRST before registering callback
*
* This prevents stale registry entries if D-Bus connection fails.
* We only register the callback if we can successfully send the request.
*/
GError *error = NULL;
GDBusConnection *conn = g_bus_get_sync(G_BUS_TYPE_SYSTEM, NULL, &error);
if (conn == NULL) {
FWUPMGR_ERROR("checkForUpdate: D-Bus connect failed: %s\n",
error ? error->message : "unknown");
if (error) g_error_free(error);
return CHECK_FOR_UPDATE_FAIL;
}
/* [3] Register callback AFTER D-Bus connection succeeds
*
* Register immediately before sending to avoid race condition where
* the daemon responds before we're ready to receive the signal.
*/
if (!internal_register_callback(handle, callback)) {
FWUPMGR_ERROR("checkForUpdate: registry full, handle='%s'\n", handle);
g_object_unref(conn);
return CHECK_FOR_UPDATE_FAIL;
}
/* [4] Fire-and-forget D-Bus CheckForUpdate method call
*
* Arguments: (s)
* s handle — identifies this app to the daemon
*
* Three trailing NULLs = fire and forget (no reply waited for).
* g_dbus_connection_call() returns immediately.
* Daemon will emit CheckForUpdateComplete signal when XConf query finishes.
*/
FWUPMGR_INFO("checkForUpdate: calling CheckForUpdate on daemon, handle='%s'\n",
handle);
g_dbus_connection_call(
conn,
DBUS_SERVICE_NAME,
DBUS_OBJECT_PATH,
DBUS_INTERFACE_NAME,
DBUS_METHOD_CHECK, /* method: CheckForUpdate */
g_variant_new("(s)", handle), /* app's handler_id string */
NULL, /* expected reply type: none */
G_DBUS_CALL_FLAGS_NONE,
DBUS_TIMEOUT_MS,
NULL, /* GCancellable: none */
NULL, /* reply callback: none */
NULL /* user_data: none */
);
g_object_unref(conn);
FWUPMGR_INFO("checkForUpdate: D-Bus call sent, returning SUCCESS. "
"Callback will fire when CheckForUpdateComplete signal arrives. "
"handle='%s'\n", handle);
/* [5] Return immediately — app is unblocked */
return CHECK_FOR_UPDATE_SUCCESS;
}
#if 0
/* ========================================================================
* LIBRARY LIFECYCLE
* ======================================================================== */
/**
* @brief Library constructor — auto-called when .so is loaded
*
* Initializes the internal async engine (registry + background thread)
* before any app code runs.
*/
__attribute__((constructor))
static void rdkFwupdateMgr_lib_init(void)
{
FWUPMGR_INFO("=== rdkFwupdateMgr library loading ===\n");
if (internal_system_init() != 0) {
FWUPMGR_ERROR("rdkFwupdateMgr_lib_init: internal_system_init FAILED\n");
}
FWUPMGR_INFO("=== rdkFwupdateMgr library ready ===\n");
}
/**
* @brief Library destructor — auto-called when .so is unloaded
*
* Stops background thread and frees all resources cleanly.
*/
__attribute__((destructor))
static void rdkFwupdateMgr_lib_deinit(void)
{
FWUPMGR_INFO("=== rdkFwupdateMgr library unloading ===\n");
internal_system_deinit();
FWUPMGR_INFO("=== rdkFwupdateMgr library unloaded ===\n");
}
#endif
/* ========================================================================
* DOWNLOAD FIRMWARE PUBLIC API
* ========================================================================
*
* Implements:
* DownloadResult downloadFirmware(FirmwareInterfaceHandle handle,
* FwDwnlReq fwdwnlreq,
* DownloadCallback callback);
*
* FLOW:
* 1. Validate: handle not NULL/empty, firmwareName not empty, callback not NULL
* 2. Connect to D-Bus (fail early if connection fails)
* 3. Register callback in download registry (AFTER D-Bus connection succeeds)
* 4. Fire DownloadFirmware D-Bus method call to daemon (fire-and-forget)
* 5. Return RDKFW_DWNL_SUCCESS immediately
*
* [later — fires multiple times as download progresses]
* Daemon emits DownloadProgress(progress%, status) signal repeatedly
* → on_download_progress_signal() fires in background thread
* → dispatch_all_dwnl_active() calls every ACTIVE DownloadCallback
* → slot stays ACTIVE until DWNL_COMPLETED or DWNL_ERROR
* ======================================================================== */
/**
* @brief Initiate firmware download — non-blocking, returns immediately
*
* @param handle Valid FirmwareInterfaceHandle from registerProcess()
* @param fwdwnlreq Download request (passed by value, library copies it)
* @param callback Invoked on each DownloadProgress signal
* @return RDKFW_DWNL_SUCCESS or RDKFW_DWNL_FAILED
*/
DownloadResult downloadFirmware(FirmwareInterfaceHandle handle,
const FwDwnlReq *fwdwnlreq,
DownloadCallback callback)
{
/* [1] Validate */
if (handle == NULL || handle[0] == '\0') {
FWUPMGR_ERROR("downloadFirmware: invalid handle (NULL or empty)\n");
return RDKFW_DWNL_FAILED;
}
if (fwdwnlreq == NULL) {
FWUPMGR_ERROR("downloadFirmware: fwdwnlreq is NULL\n");
return RDKFW_DWNL_FAILED;
}
if (fwdwnlreq->firmwareName == NULL) {
FWUPMGR_ERROR("downloadFirmware: firmwareName is NULL\n");
return RDKFW_DWNL_FAILED;
}
if (fwdwnlreq->firmwareName[0] == '\0') {
FWUPMGR_ERROR("downloadFirmware: firmwareName is empty\n");
return RDKFW_DWNL_FAILED;
}
if (callback == NULL) {
FWUPMGR_ERROR("downloadFirmware: callback is NULL\n");
return RDKFW_DWNL_FAILED;
}
FWUPMGR_INFO("downloadFirmware: handle='%s' firmware='%s' type='%s' url='%s'\n",
handle,
fwdwnlreq->firmwareName,
(fwdwnlreq->TypeOfFirmware && fwdwnlreq->TypeOfFirmware[0]) ? fwdwnlreq->TypeOfFirmware : "(none)",
(fwdwnlreq->downloadUrl && fwdwnlreq->downloadUrl[0]) ? fwdwnlreq->downloadUrl : "(use XConf)");
/* [2] Connect to D-Bus FIRST before registering callback
*
* This prevents stale registry entries if D-Bus connection fails.
*/
GError *error = NULL;
GDBusConnection *conn = g_bus_get_sync(G_BUS_TYPE_SYSTEM, NULL, &error);
if (conn == NULL) {
FWUPMGR_ERROR("downloadFirmware: D-Bus connect failed: %s\n",
error ? error->message : "unknown");
if (error) g_error_free(error);
return RDKFW_DWNL_FAILED;
}
/* [3] Register callback AFTER D-Bus connection succeeds, BEFORE sending
*
* Register immediately before sending to avoid race condition where
* the daemon responds before we're ready to receive the signal.
*/
if (!internal_dwnl_register_callback(handle, callback)) {
FWUPMGR_ERROR("downloadFirmware: registry full, handle='%s'\n", handle);
g_object_unref(conn);
return RDKFW_DWNL_FAILED;
}
/* [4] Fire-and-forget D-Bus DownloadFirmware method call
*
* Arguments: (ssss)
* s handle — identifies this app to the daemon
* s firmwareName — firmware image filename
* s downloadUrl — override URL or "" for XConf URL
* s TypeOfFirmware — "PCI" | "PDRI" | "PERIPHERAL"
*
* Three trailing NULLs = fire and forget (no reply waited for).
* g_dbus_connection_call() returns immediately.
*/
g_dbus_connection_call(
conn,
DBUS_SERVICE_NAME,
DBUS_OBJECT_PATH,
DBUS_INTERFACE_NAME,
DBUS_METHOD_DOWNLOAD, /* method: DownloadFirmware */
g_variant_new("(ssss)",
handle, /* app's handler_id string */
fwdwnlreq->firmwareName, /* firmware image name */
fwdwnlreq->downloadUrl ? fwdwnlreq->downloadUrl : "", /* override URL or "" */
fwdwnlreq->TypeOfFirmware ? fwdwnlreq->TypeOfFirmware : ""), /* PCI / PDRI / PERIPHERAL */
NULL, /* expected reply type: none */
G_DBUS_CALL_FLAGS_NONE,
DBUS_TIMEOUT_MS,
NULL, /* GCancellable: none */
NULL, /* reply callback: none */
NULL /* user_data: none */
);
g_object_unref(conn);
FWUPMGR_INFO("downloadFirmware: D-Bus call sent, returning SUCCESS. handle='%s'\n",
handle);
/* [4] Return immediately — app is unblocked */
return RDKFW_DWNL_SUCCESS;
}
/* ========================================================================
* UPDATE FIRMWARE PUBLIC API
* ========================================================================
*
* Implements:
* UpdateResult updateFirmware(FirmwareInterfaceHandle handle,
* FwUpdateReq fwupdatereq,
* UpdateCallback callback);
*
* FLOW:
* 1. Validate: handle not NULL/empty, firmwareName not empty,
* TypeOfFirmware not empty, callback not NULL
* 2. Connect to D-Bus (fail early if connection fails)
* 3. Register callback in update registry (AFTER D-Bus connection succeeds)
* 4. Fire UpdateFirmware D-Bus method call to daemon (fire-and-forget)
* 5. Return RDKFW_UPDATE_SUCCESS immediately
*
* [later — fires multiple times as flashing progresses]
* Daemon emits UpdateProgress(progress%, status) signal repeatedly
* → on_update_progress_signal() fires in background thread
* → dispatch_all_update_active() calls every ACTIVE UpdateCallback
* → slot stays ACTIVE until UPDATE_COMPLETED or UPDATE_ERROR
* ======================================================================== */
/**
* @brief Initiate firmware flashing — non-blocking, returns immediately
*
* D-Bus arguments sent to daemon: (sssss)
* s handle — identifies this app
* s firmwareName — image filename to flash
* s LocationOfFirmware — path to image ("" = use device.properties)
* s TypeOfFirmware — "PCI" | "PDRI" | "PERIPHERAL"
* s rebootImmediately — "true" or "false" (daemon expects string)
*
* @param handle Valid FirmwareInterfaceHandle from registerProcess()
* @param fwupdatereq Update request (passed by value, library copies it)
* @param callback Invoked on each UpdateProgress signal
* @return RDKFW_UPDATE_SUCCESS or RDKFW_UPDATE_FAILED
*/
UpdateResult updateFirmware(FirmwareInterfaceHandle handle,
const FwUpdateReq *fwupdatereq,
UpdateCallback callback)
{
/* [1] Validate */
if (handle == NULL || handle[0] == '\0') {
FWUPMGR_ERROR("updateFirmware: invalid handle (NULL or empty)\n");
return RDKFW_UPDATE_FAILED;
}
if (fwupdatereq == NULL) {
FWUPMGR_ERROR("updateFirmware: fwupdatereq is NULL\n");
return RDKFW_UPDATE_FAILED;
}
if (fwupdatereq->firmwareName == NULL) {
FWUPMGR_ERROR("updateFirmware: firmwareName is NULL\n");
return RDKFW_UPDATE_FAILED;
}
if (fwupdatereq->firmwareName[0] == '\0') {
FWUPMGR_ERROR("updateFirmware: firmwareName is empty\n");
return RDKFW_UPDATE_FAILED;
}
if (fwupdatereq->TypeOfFirmware == NULL) {
FWUPMGR_ERROR("updateFirmware: TypeOfFirmware is NULL\n");
return RDKFW_UPDATE_FAILED;
}
if (fwupdatereq->TypeOfFirmware[0] == '\0') {
FWUPMGR_ERROR("updateFirmware: TypeOfFirmware is empty\n");
return RDKFW_UPDATE_FAILED;
}
if (callback == NULL) {
FWUPMGR_ERROR("updateFirmware: callback is NULL\n");
return RDKFW_UPDATE_FAILED;
}
FWUPMGR_INFO("updateFirmware: handle='%s' firmware='%s' type='%s' "
"location='%s' reboot=%s\n",
handle,
fwupdatereq->firmwareName,
fwupdatereq->TypeOfFirmware,
(fwupdatereq->LocationOfFirmware && fwupdatereq->LocationOfFirmware[0])
? fwupdatereq->LocationOfFirmware
: "(use device.properties path)",
fwupdatereq->rebootImmediately ? "yes" : "no");
/* [2] Connect to D-Bus FIRST before registering callback
*
* This prevents stale registry entries if D-Bus connection fails.
*/
GError *error = NULL;
GDBusConnection *conn = g_bus_get_sync(G_BUS_TYPE_SYSTEM, NULL, &error);
if (conn == NULL) {
FWUPMGR_ERROR("updateFirmware: D-Bus connect failed: %s\n",
error ? error->message : "unknown");
if (error) g_error_free(error);
return RDKFW_UPDATE_FAILED;
}
/* [3] Register callback AFTER D-Bus connection succeeds, BEFORE sending
*
* Register immediately before sending to avoid race condition where
* the daemon responds before we're ready to receive the signal.
*/
if (!internal_update_register_callback(handle, callback)) {
FWUPMGR_ERROR("updateFirmware: registry full, handle='%s'\n", handle);
g_object_unref(conn);
return RDKFW_UPDATE_FAILED;
}
/* [4] Fire-and-forget D-Bus UpdateFirmware method call
*
* Arguments: (sssss)
* s handle — app's handler_id string
* s firmwareName — image to flash
* s LocationOfFirmware — path or "" for device.properties default
* s TypeOfFirmware — PCI / PDRI / PERIPHERAL
* s rebootImmediately — "true" or "false" (daemon expects string)
*
* Three trailing NULLs = fire and forget.
*/
g_dbus_connection_call(
conn,
DBUS_SERVICE_NAME,
DBUS_OBJECT_PATH,
DBUS_INTERFACE_NAME,
DBUS_METHOD_UPDATE, /* method: UpdateFirmware */
g_variant_new("(sssss)", /* ✅ 5 strings now! */
handle, /* app's handler_id string */
fwupdatereq->firmwareName, /* image to flash */
fwupdatereq->LocationOfFirmware ? fwupdatereq->LocationOfFirmware : "", /* path or "" */
fwupdatereq->TypeOfFirmware, /* PCI / PDRI / PERIPHERAL */
fwupdatereq->rebootImmediately ? "true" : "false"), /* reboot flag sent to daemon */
NULL, /* expected reply: none */
G_DBUS_CALL_FLAGS_NONE,
DBUS_TIMEOUT_MS,
NULL, /* GCancellable: none */
NULL, /* reply callback: none */
NULL /* user_data: none */
);
g_object_unref(conn);
FWUPMGR_INFO("updateFirmware: D-Bus call sent, returning SUCCESS. "
"handle='%s'\n", handle);
/* [4] Return immediately — app is unblocked */
return RDKFW_UPDATE_SUCCESS;
}