intel-firmware-update: implement ModemReboot version 2

Existing intel firware update doesn't support parameters for customized
reboot for firmware flashing even though the original intention of the
command is for firmware update. This extension is required to add
parameters to control boot mode when the modem firmware is rebooted
for flashing.

This extension will be available only if MBIMEx version is 2 or above.

Updated by Aleksander Morgado according to reviews in
https://gitlab.freedesktop.org/mobile-broadband/libmbim/-/merge_requests/134
diff --git a/data/mbim-service-intel-firmware-update-v2.json b/data/mbim-service-intel-firmware-update-v2.json
new file mode 100644
index 0000000..1e57c11
--- /dev/null
+++ b/data/mbim-service-intel-firmware-update-v2.json
@@ -0,0 +1,17 @@
+[
+  // *********************************************************************************
+  { "type"           : "Service",
+    "name"           : "Intel Firmware Update V2",
+    "mbimex-service" : "Intel Firmware Update",
+    "mbimex-version" : "2.0" },
+
+  // *********************************************************************************
+  { "name"     : "Modem Reboot",
+    "type"     : "Command",
+    "since"    : "1.28",
+    "set"      : [ { "name"          : "BootMode",
+                     "format"        : "guint32",
+                     "public-format" : "MbimIntelBootMode" },
+                   { "name"          : "Timeout",
+                     "format"        : "guint32" } ] }
+]
diff --git a/docs/reference/libmbim-glib/libmbim-glib-common.sections b/docs/reference/libmbim-glib/libmbim-glib-common.sections
index fbb3167..4f90850 100644
--- a/docs/reference/libmbim-glib/libmbim-glib-common.sections
+++ b/docs/reference/libmbim-glib/libmbim-glib-common.sections
@@ -407,6 +407,7 @@
 MbimQuectelRadioSwitchState
 MbimAccessMediaType
 MbimIntelServingCellInfo
+MbimIntelBootMode
 <SUBSECTION Methods>
 mbim_device_type_get_string
 mbim_cellular_class_build_string_from_mask
@@ -497,6 +498,7 @@
 mbim_quectel_radio_switch_state_get_string
 mbim_access_media_type_get_string
 mbim_intel_serving_cell_info_get_string
+mbim_intel_boot_mode_get_string
 <SUBSECTION Private>
 mbim_device_type_build_string_from_mask
 mbim_cellular_class_get_string
@@ -589,6 +591,7 @@
 mbim_quectel_radio_switch_state_build_string_from_mask
 mbim_access_media_type_build_string_from_mask
 mbim_intel_serving_cell_info_build_string_from_mask
+mbim_intel_boot_mode_build_string_from_mask
 <SUBSECTION Standard>
 MBIM_TYPE_ACTIVATION_COMMAND
 MBIM_TYPE_ACTIVATION_STATE
@@ -684,6 +687,7 @@
 MBIM_TYPE_QUECTEL_RADIO_SWITCH_STATE
 MBIM_TYPE_ACCESS_MEDIA_TYPE
 MBIM_TYPE_INTEL_SERVING_CELL_INFO
+MBIM_TYPE_INTEL_BOOT_MODE
 mbim_activation_command_get_type
 mbim_activation_state_get_type
 mbim_auth_protocol_get_type
@@ -778,6 +782,7 @@
 mbim_quectel_radio_switch_state_get_type
 mbim_access_media_type_get_type
 mbim_intel_serving_cell_info_get_type
+mbim_intel_boot_mode_get_type
 </SECTION>
 
 <SECTION>
diff --git a/docs/reference/libmbim-glib/libmbim-glib-docs.xml b/docs/reference/libmbim-glib/libmbim-glib-docs.xml
index 583e02f..8b11b72 100644
--- a/docs/reference/libmbim-glib/libmbim-glib-docs.xml
+++ b/docs/reference/libmbim-glib/libmbim-glib-docs.xml
@@ -101,6 +101,7 @@
   <chapter>
     <title>Intel-defined Services</title>
     <xi:include href="xml/mbim-intel-firmware-update.xml"/>
