broadband-modem-qmi: implement Firmware interface support using QMI

This commit is contained in:
Aleksander Morgado
2012-10-04 19:08:03 +02:00
parent 1ccf89dbb8
commit 3cf4f3cc1d
+436 -1
View File
@@ -34,6 +34,7 @@
#include "mm-iface-modem-cdma.h"
#include "mm-iface-modem-messaging.h"
#include "mm-iface-modem-location.h"
#include "mm-iface-modem-firmware.h"
#include "mm-sim-qmi.h"
#include "mm-bearer-qmi.h"
#include "mm-sms-qmi.h"
@@ -44,6 +45,7 @@ static void iface_modem_3gpp_ussd_init (MMIfaceModem3gppUssd *iface);
static void iface_modem_cdma_init (MMIfaceModemCdma *iface);
static void iface_modem_messaging_init (MMIfaceModemMessaging *iface);
static void iface_modem_location_init (MMIfaceModemLocation *iface);
static void iface_modem_firmware_init (MMIfaceModemFirmware *iface);
static MMIfaceModemLocation *iface_modem_location_parent;
@@ -53,7 +55,8 @@ G_DEFINE_TYPE_EXTENDED (MMBroadbandModemQmi, mm_broadband_modem_qmi, MM_TYPE_BRO
G_IMPLEMENT_INTERFACE (MM_TYPE_IFACE_MODEM_3GPP_USSD, iface_modem_3gpp_ussd_init)
G_IMPLEMENT_INTERFACE (MM_TYPE_IFACE_MODEM_CDMA, iface_modem_cdma_init)
G_IMPLEMENT_INTERFACE (MM_TYPE_IFACE_MODEM_MESSAGING, iface_modem_messaging_init)
G_IMPLEMENT_INTERFACE (MM_TYPE_IFACE_MODEM_LOCATION, iface_modem_location_init))
G_IMPLEMENT_INTERFACE (MM_TYPE_IFACE_MODEM_LOCATION, iface_modem_location_init)
G_IMPLEMENT_INTERFACE (MM_TYPE_IFACE_MODEM_FIRMWARE, iface_modem_firmware_init))
struct _MMBroadbandModemQmiPrivate {
/* Cached device IDs, retrieved by the modem interface when loading device
@@ -88,6 +91,10 @@ struct _MMBroadbandModemQmiPrivate {
/* Location helpers */
MMModemLocationSource enabled_sources;
guint location_event_report_indication_id;
/* Firmware helpers */
GList *firmware_list;
MMFirmwareProperties *current_firmware;
};
/*****************************************************************************/
@@ -6044,6 +6051,409 @@ enable_location_gathering (MMIfaceModemLocation *self,
ctx);
}
/*****************************************************************************/
/* Check firmware support (Firmware interface) */
typedef struct {
gchar *build_id;
GArray *modem_unique_id;
GArray *pri_unique_id;
gboolean current;
} FirmwarePair;
static void
firmware_pair_free (FirmwarePair *pair)
{
g_free (pair->build_id);
g_array_unref (pair->modem_unique_id);
g_array_unref (pair->pri_unique_id);
g_slice_free (FirmwarePair, pair);
}
typedef struct {
MMBroadbandModemQmi *self;
QmiClientDms *client;
GSimpleAsyncResult *result;
GList *pairs;
GList *l;
} FirmwareCheckSupportContext;
static void
firmware_check_support_context_complete_and_free (FirmwareCheckSupportContext *ctx)
{
g_simple_async_result_complete (ctx->result);
g_object_unref (ctx->result);
g_list_free_full (ctx->pairs, (GDestroyNotify)firmware_pair_free);
g_object_unref (ctx->self);
g_object_unref (ctx->client);
g_slice_free (FirmwareCheckSupportContext, ctx);
}
static gboolean
firmware_check_support_finish (MMIfaceModemFirmware *self,
GAsyncResult *res,
GError **error)
{
/* Never fails, just says TRUE or FALSE */
return g_simple_async_result_get_op_res_gboolean (G_SIMPLE_ASYNC_RESULT (res));
}
static void get_next_image_info (FirmwareCheckSupportContext *ctx);
static void
get_pri_image_info_ready (QmiClientDms *client,
GAsyncResult *res,
FirmwareCheckSupportContext *ctx)
{
QmiMessageDmsGetStoredImageInfoOutput *output;
GError *error = NULL;
FirmwarePair *current;
current = (FirmwarePair *)ctx->l->data;
output = qmi_client_dms_get_stored_image_info_finish (client, res, &error);
if (!output ||
!qmi_message_dms_get_stored_image_info_output_get_result (output, &error)) {
mm_warn ("Couldn't get detailed info for PRI image with build ID '%s': %s",
current->build_id,
error->message);
g_error_free (error);
} else {
gchar *unique_id_str;
MMFirmwareProperties *firmware;
firmware = mm_firmware_properties_new (MM_FIRMWARE_IMAGE_TYPE_GOBI,
current->build_id);
unique_id_str = mm_utils_bin2hexstr ((const guint8 *)current->pri_unique_id->data,
current->pri_unique_id->len);
mm_firmware_properties_set_gobi_pri_unique_id (firmware, unique_id_str);
g_free (unique_id_str);
unique_id_str = mm_utils_bin2hexstr ((const guint8 *)current->modem_unique_id->data,
current->modem_unique_id->len);
mm_firmware_properties_set_gobi_modem_unique_id (firmware, unique_id_str);
g_free (unique_id_str);
/* Boot version (optional) */
{
guint16 boot_major_version;
guint16 boot_minor_version;
if (qmi_message_dms_get_stored_image_info_output_get_boot_version (
output,
&boot_major_version,
&boot_minor_version,
NULL)) {
gchar *aux;
aux = g_strdup_printf ("%u.%u", boot_major_version, boot_minor_version);
mm_firmware_properties_set_gobi_boot_version (firmware, aux);
g_free (aux);
}
}
/* PRI version (optional) */
{
guint32 pri_version;
const gchar *pri_info;
if (qmi_message_dms_get_stored_image_info_output_get_pri_version (
output,
&pri_version,
&pri_info,
NULL)) {
gchar *aux;
aux = g_strdup_printf ("%u", pri_version);
mm_firmware_properties_set_gobi_pri_version (firmware, aux);
g_free (aux);
mm_firmware_properties_set_gobi_pri_info (firmware, pri_info);
}
}
/* Add firmware image to our internal list */
ctx->self->priv->firmware_list = g_list_append (ctx->self->priv->firmware_list,
firmware);
/* If this is is also the current image running, keep it */
if (current->current) {
if (ctx->self->priv->current_firmware)
mm_warn ("A current firmware is already set (%s), not setting '%s' as current",
mm_firmware_properties_get_unique_id (ctx->self->priv->current_firmware),
current->build_id);
else
ctx->self->priv->current_firmware = g_object_ref (firmware);
}
qmi_message_dms_get_stored_image_info_output_unref (output);
}
/* Go on to the next one */
ctx->l = g_list_next (ctx->l);
get_next_image_info (ctx);
}
static void
get_next_image_info (FirmwareCheckSupportContext *ctx)
{
QmiMessageDmsGetStoredImageInfoInputImage image_id;
QmiMessageDmsGetStoredImageInfoInput *input;
FirmwarePair *current;
if (!ctx->l) {
/* We're done */
if (!ctx->self->priv->firmware_list) {
mm_warn ("No valid firmware images listed. "
"Assuming firmware unsupported.");
g_simple_async_result_set_op_res_gboolean (ctx->result, FALSE);
} else
g_simple_async_result_set_op_res_gboolean (ctx->result, TRUE);
firmware_check_support_context_complete_and_free (ctx);
return;
}
current = (FirmwarePair *)ctx->l->data;
/* Query PRI image info */
image_id.type = QMI_DMS_FIRMWARE_IMAGE_TYPE_PRI;
image_id.unique_id = current->pri_unique_id;
image_id.build_id = current->build_id;
input = qmi_message_dms_get_stored_image_info_input_new ();
qmi_message_dms_get_stored_image_info_input_set_image (input, &image_id, NULL);
qmi_client_dms_get_stored_image_info (ctx->client,
input,
10,
NULL,
(GAsyncReadyCallback)get_pri_image_info_ready,
ctx);
qmi_message_dms_get_stored_image_info_input_unref (input);
}
static void
list_stored_images_ready (QmiClientDms *client,
GAsyncResult *res,
FirmwareCheckSupportContext *ctx)
{
GArray *array;
gint pri_id;
gint modem_id;
guint i;
guint j;
QmiMessageDmsListStoredImagesOutputListImage *image_pri;
QmiMessageDmsListStoredImagesOutputListImage *image_modem;
QmiMessageDmsListStoredImagesOutput *output;
output = qmi_client_dms_list_stored_images_finish (client, res, NULL);
if (!output ||
!qmi_message_dms_list_stored_images_output_get_result (output, NULL)) {
/* Assume firmware unsupported */
g_simple_async_result_set_op_res_gboolean (ctx->result, FALSE);
firmware_check_support_context_complete_and_free (ctx);
if (output)
qmi_message_dms_list_stored_images_output_unref (output);
return;
}
qmi_message_dms_list_stored_images_output_get_list (
output,
&array,
NULL);
/* Find which index corresponds to each image type */
pri_id = -1;
modem_id = -1;
for (i = 0; i < array->len; i++) {
QmiMessageDmsListStoredImagesOutputListImage *image;
image = &g_array_index (array,
QmiMessageDmsListStoredImagesOutputListImage,
i);
switch (image->type) {
case QMI_DMS_FIRMWARE_IMAGE_TYPE_PRI:
if (pri_id != -1)
mm_warn ("Multiple array elements found with PRI type");
else
pri_id = (gint)i;
break;
case QMI_DMS_FIRMWARE_IMAGE_TYPE_MODEM:
if (modem_id != -1)
mm_warn ("Multiple array elements found with MODEM type");
else
modem_id = (gint)i;
break;
default:
break;
}
}
if (pri_id < 0 || modem_id < 0) {
mm_warn ("We need both PRI (%s) and MODEM (%s) images. "
"Assuming firmware unsupported.",
pri_id < 0 ? "not found" : "found",
modem_id < 0 ? "not found" : "found");
g_simple_async_result_set_op_res_gboolean (ctx->result, FALSE);
firmware_check_support_context_complete_and_free (ctx);
qmi_message_dms_list_stored_images_output_unref (output);
return;
}
/* Loop PRI images and try to find a pairing MODEM image with same boot ID */
image_pri = &g_array_index (array,
QmiMessageDmsListStoredImagesOutputListImage,
pri_id);
image_modem = &g_array_index (array,
QmiMessageDmsListStoredImagesOutputListImage,
modem_id);
for (i = 0; i < image_pri->sublist->len; i++) {
QmiMessageDmsListStoredImagesOutputListImageSublistSublistElement *subimage_pri;
subimage_pri = &g_array_index (image_pri->sublist,
QmiMessageDmsListStoredImagesOutputListImageSublistSublistElement,
i);
for (j = 0; j < image_modem->sublist->len; j++) {
QmiMessageDmsListStoredImagesOutputListImageSublistSublistElement *subimage_modem;
subimage_modem = &g_array_index (image_modem->sublist,
QmiMessageDmsListStoredImagesOutputListImageSublistSublistElement,
j);
if (g_str_equal (subimage_pri->build_id, subimage_modem->build_id)) {
FirmwarePair *pair;
mm_dbg ("Found pairing PRI+MODEM images with build ID '%s'", subimage_pri->build_id);
pair = g_slice_new (FirmwarePair);
pair->build_id = g_strdup (subimage_pri->build_id);
pair->modem_unique_id = g_array_ref (subimage_modem->unique_id);
pair->pri_unique_id = g_array_ref (subimage_pri->unique_id);
pair->current = (image_pri->index_of_running_image == i ? TRUE : FALSE);
ctx->pairs = g_list_append (ctx->pairs, pair);
break;
}
}
if (j == image_modem->sublist->len)
mm_dbg ("Pairing for PRI image with build ID '%s' not found", subimage_pri->build_id);
}
if (!ctx->pairs) {
mm_warn ("No valid PRI+MODEM pairs found. "
"Assuming firmware unsupported.");
g_simple_async_result_set_op_res_gboolean (ctx->result, FALSE);
firmware_check_support_context_complete_and_free (ctx);
qmi_message_dms_list_stored_images_output_unref (output);
return;
}
/* Firmware is supported; now keep on loading info for each image and cache it */
qmi_message_dms_list_stored_images_output_unref (output);
ctx->l = ctx->pairs;
get_next_image_info (ctx);
}
static void
firmware_check_support (MMIfaceModemFirmware *self,
GAsyncReadyCallback callback,
gpointer user_data)
{
FirmwareCheckSupportContext *ctx;
QmiClient *client = NULL;
if (!ensure_qmi_client (MM_BROADBAND_MODEM_QMI (self),
QMI_SERVICE_DMS, &client,
callback, user_data))
return;
ctx = g_slice_new0 (FirmwareCheckSupportContext);
ctx->self = g_object_ref (self);
ctx->client = g_object_ref (client);
ctx->result = g_simple_async_result_new (G_OBJECT (self),
callback,
user_data,
firmware_check_support);
mm_dbg ("loading firmware images...");
qmi_client_dms_list_stored_images (QMI_CLIENT_DMS (client),
NULL,
10,
NULL,
(GAsyncReadyCallback)list_stored_images_ready,
ctx);
}
/*****************************************************************************/
/* Load firmware list (Firmware interface) */
static GList *
firmware_load_list_finish (MMIfaceModemFirmware *self,
GAsyncResult *res,
GError **error)
{
return (GList *)g_simple_async_result_get_op_res_gpointer (G_SIMPLE_ASYNC_RESULT (res));
}
static void
firmware_load_list (MMIfaceModemFirmware *_self,
GAsyncReadyCallback callback,
gpointer user_data)
{
MMBroadbandModemQmi *self = MM_BROADBAND_MODEM_QMI (_self);
GSimpleAsyncResult *result;
GList *dup;
result = g_simple_async_result_new (G_OBJECT (self),
callback,
user_data,
firmware_load_list);
/* We'll return the new list of new references we create here */
dup = g_list_copy (self->priv->firmware_list);
g_list_foreach (dup, (GFunc)g_object_ref, NULL);
g_simple_async_result_set_op_res_gpointer (result, dup, NULL);
g_simple_async_result_complete_in_idle (result);
g_object_unref (result);
}
/*****************************************************************************/
/* Load current firmware (Firmware interface) */
static MMFirmwareProperties *
firmware_load_current_finish (MMIfaceModemFirmware *self,
GAsyncResult *res,
GError **error)
{
return (MMFirmwareProperties *)g_simple_async_result_get_op_res_gpointer (G_SIMPLE_ASYNC_RESULT (res));
}
static void
firmware_load_current (MMIfaceModemFirmware *_self,
GAsyncReadyCallback callback,
gpointer user_data)
{
MMBroadbandModemQmi *self = MM_BROADBAND_MODEM_QMI (_self);
GSimpleAsyncResult *result;
result = g_simple_async_result_new (G_OBJECT (self),
callback,
user_data,
firmware_load_current);
/* We'll return the reference we create here */
g_simple_async_result_set_op_res_gpointer (
result,
self->priv->current_firmware ? g_object_ref (self->priv->current_firmware) : NULL,
NULL);
g_simple_async_result_complete_in_idle (result);
g_object_unref (result);
}
/*****************************************************************************/
/* First initialization step */
@@ -6248,6 +6658,19 @@ finalize (GObject *object)
G_OBJECT_CLASS (mm_broadband_modem_qmi_parent_class)->finalize (object);
}
static void
dispose (GObject *object)
{
MMBroadbandModemQmi *self = MM_BROADBAND_MODEM_QMI (object);
g_list_free_full (self->priv->firmware_list, (GDestroyNotify)g_object_unref);
self->priv->firmware_list = NULL;
g_clear_object (&self->priv->current_firmware);
G_OBJECT_CLASS (mm_broadband_modem_qmi_parent_class)->dispose (object);
}
static void
iface_modem_init (MMIfaceModem *iface)
{
@@ -6422,6 +6845,17 @@ iface_modem_location_init (MMIfaceModemLocation *iface)
iface->disable_location_gathering_finish = disable_location_gathering_finish;
}
static void
iface_modem_firmware_init (MMIfaceModemFirmware *iface)
{
iface->check_support = firmware_check_support;
iface->check_support_finish = firmware_check_support_finish;
iface->load_list = firmware_load_list;
iface->load_list_finish = firmware_load_list_finish;
iface->load_current = firmware_load_current;
iface->load_current_finish = firmware_load_current_finish;
}
static void
mm_broadband_modem_qmi_class_init (MMBroadbandModemQmiClass *klass)
{
@@ -6431,6 +6865,7 @@ mm_broadband_modem_qmi_class_init (MMBroadbandModemQmiClass *klass)
g_type_class_add_private (object_class, sizeof (MMBroadbandModemQmiPrivate));
object_class->finalize = finalize;
object_class->dispose = dispose;
broadband_modem_class->initialization_started = initialization_started;
broadband_modem_class->initialization_started_finish = initialization_started_finish;