aboutsummaryrefslogtreecommitdiff
path: root/src/plugins/intel/mm-port-serial-xmmrpc-xmm7360.c
diff options
context:
space:
mode:
authorThomas Vogt <acc-github@tovotu.de>2024-06-28 15:07:51 +0200
committerThomas Vogt <acc-github@tovotu.de>2025-05-09 07:31:31 +0200
commit4cae0406452ef4621aa7cc73f9d7a54db2ca0fb1 (patch)
treee7331cd0736ab0d005717e76bfb0f3351e6f9b74 /src/plugins/intel/mm-port-serial-xmmrpc-xmm7360.c
parentd165d61a9515061837ac12054d15dbeaeb134adf (diff)
intel: implement support for RPC-powered xmm7360
Signed-off-by: Thomas Vogt <acc-github@tovotu.de>
Diffstat (limited to 'src/plugins/intel/mm-port-serial-xmmrpc-xmm7360.c')
-rw-r--r--src/plugins/intel/mm-port-serial-xmmrpc-xmm7360.c750
1 files changed, 750 insertions, 0 deletions
diff --git a/src/plugins/intel/mm-port-serial-xmmrpc-xmm7360.c b/src/plugins/intel/mm-port-serial-xmmrpc-xmm7360.c
new file mode 100644
index 00000000..96c09180
--- /dev/null
+++ b/src/plugins/intel/mm-port-serial-xmmrpc-xmm7360.c
@@ -0,0 +1,750 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details:
+ *
+ * Copyright (C) 2019 James Wah
+ * Copyright (C) 2020 Marinus Enzinger <marinus@enzingerm.de>
+ * Copyright (C) 2023 Shane Parslow
+ * Copyright (C) 2024 Thomas Vogt
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+
+#include "mm-log-object.h"
+
+#include "mm-port-serial-xmmrpc-xmm7360.h"
+#include "mm-intel-enums-types.h"
+
+G_DEFINE_TYPE (MMPortSerialXmmrpcXmm7360, mm_port_serial_xmmrpc_xmm7360, MM_TYPE_PORT_SERIAL)
+
+struct _MMPortSerialXmmrpcXmm7360Private {
+ GSList *unsolicited_msg_handlers;
+ guint unsolicited_msg_handlers_i;
+};
+
+static void
+xmm7360_rpc_msg_arg_free (Xmm7360RpcMsgArg *arg)
+{
+ if (arg->type == XMM7360_RPC_MSG_ARG_TYPE_STRING) {
+ g_free ((gchar *) arg->value.string);
+ }
+ g_free (arg);
+}
+
+void
+xmm7360_rpc_response_free (Xmm7360RpcResponse *response)
+{
+ if (response) {
+ g_byte_array_free (response->body, TRUE);
+ g_ptr_array_free (response->content, TRUE);
+ g_free (response);
+ }
+}
+
+static gint
+xmm7360_byte_array_read_asn_int (GByteArray *buf, gsize *offset, Xmm7360RpcMsgArg *arg)
+{
+ gint size;
+ gint bytes_read;
+ gint val;
+
+ g_assert (buf->len > *offset + 2);
+
+ /* check ASN start byte and read int size */
+ g_assert (buf->data[(*offset)++] == 0x02);
+ size = buf->data[(*offset)++];
+
+ /* read actual value byte by byte */
+ g_assert (buf->len >= *offset + size);
+ val = 0;
+ for (bytes_read = 0; bytes_read < size; bytes_read++) {
+ val <<= 8;
+ val |= buf->data[(*offset)++];
+ }
+
+ if (arg != NULL) {
+ if (size == 0x01) {
+ arg->type = XMM7360_RPC_MSG_ARG_TYPE_BYTE;
+ arg->value.b = (gint8) val;
+ } else if (size == 0x02) {
+ arg->type = XMM7360_RPC_MSG_ARG_TYPE_SHORT;
+ arg->value.s = (gint16) val;
+ } else {
+ arg->type = XMM7360_RPC_MSG_ARG_TYPE_LONG;
+ arg->value.l = (gint32) val;
+ }
+ }
+
+ return val;
+}
+
+static gchar *
+xmm7360_byte_array_read_string (GByteArray *buf, gsize *offset, gsize *string_len)
+{
+ guchar string_type;
+ gsize string_len_padded;
+ gsize pad_len;
+ gchar *result;
+
+ g_assert (buf->len > *offset + 2);
+
+ string_type = buf->data[(*offset)++];
+ g_assert (string_type == 0x55 || string_type == 0x56 || string_type == 0x57);
+
+ *string_len = buf->data[(*offset)++];
+ if (*string_len & 0x80) {
+ guchar bytelen;
+ guchar i;
+
+ bytelen = *string_len & 0x0f;
+ *string_len = 0;
+ g_assert (bytelen <= 4);
+ g_assert (buf->len > *offset + bytelen);
+ for (i = 0; i < bytelen; i++) {
+ *string_len |= buf->data[(*offset)++] << (i * 8);
+ }
+ }
+
+ if (string_type == 0x56) {
+ /* 0x56 contains 2-byte chars */
+ *string_len <<= 1;
+ } else if (string_type == 0x57) {
+ /* 0x57 contains 4-byte chars */
+ *string_len <<= 2;
+ }
+
+ string_len_padded = (gsize) xmm7360_byte_array_read_asn_int (buf, offset, NULL);
+ pad_len = (gsize) xmm7360_byte_array_read_asn_int (buf, offset, NULL);
+
+ if (string_len_padded > 0) {
+ g_assert (string_len_padded == *string_len + pad_len);
+ } else {
+ string_len_padded = *string_len + pad_len;
+ }
+
+ /* make sure the buffer is large enough */
+ g_assert (buf->len >= *offset + string_len_padded);
+
+ /* copy the result */
+ result = g_malloc (*string_len);
+ memcpy (result, &(buf->data[*offset]), *string_len);
+
+ /* move pointer to the end of the string data */
+ *offset += (*string_len + pad_len);
+
+ return result;
+}
+
+
+void
+xmm7360_byte_array_append_asn_int4 (GByteArray *buf, gint32 value)
+{
+ gint32 value_be = GINT32_TO_BE (value);
+ g_byte_array_append (buf, (const guint8 *) "\x02\x04", 2);
+ g_byte_array_append (buf, (const guint8 *) &value_be, 4);
+}
+
+static void
+xmm7360_byte_array_append_uint8 (GByteArray *buf, gulong val)
+{
+ guint8 _val = (guint8) val;
+ g_byte_array_append (buf, &_val, 1);
+}
+
+static void
+xmm7360_byte_array_append_string (GByteArray *buf,
+ const guint8 *data,
+ gsize data_len,
+ guint data_len_padded)
+{
+ gsize i;
+ gsize pad_len;
+
+ g_assert (data_len_padded >= data_len);
+ pad_len = data_len_padded - data_len;
+
+ /* only support 1-byte element size */
+ xmm7360_byte_array_append_uint8 (buf, 0x55);
+
+ if (data_len < 0x80) {
+ xmm7360_byte_array_append_uint8 (buf, data_len);
+ } else {
+ guchar bytelen = 0x80;
+ /* write dummy first byte (updated later) */
+ xmm7360_byte_array_append_uint8 (buf, bytelen);
+ for (i = data_len; i > 0; i >>= 8) {
+ bytelen++;
+ xmm7360_byte_array_append_uint8 (buf, i & 0xff);
+ }
+ /* update first byte */
+ buf->data[buf->len - 1 - (bytelen & 0x7f)] = bytelen;
+ }
+
+ xmm7360_byte_array_append_asn_int4 (buf, data_len_padded);
+ xmm7360_byte_array_append_asn_int4 (buf, pad_len);
+ g_byte_array_append (buf, data, data_len);
+ for (i = 0; i < pad_len; i++) {
+ xmm7360_byte_array_append_uint8 (buf, 0);
+ }
+}
+
+static GString *
+xmm7360_byte_array_hexlify (GByteArray *buf) {
+ GString *string;
+ gsize i;
+
+ string = g_string_sized_new (2 * buf->len + 1);
+ for (i = 0; i < buf->len; i++) {
+ g_string_append_printf (string, "%02x", buf->data[i]);
+ }
+ g_string_append (string, "");
+
+ /* truncate very long strings to avoid overly verbose logs */
+ if (string->len > 64) {
+ gsize string_len;
+ string_len = string->len;
+ g_string_truncate (string, 48);
+ g_string_append_printf (string, "... (%ld chars)", string_len);
+ }
+
+ return string;
+}
+
+static void
+xmm7360_byte_array_log (gpointer obj, const gchar *prefix, GByteArray *buf)
+{
+ GString *hexlified;
+
+ hexlified = xmm7360_byte_array_hexlify (buf);
+ mm_obj_dbg (obj, "%sb'%s'", prefix ? prefix : "", hexlified->str);
+ g_string_free (hexlified, TRUE);
+}
+
+static GString *
+xmm7360_rpc_args_to_string (GPtrArray *args)
+{
+ guint i;
+ GString *result;
+ Xmm7360RpcMsgArg *arg;
+ gint16 arg_value_short;
+ gint32 arg_value_long;
+
+ result = g_string_new ("");
+
+ for (i = 0; i < args->len; i++) {
+ arg = (Xmm7360RpcMsgArg *) g_ptr_array_index (args, i);
+ if (i > 0) {
+ g_string_append (result, " ");
+ }
+ switch (arg->type) {
+ case XMM7360_RPC_MSG_ARG_TYPE_BYTE:
+ g_string_append_printf (result, "B(0x%02x)", arg->value.b);
+ break;
+ case XMM7360_RPC_MSG_ARG_TYPE_SHORT:
+ arg_value_short = GINT16_TO_BE (arg->value.s);
+ g_string_append_printf (result, "S(0x%04x)", arg_value_short);
+ break;
+ case XMM7360_RPC_MSG_ARG_TYPE_LONG:
+ arg_value_long = GINT32_TO_BE (arg->value.l);
+ g_string_append_printf (result, "L(0x%08x)", arg_value_long);
+ break;
+ case XMM7360_RPC_MSG_ARG_TYPE_STRING:
+ g_string_append_printf (result, "STR(size=%ld)", arg->size);
+ break;
+ case XMM7360_RPC_MSG_ARG_TYPE_UNKNOWN:
+ default:
+ g_assert_not_reached ();
+ }
+ }
+
+ return result;
+}
+
+static void
+xmm7360_rpc_msg_args_log (gpointer obj, const gchar *prefix, GPtrArray *args)
+{
+ GString *args_string;
+
+ args_string = xmm7360_rpc_args_to_string (args);
+
+ /* truncate very long strings to avoid overly verbose logs */
+ if (args_string->len > 64) {
+ g_string_truncate (args_string, 58);
+ g_string_append_printf (args_string, "... (%d args)", args->len);
+ }
+
+ mm_obj_dbg (obj, "%s%s", prefix ? prefix : "", args_string->str);
+ g_string_free (args_string, TRUE);
+}
+
+GByteArray *
+xmm7360_rpc_args_to_byte_array (const Xmm7360RpcMsgArg *args)
+{
+ GByteArray *result;
+ gint16 arg_value_short;
+ gint32 arg_value_long;
+
+ if (args == NULL)
+ return NULL;
+
+ result = g_byte_array_new ();
+ for (; args->type != XMM7360_RPC_MSG_ARG_TYPE_UNKNOWN; args++) {
+ switch (args->type) {
+ case XMM7360_RPC_MSG_ARG_TYPE_BYTE:
+ g_byte_array_append (result, (guint8[]) { 0x02, 0x01 }, 2);
+ g_byte_array_append (result, (guint8 *) &(args->value.b), 1);
+ break;
+ case XMM7360_RPC_MSG_ARG_TYPE_SHORT:
+ arg_value_short = GINT16_TO_BE (args->value.s);
+ g_byte_array_append (result, (guint8[]) { 0x02, 0x02 }, 2);
+ g_byte_array_append (result, (guint8 *) &arg_value_short, 2);
+ break;
+ case XMM7360_RPC_MSG_ARG_TYPE_LONG:
+ arg_value_long = GINT32_TO_BE (args->value.l);
+ g_byte_array_append (result, (guint8[]) { 0x02, 0x04 }, 2);
+ g_byte_array_append (result, (guint8 *) &arg_value_long, 4);
+ break;
+ case XMM7360_RPC_MSG_ARG_TYPE_STRING:
+ xmm7360_byte_array_append_string (result,
+ (const guint8 *) args->value.string,
+ args->size,
+ args->size + args->pad);
+ break;
+ case XMM7360_RPC_MSG_ARG_TYPE_UNKNOWN:
+ default:
+ /* should be unreachable */
+ return NULL;
+ }
+ }
+ return result;
+}
+
+static GByteArray *
+xmm7360_command_to_byte_array (Xmm7360RpcCallId callid,
+ gboolean is_async,
+ GByteArray *body)
+{
+ GByteArray *buf;
+ gint32 tid;
+ gint32 tid_word;
+ gint32 tid_word_be;
+ guint32 total_len;
+
+ buf = g_byte_array_sized_new (22);
+
+ if (body == NULL) {
+ body = g_byte_array_new ();
+ xmm7360_byte_array_append_asn_int4 (body, 0);
+ } else {
+ g_byte_array_ref (body);
+ }
+
+ if (is_async) {
+ tid = 0x11000101;
+ } else {
+ tid = 0;
+ }
+ tid_word = 0x11000100 | tid;
+
+ total_len = body->len + 16;
+ if (tid) {
+ total_len += 6;
+ }
+ g_byte_array_append (buf, (const guint8 *) &total_len, 4);
+ xmm7360_byte_array_append_asn_int4 (buf, total_len);
+ xmm7360_byte_array_append_asn_int4 (buf, callid);
+ tid_word_be = GINT32_TO_BE (tid_word);
+ g_byte_array_append (buf, (const guint8 *) &tid_word_be, 4);
+ if (tid) {
+ xmm7360_byte_array_append_asn_int4 (buf, tid);
+ }
+
+ g_assert (total_len == buf->len + body->len - 4);
+ g_byte_array_append (buf, (const guint8 *) body->data, body->len);
+ g_byte_array_unref (body);
+
+ return buf;
+}
+
+static int
+xmm7360_rpc_msg_body_unpack (GByteArray *buf, GPtrArray *args, GError **error)
+{
+ GError *inner_error = NULL;
+ gsize offset;
+ gchar *str_value;
+ Xmm7360RpcMsgArg *arg;
+
+ g_assert (buf != NULL && args != NULL);
+
+ offset = 0;
+ while (offset < buf->len) {
+ arg = g_new0 (Xmm7360RpcMsgArg, 1);
+ arg->size = 0;
+ switch (buf->data[offset]) {
+ case 0x02:
+ xmm7360_byte_array_read_asn_int (buf, &offset, arg);
+ break;
+ case 0x55:
+ case 0x56:
+ case 0x57:
+ arg->type = XMM7360_RPC_MSG_ARG_TYPE_STRING;
+ str_value = xmm7360_byte_array_read_string (buf, &offset, &arg->size);
+ if (str_value == NULL) {
+ inner_error = g_error_new (MM_CORE_ERROR,
+ MM_CORE_ERROR_FAILED,
+ "Parsing a string failed");
+ goto out;
+ }
+ arg->value.string = str_value;
+ break;
+ default:
+ arg->type = XMM7360_RPC_MSG_ARG_TYPE_UNKNOWN;
+ inner_error = g_error_new (MM_CORE_ERROR,
+ MM_CORE_ERROR_FAILED,
+ "Unknown type 0x%x",
+ buf->data[offset]);
+ goto out;
+ }
+ g_ptr_array_add (args, arg);
+ }
+
+out:
+ if (inner_error) {
+ g_propagate_error (error, inner_error);
+ return -1;
+ }
+
+ return 0;
+}
+
+static Xmm7360RpcResponse *
+parse_response_xmm7360 (GByteArray *buf)
+{
+ GError *error = NULL;
+ Xmm7360RpcResponse *response = NULL;
+ gsize offset;
+ Xmm7360RpcMsgArg *arg;
+
+ /* first 20 bytes are format header:
+ * message size ( int32, 4)
+ * message size (again!) (asn int, 6)
+ * unsolicited message id (asn int, 6)
+ * transmission id ( int32, 4)
+ */
+ g_assert (buf->len > 20);
+
+ offset = 4;
+ if (*(gint32 *) buf->data != xmm7360_byte_array_read_asn_int (buf, &offset, NULL)) {
+ mm_obj_dbg (NULL, "error parsing RPC message: length mismatch");
+ goto err;
+ }
+
+ /* initialize all fields of the response */
+ response = g_new0 (Xmm7360RpcResponse, 1);
+ response->id = GINT32_FROM_BE (*(gint32 *) (buf->data + 16));
+ response->type = XMM7360_RPC_RESPONSE_TYPE_UNKNOWN;
+ response->unsol_id = xmm7360_byte_array_read_asn_int (buf, &offset, NULL);
+ response->body = g_byte_array_new ();
+ response->content = g_ptr_array_new_with_free_func ((GDestroyNotify)xmm7360_rpc_msg_arg_free);
+
+ g_byte_array_append (response->body, &buf->data[20], buf->len - 20);
+ if (xmm7360_rpc_msg_body_unpack (response->body, response->content, &error) != 0) {
+ mm_obj_dbg (NULL, "error parsing RPC message: unpacking failed: %s", error->message);
+ g_error_free (error);
+ goto err;
+ }
+
+ if (response->id == 0x11000100) {
+ response->type = XMM7360_RPC_RESPONSE_TYPE_RESPONSE;
+ } else if ((response->id & 0xffffff00) == 0x11000100) {
+ if (response->unsol_id >= 0x7d0) {
+ response->type = XMM7360_RPC_RESPONSE_TYPE_ASYNC_ACK;
+ } else {
+ response->type = XMM7360_RPC_RESPONSE_TYPE_RESPONSE;
+
+ /* discard first element in content (equals transmission id) */
+ arg = (Xmm7360RpcMsgArg *) g_ptr_array_index (response->content, 0);
+ if (arg->type != XMM7360_RPC_MSG_ARG_TYPE_LONG
+ || (guint32) XMM7360_RPC_MSG_ARG_GET_INT (arg) != response->id) {
+ mm_obj_dbg (NULL, "error parsing RPC message: invalid start of async response body");
+ goto err;
+ }
+ g_ptr_array_remove_index (response->content, 0);
+ g_byte_array_remove_range (response->body, 0, 6);
+ }
+ } else {
+ response->type = XMM7360_RPC_RESPONSE_TYPE_UNSOLICITED;
+ }
+
+ return response;
+
+err:
+ if (response)
+ xmm7360_rpc_response_free (response);
+ return NULL;
+}
+
+static MMPortSerialResponseType
+parse_response (MMPortSerial *port,
+ GByteArray *response_buffer,
+ GByteArray **parsed_response,
+ GError **error)
+{
+ if (!response_buffer->len)
+ return MM_PORT_SERIAL_RESPONSE_NONE;
+
+ *parsed_response = g_byte_array_new ();
+ g_byte_array_append (*parsed_response, response_buffer->data, response_buffer->len);
+
+ /* Fully cleanup the response array, we'll consider the contents we got
+ * as the full reply that the command may expect. */
+ g_byte_array_remove_range (response_buffer, 0, response_buffer->len);
+
+ return MM_PORT_SERIAL_RESPONSE_BUFFER;
+}
+
+static void
+serial_command_ready (MMPortSerial *port,
+ GAsyncResult *res,
+ GTask *task)
+{
+ GByteArray *response_buffer;
+ GError *error = NULL;
+ Xmm7360RpcResponse *response;
+
+ response_buffer = mm_port_serial_command_finish (port, res, &error);
+ if (!response_buffer) {
+ g_task_return_error (task, error);
+ g_object_unref (task);
+ return;
+ }
+
+ response = parse_response_xmm7360 (response_buffer);
+ if (response_buffer->len > 0)
+ g_byte_array_remove_range (response_buffer, 0, response_buffer->len);
+ g_byte_array_unref (response_buffer);
+
+ g_task_return_pointer (task, response, (GDestroyNotify) xmm7360_rpc_response_free);
+ g_object_unref (task);
+}
+
+Xmm7360RpcResponse *
+mm_port_serial_xmmrpc_xmm7360_command_finish (MMPortSerialXmmrpcXmm7360 *self,
+ GAsyncResult *res,
+ GError **error)
+{
+ return g_task_propagate_pointer (G_TASK (res), error);
+}
+
+void
+mm_port_serial_xmmrpc_xmm7360_command (MMPortSerialXmmrpcXmm7360 *self,
+ Xmm7360RpcCallId callid,
+ gboolean is_async,
+ GByteArray *body,
+ guint32 timeout_seconds,
+ gboolean allow_cached,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ g_autoptr(GByteArray) buf = NULL;
+ GTask *task;
+
+ g_return_if_fail (self != NULL);
+ g_return_if_fail (MM_IS_PORT_SERIAL_XMMRPC_XMM7360 (self));
+
+ buf = xmm7360_command_to_byte_array (callid, is_async, body);
+ g_return_if_fail (buf != NULL);
+
+ task = g_task_new (self, NULL, callback, user_data);
+
+ mm_obj_dbg (self, "--> %s%s", is_async ? "(async) " : "", xmm_7360_rpc_call_id_get_string (callid));
+
+ xmm7360_byte_array_log (self, "", buf);
+
+ mm_port_serial_command (MM_PORT_SERIAL (self),
+ buf,
+ timeout_seconds,
+ allow_cached,
+ TRUE, /* raw commands always run next, never queued last */
+ cancellable,
+ (GAsyncReadyCallback)serial_command_ready,
+ task);
+}
+
+/*****************************************************************************/
+
+typedef struct {
+ guint id;
+ MMPortSerialXmmrpcXmm7360UnsolicitedMsgFn callback;
+ gboolean enable;
+ gpointer user_data;
+ GDestroyNotify notify;
+} MMRpcUnsolicitedMsgHandler;
+
+static gint
+unsolicited_msg_handler_cmp (MMRpcUnsolicitedMsgHandler *handler, guint *id)
+{
+ return handler->id == *id;
+}
+
+guint
+mm_port_serial_xmmrpc_xmm7360_add_unsolicited_msg_handler (MMPortSerialXmmrpcXmm7360 *self,
+ MMPortSerialXmmrpcXmm7360UnsolicitedMsgFn callback,
+ gpointer user_data,
+ GDestroyNotify notify)
+{
+ MMRpcUnsolicitedMsgHandler *handler;
+
+ g_return_val_if_fail (MM_IS_PORT_SERIAL_XMMRPC_XMM7360 (self), 0);
+
+ handler = g_slice_new (MMRpcUnsolicitedMsgHandler);
+ handler->id = ++(self->priv->unsolicited_msg_handlers_i);
+ handler->callback = callback;
+ handler->enable = TRUE;
+ handler->user_data = user_data;
+ handler->notify = notify;
+
+ /* The new handler is always PREPENDED */
+ self->priv->unsolicited_msg_handlers = g_slist_prepend (self->priv->unsolicited_msg_handlers, handler);
+
+ return handler->id;
+}
+
+void
+mm_port_serial_xmmrpc_xmm7360_enable_unsolicited_msg_handler (MMPortSerialXmmrpcXmm7360 *self,
+ guint handler_id,
+ gboolean enable)
+{
+ GSList *existing;
+ MMRpcUnsolicitedMsgHandler *handler;
+
+ g_return_if_fail (MM_IS_PORT_SERIAL_XMMRPC_XMM7360 (self));
+
+ existing = g_slist_find_custom (self->priv->unsolicited_msg_handlers,
+ &handler_id,
+ (GCompareFunc)unsolicited_msg_handler_cmp);
+ if (existing) {
+ handler = existing->data;
+ handler->enable = enable;
+ }
+}
+
+static void
+parse_unsolicited (MMPortSerial *port, GByteArray *response_buffer)
+{
+ MMPortSerialXmmrpcXmm7360 *self = MM_PORT_SERIAL_XMMRPC_XMM7360 (port);
+ g_autoptr(Xmm7360RpcResponse) response = NULL;
+ GSList *iter;
+
+ response = parse_response_xmm7360 (response_buffer);
+
+ if (!response) {
+ return;
+ }
+
+ if (response->type == XMM7360_RPC_RESPONSE_TYPE_RESPONSE) {
+ mm_obj_dbg (port, "<-- (response)");
+ xmm7360_byte_array_log (port, "", response->body);
+ xmm7360_rpc_msg_args_log (port, "", response->content);
+ return;
+ }
+
+ if (response->type == XMM7360_RPC_RESPONSE_TYPE_ASYNC_ACK) {
+ /* discard ASYNC_ACK message since the RESPONSE is expected to follow later
+ * empty the buffer to show that the message is dealt with
+ */
+ mm_obj_dbg (port, "<-- (async-ack)");
+ g_byte_array_remove_range (response_buffer, 0, response_buffer->len);
+ return;
+ }
+
+ mm_obj_dbg (port, "<-- (unsolicited) %s",
+ xmm_7360_rpc_unsol_id_get_string (response->unsol_id));
+ xmm7360_byte_array_log (port, "", response->body);
+ xmm7360_rpc_msg_args_log (port, "", response->content);
+
+ for (iter = self->priv->unsolicited_msg_handlers; iter; iter = iter->next) {
+ MMRpcUnsolicitedMsgHandler *handler = (MMRpcUnsolicitedMsgHandler *) iter->data;
+
+ if (!handler->enable)
+ continue;
+
+ if (handler->callback (self, response, handler->user_data)) {
+ /* if successful, empty the buffer to show that the message is dealt with */
+ g_byte_array_remove_range (response_buffer, 0, response_buffer->len);
+ return;
+ }
+ }
+
+ /* unhandled unsolicited message is discarded */
+ g_byte_array_remove_range (response_buffer, 0, response_buffer->len);
+}
+
+/*****************************************************************************/
+
+MMPortSerialXmmrpcXmm7360 *
+mm_port_serial_xmmrpc_xmm7360_new (const char *name)
+{
+ return MM_PORT_SERIAL_XMMRPC_XMM7360 (g_object_new (MM_TYPE_PORT_SERIAL_XMMRPC_XMM7360,
+ MM_PORT_DEVICE, name,
+ MM_PORT_SUBSYS, MM_PORT_SUBSYS_WWAN,
+ MM_PORT_TYPE, MM_PORT_TYPE_XMMRPC,
+ NULL));
+}
+
+static void
+mm_port_serial_xmmrpc_xmm7360_init (MMPortSerialXmmrpcXmm7360 *self)
+{
+ /* Initialize private data */
+ self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self,
+ MM_TYPE_PORT_SERIAL_XMMRPC_XMM7360,
+ MMPortSerialXmmrpcXmm7360Private);
+
+ self->priv->unsolicited_msg_handlers_i = 0;
+ self->priv->unsolicited_msg_handlers = NULL;
+}
+
+static void
+finalize (GObject *object)
+{
+ MMPortSerialXmmrpcXmm7360 *self = MM_PORT_SERIAL_XMMRPC_XMM7360 (object);
+
+ while (self->priv->unsolicited_msg_handlers) {
+ MMRpcUnsolicitedMsgHandler *handler = (MMRpcUnsolicitedMsgHandler *) self->priv->unsolicited_msg_handlers->data;
+
+ if (handler->notify)
+ handler->notify (handler->user_data);
+
+ g_slice_free (MMRpcUnsolicitedMsgHandler, handler);
+ self->priv->unsolicited_msg_handlers = g_slist_delete_link (self->priv->unsolicited_msg_handlers,
+ self->priv->unsolicited_msg_handlers);
+ }
+
+ G_OBJECT_CLASS (mm_port_serial_xmmrpc_xmm7360_parent_class)->finalize (object);
+}
+
+static void
+mm_port_serial_xmmrpc_xmm7360_class_init (MMPortSerialXmmrpcXmm7360Class *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+ MMPortSerialClass *serial_class = MM_PORT_SERIAL_CLASS (klass);
+
+ g_type_class_add_private (object_class, sizeof (MMPortSerialXmmrpcXmm7360Private));
+
+ object_class->finalize = finalize;
+
+ serial_class->parse_response = parse_response;
+ serial_class->parse_unsolicited = parse_unsolicited;
+}