+    <xi:include href="xml/mbim-intel-firmware-update-v2.xml"/>
     <xi:include href="xml/mbim-intel-thermal-rf.xml"/>
   </chapter>
 
diff --git a/src/libmbim-glib/generated/meson.build b/src/libmbim-glib/generated/meson.build
index 0e19a2f..2ec85ea 100644
--- a/src/libmbim-glib/generated/meson.build
+++ b/src/libmbim-glib/generated/meson.build
@@ -92,7 +92,7 @@
   ['auth'],
   ['basic-connect', 'ms-basic-connect-v2', 'ms-basic-connect-v3'],
   ['dss'],
-  ['intel-firmware-update'],
+  ['intel-firmware-update', 'intel-firmware-update-v2'],
   ['intel-thermal-rf'],
   ['ms-basic-connect-extensions', 'ms-basic-connect-extensions-v2', 'ms-basic-connect-extensions-v3'],
   ['ms-uicc-low-level-access'],
diff --git a/src/libmbim-glib/mbim-enums.h b/src/libmbim-glib/mbim-enums.h
index 5a03492..44c1257 100644
--- a/src/libmbim-glib/mbim-enums.h
+++ b/src/libmbim-glib/mbim-enums.h
@@ -1858,6 +1858,31 @@
     MBIM_INTEL_SERVING_CELL_INFO_RADIO_OFF = 0xFFFFFFFF,
 } MbimIntelServingCellInfo;
 
+/*****************************************************************************/
+/* 'Mbim Intel Boot Mode enums */
+
+/**
+ * MbimIntelBootMode:
+ * @MBIM_INTEL_BOOT_MODE_NORMAL_MODE: Normal boot mode.
+ * @MBIM_INTEL_BOOT_MODE_DOWNLOAD_MODE: Download boot mode.
+ * @MBIM_INTEL_BOOT_MODE_POWER_OFF_DEVICE: Power off device.
+ * @MBIM_INTEL_BOOT_MODE_NON_RESETABLE_REGISTER: Configure non-resetable register without reboot or power off.
+ * @MBIM_INTEL_BOOT_MODE_WITHOUT_REBOOT_POWER_OFF: Configure without reboot power-off.
+ * @MBIM_INTEL_BOOT_MODE_FAST_DOWNLOAD_MODE: Fast boot in download mode.
+ *
+ * Modem intel boot mode.
+ *
+ * Since: 1.28
+ */
+typedef enum { /*< since=1.28 >*/
+    MBIM_INTEL_BOOT_MODE_NORMAL_MODE              = 0,
+    MBIM_INTEL_BOOT_MODE_DOWNLOAD_MODE            = 1,
+    MBIM_INTEL_BOOT_MODE_POWER_OFF_DEVICE         = 2,
+    MBIM_INTEL_BOOT_MODE_NON_RESETABLE_REGISTER   = 3,
+    MBIM_INTEL_BOOT_MODE_WITHOUT_REBOOT_POWER_OFF = 4,
+    MBIM_INTEL_BOOT_MODE_FAST_DOWNLOAD_MODE       = 5,
+} MbimIntelBootMode;
+
 G_END_DECLS
 
 #endif /* _LIBMBIM_GLIB_MBIM_ENUMS_H_ */
diff --git a/src/libmbim-glib/mbim-message.c b/src/libmbim-glib/mbim-message.c
index eb8b71a..9274d35 100644
--- a/src/libmbim-glib/mbim-message.c
+++ b/src/libmbim-glib/mbim-message.c
@@ -1923,7 +1923,15 @@
             fields_printable = __mbim_message_atds_get_printable_fields (self, line_prefix, &inner_error);
             break;
         case MBIM_SERVICE_INTEL_FIRMWARE_UPDATE:
-            fields_printable = __mbim_message_intel_firmware_update_get_printable_fields (self, line_prefix, &inner_error);
+            if (mbimex_version_major < 2)
+                fields_printable = __mbim_message_intel_firmware_update_get_printable_fields (self, line_prefix, &inner_error);
+            else if (mbimex_version_major >= 2) {
+                fields_printable = __mbim_message_intel_firmware_update_v2_get_printable_fields (self, line_prefix, &inner_error);
+                if (g_error_matches (inner_error, MBIM_CORE_ERROR, MBIM_CORE_ERROR_UNSUPPORTED)) {
+                    g_clear_error (&inner_error);
+                    fields_printable = __mbim_message_intel_firmware_update_get_printable_fields (self, line_prefix, &inner_error);
+                }
+            }
             break;
         case MBIM_SERVICE_QDU:
             fields_printable = __mbim_message_qdu_get_printable_fields (self, line_prefix, &inner_error);
diff --git a/src/mbimcli/mbimcli-helpers.h b/src/mbimcli/mbimcli-helpers.h
index cf99bb6..b512005 100644
--- a/src/mbimcli/mbimcli-helpers.h
+++ b/src/mbimcli/mbimcli-helpers.h
@@ -64,7 +64,8 @@
     MBIMCLI_ENUM_LIST_ITEM (MbimDrxCycle,                 drx_cycle,                   "drx cycle")                   \
     MBIMCLI_ENUM_LIST_ITEM (MbimLadnInfo,                 ladn_info,                   "ladn info")                   \
     MBIMCLI_ENUM_LIST_ITEM (MbimDefaultPduActivationHint, default_pdu_activation_hint, "default pdu activation hint") \
-    MBIMCLI_ENUM_LIST_ITEM (MbimAccessMediaType,          access_media_type,           "access media type")
+    MBIMCLI_ENUM_LIST_ITEM (MbimAccessMediaType,          access_media_type,           "access media type")           \
+    MBIMCLI_ENUM_LIST_ITEM (MbimIntelBootMode,            intel_boot_mode,             "intel boot mode")
 
 #define MBIMCLI_ENUM_LIST_ITEM(TYPE,TYPE_UNDERSCORE,DESCR)        \
     gboolean mbimcli_read_## TYPE_UNDERSCORE ##_from_string (const gchar *str, TYPE *out);
diff --git a/src/mbimcli/mbimcli-intel-firmware-update.c b/src/mbimcli/mbimcli-intel-firmware-update.c
index fdd2d28..d42736d 100644
--- a/src/mbimcli/mbimcli-intel-firmware-update.c
+++ b/src/mbimcli/mbimcli-intel-firmware-update.c
@@ -19,21 +19,28 @@
 #include <libmbim-glib.h>
 
 #include "mbimcli.h"
+#include "mbimcli-helpers.h"
 
 /* Context */
 typedef struct {
-    MbimDevice *device;
+    MbimDevice   *device;
     GCancellable *cancellable;
 } Context;
 static Context *ctx;
 
 /* Options */
-static gboolean modem_reboot_flag;
+static gboolean  modem_reboot_set;
+static gchar    *modem_reboot_str;
+
+static gboolean modem_reboot_arg_parse (const gchar  *option_name,
+                                        const gchar  *value,
+                                        gpointer      user_data,
+                                        GError      **error);
 
 static GOptionEntry entries[] = {
-    { "intel-modem-reboot", 0, 0, G_OPTION_ARG_NONE, &modem_reboot_flag,
-      "Reboot modem",
-      NULL
+    { "intel-modem-reboot", 0, G_OPTION_FLAG_OPTIONAL_ARG, G_OPTION_ARG_CALLBACK, G_CALLBACK (modem_reboot_arg_parse),
+      "Reboot modem. Boot mode and timeout arguments only required if MBIMEx >= 2.0.",
+      "[(Boot Mode),(Timeout)]"
     },
     { NULL }
 };
@@ -53,16 +60,27 @@
    return group;
 }
 
+static gboolean
+modem_reboot_arg_parse (const gchar  *option_name,
+                        const gchar  *value,
+                        gpointer      user_data,
+                        GError      **error)
+{
+    modem_reboot_set = TRUE;
+    modem_reboot_str = g_strdup (value);
+    return TRUE;
+}
+
 gboolean
 mbimcli_intel_firmware_update_options_enabled (void)
 {
-    static guint n_actions = 0;
+    static guint    n_actions = 0;
     static gboolean checked = FALSE;
 
     if (checked)
         return !!n_actions;
 
-    n_actions = modem_reboot_flag;
+    n_actions = modem_reboot_set;
 
     if (n_actions > 1) {
         g_printerr ("error: too many Intel Firmware Update Service actions requested\n");
@@ -114,11 +132,44 @@
     shutdown (TRUE);
 }
 
+static gboolean
+modem_reboot_v2_input_parse (const gchar       *str,
+                             MbimIntelBootMode *out_boot_mode,
+                             guint32           *out_timeout)
+{
+    g_auto(GStrv) split = NULL;
+
+    split = g_strsplit (modem_reboot_str, ",", -1);
+
+    if (g_strv_length (split) > 2) {
+        g_printerr ("error: couldn't parse input string, too many arguments\n");
+        return FALSE;
+    }
+
+    if (g_strv_length (split) < 2) {
+        g_printerr ("error: couldn't parse input string, missing arguments\n");
+        return FALSE;
+    }
+
+    if (!mbimcli_read_intel_boot_mode_from_string (split[0], out_boot_mode)) {
+        g_printerr ("error: couldn't read boot mode, wrong value given as input\n");
+        return FALSE;
+    }
+
+    if (!mbimcli_read_uint_from_string (split[1], out_timeout)) {
+        g_printerr ("error: couldn't read timeout value\n");
+        return FALSE;
+    }
+
+    return TRUE;
+}
+
 void
 mbimcli_intel_firmware_update_run (MbimDevice   *device,
                                    GCancellable *cancellable)
 {
     g_autoptr(MbimMessage) request = NULL;
+    g_autoptr(GError)      error = NULL;
 
     /* Initialize context */
     ctx = g_slice_new (Context);
@@ -126,9 +177,30 @@
     ctx->cancellable = cancellable ? g_object_ref (cancellable) : NULL;
 
     /* Request to reboot modem? */
-    if (modem_reboot_flag) {
-        g_debug ("Asynchronously rebooting modem...");
-        request = (mbim_message_intel_firmware_update_modem_reboot_set_new (NULL));
+    if (modem_reboot_set) {
+        if (mbim_device_check_ms_mbimex_version (device, 2, 0)) {
+            MbimIntelBootMode  boot_mode = MBIM_INTEL_BOOT_MODE_NORMAL_MODE;
+            guint32            timeout = 0;
+
+            if (!modem_reboot_v2_input_parse (modem_reboot_str, &boot_mode, &timeout)) {
+                g_printerr ("error: couldn't parse input arguments\n");
+                g_printerr ("error: device in MBIMEx >= 2.0 requires boot mode and timeout arguments.\n");
+                shutdown (FALSE);
+                return;
+            }
+
+            request = mbim_message_intel_firmware_update_v2_modem_reboot_set_new (boot_mode, timeout, NULL);
+        } else {
+            if (modem_reboot_str) {
+                g_printerr ("error: arguments are not expected in MBIMEx < 2.0\n");
+                shutdown (FALSE);
+                return;
+            }
+
+            g_debug ("Asynchronously rebooting modem...");
+            request = mbim_message_intel_firmware_update_modem_reboot_set_new (NULL);
+        }
+
         mbim_device_command (ctx->device,
                              request,
                              10,