diff options
author | Thomas Vogt <acc-github@tovotu.de> | 2024-06-28 15:07:51 +0200 |
---|---|---|
committer | Thomas Vogt <acc-github@tovotu.de> | 2025-05-09 07:31:31 +0200 |
commit | 4cae0406452ef4621aa7cc73f9d7a54db2ca0fb1 (patch) | |
tree | e7331cd0736ab0d005717e76bfb0f3351e6f9b74 | |
parent | d165d61a9515061837ac12054d15dbeaeb134adf (diff) |
intel: implement support for RPC-powered xmm7360
Signed-off-by: Thomas Vogt <acc-github@tovotu.de>
l--------- | build-aux/templates/mm-intel-enums-types.c.template | 1 | ||||
l--------- | build-aux/templates/mm-intel-enums-types.h.template | 1 | ||||
-rw-r--r-- | data/dispatcher-fcc-unlock/8086:7360 | 131 | ||||
-rw-r--r-- | data/dispatcher-fcc-unlock/meson.build | 1 | ||||
-rw-r--r-- | src/plugins/intel/mm-bearer-xmm7360.c | 766 | ||||
-rw-r--r-- | src/plugins/intel/mm-bearer-xmm7360.h | 56 | ||||
-rw-r--r-- | src/plugins/intel/mm-broadband-modem-xmm7360-rpc.c | 577 | ||||
-rw-r--r-- | src/plugins/intel/mm-broadband-modem-xmm7360-rpc.h | 173 | ||||
-rw-r--r-- | src/plugins/intel/mm-broadband-modem-xmm7360.c | 1229 | ||||
-rw-r--r-- | src/plugins/intel/mm-broadband-modem-xmm7360.h | 71 | ||||
-rw-r--r-- | src/plugins/intel/mm-plugin-intel.c | 16 | ||||
-rw-r--r-- | src/plugins/intel/mm-port-serial-xmmrpc-xmm7360.c | 750 | ||||
-rw-r--r-- | src/plugins/intel/mm-port-serial-xmmrpc-xmm7360.h | 590 | ||||
-rw-r--r-- | src/plugins/intel/mm-sim-xmm7360.c | 445 | ||||
-rw-r--r-- | src/plugins/intel/mm-sim-xmm7360.h | 68 | ||||
-rw-r--r-- | src/plugins/meson.build | 40 |
16 files changed, 4909 insertions, 6 deletions
diff --git a/build-aux/templates/mm-intel-enums-types.c.template b/build-aux/templates/mm-intel-enums-types.c.template new file mode 120000 index 00000000..6a247265 --- /dev/null +++ b/build-aux/templates/mm-intel-enums-types.c.template @@ -0,0 +1 @@ +mm-enumflags-types.c.template
\ No newline at end of file diff --git a/build-aux/templates/mm-intel-enums-types.h.template b/build-aux/templates/mm-intel-enums-types.h.template new file mode 120000 index 00000000..7fada6bc --- /dev/null +++ b/build-aux/templates/mm-intel-enums-types.h.template @@ -0,0 +1 @@ +mm-enumflags-types.h.template
\ No newline at end of file diff --git a/data/dispatcher-fcc-unlock/8086:7360 b/data/dispatcher-fcc-unlock/8086:7360 new file mode 100644 index 00000000..d6116dd8 --- /dev/null +++ b/data/dispatcher-fcc-unlock/8086:7360 @@ -0,0 +1,131 @@ +#!/bin/bash + +# SPDX-License-Identifier: CC0-1.0 +# 2024 Thomas Vogt +# +# RPC-powered Intel XMM7360 (8086:7360) FCC unlock + +if [[ "$FCC_UNLOCK_DEBUG_LOG" == '1' ]]; then + exec 3>&1 4>&2 + trap 'exec 2>&4 1>&3' 0 1 2 3 + exec 1>>/var/log/mm-xmm7360-fcc.log 2>&1 +fi + +# require program name and at least 2 arguments +[ $# -lt 2 ] && exit 1 + +# first argument is DBus path, not needed here +shift + +# second and next arguments are control port names +for PORT in "$@"; do + # support for XMM7360 has been added in 5.18 + # match port type, assuming Linux 5.14 and newer + grep -q XMMRPC "/sys/class/wwan/$PORT/type" 2>/dev/null && { + XMMRPC_PORT=$PORT + break + } +done + +# fail if no XMMRPC port exposed +[ -n "$XMMRPC_PORT" ] || exit 2 + +DEVICE=/dev/${XMMRPC_PORT} + +log() { + echo "$1" +} + +error() { + echo "$1" >&2 +} + +reverseHexEndianness() { + num="$1" + printf "%s" "${num:6:2}${num:4:2}${num:2:2}${num:0:2}" +} + +littleEndianToDec() { + printf "%d" "0x$1" +} + +bigEndianToDec() { + littleEndianToDec "$(reverseHexEndianness "$1")" +} + +readNBytesAsHex() { + data=$(head "-c$1" <&99 | xxd -p -c0 | tr -d '\n') + printf "%s" "$data" +} + +writeHexAsBinary() { + printf "%s" "$1" | xxd -r -p >&99 +} + +readResponseAsHex() { + length_hex=$(readNBytesAsHex 4) + length=$(bigEndianToDec "$length_hex") + content=$(readNBytesAsHex $length) + printf "%s" "$length_hex$content" +} + +rpc_command() { + exec 99<>"$DEVICE" + writeHexAsBinary $1 + answer=$(readResponseAsHex) # ignore "async-ack" response + answer=$(readResponseAsHex) + printf "%s" "$answer" + exec 99>&- +} + +VENDOR_ID_HASH="3df8c719" + +for i in {1..3}; do + log "Attempt #${i} to unlock WWAN modem" + + log "Requesting FCC lock state from modem" + # --> (async) csi-fcc-lock-query-req + RESP=$(rpc_command "1c00000002040000001c02040000018e11000101020411000101020400000000") + MODE=$(littleEndianToDec "${RESP: -8}") + STATE=$(littleEndianToDec "${RESP: -20:8}") + + log "Got response from modem: state=$STATE, mode=$MODE" + + if [ "$MODE" = "0" ]; then + log "FCC lock is deactivated, nothing to do." + exit 0 + fi + + if [ "$STATE" != "0" ]; then + log "FCC already unlocked, nothing to do." + exit 0 + fi + + log "Requesting challenge from modem" + # --> (async) csi-fcc-lock-gen-challenge-req + RESP=$(rpc_command "1c00000002040000001c02040000019011000101020411000101020400000000") + HEX_CHALLENGE=$(printf "%s" "${RESP: -8}") + + log "Got challenge from modem: $HEX_CHALLENGE" + REVERSE_HEX_CHALLENGE=$(reverseHexEndianness "${HEX_CHALLENGE}") + COMBINED_CHALLENGE="${REVERSE_HEX_CHALLENGE}${VENDOR_ID_HASH}" + RESPONSE_HASH=$(printf "%s" "$COMBINED_CHALLENGE" | xxd -r -p | sha256sum | cut -d ' ' -f 1) + REVERSED_RESPONSE=$(reverseHexEndianness "${RESPONSE_HASH:0:8}") + + log "Sending hash to modem: $REVERSED_RESPONSE" + # --> (async) csi-fcc-lock-ver-challenge-req + RESP=$(rpc_command "1c00000002040000001c020400000192110001010204110001010204${REVERSED_RESPONSE}") + UNLOCK_RESPONSE=$(littleEndianToDec "${RESP: -8}") + + if [ "$UNLOCK_RESPONSE" = "0" ]; then + error "Unlock failed." + else + log "FCC unlock successful (response $UNLOCK_RESPONSE)" + exit 0 + fi + + sleep 0.5 +done + +exit 2 + diff --git a/data/dispatcher-fcc-unlock/meson.build b/data/dispatcher-fcc-unlock/meson.build index b834dd56..b82889cd 100644 --- a/data/dispatcher-fcc-unlock/meson.build +++ b/data/dispatcher-fcc-unlock/meson.build @@ -15,6 +15,7 @@ examples = files( '1199', '14c3', '2c7c', + '8086:7360', ) install_data( diff --git a/src/plugins/intel/mm-bearer-xmm7360.c b/src/plugins/intel/mm-bearer-xmm7360.c new file mode 100644 index 00000000..10dd2c26 --- /dev/null +++ b/src/plugins/intel/mm-bearer-xmm7360.c @@ -0,0 +1,766 @@ +/* -*- 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 <ModemManager.h> +#define _LIBMM_INSIDE_MM +#include <libmm-glib.h> + +#include "mm-log-object.h" + +#include "mm-bearer-xmm7360.h" +#include "mm-broadband-modem-xmm7360.h" +#include "mm-broadband-modem-xmm7360-rpc.h" +#include "mm-port-serial-xmmrpc-xmm7360.h" + +G_DEFINE_TYPE (MMBearerXmm7360, mm_bearer_xmm7360, MM_TYPE_BASE_BEARER) + +struct _MMBearerXmm7360Private { + gboolean is_connected; +}; + +/*****************************************************************************/ +/* Connect */ + +typedef struct { + MMPort *data; + MMPortSerialXmmrpcXmm7360 *port; + guint unsol_handler_id; + gboolean is_attach_allowed; + gboolean is_attached; + guint attach_attempts; + guint attach_allowed_timeout_id; + Xmm7360RpcResponse *ps_connect_response; + GInetAddress *ip; + GPtrArray *dns; +} ConnectContext; + +static void +connect_context_free (ConnectContext *ctx) +{ + if (ctx->ps_connect_response) + xmm7360_rpc_response_free (ctx->ps_connect_response); + if (ctx->attach_allowed_timeout_id) + g_source_remove (ctx->attach_allowed_timeout_id); + if (ctx->unsol_handler_id) { + mm_port_serial_xmmrpc_xmm7360_enable_unsolicited_msg_handler ( + ctx->port, + ctx->unsol_handler_id, + FALSE); + } + g_clear_object (&ctx->ip); + if (ctx->dns) + g_ptr_array_free (ctx->dns, TRUE); + mm_port_serial_close (MM_PORT_SERIAL (ctx->port)); + g_clear_object (&ctx->port); + g_clear_object (&ctx->data); + g_slice_free (ConnectContext, ctx); +} + +static MMBearerConnectResult * +connect_finish (MMBaseBearer *self, + GAsyncResult *res, + GError **error) +{ + return g_task_propagate_pointer (G_TASK (res), error); +} + +static void +xmm7360_connect_apn_ready (MMBroadbandModemXmm7360 *modem, + GAsyncResult *res, + GTask *task) +{ + GError *error = NULL; + MMBearerXmm7360 *self; + MMBearerConnectResult *connect_result; + + self = g_task_get_source_object (task); + + connect_result = g_task_propagate_pointer (G_TASK (res), &error); + if (error) { + g_task_return_error (task, error); + } else { + self->priv->is_connected = TRUE; + g_task_return_pointer (task, + connect_result, + (GDestroyNotify)mm_bearer_connect_result_unref); + } + g_object_unref (task); +} + +static void +ps_connect_setup_ready (MMBroadbandModemXmm7360 *modem, + GAsyncResult *res, + GTask *task) +{ + GError *error = NULL; + ConnectContext *ctx; + g_autoptr(Xmm7360RpcResponse) response = NULL; + g_autoptr(MMBearerIpConfig) ip_config = NULL; + g_autofree gchar *ipaddr = NULL; + g_auto(GStrv) dnsaddrs = NULL; + GInetAddress *dns; + guint i; + guint n; + + ctx = g_task_get_task_data (task); + + response = mm_broadband_modem_xmm7360_rpc_command_finish (modem, res, &error); + if (error) { + g_task_return_error (task, error); + g_object_unref (task); + return; + } + + /* we get static IPs to set */ + ip_config = mm_bearer_ip_config_new (); + ipaddr = g_inet_address_to_string (ctx->ip); + dnsaddrs = g_new0 (gchar *, ctx->dns->len + 1); + for (i = 0, n = 0; i < ctx->dns->len; i++) { + dns = g_ptr_array_index (ctx->dns, i); + if (g_inet_address_get_family (dns) == G_SOCKET_FAMILY_IPV4 + && !g_inet_address_get_is_any (dns)) { + dnsaddrs[n++] = g_inet_address_to_string (dns); + } + } + mm_bearer_ip_config_set_method (ip_config, MM_BEARER_IP_METHOD_STATIC); + mm_bearer_ip_config_set_address (ip_config, ipaddr); + mm_bearer_ip_config_set_dns (ip_config, (const gchar **)dnsaddrs); + + g_task_return_pointer (task, + mm_bearer_connect_result_new (ctx->data, ip_config, NULL), + (GDestroyNotify)mm_bearer_connect_result_unref); + g_object_unref (task); +} + +static void +ps_connect_to_datachannel_ready (MMBroadbandModemXmm7360 *modem, + GAsyncResult *res, + GTask *task) +{ + GError *error = NULL; + ConnectContext *ctx; + g_autoptr(Xmm7360RpcResponse) response = NULL; + g_autoptr(GByteArray) connect_setup_body = NULL; + + ctx = g_task_get_task_data (task); + + response = mm_broadband_modem_xmm7360_rpc_command_finish (modem, res, &error); + if (error) { + g_task_return_error (task, error); + g_object_unref (task); + return; + } + + connect_setup_body = g_byte_array_new (); + g_byte_array_append (connect_setup_body, + ctx->ps_connect_response->body->data, + ctx->ps_connect_response->body->len - 6); + g_byte_array_append (connect_setup_body, + response->body->data, + response->body->len); + xmm7360_byte_array_append_asn_int4 (connect_setup_body, 0); + + mm_broadband_modem_xmm7360_rpc_command_full (modem, + ctx->port, + XMM7360_RPC_CALL_UTA_RPC_PS_CONNECT_SETUP_REQ, + FALSE, + connect_setup_body, + 3, + FALSE, + NULL, /* cancellable */ + (GAsyncReadyCallback)ps_connect_setup_ready, + task); +} + +static GByteArray * +pack_uta_rpc_ps_connect_to_datachannel_req (void) +{ + static const Xmm7360RpcMsgArg args[] = { + /* size is length of string + null byte */ + { XMM7360_RPC_MSG_ARG_TYPE_STRING, { .string = "/sioscc/PCIE/IOSM/IPS/0" }, 24 }, + { XMM7360_RPC_MSG_ARG_TYPE_UNKNOWN }, + }; + return xmm7360_rpc_args_to_byte_array (args); +} + +static void +ps_connect_ready (MMBroadbandModemXmm7360 *modem, + GAsyncResult *res, + GTask *task) +{ + GError *error = NULL; + ConnectContext *ctx; + Xmm7360RpcResponse *response = NULL; + g_autoptr(GByteArray) body = NULL; + + ctx = g_task_get_task_data (task); + + response = mm_broadband_modem_xmm7360_rpc_command_finish (modem, res, &error); + if (error) { + g_task_return_error (task, error); + g_object_unref (task); + xmm7360_rpc_response_free (response); + return; + } + + ctx->ps_connect_response = response; + + body = pack_uta_rpc_ps_connect_to_datachannel_req (); + + mm_broadband_modem_xmm7360_rpc_command_full (modem, + ctx->port, + XMM7360_RPC_CALL_UTA_RPC_PS_CONNECT_TO_DATACHANNEL_REQ, + FALSE, + body, + 3, + FALSE, + NULL, /* cancellable */ + (GAsyncReadyCallback)ps_connect_to_datachannel_ready, + task); +} + +static GByteArray * +pack_uta_ms_call_ps_connect_req (void) +{ + static const Xmm7360RpcMsgArg args[] = { + { XMM7360_RPC_MSG_ARG_TYPE_BYTE, { .b = 0 } }, + { XMM7360_RPC_MSG_ARG_TYPE_LONG, { .l = 6 } }, + { XMM7360_RPC_MSG_ARG_TYPE_LONG, { .l = 0 } }, + { XMM7360_RPC_MSG_ARG_TYPE_LONG, { .l = 0 } }, + { XMM7360_RPC_MSG_ARG_TYPE_UNKNOWN }, + }; + return xmm7360_rpc_args_to_byte_array (args); +} + +static void +get_dns_ready (MMBroadbandModemXmm7360 *modem, + GAsyncResult *res, + GTask *task) +{ + GError *error = NULL; + ConnectContext *ctx; + g_autoptr(Xmm7360RpcResponse) response = NULL; + Xmm7360RpcMsgArg *arg; + guint i; + g_autoptr(GByteArray) body = NULL; + + ctx = g_task_get_task_data (task); + + response = mm_broadband_modem_xmm7360_rpc_command_finish (modem, res, &error); + if (error) { + g_task_return_error (task, error); + g_object_unref (task); + return; + } + + for (i = 1; i < 17; i += 2) { + /* iterate over pairs of args: IP [i] and IP format (v4(1) or v6(2) or MISSING(0)) [i+1] */ + arg = (Xmm7360RpcMsgArg *) g_ptr_array_index (response->content, i + 1); + if (XMM7360_RPC_MSG_ARG_GET_INT (arg) == 1) { + arg = (Xmm7360RpcMsgArg *) g_ptr_array_index (response->content, i); + g_assert (arg->size >= 4); + g_ptr_array_add (ctx->dns, g_inet_address_new_from_bytes ((const guint8 *)arg->value.string, + G_SOCKET_FAMILY_IPV4)); + } else if (XMM7360_RPC_MSG_ARG_GET_INT (arg) == 2) { + arg = (Xmm7360RpcMsgArg *) g_ptr_array_index (response->content, i); + g_assert (arg->size >= 16); + g_ptr_array_add (ctx->dns, g_inet_address_new_from_bytes ((const guint8 *)arg->value.string, + G_SOCKET_FAMILY_IPV6)); + } + } + + body = pack_uta_ms_call_ps_connect_req (); + + mm_broadband_modem_xmm7360_rpc_command_full (modem, + ctx->port, + XMM7360_RPC_CALL_UTA_MS_CALL_PS_CONNECT_REQ, + TRUE, + body, + 3, + FALSE, + NULL, /* cancellable */ + (GAsyncReadyCallback)ps_connect_ready, + task); +} + +static GByteArray * +pack_uta_ms_call_ps_get_negotiated_dns_req (void) +{ + static const Xmm7360RpcMsgArg args[] = { + { XMM7360_RPC_MSG_ARG_TYPE_BYTE, { .b = 0 } }, + { XMM7360_RPC_MSG_ARG_TYPE_LONG, { .l = 0 } }, + { XMM7360_RPC_MSG_ARG_TYPE_LONG, { .l = 0 } }, + { XMM7360_RPC_MSG_ARG_TYPE_UNKNOWN }, + }; + return xmm7360_rpc_args_to_byte_array (args); +} + +static void +get_ip_addr_ready (MMBroadbandModemXmm7360 *modem, + GAsyncResult *res, + GTask *task) +{ + GError *error = NULL; + ConnectContext *ctx; + g_autoptr(Xmm7360RpcResponse) response = NULL; + Xmm7360RpcMsgArg *arg; + gint i; + guint32 *ip_bytes; + g_autoptr(GByteArray) body = NULL; + + ctx = g_task_get_task_data (task); + + response = mm_broadband_modem_xmm7360_rpc_command_finish (modem, res, &error); + if (error) { + g_task_return_error (task, error); + g_object_unref (task); + return; + } + + arg = (Xmm7360RpcMsgArg *) g_ptr_array_index (response->content, 1); + if (arg->size < 12) { + g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, + "The IP address field is invalid (too short)"); + g_object_unref (task); + return; + } + + /* the STRING arg contains three IP addresses, we only use the last non-zero address */ + ip_bytes = (guint32 *)arg->value.string; + for (i = 2; i >= 0; i--) { + if (ip_bytes[i] != 0) { + ctx->ip = g_inet_address_new_from_bytes ((const guint8 *)&ip_bytes[i], + G_SOCKET_FAMILY_IPV4); + break; + } + } + + if (ctx->ip == NULL) { + g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, + "The IP address field is invalid (all zeros)"); + g_object_unref (task); + return; + } + + body = pack_uta_ms_call_ps_get_negotiated_dns_req (); + + mm_broadband_modem_xmm7360_rpc_command_full (modem, + ctx->port, + XMM7360_RPC_CALL_UTA_MS_CALL_PS_GET_NEGOTIATED_DNS_REQ, + TRUE, + body, + 3, + FALSE, + NULL, /* cancellable */ + (GAsyncReadyCallback)get_dns_ready, + task); +} + +static GByteArray * +pack_uta_ms_call_ps_get_neg_ip_addr_req (void) +{ + static const Xmm7360RpcMsgArg args[] = { + { XMM7360_RPC_MSG_ARG_TYPE_BYTE, { .b = 0 } }, + { XMM7360_RPC_MSG_ARG_TYPE_LONG, { .l = 0 } }, + { XMM7360_RPC_MSG_ARG_TYPE_LONG, { .l = 0 } }, + { XMM7360_RPC_MSG_ARG_TYPE_UNKNOWN }, + }; + return xmm7360_rpc_args_to_byte_array (args); +} + +static gboolean +get_ip_config (GTask *task) +{ + ConnectContext *ctx; + MMBroadbandModemXmm7360 *modem; + g_autoptr(GByteArray) body = NULL; + + if (g_cancellable_is_cancelled (g_task_get_cancellable (task))) { + g_task_return_new_error (task, G_IO_ERROR, G_IO_ERROR_CANCELLED, + "operation has been cancelled"); + g_object_unref (task); + return G_SOURCE_REMOVE; + } + + ctx = g_task_get_task_data (task); + + modem = g_task_get_source_object (task); + + body = pack_uta_ms_call_ps_get_neg_ip_addr_req (); + + mm_broadband_modem_xmm7360_rpc_command_full (modem, + ctx->port, + XMM7360_RPC_CALL_UTA_MS_CALL_PS_GET_NEG_IP_ADDR_REQ, + TRUE, + body, + 3, + FALSE, + NULL, /* cancellable */ + (GAsyncReadyCallback)get_ip_addr_ready, + task); + + return G_SOURCE_REMOVE; +} + +static gboolean +attach_allowed_timeout_cb (GTask *task) +{ + g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, + "Connecting timed out (waiting for attach-allowed)"); + g_object_unref (task); + return G_SOURCE_REMOVE; +} + +static void net_attach_command (GTask *task); + +static void +net_attach_command_ready (MMBroadbandModemXmm7360 *modem, + GAsyncResult *res, + GTask *task) +{ + GError *error = NULL; + ConnectContext *ctx; + g_autoptr(Xmm7360RpcResponse) response = NULL; + Xmm7360RpcMsgArg *arg; + gint status; + + ctx = g_task_get_task_data (task); + + response = mm_broadband_modem_xmm7360_rpc_command_finish (modem, res, &error); + if (error) { + g_task_return_error (task, error); + g_object_unref (task); + return; + } + + if (response->content->len < 2) { + g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, + "The response for net-attach is invalid (too short)"); + g_object_unref (task); + return; + } + + /* status code */ + arg = (Xmm7360RpcMsgArg *) g_ptr_array_index (response->content, 1); + g_assert (arg->type == XMM7360_RPC_MSG_ARG_TYPE_LONG); + status = XMM7360_RPC_MSG_ARG_GET_INT (arg); + + if (status != (gint32)0xffffffff) { + ctx->is_attached = TRUE; + /* give the modem a second before requesting the IP */ + g_timeout_add_seconds (1, (GSourceFunc)get_ip_config, task); + } else if (ctx->is_attach_allowed && ctx->attach_attempts < 2) { + /* immediately try a second time if it should have been allowed */ + ctx->is_attach_allowed = FALSE; + net_attach_command (task); + } else if (ctx->attach_attempts < 3) { + /* give up if we do not receive an attach-allowed unsolicited message within 5 seconds */ + ctx->attach_allowed_timeout_id = g_timeout_add_seconds (5, (GSourceFunc)attach_allowed_timeout_cb, task); + } else { + g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, + "Giving up on attach-net operation after three failed attempts"); + g_object_unref (task); + } +} + +static GByteArray * +pack_uta_ms_net_attach_req (void) +{ + static const Xmm7360RpcMsgArg args[] = { + { XMM7360_RPC_MSG_ARG_TYPE_BYTE, { .b = 0 } }, + { XMM7360_RPC_MSG_ARG_TYPE_LONG, { .l = 0 } }, + { XMM7360_RPC_MSG_ARG_TYPE_LONG, { .l = 0 } }, + { XMM7360_RPC_MSG_ARG_TYPE_LONG, { .l = 0 } }, + { XMM7360_RPC_MSG_ARG_TYPE_LONG, { .l = 0 } }, + { XMM7360_RPC_MSG_ARG_TYPE_SHORT, { .s = 0xffff } }, + { XMM7360_RPC_MSG_ARG_TYPE_SHORT, { .s = 0xffff } }, + { XMM7360_RPC_MSG_ARG_TYPE_LONG, { .l = 0 } }, + { XMM7360_RPC_MSG_ARG_TYPE_LONG, { .l = 0 } }, + { XMM7360_RPC_MSG_ARG_TYPE_UNKNOWN }, + }; + return xmm7360_rpc_args_to_byte_array (args); +} + +static void +net_attach_command (GTask *task) +{ + ConnectContext *ctx; + MMBroadbandModemXmm7360 *modem; + g_autoptr(GByteArray) body = NULL; + + ctx = g_task_get_task_data (task); + ctx->attach_attempts++; + + modem = g_task_get_source_object (task); + + body = pack_uta_ms_net_attach_req (); + + mm_broadband_modem_xmm7360_rpc_command_full (modem, + ctx->port, + XMM7360_RPC_CALL_UTA_MS_NET_ATTACH_REQ, + TRUE, + body, + 3, + FALSE, + NULL, /* cancellable */ + (GAsyncReadyCallback)net_attach_command_ready, + task); +} + +static gboolean +connect_unsol_handler (MMPortSerialXmmrpcXmm7360 *port, + Xmm7360RpcResponse *response, + GTask *task) +{ + Xmm7360RpcMsgArg *arg; + ConnectContext *ctx; + + if (response->unsol_id != XMM7360_RPC_UNSOL_UTA_MS_NET_IS_ATTACH_ALLOWED_IND_CB) { + return FALSE; + } + + if (response->content->len <= 2) { + mm_obj_dbg (port, "Ignoring invalid is-attach-allowed message (too short)"); + return TRUE; + } + + ctx = g_task_get_task_data (task); + + if (ctx->is_attached) { + /* already attached, ignore the attach-allowed status message */ + return TRUE; + } + + arg = (Xmm7360RpcMsgArg *) g_ptr_array_index (response->content, 2); + if (XMM7360_RPC_MSG_ARG_GET_INT (arg)) { + ctx->is_attach_allowed = TRUE; + if (ctx->attach_allowed_timeout_id) { + g_source_remove (ctx->attach_allowed_timeout_id); + ctx->attach_allowed_timeout_id = 0; + /* attach-net is allowed now, retry */ + net_attach_command (task); + } + } + + return TRUE; +} + +static void +xmm7360_connect (MMBroadbandModemXmm7360 *modem, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data) +{ + GError *error = NULL; + GTask *task; + ConnectContext *ctx; + + task = g_task_new (modem, cancellable, callback, user_data); + + ctx = g_slice_new0 (ConnectContext); + ctx->port = mm_broadband_modem_xmm7360_get_port_xmmrpc (modem); + ctx->dns = g_ptr_array_new_with_free_func ((GDestroyNotify)g_object_unref); + g_task_set_task_data (task, ctx, (GDestroyNotify)connect_context_free); + + /* Grab a data port */ + ctx->data = mm_base_modem_get_best_data_port (MM_BASE_MODEM (modem), MM_PORT_TYPE_NET); + if (!ctx->data) { + g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_NOT_FOUND, + "No valid data port found to launch connection"); + g_object_unref (task); + return; + } + + /* Open XMMRPC port for initialization */ + if (!mm_port_serial_open (MM_PORT_SERIAL (ctx->port), &error)) { + g_prefix_error (&error, "Couldn't open XMMRPC port for connection setup: "); + g_task_return_error (task, error); + g_object_unref (task); + return; + } + + ctx->unsol_handler_id = mm_port_serial_xmmrpc_xmm7360_add_unsolicited_msg_handler ( + ctx->port, + (MMPortSerialXmmrpcXmm7360UnsolicitedMsgFn)connect_unsol_handler, + task, + NULL); + + net_attach_command (task); +} + +static void +connect (MMBaseBearer *self, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data) +{ + GTask *task; + g_autoptr(MMBaseModem) modem = NULL; + + /* Get the owner modem object */ + g_object_get (self, + MM_BASE_BEARER_MODEM, &modem, + NULL); + g_assert (modem != NULL); + + task = g_task_new (self, cancellable, callback, user_data); + + /* the connect routine is independent of the bearer object */ + xmm7360_connect (MM_BROADBAND_MODEM_XMM7360 (modem), + cancellable, + (GAsyncReadyCallback)xmm7360_connect_apn_ready, + task); +} + +/*****************************************************************************/ +/* Disconnect */ + +static gboolean +disconnect_finish (MMBaseBearer *self, + GAsyncResult *res, + GError **error) +{ + return g_task_propagate_boolean (G_TASK (res), error); +} + +static void +disconnect_sequence_ready (MMBroadbandModem *modem, + GAsyncResult *res, + GTask *task) +{ + GError *error = NULL; + g_autoptr(Xmm7360RpcResponse) response = NULL; + + response = mm_broadband_modem_xmm7360_rpc_sequence_finish (MM_BROADBAND_MODEM_XMM7360 (modem), + res, + &error); + + if (error) { + g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, + "Failed to complete disconnect sequence: %s", error->message); + g_object_unref (task); + return; + } + + g_task_return_boolean (task, TRUE); + g_object_unref (task); +} + +static const MMBroadbandModemXmm7360RpcCommand disconnect_sequence[] = { + { + XMM7360_RPC_CALL_UTA_MS_CALL_PS_DEACTIVATE_REQ, + TRUE, + (Xmm7360RpcMsgArg[]) { + { XMM7360_RPC_MSG_ARG_TYPE_BYTE, { .b = 0 } }, + { XMM7360_RPC_MSG_ARG_TYPE_LONG, { .l = 0 } }, + { XMM7360_RPC_MSG_ARG_TYPE_LONG, { .l = 0 } }, + { XMM7360_RPC_MSG_ARG_TYPE_UNKNOWN }, + }, + 3, + FALSE, + mm_broadband_modem_xmm7360_rpc_response_processor_continue_on_success + /* response will be: L(0x0) L(0x0) L(0x5dffffff) L(0x0) (meaning unknown) */ + }, + { + XMM7360_RPC_CALL_UTA_RPC_PS_CONNECT_RELEASE_REQ, + FALSE, + (Xmm7360RpcMsgArg[]) { + /* the meaning of this value is unknown, but it is used by the Windows driver */ + { XMM7360_RPC_MSG_ARG_TYPE_LONG, { .l = 0x20017 } }, + { XMM7360_RPC_MSG_ARG_TYPE_UNKNOWN }, + }, + 3, + FALSE, + mm_broadband_modem_xmm7360_rpc_response_processor_final + /* response will be: L(0x0) (meaning unknown) */ + }, + { 0 } +}; + +static void +disconnect (MMBaseBearer *bearer, + GAsyncReadyCallback callback, + gpointer user_data) +{ + GTask *task; + g_autoptr(MMBaseModem) modem = NULL; + g_autoptr(MMPortSerialXmmrpcXmm7360) port = NULL; + + task = g_task_new (bearer, NULL, callback, user_data); + + /* Get the owner modem object */ + g_object_get (bearer, MM_BASE_BEARER_MODEM, &modem, NULL); + g_assert (modem != NULL); + + port = mm_broadband_modem_xmm7360_get_port_xmmrpc (MM_BROADBAND_MODEM_XMM7360 (modem)); + + mm_broadband_modem_xmm7360_rpc_sequence_full (MM_BROADBAND_MODEM_XMM7360 (modem), + port, + disconnect_sequence, + NULL, /* cancellable */ + (GAsyncReadyCallback)disconnect_sequence_ready, + task); +} + +/*****************************************************************************/ + +MMBaseBearer * +mm_bearer_xmm7360_new (MMBroadbandModemXmm7360 *modem, + MMBearerProperties *config) +{ + MMBaseBearer *base_bearer; + MMBearerXmm7360 *bearer; + + /* The Xmm7360 bearer inherits from MMBaseBearer (so it's not a MMBroadbandBearer) + * and that means that the object is not async-initable, so we just use + * g_object_new here */ + bearer = g_object_new (MM_TYPE_BEARER_XMM7360, + MM_BASE_BEARER_MODEM, modem, + MM_BASE_BEARER_CONFIG, config, + NULL); + + base_bearer = MM_BASE_BEARER (bearer); + /* Only export valid bearers */ + mm_base_bearer_export (base_bearer); + + return base_bearer; +} + +static void +mm_bearer_xmm7360_init (MMBearerXmm7360 *self) +{ + /* Initialize private data */ + self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, + MM_TYPE_BEARER_XMM7360, + MMBearerXmm7360Private); +} + +static void +mm_bearer_xmm7360_class_init (MMBearerXmm7360Class *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + MMBaseBearerClass *base_bearer_class = MM_BASE_BEARER_CLASS (klass); + + g_type_class_add_private (object_class, sizeof (MMBearerXmm7360Private)); + + base_bearer_class->connect = connect; + base_bearer_class->connect_finish = connect_finish; + + base_bearer_class->disconnect = disconnect; + base_bearer_class->disconnect_finish = disconnect_finish; +} diff --git a/src/plugins/intel/mm-bearer-xmm7360.h b/src/plugins/intel/mm-bearer-xmm7360.h new file mode 100644 index 00000000..3e39525c --- /dev/null +++ b/src/plugins/intel/mm-bearer-xmm7360.h @@ -0,0 +1,56 @@ +/* -*- 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 + */ + +#ifndef MM_BEARER_XMM7360_H +#define MM_BEARER_XMM7360_H + +#include <glib.h> +#include <glib-object.h> + +#define _LIBMM_INSIDE_MM +#include <libmm-glib.h> + +#include "mm-base-bearer.h" +#include "mm-broadband-modem-xmm7360.h" + +#define MM_TYPE_BEARER_XMM7360 (mm_bearer_xmm7360_get_type ()) +#define MM_BEARER_XMM7360(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MM_TYPE_BEARER_XMM7360, MMBearerXmm7360)) +#define MM_BEARER_XMM7360_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), MM_TYPE_BEARER_XMM7360, MMBearerXmm7360Class)) +#define MM_IS_BEARER_XMM7360(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MM_TYPE_BEARER_XMM7360)) +#define MM_IS_BEARER_XMM7360_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), MM_TYPE_BEARER_XMM7360)) +#define MM_BEARER_XMM7360_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), MM_TYPE_BEARER_XMM7360, MMBearerXmm7360Class)) + +typedef struct _MMBearerXmm7360 MMBearerXmm7360; +typedef struct _MMBearerXmm7360Class MMBearerXmm7360Class; +typedef struct _MMBearerXmm7360Private MMBearerXmm7360Private; + +struct _MMBearerXmm7360 { + MMBaseBearer parent; + MMBearerXmm7360Private *priv; +}; + +struct _MMBearerXmm7360Class { + MMBaseBearerClass parent; +}; + +GType mm_bearer_xmm7360_get_type (void); + +MMBaseBearer *mm_bearer_xmm7360_new (MMBroadbandModemXmm7360 *modem, + MMBearerProperties *config); + +#endif /* MM_BEARER_XMM7360_H */ diff --git a/src/plugins/intel/mm-broadband-modem-xmm7360-rpc.c b/src/plugins/intel/mm-broadband-modem-xmm7360-rpc.c new file mode 100644 index 00000000..85bc009b --- /dev/null +++ b/src/plugins/intel/mm-broadband-modem-xmm7360-rpc.c @@ -0,0 +1,577 @@ +/* -*- 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) 2024 Thomas Vogt + */ + +#include <glib.h> +#include <glib-object.h> + +#include <ModemManager.h> + +#include "mm-log-object.h" +#include "mm-errors-types.h" + +#include "mm-broadband-modem-xmm7360-rpc.h" + +static gboolean +abort_task_if_port_unusable (MMBroadbandModemXmm7360 *self, + MMPortSerialXmmrpcXmm7360 *port, + GTask *task) +{ + GError *error = NULL; + + /* If no port given, probably the port disappeared */ + if (!port) { + g_task_return_new_error (task, + MM_CORE_ERROR, + MM_CORE_ERROR_NOT_FOUND, + "Cannot run sequence: port not given"); + g_object_unref (task); + return FALSE; + } + + /* Ensure we don't try to use a connected port */ + if (mm_port_get_connected (MM_PORT (port))) { + g_task_return_new_error (task, + MM_CORE_ERROR, + MM_CORE_ERROR_CONNECTED, + "Cannot run sequence: port is connected"); + g_object_unref (task); + return FALSE; + } + + /* Ensure we have a port open during the sequence */ + if (!mm_port_serial_open (MM_PORT_SERIAL (port), &error)) { + g_task_return_new_error (task, + MM_CORE_ERROR, + MM_CORE_ERROR_CONNECTED, + "Cannot run sequence: '%s'", + error->message); + g_error_free (error); + g_object_unref (task); + return FALSE; + } + + return TRUE; +} + +static void +parent_cancellable_cancelled (GCancellable *parent_cancellable, + GCancellable *cancellable) +{ + g_cancellable_cancel (cancellable); +} + +/*****************************************************************************/ +/* RPC sequence handling */ + +static void rpc_sequence_parse_response (MMPortSerialXmmrpcXmm7360 *port, + GAsyncResult *res, + GTask *task); + +typedef struct { + MMPortSerialXmmrpcXmm7360 *port; + gulong cancelled_id; + GCancellable *parent_cancellable; + const MMBroadbandModemXmm7360RpcCommand *current; + const MMBroadbandModemXmm7360RpcCommand *sequence; + guint next_command_wait_id; +} RpcSequenceContext; + +static void +rpc_sequence_context_free (RpcSequenceContext *ctx) +{ + mm_port_serial_close (MM_PORT_SERIAL (ctx->port)); + g_object_unref (ctx->port); + + if (ctx->parent_cancellable) { + g_cancellable_disconnect (ctx->parent_cancellable, + ctx->cancelled_id); + g_object_unref (ctx->parent_cancellable); + } + + if (ctx->next_command_wait_id > 0) { + g_source_remove (ctx->next_command_wait_id); + ctx->next_command_wait_id = 0; + } + + g_free (ctx); +} + +Xmm7360RpcResponse * +mm_broadband_modem_xmm7360_rpc_sequence_full_finish (MMBroadbandModemXmm7360 *self, + GAsyncResult *res, + GError **error) +{ + return g_task_propagate_pointer (G_TASK (res), error); +} + +static gboolean +rpc_sequence_next_command (GTask *task) +{ + RpcSequenceContext *ctx; + g_autoptr(GByteArray) body = NULL; + + ctx = g_task_get_task_data (task); + ctx->next_command_wait_id = 0; + + body = xmm7360_rpc_args_to_byte_array ((const Xmm7360RpcMsgArg *)ctx->current->body); + + /* Schedule the next command in the probing group */ + mm_port_serial_xmmrpc_xmm7360_command ( + ctx->port, + ctx->current->callid, + ctx->current->is_async, + body, + ctx->current->timeout, + ctx->current->allow_cached, + g_task_get_cancellable (task), + (GAsyncReadyCallback)rpc_sequence_parse_response, + task); + + return G_SOURCE_REMOVE; +} + +static void +rpc_sequence_parse_response (MMPortSerialXmmrpcXmm7360 *port, + GAsyncResult *res, + GTask *task) +{ + MMBroadbandModemXmm7360RpcResponseProcessorResult processor_result; + GError *result_error = NULL; + RpcSequenceContext *ctx; + Xmm7360RpcResponse *response = NULL; + g_autoptr(GError) command_error = NULL; + + response = mm_port_serial_xmmrpc_xmm7360_command_finish (port, res, &command_error); + + /* Cancelled? */ + if (g_task_return_error_if_cancelled (task)) { + g_object_unref (task); + return; + } + + ctx = g_task_get_task_data (task); + if (!ctx->current->response_processor) + processor_result = MM_BROADBAND_MODEM_XMM7360_RPC_RESPONSE_PROCESSOR_RESULT_CONTINUE; + else { + const MMBroadbandModemXmm7360RpcCommand *next = ctx->current + 1; + + /* Response processor will tell us if we need to keep on the sequence */ + processor_result = ctx->current->response_processor (g_task_get_source_object (task), + response, + next->callid ? FALSE : TRUE, /* Last command in sequence? */ + command_error, + &result_error); + switch (processor_result) { + case MM_BROADBAND_MODEM_XMM7360_RPC_RESPONSE_PROCESSOR_RESULT_CONTINUE: + g_assert (!result_error); + if (response) + xmm7360_rpc_response_free (response); + break; + case MM_BROADBAND_MODEM_XMM7360_RPC_RESPONSE_PROCESSOR_RESULT_SUCCESS: + g_assert (!result_error); + break; + case MM_BROADBAND_MODEM_XMM7360_RPC_RESPONSE_PROCESSOR_RESULT_FAILURE: + /* On failure, complete with error right away */ + g_assert (result_error); + if (response) + xmm7360_rpc_response_free (response); + g_task_return_error (task, result_error); + g_object_unref (task); + return; + default: + g_assert_not_reached (); + } + } + + if (processor_result == MM_BROADBAND_MODEM_XMM7360_RPC_RESPONSE_PROCESSOR_RESULT_CONTINUE) { + ctx->current++; + if (ctx->current->callid) { + g_assert (!ctx->next_command_wait_id); + ctx->next_command_wait_id = g_timeout_add_seconds (ctx->current->wait_seconds, + (GSourceFunc) rpc_sequence_next_command, + task); + return; + } + /* On last command, end. */ + } + + g_task_return_pointer (task, response, (GDestroyNotify) xmm7360_rpc_response_free); + g_object_unref (task); +} + +static void +rpc_sequence_common (MMBroadbandModemXmm7360 *self, + MMPortSerialXmmrpcXmm7360 *port, + const MMBroadbandModemXmm7360RpcCommand *sequence, + GTask *task, + GCancellable *parent_cancellable) +{ + RpcSequenceContext *ctx; + g_autoptr(GByteArray) body = NULL; + + /* Ensure that we have an open port */ + if (!abort_task_if_port_unusable (self, port, task)) + return; + + /* Setup context */ + ctx = g_new0 (RpcSequenceContext, 1); + ctx->port = g_object_ref (port); + ctx->current = ctx->sequence = sequence; + + /* Ensure the cancellable that's already associated with the modem + * will also get cancelled if the modem wide-one gets cancelled */ + if (parent_cancellable) { + GCancellable *cancellable; + + cancellable = g_task_get_cancellable (task); + ctx->parent_cancellable = g_object_ref (parent_cancellable); + ctx->cancelled_id = g_cancellable_connect (ctx->parent_cancellable, + G_CALLBACK (parent_cancellable_cancelled), + cancellable, + NULL); + } + + g_task_set_task_data (task, ctx, (GDestroyNotify)rpc_sequence_context_free); + + body = xmm7360_rpc_args_to_byte_array ((const Xmm7360RpcMsgArg *)ctx->current->body); + + /* Go on with the first one in the sequence */ + mm_port_serial_xmmrpc_xmm7360_command ( + ctx->port, + ctx->current->callid, + ctx->current->is_async, + body, + ctx->current->timeout, + ctx->current->allow_cached, + g_task_get_cancellable (task), + (GAsyncReadyCallback)rpc_sequence_parse_response, + task); +} + +void +mm_broadband_modem_xmm7360_rpc_sequence_full (MMBroadbandModemXmm7360 *self, + MMPortSerialXmmrpcXmm7360 *port, + const MMBroadbandModemXmm7360RpcCommand *sequence, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data) +{ + GCancellable *modem_cancellable; + GTask *task; + + modem_cancellable = mm_base_modem_peek_cancellable (MM_BASE_MODEM (self)); + task = g_task_new (self, + cancellable ? cancellable : modem_cancellable, + callback, + user_data); + + rpc_sequence_common (self, + port, + sequence, + task, + cancellable ? modem_cancellable : NULL); +} + +Xmm7360RpcResponse * +mm_broadband_modem_xmm7360_rpc_sequence_finish (MMBroadbandModemXmm7360 *self, + GAsyncResult *res, + GError **error) +{ + return mm_broadband_modem_xmm7360_rpc_sequence_full_finish (self, res, error); +} + +void +mm_broadband_modem_xmm7360_rpc_sequence (MMBroadbandModemXmm7360 *self, + const MMBroadbandModemXmm7360RpcCommand *sequence, + GAsyncReadyCallback callback, + gpointer user_data) +{ + MMPortSerialXmmrpcXmm7360 *port; + GError *error = NULL; + GTask *task; + + task = g_task_new (self, + mm_base_modem_peek_cancellable (MM_BASE_MODEM (self)), + callback, + user_data); + + /* No port given, so we'll try to guess which is best */ + port = mm_broadband_modem_xmm7360_peek_port_xmmrpc (self); + if (!port) { + g_set_error (&error, + MM_CORE_ERROR, + MM_CORE_ERROR_CONNECTED, + "No XMMRPC port available to run command"); + g_task_return_error (task, error); + g_object_unref (task); + return; + } + + rpc_sequence_common (self, + port, + sequence, + task, + NULL); +} + +/*****************************************************************************/ +/* Response processor helpers */ + +MMBroadbandModemXmm7360RpcResponseProcessorResult +mm_broadband_modem_xmm7360_rpc_response_processor_final (MMBroadbandModemXmm7360 *self, + Xmm7360RpcResponse *response, + gboolean last_command, + const GError *error, + GError **result_error) +{ + if (error) { + *result_error = g_error_copy (error); + return MM_BROADBAND_MODEM_XMM7360_RPC_RESPONSE_PROCESSOR_RESULT_FAILURE; + } + + *result_error = NULL; + return MM_BROADBAND_MODEM_XMM7360_RPC_RESPONSE_PROCESSOR_RESULT_SUCCESS; +} + +MMBroadbandModemXmm7360RpcResponseProcessorResult +mm_broadband_modem_xmm7360_rpc_response_processor_continue_on_success (MMBroadbandModemXmm7360 *self, + Xmm7360RpcResponse *response, + gboolean last_command, + const GError *error, + GError **result_error) +{ + if (error) { + *result_error = g_error_copy (error); + return MM_BROADBAND_MODEM_XMM7360_RPC_RESPONSE_PROCESSOR_RESULT_FAILURE; + } + + *result_error = NULL; + return MM_BROADBAND_MODEM_XMM7360_RPC_RESPONSE_PROCESSOR_RESULT_CONTINUE; +} + +MMBroadbandModemXmm7360RpcResponseProcessorResult +mm_broadband_modem_xmm7360_rpc_response_processor_continue_on_error (MMBroadbandModemXmm7360 *self, + Xmm7360RpcResponse *response, + gboolean last_command, + const GError *error, + GError **result_error) +{ + *result_error = NULL; + return (error ? + MM_BROADBAND_MODEM_XMM7360_RPC_RESPONSE_PROCESSOR_RESULT_CONTINUE : + MM_BROADBAND_MODEM_XMM7360_RPC_RESPONSE_PROCESSOR_RESULT_SUCCESS); +} + +/*****************************************************************************/ +/* Single RPC command handling */ + +typedef struct { + MMPortSerialXmmrpcXmm7360 *port; + gulong cancelled_id; + GCancellable *parent_cancellable; +} RpcCommandContext; + +static void +rpc_command_context_free (RpcCommandContext *ctx) +{ + mm_port_serial_close (MM_PORT_SERIAL (ctx->port)); + + if (ctx->parent_cancellable) { + g_cancellable_disconnect (ctx->parent_cancellable, + ctx->cancelled_id); + g_object_unref (ctx->parent_cancellable); + } + + g_object_unref (ctx->port); + g_free (ctx); +} + +Xmm7360RpcResponse * +mm_broadband_modem_xmm7360_rpc_command_full_finish (MMBroadbandModemXmm7360 *self, + GAsyncResult *res, + GError **error) +{ + return g_task_propagate_pointer (G_TASK (res), error); +} + +static void +rpc_command_ready (MMPortSerialXmmrpcXmm7360 *port, + GAsyncResult *res, + GTask *task) +{ + g_autoptr(GError) command_error = NULL; + Xmm7360RpcResponse *response; + + response = mm_port_serial_xmmrpc_xmm7360_command_finish (port, res, &command_error); + + if (g_task_return_error_if_cancelled (task)) { + /* task cancelled */ + g_object_unref (task); + return; + } + + if (command_error) + /* error coming from the serial port */ + g_task_return_error (task, g_steal_pointer (&command_error)); + else if (response) + /* valid response */ + g_task_return_pointer (task, response, (GDestroyNotify)xmm7360_rpc_response_free); + else + g_assert_not_reached (); + g_object_unref (task); +} + +static void +rpc_command_common (MMBroadbandModemXmm7360 *self, + MMPortSerialXmmrpcXmm7360 *port, + Xmm7360RpcCallId callid, + gboolean is_async, + GByteArray *body, + guint timeout, + gboolean allow_cached, + GTask *task, + GCancellable *parent_cancellable) +{ + RpcCommandContext *ctx; + + /* Ensure that we have an open port */ + if (!abort_task_if_port_unusable (self, port, task)) + return; + + ctx = g_new0 (RpcCommandContext, 1); + ctx->port = g_object_ref (port); + + /* Ensure the cancellable that's already associated with the modem + * will also get cancelled if the modem wide-one gets cancelled */ + if (parent_cancellable) { + GCancellable *cancellable; + + cancellable = g_task_get_cancellable (task); + ctx->parent_cancellable = g_object_ref (parent_cancellable); + ctx->cancelled_id = g_cancellable_connect (ctx->parent_cancellable, + G_CALLBACK (parent_cancellable_cancelled), + cancellable, + NULL); + } + + g_task_set_task_data (task, ctx, (GDestroyNotify)rpc_command_context_free); + + /* Go on with the command */ + mm_port_serial_xmmrpc_xmm7360_command ( + port, + callid, + is_async, + body, + timeout, + allow_cached, + g_task_get_cancellable (task), + (GAsyncReadyCallback)rpc_command_ready, + task); +} + +void +mm_broadband_modem_xmm7360_rpc_command_full (MMBroadbandModemXmm7360 *self, + MMPortSerialXmmrpcXmm7360 *port, + Xmm7360RpcCallId callid, + gboolean is_async, + GByteArray *body, + guint timeout, + gboolean allow_cached, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data) +{ + GCancellable *modem_cancellable; + GTask *task; + + modem_cancellable = mm_base_modem_peek_cancellable (MM_BASE_MODEM (self)); + task = g_task_new (self, + cancellable ? cancellable : modem_cancellable, + callback, + user_data); + + rpc_command_common (self, + port, + callid, + is_async, + body, + timeout, + allow_cached, + task, + cancellable ? modem_cancellable : NULL); +} + +Xmm7360RpcResponse * +mm_broadband_modem_xmm7360_rpc_command_finish (MMBroadbandModemXmm7360 *self, + GAsyncResult *res, + GError **error) +{ + return mm_broadband_modem_xmm7360_rpc_command_full_finish (self, res, error); +} + +static void +_rpc_command (MMBroadbandModemXmm7360 *self, + Xmm7360RpcCallId callid, + gboolean is_async, + GByteArray *body, + guint timeout, + gboolean allow_cached, + GAsyncReadyCallback callback, + gpointer user_data) +{ + MMPortSerialXmmrpcXmm7360 *port; + GError *error = NULL; + GTask *task; + + task = g_task_new (self, + mm_base_modem_peek_cancellable (MM_BASE_MODEM (self)), + callback, + user_data); + + /* No port given, so we'll try to guess which is best */ + port = mm_broadband_modem_xmm7360_peek_port_xmmrpc (self); + if (!port) { + g_set_error (&error, + MM_CORE_ERROR, + MM_CORE_ERROR_CONNECTED, + "No XMMRPC port available to run command"); + g_task_return_error (task, error); + g_object_unref (task); + return; + } + + rpc_command_common (self, + port, + callid, + is_async, + body, + timeout, + allow_cached, + task, + NULL); +} + +void +mm_broadband_modem_xmm7360_rpc_command (MMBroadbandModemXmm7360 *self, + Xmm7360RpcCallId callid, + gboolean is_async, + GByteArray *body, + guint timeout, + gboolean allow_cached, + GAsyncReadyCallback callback, + gpointer user_data) +{ + _rpc_command (self, callid, is_async, body, timeout, allow_cached, callback, user_data); +} diff --git a/src/plugins/intel/mm-broadband-modem-xmm7360-rpc.h b/src/plugins/intel/mm-broadband-modem-xmm7360-rpc.h new file mode 100644 index 00000000..18d107bc --- /dev/null +++ b/src/plugins/intel/mm-broadband-modem-xmm7360-rpc.h @@ -0,0 +1,173 @@ +/* -*- 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) 2024 Thomas Vogt + */ + +#ifndef MM_BROADBAND_MODEM_XMM7360_RPC_H +#define MM_BROADBAND_MODEM_XMM7360_RPC_H + +#include <gio/gio.h> + +#include "mm-broadband-modem-xmm7360.h" +#include "mm-port-serial-xmmrpc-xmm7360.h" + +typedef enum { + MM_BROADBAND_MODEM_XMM7360_RPC_RESPONSE_PROCESSOR_RESULT_CONTINUE, + MM_BROADBAND_MODEM_XMM7360_RPC_RESPONSE_PROCESSOR_RESULT_SUCCESS, + MM_BROADBAND_MODEM_XMM7360_RPC_RESPONSE_PROCESSOR_RESULT_FAILURE, +} MMBroadbandModemXmm7360RpcResponseProcessorResult; + +/* + * SUCCESS must be returned when the operation is to be considered successful, + * and a result may be given. + * + * FAILURE must be returned when a GError is propagated into result_error, + * which will be treated as a critical error and therefore the operation will be aborted. + * + * CONTINUE must be returned when no result_error is given and + * the operation should go on with the next scheduled command. + * + * This setup, therefore allows: + * - Running a single command and processing its result. + * - Running a set of N commands out of M (N<M), where the global result is + * obtained without having executed all configured commands. + */ +typedef MMBroadbandModemXmm7360RpcResponseProcessorResult (* MMBroadbandModemXmm7360RpcResponseProcessor) ( + MMBroadbandModemXmm7360 *self, + Xmm7360RpcResponse *response, + gboolean last_command, + const GError *error, + GError **result_error); + +/* Struct to configure XMMRPC command operations */ +typedef struct { + /* The RCP command's call ID */ + Xmm7360RpcCallId callid; + /* Async or sync type RCP command */ + gboolean is_async; + /* payload */ + const Xmm7360RpcMsgArg *body; + /* Timeout of the command, in seconds */ + guint timeout; + /* Flag to allow cached replies */ + gboolean allow_cached; + /* The response processor */ + MMBroadbandModemXmm7360RpcResponseProcessor response_processor; + /* Time to wait before sending this command (in seconds) */ + guint wait_seconds; +} MMBroadbandModemXmm7360RpcCommand; + +/* Generic RPC sequence handling, using the first XMMRPC port available and without + * explicit cancellations. */ +void mm_broadband_modem_xmm7360_rpc_sequence ( + MMBroadbandModemXmm7360 *self, + const MMBroadbandModemXmm7360RpcCommand *sequence, + GAsyncReadyCallback callback, + gpointer user_data); +Xmm7360RpcResponse *mm_broadband_modem_xmm7360_rpc_sequence_finish ( + MMBroadbandModemXmm7360 *self, + GAsyncResult *res, + GError **error); + +/* Fully detailed RPC sequence handling, when specific XMMRPC port and/or explicit + * cancellations need to be used. */ +void mm_broadband_modem_xmm7360_rpc_sequence_full ( + MMBroadbandModemXmm7360 *self, + MMPortSerialXmmrpcXmm7360 *port, + const MMBroadbandModemXmm7360RpcCommand *sequence, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data); +Xmm7360RpcResponse *mm_broadband_modem_xmm7360_rpc_sequence_full_finish ( + MMBroadbandModemXmm7360 *self, + GAsyncResult *res, + GError **error); + +/* Common helper response processors */ + +/* + * Response processor for commands that are treated as MANDATORY, where a + * failure in the command triggers a failure in the sequence. + */ +MMBroadbandModemXmm7360RpcResponseProcessorResult mm_broadband_modem_xmm7360_rpc_response_processor_final ( + MMBroadbandModemXmm7360 *self, + Xmm7360RpcResponse *response, + gboolean last_command, + const GError *error, + GError **result_error); + +/* + * Response processor for commands that are treated as MANDATORY, where a + * failure in the command triggers a failure in the sequence. If successful, + * it will run the next command in the sequence. + * + * E.g. used when we provide a list of commands and we want to run all of + * them successfully, and fail the sequence if one of them fails. + */ +MMBroadbandModemXmm7360RpcResponseProcessorResult mm_broadband_modem_xmm7360_rpc_response_processor_continue_on_success ( + MMBroadbandModemXmm7360 *self, + Xmm7360RpcResponse *response, + gboolean last_command, + const GError *error, + GError **result_error); + +/* + * Response processor for commands that are treated as OPTIONAL, where a + * failure in the command doesn't trigger a failure in the sequence. If + * successful, it finishes the sequence. + * + * E.g. used when we provide a list of commands and we want to stop + * as soon as one of them doesn't fail. + */ +MMBroadbandModemXmm7360RpcResponseProcessorResult mm_broadband_modem_xmm7360_rpc_response_processor_continue_on_error ( + MMBroadbandModemXmm7360 *self, + Xmm7360RpcResponse *response, + gboolean last_command, + const GError *error, + GError **result_error); + +/* Generic RPC command handling, using the best XMMRPC port available and without + * explicit cancellations. */ +void mm_broadband_modem_xmm7360_rpc_command ( + MMBroadbandModemXmm7360 *self, + Xmm7360RpcCallId callid, + gboolean is_async, + GByteArray *body, + guint timeout, + gboolean allow_cached, + GAsyncReadyCallback callback, + gpointer user_data); +Xmm7360RpcResponse *mm_broadband_modem_xmm7360_rpc_command_finish ( + MMBroadbandModemXmm7360 *self, + GAsyncResult *res, + GError **error); + +/* Fully detailed RPC command handling, when specific XMMRPC port and/or explicit + * cancellations need to be used. */ +void mm_broadband_modem_xmm7360_rpc_command_full ( + MMBroadbandModemXmm7360 *self, + MMPortSerialXmmrpcXmm7360 *port, + Xmm7360RpcCallId callid, + gboolean is_async, + GByteArray *body, + guint timeout, + gboolean allow_cached, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data); +Xmm7360RpcResponse *mm_broadband_modem_xmm7360_rpc_command_full_finish ( + MMBroadbandModemXmm7360 *self, + GAsyncResult *res, + GError **error); + +#endif /* MM_BROADBAND_MODEM_XMM7360_RPC_H */ diff --git a/src/plugins/intel/mm-broadband-modem-xmm7360.c b/src/plugins/intel/mm-broadband-modem-xmm7360.c new file mode 100644 index 00000000..a4357079 --- /dev/null +++ b/src/plugins/intel/mm-broadband-modem-xmm7360.c @@ -0,0 +1,1229 @@ +/* -*- 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 <config.h> + +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <unistd.h> +#include <ctype.h> + +#include <ModemManager.h> +#define _LIBMM_INSIDE_MM +#include <libmm-glib.h> + +#include "mm-log-object.h" +#include "mm-iface-modem.h" +#include "mm-iface-modem-3gpp.h" +#include "mm-iface-modem-location.h" +#include "mm-broadband-modem-xmm.h" + +#include "mm-broadband-modem-xmm7360.h" +#include "mm-broadband-modem-xmm7360-rpc.h" +#include "mm-port-serial-xmmrpc-xmm7360.h" +#include "mm-bearer-xmm7360.h" +#include "mm-sim-xmm7360.h" + +static void iface_modem_init (MMIfaceModemInterface *iface); +static void iface_modem_3gpp_init (MMIfaceModem3gppInterface *iface); +static void iface_modem_location_init (MMIfaceModemLocationInterface *iface); + +static MMIfaceModemInterface *iface_modem_parent; + +G_DEFINE_TYPE_EXTENDED (MMBroadbandModemXmm7360, mm_broadband_modem_xmm7360, MM_TYPE_BROADBAND_MODEM_XMM, 0, + G_IMPLEMENT_INTERFACE (MM_TYPE_IFACE_MODEM, iface_modem_init) + G_IMPLEMENT_INTERFACE (MM_TYPE_IFACE_MODEM_3GPP, iface_modem_3gpp_init) + G_IMPLEMENT_INTERFACE (MM_TYPE_IFACE_MODEM_LOCATION, iface_modem_location_init) +) + +struct _MMBroadbandModemXmm7360Private { + MMUnlockRetries *unlock_retries; +}; + +/*****************************************************************************/ +/* Create Bearer (Modem interface) */ + +static void +create_bearer (MMIfaceModem *self, + MMBearerProperties *properties, + GAsyncReadyCallback callback, + gpointer user_data) +{ + MMBaseBearer *bearer; + GTask *task; + + bearer = mm_bearer_xmm7360_new (MM_BROADBAND_MODEM_XMM7360 (self), properties); + + task = g_task_new (self, NULL, callback, user_data); + g_task_return_pointer (task, bearer, (GDestroyNotify)g_object_unref); + g_object_unref (task); +} + +static MMBaseBearer * +create_bearer_finish (MMIfaceModem *self, + GAsyncResult *res, + GError **error) +{ + return g_task_propagate_pointer (G_TASK (res), error); +} + +/*****************************************************************************/ +/* Create SIM (Modem interface) */ + +static MMBaseSim * +create_sim_finish (MMIfaceModem *self, + GAsyncResult *res, + GError **error) +{ + return mm_sim_xmm7360_new_finish (res, error); +} + +static void +create_sim (MMIfaceModem *self, + GAsyncReadyCallback callback, + gpointer user_data) +{ + /* New XMM7360 SIM */ + mm_sim_xmm7360_new (MM_BASE_MODEM (self), + NULL, /* cancellable */ + callback, + user_data); +} + +/*****************************************************************************/ +/* Set initial EPS bearer settings (3GPP interface) */ + +typedef enum { + APN_AUTH_TYPE_NONE = 0, + APN_AUTH_TYPE_PAP = 1, + APN_AUTH_TYPE_CHAP = 2, +} ApnAuthType; + +static gboolean +modem_3gpp_set_initial_eps_bearer_settings_finish (MMIfaceModem3gpp *self, + GAsyncResult *res, + GError **error) +{ + return g_task_propagate_boolean (G_TASK (res), error); +} + +static void +attach_apn_config_ready (MMBroadbandModemXmm7360 *modem, + GAsyncResult *res, + GTask *task) +{ + GError *error = NULL; + g_autoptr(Xmm7360RpcResponse) response = NULL; + + response = mm_broadband_modem_xmm7360_rpc_command_finish (modem, res, &error); + if (error) { + g_task_return_error (task, error); + } else { + g_task_return_boolean (task, TRUE); + } + g_object_unref (task); + + +} + +static GByteArray * +padded_pack_uta_ms_call_ps_attach_apn_config_req (const gchar *apn_padded, + ApnAuthType auth_type, + const gchar *user_padded, + const gchar *password_padded) +{ + static const gchar zeroes[270] = { 0 }; + const Xmm7360RpcMsgArg args[] = { + { XMM7360_RPC_MSG_ARG_TYPE_BYTE, { .b = 0 } }, + { XMM7360_RPC_MSG_ARG_TYPE_STRING, { .string = zeroes }, 257, 3 }, + { XMM7360_RPC_MSG_ARG_TYPE_LONG, { .l = 0 } }, + { XMM7360_RPC_MSG_ARG_TYPE_STRING, { .string = zeroes }, 65, 1 }, + { XMM7360_RPC_MSG_ARG_TYPE_STRING, { .string = zeroes }, 65, 0 }, + { XMM7360_RPC_MSG_ARG_TYPE_STRING, { .string = zeroes }, 250, 0 }, + { XMM7360_RPC_MSG_ARG_TYPE_BYTE, { .b = 0 } }, + { XMM7360_RPC_MSG_ARG_TYPE_STRING, { .string = zeroes }, 250, 2 }, + { XMM7360_RPC_MSG_ARG_TYPE_SHORT, { .s = 0 } }, + { XMM7360_RPC_MSG_ARG_TYPE_LONG, { .l = 0 } }, + { XMM7360_RPC_MSG_ARG_TYPE_LONG, { .l = 0 } }, + { XMM7360_RPC_MSG_ARG_TYPE_LONG, { .l = 0 } }, + { XMM7360_RPC_MSG_ARG_TYPE_LONG, { .l = 0 } }, + { XMM7360_RPC_MSG_ARG_TYPE_LONG, { .l = 0 } }, + { XMM7360_RPC_MSG_ARG_TYPE_LONG, { .l = 0 } }, + { XMM7360_RPC_MSG_ARG_TYPE_LONG, { .l = 0 } }, + { XMM7360_RPC_MSG_ARG_TYPE_LONG, { .l = 0 } }, + { XMM7360_RPC_MSG_ARG_TYPE_LONG, { .l = 0 } }, + { XMM7360_RPC_MSG_ARG_TYPE_LONG, { .l = 0 } }, + { XMM7360_RPC_MSG_ARG_TYPE_LONG, { .l = 0 } }, + { XMM7360_RPC_MSG_ARG_TYPE_LONG, { .l = 0 } }, + { XMM7360_RPC_MSG_ARG_TYPE_LONG, { .l = 0 } }, + { XMM7360_RPC_MSG_ARG_TYPE_LONG, { .l = 0 } }, + { XMM7360_RPC_MSG_ARG_TYPE_LONG, { .l = 0 } }, + { XMM7360_RPC_MSG_ARG_TYPE_LONG, { .l = 0 } }, + { XMM7360_RPC_MSG_ARG_TYPE_LONG, { .l = 0 } }, + { XMM7360_RPC_MSG_ARG_TYPE_LONG, { .l = 0 } }, + { XMM7360_RPC_MSG_ARG_TYPE_LONG, { .l = 0 } }, + { XMM7360_RPC_MSG_ARG_TYPE_LONG, { .l = 0 } }, + { XMM7360_RPC_MSG_ARG_TYPE_LONG, { .l = 0 } }, + { XMM7360_RPC_MSG_ARG_TYPE_STRING, { .string = zeroes }, 20, 0 }, + { XMM7360_RPC_MSG_ARG_TYPE_LONG, { .l = 0 } }, + { XMM7360_RPC_MSG_ARG_TYPE_STRING, { .string = zeroes }, 101, 3 }, + { XMM7360_RPC_MSG_ARG_TYPE_STRING, { .string = zeroes }, 257, 3 }, + { XMM7360_RPC_MSG_ARG_TYPE_LONG, { .l = 0 } }, + { XMM7360_RPC_MSG_ARG_TYPE_STRING, { .string = zeroes }, 65, 1 }, + { XMM7360_RPC_MSG_ARG_TYPE_STRING, { .string = zeroes }, 65, 0 }, + { XMM7360_RPC_MSG_ARG_TYPE_STRING, { .string = zeroes }, 250, 0 }, + { XMM7360_RPC_MSG_ARG_TYPE_BYTE, { .b = 0 } }, + { XMM7360_RPC_MSG_ARG_TYPE_STRING, { .string = zeroes }, 250, 2 }, + { XMM7360_RPC_MSG_ARG_TYPE_SHORT, { .s = 0 } }, + { XMM7360_RPC_MSG_ARG_TYPE_LONG, { .l = 0 } }, + { XMM7360_RPC_MSG_ARG_TYPE_LONG, { .l = 0 } }, + { XMM7360_RPC_MSG_ARG_TYPE_LONG, { .l = 0 } }, + { XMM7360_RPC_MSG_ARG_TYPE_LONG, { .l = 0 } }, + { XMM7360_RPC_MSG_ARG_TYPE_LONG, { .l = 0 } }, + { XMM7360_RPC_MSG_ARG_TYPE_LONG, { .l = 0 } }, + { XMM7360_RPC_MSG_ARG_TYPE_LONG, { .l = 0 } }, + { XMM7360_RPC_MSG_ARG_TYPE_LONG, { .l = 0 } }, + { XMM7360_RPC_MSG_ARG_TYPE_LONG, { .l = 0 } }, + { XMM7360_RPC_MSG_ARG_TYPE_LONG, { .l = 0 } }, + { XMM7360_RPC_MSG_ARG_TYPE_LONG, { .l = 0 } }, + { XMM7360_RPC_MSG_ARG_TYPE_LONG, { .l = 0 } }, + { XMM7360_RPC_MSG_ARG_TYPE_LONG, { .l = 0 } }, + { XMM7360_RPC_MSG_ARG_TYPE_LONG, { .l = 0 } }, + { XMM7360_RPC_MSG_ARG_TYPE_LONG, { .l = 0 } }, + { XMM7360_RPC_MSG_ARG_TYPE_LONG, { .l = 0 } }, + { XMM7360_RPC_MSG_ARG_TYPE_LONG, { .l = 0 } }, + { XMM7360_RPC_MSG_ARG_TYPE_LONG, { .l = 0 } }, + { XMM7360_RPC_MSG_ARG_TYPE_LONG, { .l = 0 } }, + { XMM7360_RPC_MSG_ARG_TYPE_LONG, { .l = 0 } }, + { XMM7360_RPC_MSG_ARG_TYPE_LONG, { .l = 0 } }, + { XMM7360_RPC_MSG_ARG_TYPE_STRING, { .string = zeroes }, 20, 0 }, + { XMM7360_RPC_MSG_ARG_TYPE_LONG, { .l = 0 } }, + { XMM7360_RPC_MSG_ARG_TYPE_STRING, { .string = zeroes }, 101, 3 }, + { XMM7360_RPC_MSG_ARG_TYPE_STRING, { .string = zeroes }, 257, 3 }, + { XMM7360_RPC_MSG_ARG_TYPE_LONG, { .l = auth_type } }, + { XMM7360_RPC_MSG_ARG_TYPE_STRING, { .string = password_padded }, 65, 1 }, + { XMM7360_RPC_MSG_ARG_TYPE_STRING, { .string = user_padded }, 65, 0 }, + { XMM7360_RPC_MSG_ARG_TYPE_STRING, { .string = zeroes }, 250, 0 }, + { XMM7360_RPC_MSG_ARG_TYPE_BYTE, { .b = 0 } }, + { XMM7360_RPC_MSG_ARG_TYPE_STRING, { .string = zeroes }, 250, 2 }, + { XMM7360_RPC_MSG_ARG_TYPE_SHORT, { .s = 0 } }, + { XMM7360_RPC_MSG_ARG_TYPE_LONG, { .l = 0 } }, + { XMM7360_RPC_MSG_ARG_TYPE_LONG, { .l = 0 } }, + { XMM7360_RPC_MSG_ARG_TYPE_LONG, { .l = 0 } }, + { XMM7360_RPC_MSG_ARG_TYPE_LONG, { .l = 0 } }, + { XMM7360_RPC_MSG_ARG_TYPE_LONG, { .l = 1 } }, + { XMM7360_RPC_MSG_ARG_TYPE_LONG, { .l = 0 } }, + { XMM7360_RPC_MSG_ARG_TYPE_LONG, { .l = 0 } }, + { XMM7360_RPC_MSG_ARG_TYPE_LONG, { .l = 0 } }, + { XMM7360_RPC_MSG_ARG_TYPE_LONG, { .l = 0 } }, + { XMM7360_RPC_MSG_ARG_TYPE_LONG, { .l = 0 } }, + { XMM7360_RPC_MSG_ARG_TYPE_LONG, { .l = 0 } }, + { XMM7360_RPC_MSG_ARG_TYPE_LONG, { .l = 0 } }, + { XMM7360_RPC_MSG_ARG_TYPE_LONG, { .l = 1 } }, + { XMM7360_RPC_MSG_ARG_TYPE_LONG, { .l = 0 } }, + { XMM7360_RPC_MSG_ARG_TYPE_LONG, { .l = 0 } }, + { XMM7360_RPC_MSG_ARG_TYPE_LONG, { .l = 0x404 } }, + { XMM7360_RPC_MSG_ARG_TYPE_LONG, { .l = 1 } }, + { XMM7360_RPC_MSG_ARG_TYPE_LONG, { .l = 0 } }, + { XMM7360_RPC_MSG_ARG_TYPE_LONG, { .l = 1 } }, + { XMM7360_RPC_MSG_ARG_TYPE_LONG, { .l = 0 } }, + { XMM7360_RPC_MSG_ARG_TYPE_LONG, { .l = 0 } }, + { XMM7360_RPC_MSG_ARG_TYPE_STRING, { .string = zeroes }, 20, 0 }, + { XMM7360_RPC_MSG_ARG_TYPE_LONG, { .l = 3 } }, + { XMM7360_RPC_MSG_ARG_TYPE_STRING, { .string = apn_padded }, 101, 3 }, + { XMM7360_RPC_MSG_ARG_TYPE_STRING, { .string = zeroes }, 257, 3 }, + { XMM7360_RPC_MSG_ARG_TYPE_LONG, { .l = auth_type } }, + { XMM7360_RPC_MSG_ARG_TYPE_STRING, { .string = password_padded }, 65, 1 }, + { XMM7360_RPC_MSG_ARG_TYPE_STRING, { .string = user_padded }, 65, 0 }, + { XMM7360_RPC_MSG_ARG_TYPE_STRING, { .string = zeroes }, 250, 0 }, + { XMM7360_RPC_MSG_ARG_TYPE_BYTE, { .b = 0 } }, + { XMM7360_RPC_MSG_ARG_TYPE_STRING, { .string = zeroes }, 250, 2 }, + { XMM7360_RPC_MSG_ARG_TYPE_SHORT, { .s = 0 } }, + { XMM7360_RPC_MSG_ARG_TYPE_LONG, { .l = 0 } }, + { XMM7360_RPC_MSG_ARG_TYPE_LONG, { .l = 0 } }, + { XMM7360_RPC_MSG_ARG_TYPE_LONG, { .l = 0 } }, + { XMM7360_RPC_MSG_ARG_TYPE_LONG, { .l = 0 } }, + { XMM7360_RPC_MSG_ARG_TYPE_LONG, { .l = 1 } }, + { XMM7360_RPC_MSG_ARG_TYPE_LONG, { .l = 0 } }, + { XMM7360_RPC_MSG_ARG_TYPE_LONG, { .l = 0 } }, + { XMM7360_RPC_MSG_ARG_TYPE_LONG, { .l = 0 } }, + { XMM7360_RPC_MSG_ARG_TYPE_LONG, { .l = 0 } }, + { XMM7360_RPC_MSG_ARG_TYPE_LONG, { .l = 0 } }, + { XMM7360_RPC_MSG_ARG_TYPE_LONG, { .l = 0 } }, + { XMM7360_RPC_MSG_ARG_TYPE_LONG, { .l = 0 } }, + { XMM7360_RPC_MSG_ARG_TYPE_LONG, { .l = 1 } }, + { XMM7360_RPC_MSG_ARG_TYPE_LONG, { .l = 0 } }, + { XMM7360_RPC_MSG_ARG_TYPE_LONG, { .l = 0 } }, + { XMM7360_RPC_MSG_ARG_TYPE_LONG, { .l = 0x404 } }, + { XMM7360_RPC_MSG_ARG_TYPE_LONG, { .l = 1 } }, + { XMM7360_RPC_MSG_ARG_TYPE_LONG, { .l = 0 } }, + { XMM7360_RPC_MSG_ARG_TYPE_LONG, { .l = 1 } }, + { XMM7360_RPC_MSG_ARG_TYPE_LONG, { .l = 0 } }, + { XMM7360_RPC_MSG_ARG_TYPE_LONG, { .l = 0 } }, + { XMM7360_RPC_MSG_ARG_TYPE_STRING, { .string = zeroes }, 20, 0 }, + { XMM7360_RPC_MSG_ARG_TYPE_LONG, { .l = 3 } }, + { XMM7360_RPC_MSG_ARG_TYPE_STRING, { .string = apn_padded }, 101, 2 }, + { XMM7360_RPC_MSG_ARG_TYPE_BYTE, { .b = 3 } }, + { XMM7360_RPC_MSG_ARG_TYPE_LONG, { .l = 0 } }, + { XMM7360_RPC_MSG_ARG_TYPE_UNKNOWN }, + }; + return xmm7360_rpc_args_to_byte_array (args); +} + +static GByteArray * +pack_uta_ms_call_ps_attach_apn_config_req (const gchar *apn, + MMBearerAllowedAuth allowed_auth, + const gchar *user, + const gchar *password) +{ + gchar apn_padded[102] = { 0 }; + gchar user_padded[66] = { 0 }; + gchar password_padded[66] = { 0 }; + ApnAuthType auth_type; + + if (apn != NULL) + g_strlcpy (apn_padded, apn, sizeof (apn_padded)); + if (user != NULL) + g_strlcpy (user_padded, user, sizeof (user_padded)); + if (password != NULL) + g_strlcpy (password_padded, password, sizeof (password_padded)); + + if (allowed_auth & MM_BEARER_ALLOWED_AUTH_NONE) { + auth_type = APN_AUTH_TYPE_NONE; + } else if (allowed_auth & MM_BEARER_ALLOWED_AUTH_PAP) { + auth_type = APN_AUTH_TYPE_PAP; + } else if (allowed_auth & MM_BEARER_ALLOWED_AUTH_CHAP) { + auth_type = APN_AUTH_TYPE_CHAP; + } else { + gchar *str; + + str = mm_bearer_allowed_auth_build_string_from_mask (allowed_auth); + mm_obj_dbg (NULL, + "Specified APN authentication methods unknown (%s)." + " Falling back to default method (none).", + str); + auth_type = APN_AUTH_TYPE_NONE; + g_free (str); + } + + return padded_pack_uta_ms_call_ps_attach_apn_config_req (apn_padded, + auth_type, + user_padded, + password_padded); +} + +static void +modem_3gpp_set_initial_eps_bearer_settings (MMIfaceModem3gpp *_self, + MMBearerProperties *config, + GAsyncReadyCallback callback, + gpointer user_data) +{ + MMBroadbandModemXmm7360 *self = MM_BROADBAND_MODEM_XMM7360 (_self); + g_autoptr(MMPortSerialXmmrpcXmm7360) port = NULL; + GTask *task; + g_autoptr(GByteArray) body = NULL; + + port = mm_broadband_modem_xmm7360_get_port_xmmrpc (self); + + task = g_task_new (self, NULL, callback, user_data); + + body = pack_uta_ms_call_ps_attach_apn_config_req (mm_bearer_properties_get_apn (config), + mm_bearer_properties_get_allowed_auth (config), + mm_bearer_properties_get_user (config), + mm_bearer_properties_get_password (config)); + + mm_broadband_modem_xmm7360_rpc_command_full (self, + port, + XMM7360_RPC_CALL_UTA_MS_CALL_PS_ATTACH_APN_CONFIG_REQ, + TRUE, + body, + 3, + FALSE, + NULL, /* cancellable */ + (GAsyncReadyCallback)attach_apn_config_ready, + task); +} + +/*****************************************************************************/ + +MMPortSerialXmmrpcXmm7360 * +mm_broadband_modem_xmm7360_peek_port_xmmrpc (MMBroadbandModemXmm7360 *self) +{ + MMPortSerialXmmrpcXmm7360 *primary_xmmrpc_port = NULL; + GList *xmmrpc_ports; + + g_assert (MM_IS_BROADBAND_MODEM_XMM7360 (self)); + + xmmrpc_ports = mm_base_modem_find_ports (MM_BASE_MODEM (self), + MM_PORT_SUBSYS_UNKNOWN, + MM_PORT_TYPE_XMMRPC); + + /* First XMMRPC port in the list is the primary one always */ + if (xmmrpc_ports) { + primary_xmmrpc_port = mm_port_serial_xmmrpc_xmm7360_new ( + mm_port_get_device (MM_PORT (xmmrpc_ports->data)) + ); + } + + g_list_free_full (xmmrpc_ports, g_object_unref); + + return primary_xmmrpc_port; +} + +MMPortSerialXmmrpcXmm7360 * +mm_broadband_modem_xmm7360_get_port_xmmrpc (MMBroadbandModemXmm7360 *self) +{ + MMPortSerialXmmrpcXmm7360 *primary_xmmrpc_port; + + g_assert (MM_IS_BROADBAND_MODEM_XMM7360 (self)); + + primary_xmmrpc_port = mm_broadband_modem_xmm7360_peek_port_xmmrpc (self); + return (primary_xmmrpc_port ? + MM_PORT_SERIAL_XMMRPC_XMM7360 (g_object_ref (primary_xmmrpc_port)) : + NULL); +} + +/*****************************************************************************/ + +void +mm_broadband_modem_xmm7360_set_unlock_retries (MMBroadbandModemXmm7360 *self, + MMModemLock lock_type, + guint32 remaining_attempts) +{ + g_assert (MM_IS_BROADBAND_MODEM_XMM7360 (self)); + + if (!self->priv->unlock_retries) + self->priv->unlock_retries = mm_unlock_retries_new (); + + /* Interpret 0xffffffff as device not supporting this information. */ + if (remaining_attempts != G_MAXUINT32) + mm_unlock_retries_set (self->priv->unlock_retries, + lock_type, + remaining_attempts); +} + +/*****************************************************************************/ + +typedef struct { + MMPortSerialXmmrpcXmm7360 *port; + gboolean port_dispose; + gboolean unlock; +} CheckFccLockContext; + +static void +check_fcc_lock_context_free (CheckFccLockContext *ctx) +{ + if (ctx->port_dispose) { + mm_port_serial_close (MM_PORT_SERIAL (ctx->port)); + g_clear_object (&ctx->port); + } + g_slice_free (CheckFccLockContext, ctx); +} + +gboolean +mm_broadband_modem_xmm7360_check_fcc_lock_finish (MMBroadbandModemXmm7360 *self, + GAsyncResult *res, + GError **error) +{ + return g_task_propagate_boolean (G_TASK (res), error); +} + +static void +fcc_unlock_ready (MMBroadbandModem *self, + GAsyncResult *res, + GTask *task) +{ + GError *error = NULL; + g_autoptr(Xmm7360RpcResponse) response = NULL; + Xmm7360RpcMsgArg *arg; + + response = mm_broadband_modem_xmm7360_rpc_command_finish (MM_BROADBAND_MODEM_XMM7360 (self), + res, + &error); + + if (error) { + g_task_return_error (task, error); + g_object_unref (task); + return; + } + + if (response->content->len < 1) { + g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, + "Invalid response after answering FCC unlock challenge (too short)"); + g_object_unref (task); + return; + } + arg = (Xmm7360RpcMsgArg *) g_ptr_array_index (response->content, 0); + g_assert (arg->type == XMM7360_RPC_MSG_ARG_TYPE_LONG); + if (XMM7360_RPC_MSG_ARG_GET_INT (arg) != 1) { + g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, + "Our answer to the FCC unlock challenge was not accepted"); + g_object_unref (task); + return; + } + + /* successfully unlocked, return FALSE (unlock not required) */ + g_task_return_boolean (task, FALSE); + g_object_unref (task); +} + +static void +fcc_unlock_challenge_ready (MMBroadbandModem *self, + GAsyncResult *res, + GTask *task) +{ + GError *error = NULL; + CheckFccLockContext *ctx; + g_autoptr(Xmm7360RpcResponse) response = NULL; + Xmm7360RpcMsgArg *arg; + gint32 fcc_challenge; + GChecksum *checksum; + guchar salt[] = { 0x3d, 0xf8, 0xc7, 0x19 }; + guint8 digest[32] = { 0 }; + gsize digest_len = 32; + g_autoptr(GByteArray) digest_response = NULL; + + response = mm_broadband_modem_xmm7360_rpc_command_finish (MM_BROADBAND_MODEM_XMM7360 (self), + res, + &error); + + if (error) { + g_task_return_error (task, error); + g_object_unref (task); + return; + } + + arg = (Xmm7360RpcMsgArg *) g_ptr_array_index (response->content, 1); + g_assert (arg->type == XMM7360_RPC_MSG_ARG_TYPE_LONG); + fcc_challenge = XMM7360_RPC_MSG_ARG_GET_INT (arg); + + checksum = g_checksum_new (G_CHECKSUM_SHA256); + g_checksum_update (checksum, (guchar *) &fcc_challenge, 4); + g_checksum_update (checksum, salt, 4); + g_checksum_get_digest (checksum, digest, &digest_len); + g_checksum_free (checksum); + + digest_response = g_byte_array_new (); + xmm7360_byte_array_append_asn_int4 (digest_response, GINT32_FROM_LE (*(gint32 *) digest)); + + ctx = g_task_get_task_data (task); + mm_broadband_modem_xmm7360_rpc_command_full (MM_BROADBAND_MODEM_XMM7360 (self), + ctx->port, + XMM7360_RPC_CALL_CSI_FCC_LOCK_VER_CHALLENGE_REQ, + TRUE, + digest_response, + 3, + FALSE, + NULL, /* cancellable */ + (GAsyncReadyCallback)fcc_unlock_ready, + task); +} + +static void +fcc_lock_query_ready (MMBroadbandModem *self, + GAsyncResult *res, + GTask *task) +{ + GError *error = NULL; + CheckFccLockContext *ctx; + g_autoptr(Xmm7360RpcResponse) response = NULL; + Xmm7360RpcMsgArg *arg; + + response = mm_broadband_modem_xmm7360_rpc_command_finish (MM_BROADBAND_MODEM_XMM7360 (self), + res, + &error); + + if (error) { + g_task_return_error (task, error); + g_object_unref (task); + return; + } + + if (response->content->len < 2) { + g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, + "The response to the FCC check is invalid (too short)"); + g_object_unref (task); + return; + } + + /* second argument is fcc_state */ + arg = (Xmm7360RpcMsgArg *) g_ptr_array_index (response->content, 1); + g_assert (arg->type == XMM7360_RPC_MSG_ARG_TYPE_LONG); + if (XMM7360_RPC_MSG_ARG_GET_INT (arg)) { + /* no FCC unlock required: FCC state is != 0 */ + g_task_return_boolean (task, FALSE); + g_object_unref (task); + return; + } + + /* third argument is fcc_mode */ + arg = (Xmm7360RpcMsgArg *) g_ptr_array_index (response->content, 2); + g_assert (arg->type == XMM7360_RPC_MSG_ARG_TYPE_LONG); + if (!XMM7360_RPC_MSG_ARG_GET_INT (arg)) { + /* no FCC unlock required: FCC mode is == 0 */ + g_task_return_boolean (task, FALSE); + g_object_unref (task); + return; + } + + /* FCC unlock required: FCC mode is != 0 */ + ctx = g_task_get_task_data (task); + + if (!ctx->unlock) { + /* we are told not to unlock, return TRUE (unlock required) */ + g_task_return_boolean (task, TRUE); + g_object_unref (task); + return; + } + + /* request unlock challenge */ + mm_broadband_modem_xmm7360_rpc_command_full (MM_BROADBAND_MODEM_XMM7360 (self), + ctx->port, + XMM7360_RPC_CALL_CSI_FCC_LOCK_GEN_CHALLENGE_REQ, + TRUE, + NULL, /* body */ + 3, + FALSE, + NULL, /* cancellable */ + (GAsyncReadyCallback)fcc_unlock_challenge_ready, + task); +} + +void +mm_broadband_modem_xmm7360_check_fcc_lock (MMBroadbandModemXmm7360 *self, + GAsyncReadyCallback callback, + gpointer user_data, + MMPortSerialXmmrpcXmm7360 *port, + gboolean unlock) +{ + GError *error = NULL; + CheckFccLockContext *ctx; + GTask *task; + + task = g_task_new (self, NULL, callback, user_data); + + ctx = g_slice_new0 (CheckFccLockContext); + ctx->port = port; + ctx->port_dispose = FALSE; + ctx->unlock = unlock; + g_task_set_task_data (task, ctx, (GDestroyNotify)check_fcc_lock_context_free); + + if (!port) { + /* if no port is given, open a new XMMRPC port for the FCC lock query */ + ctx->port = mm_broadband_modem_xmm7360_get_port_xmmrpc (self); + ctx->port_dispose = TRUE; + + if (!mm_port_serial_open (MM_PORT_SERIAL (ctx->port), &error)) { + g_prefix_error (&error, "Couldn't open XMMRPC port during FCC lock query: "); + g_task_return_error (task, error); + g_object_unref (task); + return; + } + } + + mm_broadband_modem_xmm7360_rpc_command_full (MM_BROADBAND_MODEM_XMM7360 (self), + ctx->port, + XMM7360_RPC_CALL_CSI_FCC_LOCK_QUERY_REQ, + TRUE, + NULL, /* body */ + 3, + FALSE, + NULL, /* cancellable */ + (GAsyncReadyCallback)fcc_lock_query_ready, + task); +} + +/*****************************************************************************/ + +typedef struct { + MMPortSerialXmmrpcXmm7360 *port; + guint unsol_handler_id; + guint timeout_id; + gboolean is_fcc_unlocked; + gboolean is_uta_mode_set; + gboolean is_sim_initialized; + gboolean awaiting_uta_mode_set_rsp_cb; +} PowerUpContext; + +static void +power_up_context_free (PowerUpContext *ctx) +{ + if (ctx->timeout_id) + g_source_remove (ctx->timeout_id); + if (ctx->unsol_handler_id) + mm_port_serial_xmmrpc_xmm7360_enable_unsolicited_msg_handler ( + ctx->port, + ctx->unsol_handler_id, + FALSE); + mm_port_serial_close (MM_PORT_SERIAL (ctx->port)); + g_clear_object (&ctx->port); + g_slice_free (PowerUpContext, ctx); +} + +static gboolean +modem_power_up_finish (MMIfaceModem *self, + GAsyncResult *res, + GError **error) +{ + return g_task_propagate_boolean (G_TASK (res), error); +} + +static void +parent_modem_power_up_ready (MMIfaceModem *self, + GAsyncResult *res, + GTask *task) +{ + gboolean parent_res; + GError *error = NULL; + + parent_res = iface_modem_parent->modem_power_up_finish (self, res, &error); + + if (error) { + g_task_return_error (task, error); + g_object_unref (task); + return; + } + + /* parent's result passed here */ + g_task_return_boolean (task, parent_res); + g_object_unref (task); +} + +static void +modem_power_up_ready (MMIfaceModem *self, + GAsyncResult *res, + GTask *task) +{ + GError *error = NULL; + gboolean success; + + success = g_task_propagate_boolean (G_TASK (res), &error); + if (error || !success) { + if (error) { + g_task_return_error (task, error); + } else { + g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, + "Powering up XMM7360 failed (unknown reason)"); + } + g_object_unref (task); + return; + } + + iface_modem_parent->modem_power_up ( + self, + (GAsyncReadyCallback)parent_modem_power_up_ready, + task); +} + +static gboolean +power_up_timeout_cb (GTask *task) +{ + MMBroadbandModemXmm7360 *self; + PowerUpContext *ctx; + + ctx = g_task_get_task_data (task); + self = g_task_get_source_object (task); + + if (ctx->is_uta_mode_set) { + if (!ctx->is_sim_initialized) { + /* this can happen if the device was initialized before */ + mm_obj_warn (self, "Waiting for SIM init timed out (trying to continue anyway...)"); + g_task_return_boolean (task, TRUE); + } + } else { + g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, + "Initialization timed out (waiting for UTA mode)"); + } + g_object_unref (task); + + return G_SOURCE_REMOVE; +} + +static void +uta_mode_set_ready (MMBroadbandModem *self, + GAsyncResult *res, + GTask *task) +{ + GError *error = NULL; + PowerUpContext *ctx; + g_autoptr(Xmm7360RpcResponse) response = NULL; + + response = mm_broadband_modem_xmm7360_rpc_command_finish (MM_BROADBAND_MODEM_XMM7360 (self), + res, + &error); + + if (error) { + g_task_return_error (task, error); + g_object_unref (task); + return; + } + + if (response->content->len < 1) { + g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, + "Invalid response setting UTA mode (too short)"); + g_object_unref (task); + return; + } + + ctx = g_task_get_task_data (task); + ctx->awaiting_uta_mode_set_rsp_cb = TRUE; + ctx->timeout_id = g_timeout_add_seconds (5, (GSourceFunc)power_up_timeout_cb, task); +} + +static GByteArray * +pack_uta_mode_set (gint32 mode) +{ + const Xmm7360RpcMsgArg args[] = { + { XMM7360_RPC_MSG_ARG_TYPE_LONG, { .l = 0 } }, + { XMM7360_RPC_MSG_ARG_TYPE_LONG, { .l = 15 } }, + { XMM7360_RPC_MSG_ARG_TYPE_LONG, { .l = mode } }, + { XMM7360_RPC_MSG_ARG_TYPE_UNKNOWN }, + }; + return xmm7360_rpc_args_to_byte_array (args); +} + +static void +check_fcc_lock_ready (MMBroadbandModem *self, + GAsyncResult *res, + GTask *task) +{ + GError *error = NULL; + gboolean locked; + PowerUpContext *ctx; + g_autoptr(GByteArray) body = NULL; + + locked = mm_broadband_modem_xmm7360_check_fcc_lock_finish (MM_BROADBAND_MODEM_XMM7360 (self), + res, + &error); + + if (error) { + g_task_return_error (task, error); + g_object_unref (task); + return; + } + + if (locked) { + g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_RETRY, + "Modem is FCC locked."); + g_object_unref (task); + return; + } + + ctx = g_task_get_task_data (task); + ctx->is_fcc_unlocked = TRUE; + + body = pack_uta_mode_set (1); + + mm_broadband_modem_xmm7360_rpc_command_full (MM_BROADBAND_MODEM_XMM7360 (self), + ctx->port, + XMM7360_RPC_CALL_UTA_MODE_SET_REQ, + FALSE, + body, + 3, + FALSE, + NULL, /* cancellable */ + (GAsyncReadyCallback)uta_mode_set_ready, + task); +} + +static gboolean +power_up_unsol_handler (MMPortSerialXmmrpcXmm7360 *port, + Xmm7360RpcResponse *response, + GTask *task) +{ + PowerUpContext *ctx; + Xmm7360RpcMsgArg *arg; + gint32 value; + + ctx = g_task_get_task_data (task); + + if (response->unsol_id == XMM7360_RPC_UNSOL_UTA_MS_SIM_INIT_IND_CB) { + ctx->is_sim_initialized = TRUE; + } else if (response->unsol_id == XMM7360_RPC_UNSOL_UTA_MODE_SET_RSP_CB) { + if (!ctx->awaiting_uta_mode_set_rsp_cb) { + mm_obj_dbg (port, "Ignoring premature MODE_SET_RSP_CB ..."); + return TRUE; + } + + arg = (Xmm7360RpcMsgArg *) g_ptr_array_index (response->content, 0); + + if (arg->type != XMM7360_RPC_MSG_ARG_TYPE_LONG) { + g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, + "The response to the UTA mode-set is invalid (wrong type)"); + g_object_unref (task); + return TRUE; + } + + value = XMM7360_RPC_MSG_ARG_GET_INT (arg); + if (value != 1) { + g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, + "Setting UTA mode failed (wrong value: %d)", value); + g_object_unref (task); + return TRUE; + } + + ctx->is_uta_mode_set = TRUE; + } else { + return FALSE; + } + + /* we check each time since the two messages might come in any order */ + if (ctx->awaiting_uta_mode_set_rsp_cb && ctx->is_uta_mode_set && ctx->is_sim_initialized) { + g_task_return_boolean (task, TRUE); + g_object_unref (task); + } + + return TRUE; +} + +static void +modem_power_up (MMIfaceModem *iface, + GAsyncReadyCallback callback, + gpointer user_data) +{ + GError *error = NULL; + MMBroadbandModemXmm7360 *self = MM_BROADBAND_MODEM_XMM7360 (iface); + PowerUpContext *ctx; + GTask *task; + + task = g_task_new (self, + NULL, + (GAsyncReadyCallback)modem_power_up_ready, + g_task_new (self, NULL, callback, user_data)); + + ctx = g_slice_new0 (PowerUpContext); + ctx->port = mm_broadband_modem_xmm7360_get_port_xmmrpc (self); + ctx->is_fcc_unlocked = FALSE; + ctx->is_uta_mode_set = FALSE; + ctx->is_sim_initialized = FALSE; + ctx->awaiting_uta_mode_set_rsp_cb = FALSE; + ctx->timeout_id = 0; + g_task_set_task_data (task, ctx, (GDestroyNotify)power_up_context_free); + + /* Open XMMRPC port for power-up */ + if (!mm_port_serial_open (MM_PORT_SERIAL (ctx->port), &error)) { + g_prefix_error (&error, "Couldn't open XMMRPC port during power-up: "); + g_task_return_error (task, error); + g_object_unref (task); + return; + } + + ctx->unsol_handler_id = mm_port_serial_xmmrpc_xmm7360_add_unsolicited_msg_handler ( + ctx->port, + (MMPortSerialXmmrpcXmm7360UnsolicitedMsgFn)power_up_unsol_handler, + task, + NULL); + + mm_broadband_modem_xmm7360_check_fcc_lock (MM_BROADBAND_MODEM_XMM7360 (self), + (GAsyncReadyCallback)check_fcc_lock_ready, + task, + ctx->port, + FALSE); +} + +/*****************************************************************************/ + +typedef struct { + MMPortSerialXmmrpcXmm7360 *port; + guint unsol_handler_id; + guint timeout_id; + gboolean is_sim_initialized; +} InitializationStartedContext; + +static void +initialization_started_context_free (InitializationStartedContext *ctx) +{ + if (ctx->timeout_id) + g_source_remove (ctx->timeout_id); + if (ctx->unsol_handler_id) + mm_port_serial_xmmrpc_xmm7360_enable_unsolicited_msg_handler ( + ctx->port, + ctx->unsol_handler_id, + FALSE); + mm_port_serial_close (MM_PORT_SERIAL (ctx->port)); + g_clear_object (&ctx->port); + g_slice_free (InitializationStartedContext, ctx); +} + +static gpointer +initialization_started_finish (MMBroadbandModem *self, + GAsyncResult *res, + GError **error) +{ + return g_task_propagate_pointer (G_TASK (res), error); +} + +static void +parent_initialization_started_ready (MMBroadbandModem *self, + GAsyncResult *res, + GTask *task) +{ + gpointer parent_ctx; + GError *error = NULL; + + parent_ctx = MM_BROADBAND_MODEM_CLASS (mm_broadband_modem_xmm7360_parent_class)->initialization_started_finish ( + self, + res, + &error); + if (error) { + g_task_return_error (task, error); + g_object_unref (task); + return; + } + + /* Just parent's pointer passed here */ + g_task_return_pointer (task, parent_ctx, NULL); + g_object_unref (task); +} + +static void +initialization_started_ready (MMBroadbandModem *self, + GAsyncResult *res, + GTask *task) +{ + GError *error = NULL; + gboolean success; + + success = g_task_propagate_boolean (G_TASK (res), &error); + if (error || !success) { + if (error) { + g_task_return_error (task, error); + } else { + g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, + "Initializing XMM7360 failed (unknown reason)"); + } + g_object_unref (task); + return; + } + + MM_BROADBAND_MODEM_CLASS (mm_broadband_modem_xmm7360_parent_class)->initialization_started ( + self, + (GAsyncReadyCallback)parent_initialization_started_ready, + task); +} + +static gboolean +init_timeout_cb (GTask *task) +{ + MMBroadbandModemXmm7360 *self; + InitializationStartedContext *ctx; + + ctx = g_task_get_task_data (task); + self = g_task_get_source_object (task); + + if (!ctx->is_sim_initialized) { + /* this can happen if the device was initialized before */ + mm_obj_warn (self, "Waiting for SIM init timed out (trying to continue anyway...)"); + g_task_return_boolean (task, TRUE); + } + + g_object_unref (task); + + return G_SOURCE_REMOVE; +} + +static void +init_sequence_ready (MMBroadbandModem *self, + GAsyncResult *res, + GTask *task) +{ + GError *error = NULL; + g_autoptr(Xmm7360RpcResponse) response = NULL; + InitializationStartedContext *ctx; + + response = mm_broadband_modem_xmm7360_rpc_sequence_finish (MM_BROADBAND_MODEM_XMM7360 (self), + res, + &error); + + if (error) { + g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, + "Failed to complete init sequence: %s", error->message); + g_object_unref (task); + return; + } + + ctx = g_task_get_task_data (task); + ctx->timeout_id = g_timeout_add_seconds (5, (GSourceFunc)init_timeout_cb, task); +} + +static gboolean +init_unsol_handler (MMPortSerialXmmrpcXmm7360 *port, + Xmm7360RpcResponse *response, + GTask *task) +{ + InitializationStartedContext *ctx; + + ctx = g_task_get_task_data (task); + + if (response->unsol_id != XMM7360_RPC_UNSOL_UTA_MS_SIM_INIT_IND_CB) { + return FALSE; + } + + ctx->is_sim_initialized = TRUE; + g_task_return_boolean (task, TRUE); + g_object_unref (task); + return TRUE; +} + +static const Xmm7360RpcMsgArg set_radio_signal_reporting_args[] = { + { XMM7360_RPC_MSG_ARG_TYPE_LONG, { .l = 1 } }, + { XMM7360_RPC_MSG_ARG_TYPE_LONG, { .l = 0 } }, + { XMM7360_RPC_MSG_ARG_TYPE_UNKNOWN }, +}; + +static const MMBroadbandModemXmm7360RpcCommand init_sequence[] = { + { XMM7360_RPC_CALL_UTA_MS_SMS_INIT, FALSE, NULL, 3, FALSE, mm_broadband_modem_xmm7360_rpc_response_processor_continue_on_success }, + { XMM7360_RPC_CALL_UTA_MS_CBS_INIT, FALSE, NULL, 3, FALSE, mm_broadband_modem_xmm7360_rpc_response_processor_continue_on_success }, + { XMM7360_RPC_CALL_UTA_MS_NET_OPEN, FALSE, NULL, 3, FALSE, mm_broadband_modem_xmm7360_rpc_response_processor_continue_on_success }, + { XMM7360_RPC_CALL_UTA_MS_NET_SET_RADIO_SIGNAL_REPORTING, FALSE, set_radio_signal_reporting_args, 3, FALSE, mm_broadband_modem_xmm7360_rpc_response_processor_continue_on_success}, + { XMM7360_RPC_CALL_UTA_MS_CALL_CS_INIT, FALSE, NULL, 3, FALSE, mm_broadband_modem_xmm7360_rpc_response_processor_continue_on_success }, + { XMM7360_RPC_CALL_UTA_MS_CALL_PS_INITIALIZE, FALSE, NULL, 3, FALSE, mm_broadband_modem_xmm7360_rpc_response_processor_continue_on_success }, + { XMM7360_RPC_CALL_UTA_MS_SS_INIT, FALSE, NULL, 3, FALSE, mm_broadband_modem_xmm7360_rpc_response_processor_continue_on_success }, + { XMM7360_RPC_CALL_UTA_MS_SIM_OPEN_REQ, FALSE, NULL, 3, FALSE, mm_broadband_modem_xmm7360_rpc_response_processor_final }, + { 0 } +}; + +static void +initialization_started (MMBroadbandModem *modem, + GAsyncReadyCallback callback, + gpointer user_data) +{ + GError *error = NULL; + MMBroadbandModemXmm7360 *self = MM_BROADBAND_MODEM_XMM7360 (modem); + InitializationStartedContext *ctx; + GTask *task; + + task = g_task_new (self, + NULL, + (GAsyncReadyCallback)initialization_started_ready, + g_task_new (self, NULL, callback, user_data)); + + ctx = g_slice_new0 (InitializationStartedContext); + ctx->port = mm_broadband_modem_xmm7360_get_port_xmmrpc (self); + ctx->is_sim_initialized = FALSE; + ctx->timeout_id = 0; + g_task_set_task_data (task, ctx, (GDestroyNotify)initialization_started_context_free); + + /* Open XMMRPC port for initialization */ + if (!mm_port_serial_open (MM_PORT_SERIAL (ctx->port), &error)) { + g_prefix_error (&error, "Couldn't open XMMRPC port during initialization: "); + g_task_return_error (task, error); + g_object_unref (task); + return; + } + + ctx->unsol_handler_id = mm_port_serial_xmmrpc_xmm7360_add_unsolicited_msg_handler ( + ctx->port, + (MMPortSerialXmmrpcXmm7360UnsolicitedMsgFn)init_unsol_handler, + task, + NULL); + + mm_obj_dbg (self, "running init sequence..."); + mm_broadband_modem_xmm7360_rpc_sequence_full (MM_BROADBAND_MODEM_XMM7360 (self), + ctx->port, + init_sequence, + NULL, /* cancellable */ + (GAsyncReadyCallback)init_sequence_ready, + task); +} + +/*****************************************************************************/ + +MMBroadbandModemXmm7360 * +mm_broadband_modem_xmm7360_new (const gchar *device, + const gchar *physdev, + const gchar **drivers, + const gchar *plugin, + guint16 vendor_id, + guint16 product_id) +{ + return g_object_new (MM_TYPE_BROADBAND_MODEM_XMM7360, + MM_BASE_MODEM_DEVICE, device, + MM_BASE_MODEM_PHYSDEV, physdev, + MM_BASE_MODEM_DRIVERS, drivers, + MM_BASE_MODEM_PLUGIN, plugin, + MM_BASE_MODEM_VENDOR_ID, vendor_id, + MM_BASE_MODEM_PRODUCT_ID, product_id, + MM_BASE_MODEM_DATA_NET_SUPPORTED, TRUE, + MM_BASE_MODEM_DATA_TTY_SUPPORTED, TRUE, + NULL); +} + +static void +mm_broadband_modem_xmm7360_init (MMBroadbandModemXmm7360 *self) +{ + /* Initialize private data */ + self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, + MM_TYPE_BROADBAND_MODEM_XMM7360, + MMBroadbandModemXmm7360Private); +} + +static void +dispose (GObject *object) +{ + MMBroadbandModemXmm7360 *self = MM_BROADBAND_MODEM_XMM7360 (object); + + g_clear_object (&self->priv->unlock_retries); + + G_OBJECT_CLASS (mm_broadband_modem_xmm7360_parent_class)->dispose (object); +} + + +static void +iface_modem_init (MMIfaceModemInterface *iface) +{ + iface_modem_parent = g_type_interface_peek_parent (iface); + iface->modem_power_up = modem_power_up; + iface->modem_power_up_finish = modem_power_up_finish; + iface->create_bearer = create_bearer; + iface->create_bearer_finish = create_bearer_finish; + iface->create_sim = create_sim; + iface->create_sim_finish = create_sim_finish; +} + +static void +iface_modem_3gpp_init (MMIfaceModem3gppInterface *iface) +{ + iface->set_initial_eps_bearer_settings = modem_3gpp_set_initial_eps_bearer_settings; + iface->set_initial_eps_bearer_settings_finish = modem_3gpp_set_initial_eps_bearer_settings_finish; +} + +static void +iface_modem_location_init (MMIfaceModemLocationInterface *iface) +{ + /* asking for location capabilities can destabilize the device */ + iface->load_capabilities = NULL; + iface->load_capabilities_finish = NULL; + iface->enable_location_gathering = NULL; + iface->enable_location_gathering_finish = NULL; +} + +static void +mm_broadband_modem_xmm7360_class_init (MMBroadbandModemXmm7360Class *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + MMBroadbandModemClass *broadband_modem_class = MM_BROADBAND_MODEM_CLASS (klass); + + g_type_class_add_private (object_class, sizeof (MMBroadbandModemXmm7360Private)); + + object_class->dispose = dispose; + + broadband_modem_class->initialization_started = initialization_started; + broadband_modem_class->initialization_started_finish = initialization_started_finish; +} diff --git a/src/plugins/intel/mm-broadband-modem-xmm7360.h b/src/plugins/intel/mm-broadband-modem-xmm7360.h new file mode 100644 index 00000000..99f68abf --- /dev/null +++ b/src/plugins/intel/mm-broadband-modem-xmm7360.h @@ -0,0 +1,71 @@ +/* -*- 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 + */ + +#ifndef MM_BROADBAND_MODEM_XMM7360_H +#define MM_BROADBAND_MODEM_XMM7360_H + +#include "mm-broadband-modem.h" +#include "mm-port-serial-xmmrpc-xmm7360.h" + +#define MM_TYPE_BROADBAND_MODEM_XMM7360 (mm_broadband_modem_xmm7360_get_type ()) +#define MM_BROADBAND_MODEM_XMM7360(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MM_TYPE_BROADBAND_MODEM_XMM7360, MMBroadbandModemXmm7360)) +#define MM_BROADBAND_MODEM_XMM7360_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), MM_TYPE_BROADBAND_MODEM_XMM7360, MMBroadbandModemXmm7360Class)) +#define MM_IS_BROADBAND_MODEM_XMM7360(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MM_TYPE_BROADBAND_MODEM_XMM7360)) +#define MM_IS_BROADBAND_MODEM_XMM7360_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), MM_TYPE_BROADBAND_MODEM_XMM7360)) +#define MM_BROADBAND_MODEM_XMM7360_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), MM_TYPE_BROADBAND_MODEM_XMM7360, MMBroadbandModemXmm7360Class)) + +typedef struct _MMBroadbandModemXmm7360 MMBroadbandModemXmm7360; +typedef struct _MMBroadbandModemXmm7360Class MMBroadbandModemXmm7360Class; +typedef struct _MMBroadbandModemXmm7360Private MMBroadbandModemXmm7360Private; + +struct _MMBroadbandModemXmm7360 { + MMBroadbandModem parent; + MMBroadbandModemXmm7360Private *priv; +}; + +struct _MMBroadbandModemXmm7360Class{ + MMBroadbandModemClass parent; +}; + +GType mm_broadband_modem_xmm7360_get_type (void); + +MMBroadbandModemXmm7360 *mm_broadband_modem_xmm7360_new (const gchar *device, + const gchar *physdev, + const gchar **driver, + const gchar *plugin, + guint16 vendor_id, + guint16 product_id); + +MMPortSerialXmmrpcXmm7360 *mm_broadband_modem_xmm7360_get_port_xmmrpc (MMBroadbandModemXmm7360 *self); +MMPortSerialXmmrpcXmm7360 *mm_broadband_modem_xmm7360_peek_port_xmmrpc (MMBroadbandModemXmm7360 *self); + +void mm_broadband_modem_xmm7360_check_fcc_lock (MMBroadbandModemXmm7360 *self, + GAsyncReadyCallback callback, + gpointer user_data, + MMPortSerialXmmrpcXmm7360 *port, + gboolean unlock); + +gboolean mm_broadband_modem_xmm7360_check_fcc_lock_finish (MMBroadbandModemXmm7360 *self, + GAsyncResult *res, + GError **error); + +void mm_broadband_modem_xmm7360_set_unlock_retries (MMBroadbandModemXmm7360 *self, + MMModemLock lock_type, + guint32 remaining_attempts); + +#endif /* MM_BROADBAND_MODEM_XMM7360_H */ diff --git a/src/plugins/intel/mm-plugin-intel.c b/src/plugins/intel/mm-plugin-intel.c index 20392114..571c0a0a 100644 --- a/src/plugins/intel/mm-plugin-intel.c +++ b/src/plugins/intel/mm-plugin-intel.c @@ -11,6 +11,7 @@ * GNU General Public License for more details: * * Copyright (C) 2021-2022 Intel Corporation + * Copyright (c) 2024 Thomas Vogt */ #include <stdio.h> @@ -23,6 +24,7 @@ #include "mm-log-object.h" #include "mm-broadband-modem.h" +#include "mm-broadband-modem-xmm7360.h" #if defined WITH_MBIM #include "mm-broadband-modem-mbim-intel.h" #endif @@ -57,15 +59,19 @@ create_modem (MMPlugin *self, #endif if (mm_port_probe_list_has_xmmrpc_port (probes)) { - mm_obj_dbg (self, "Intel modem with RPC control port found..."); if (product == 0x7360) { - g_set_error_literal (error, MM_CORE_ERROR, MM_CORE_ERROR_UNSUPPORTED, - "Intel XMM7360 in RPC mode not supported"); - return NULL; + mm_obj_dbg (self, "Intel XMM7360 in RPC mode found..."); + return MM_BASE_MODEM (mm_broadband_modem_xmm7360_new (uid, + physdev, + drivers, + mm_plugin_get_name (self), + vendor, + product)); + } else { + mm_obj_dbg (self, "Ignoring unknown XMMRPC control port..."); } } - mm_obj_dbg (self, "Generic Intel modem found..."); return MM_BASE_MODEM (mm_broadband_modem_new (uid, physdev, 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; +} diff --git a/src/plugins/intel/mm-port-serial-xmmrpc-xmm7360.h b/src/plugins/intel/mm-port-serial-xmmrpc-xmm7360.h new file mode 100644 index 00000000..59cc0dd6 --- /dev/null +++ b/src/plugins/intel/mm-port-serial-xmmrpc-xmm7360.h @@ -0,0 +1,590 @@ +/* -*- 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 + */ + +#ifndef MM_PORT_SERIAL_XMMRPC_XMM7360_H +#define MM_PORT_SERIAL_XMMRPC_XMM7360_H + +#include <glib.h> +#include <glib-object.h> + +#include "mm-port-serial.h" + +typedef enum { + XMM7360_RPC_CALL_UTA_MS_SIM_OPEN_REQ = 0x001, + XMM7360_RPC_CALL_UTA_MS_SIM_APDU_CMD_REQ = 0x002, + XMM7360_RPC_CALL_UTA_MS_SIM_APPLICATION_REQ = 0x004, + XMM7360_RPC_CALL_UTA_MS_SIM_DECODE_FCP = 0x006, + XMM7360_RPC_CALL_UTA_MS_SIM_PB_READ_ENTRY_REQ = 0x00d, + XMM7360_RPC_CALL_UTA_MS_SIM_GEN_PIN_REQ = 0x00f, + XMM7360_RPC_CALL_UTA_MS_SIM_MODIFY_LOCK_REQ = 0x011, + XMM7360_RPC_CALL_UTA_MS_SIM_TK_PROACTIVE_COMMAND_RSP = 0x016, + XMM7360_RPC_CALL_UTA_MS_SIM_TK_ENVELOPE_COMMAND_REQ = 0x017, + XMM7360_RPC_CALL_UTA_MS_SIM_TK_TERMINAL_PROFILE_READ_REQ = 0x019, + XMM7360_RPC_CALL_UTA_MS_SIM_TK_REGISTER_HANDLER = 0x01c, + XMM7360_RPC_CALL_UTA_MS_SIM_TK_DEREGISTER_HANDLER = 0x01d, + XMM7360_RPC_CALL_UTA_MS_CPS_SET_MODE_REQ = 0x01f, + XMM7360_RPC_CALL_UTA_MS_CPS_SET_STACK_MODE_CONFIGURATION = 0x020, + XMM7360_RPC_CALL_UTA_MS_CPS_SET_SIM_MODE_CONFIGURATION = 0x021, + XMM7360_RPC_CALL_UTA_MS_CPS_READ_IMEI = 0x023, + XMM7360_RPC_CALL_UTA_MS_CALL_CS_INIT = 0x024, + XMM7360_RPC_CALL_UTA_MS_CBS_INIT = 0x025, + XMM7360_RPC_CALL_UTA_MS_SS_INIT = 0x026, + XMM7360_RPC_CALL_UTA_MS_SS_SEND_USSD_REQ = 0x027, + XMM7360_RPC_CALL_UTA_MS_SS_RESPOND_USSD = 0x028, + XMM7360_RPC_CALL_UTA_MS_SS_ABORT = 0x029, + XMM7360_RPC_CALL_UTA_MS_SMS_INIT = 0x030, + XMM7360_RPC_CALL_UTA_MS_SMS_SEND_REQ = 0x031, + XMM7360_RPC_CALL_UTA_MS_SMS_SET_MEMORY_AVAILABLE_REQ = 0x034, + XMM7360_RPC_CALL_UTA_MS_SMS_INCOMING_SMS_ACK = 0x036, + XMM7360_RPC_CALL_UTA_MS_SMS_SIM_MSG_COUNT_REQ = 0x038, + XMM7360_RPC_CALL_UTA_MS_CALL_PS_INITIALIZE = 0x03a, + XMM7360_RPC_CALL_UTA_MS_CALL_PS_OBTAIN_PDP_CONTEXT_ID = 0x03b, + XMM7360_RPC_CALL_UTA_MS_CALL_PS_RELEASE_PDP_CONTEXT_ID = 0x03c, + XMM7360_RPC_CALL_UTA_MS_CALL_PS_DEFINE_PRIMARY_REQ = 0x03d, + XMM7360_RPC_CALL_UTA_MS_CALL_PS_UNDEFINE_PRIMARY_REQ = 0x03f, + XMM7360_RPC_CALL_UTA_MS_CALL_PS_GET_PRIMARY_REQ = 0x041, + XMM7360_RPC_CALL_UTA_MS_CALL_PS_SET_AUTHENTICATION_REQ = 0x043, + XMM7360_RPC_CALL_UTA_MS_CALL_PS_SET_DNS_REQ = 0x045, + XMM7360_RPC_CALL_UTA_MS_CALL_PS_GET_NEGOTIATED_DNS_REQ = 0x047, + XMM7360_RPC_CALL_UTA_MS_CALL_PS_GET_NEG_IP_ADDR_REQ = 0x049, + XMM7360_RPC_CALL_UTA_MS_CALL_PS_ACTIVATE_REQ = 0x04b, + XMM7360_RPC_CALL_UTA_MS_CALL_PS_DEACTIVATE_REQ = 0x04e, + XMM7360_RPC_CALL_UTA_MS_CALL_PS_CONNECT_REQ = 0x051, + XMM7360_RPC_CALL_UTA_MS_NET_OPEN = 0x053, + XMM7360_RPC_CALL_UTA_MS_NET_SET_RADIO_SIGNAL_REPORTING = 0x054, + XMM7360_RPC_CALL_UTA_MS_NET_SINGLE_SHOT_RADIO_SIGNAL_REPORTING_REQ = 0x055, + XMM7360_RPC_CALL_UTA_MS_NET_ATTACH_REQ = 0x05c, + XMM7360_RPC_CALL_UTA_MS_NET_PS_ATTACH_REQ = 0x05d, + XMM7360_RPC_CALL_UTA_MS_NET_PS_DETACH_REQ = 0x05e, + XMM7360_RPC_CALL_UTA_MS_NET_SCAN_REQ = 0x05f, + XMM7360_RPC_CALL_UTA_MS_NET_SCAN_ABORT = 0x060, + XMM7360_RPC_CALL_UTA_MS_NET_POWER_DOWN_REQ = 0x061, + XMM7360_RPC_CALL_UTA_MS_NET_EXT_SCAN_REQ = 0x062, + XMM7360_RPC_CALL_UTA_MS_NET_SET_FD_CONFIG_REQ = 0x06e, + XMM7360_RPC_CALL_UTA_MS_NET_GET_FD_CONFIG_REQ = 0x071, + XMM7360_RPC_CALL_UTA_MS_NET_CONFIGURE_NETWORK_MODE_REQ = 0x073, + XMM7360_RPC_CALL_UTA_MS_NET_RAT_MODE_STATUS_REQ = 0x076, + XMM7360_RPC_CALL_UTA_NVM_READ = 0x079, + XMM7360_RPC_CALL_UTA_NVM_WRITE = 0x07a, + XMM7360_RPC_CALL_UTA_NVM_WRITE_COMMIT = 0x07b, + XMM7360_RPC_CALL_UTA_SYS_GET_INFO = 0x07c, + XMM7360_RPC_CALL_UTA_RPC_PS_CONNECT_SETUP_REQ = 0x07d, + XMM7360_RPC_CALL_UTA_RPC_PS_CONNECT_TO_DATACHANNEL_REQ = 0x07e, + XMM7360_RPC_CALL_UTA_RPC_PS_CONNECT_RELEASE_REQ = 0x07f, + XMM7360_RPC_CALL_UTA_MS_NET_DC_SET_VOICE_DOMAIN_PREFERENCE_CONFIG_REQ = 0x080, + XMM7360_RPC_CALL_UTA_MS_CALL_CS_SETUP_VOICE_CALL_REQ = 0x082, + XMM7360_RPC_CALL_UTA_MS_CALL_CS_RELEASE_CALL_REQ = 0x088, + XMM7360_RPC_CALL_UTA_MS_CALL_CS_ACCEPT_CALL_REQ = 0x08d, + XMM7360_RPC_CALL_UTA_MS_CALL_CS_SWAP_CALLS_REQ = 0x090, + XMM7360_RPC_CALL_UTA_MS_CALL_CS_HOLD_CALL_REQ = 0x092, + XMM7360_RPC_CALL_UTA_MS_CALL_CS_RETRIEVE_CALL_REQ = 0x094, + XMM7360_RPC_CALL_UTA_MS_CALL_CS_SPLIT_MPTY_REQ = 0x096, + XMM7360_RPC_CALL_UTA_MS_CALL_CS_JOIN_CALLS_REQ = 0x098, + XMM7360_RPC_CALL_UTA_MS_CALL_CS_TRANSFER_CALLS_REQ = 0x09a, + XMM7360_RPC_CALL_UTA_MS_CALL_CS_START_DTMF_REQ = 0x09c, + XMM7360_RPC_CALL_UTA_MS_CALL_CS_STOP_DTMF_REQ = 0x09e, + XMM7360_RPC_CALL_UTA_MS_CALL_CS_SET_UUS1_INFO = 0x0a6, + XMM7360_RPC_CALL_UTA_MS_CALL_CS_SET_TTY_DEVICE_MODE = 0x0a7, + XMM7360_RPC_CALL_UTA_MS_CALL_CS_GET_TTY_DEVICE_MODE = 0x0a8, + XMM7360_RPC_CALL_UTA_MS_CALL_MULTIMEDIA_SETUP_CALL_REQ = 0x0ac, + XMM7360_RPC_CALL_UTA_MS_CALL_MULTIMEDIA_UPDATE_CALL_REQ = 0x0ad, + XMM7360_RPC_CALL_UTA_MS_CPS_SET_SIM_MODE_REQ = 0x0b0, + XMM7360_RPC_CALL_UTA_MS_SS_CALL_FORWARD_REQ = 0x0b2, + XMM7360_RPC_CALL_UTA_MS_SS_CALL_WAITING_REQ = 0x0b4, + XMM7360_RPC_CALL_UTA_MS_SS_CALL_BARRING_REQ = 0x0b6, + XMM7360_RPC_CALL_UTA_MS_SS_IDENTIFICATION_REQ = 0x0b8, + XMM7360_RPC_CALL_UTA_MS_SMS_SET_SEND_MORE_MESSAGES_STATUS = 0x0ba, + XMM7360_RPC_CALL_UTA_MS_SMS_DATA_DOWNLOAD_REQ = 0x0bb, + XMM7360_RPC_CALL_UTA_MS_SMS_DATA_DOWNLOAD_ACK = 0x0bd, + XMM7360_RPC_CALL_UTA_MS_CALL_PS_GET_NEG_QOS_REQ = 0x0be, + XMM7360_RPC_CALL_UTA_MS_CALL_PS_GET_TFT_REQ = 0x0c0, + XMM7360_RPC_CALL_UTA_MS_CALL_PS_SET_PCO_REQ = 0x0c2, + XMM7360_RPC_CALL_UTA_MS_CALL_PS_GET_NW_PCO_REQ = 0x0c4, + XMM7360_RPC_CALL_UTA_MS_CALL_PS_NW_ACTIVATE_ACCEPT_REQ = 0x0c7, + XMM7360_RPC_CALL_UTA_MS_CALL_PS_NW_ACTIVATE_REJECT_REQ = 0x0c9, + XMM7360_RPC_CALL_UTA_MS_CALL_PS_SET_DATA_PREF_REQ = 0x0cd, + XMM7360_RPC_CALL_UTA_MS_CBS_START_REQ = 0x0cf, + XMM7360_RPC_CALL_UTA_MS_CBS_STOP_REQ = 0x0d0, + XMM7360_RPC_CALL_UTA_MS_CBS_SET_MSG_FILTER = 0x0d3, + XMM7360_RPC_CALL_UTA_MS_CBS_GET_MSG_FILTER = 0x0d4, + XMM7360_RPC_CALL_UTA_MS_CBS_ETWS_CONFIG_REQ = 0x0d6, + XMM7360_RPC_CALL_UTA_MS_CBS_ETWS_START_REQ = 0x0d8, + XMM7360_RPC_CALL_UTA_MS_CBS_ETWS_STOP_REQ = 0x0da, + XMM7360_RPC_CALL_UTA_MS_CPS_NVM_WRITE = 0x0de, + XMM7360_RPC_CALL_UTA_MS_CPS_NVM_READ = 0x0df, + XMM7360_RPC_CALL_UTA_MS_NET_CONFIGURE_RX_DIVERSITY_DARP = 0x0e0, + XMM7360_RPC_CALL_UTA_MS_NET_LDR_GET_APN_PARAMETER_LIST = 0x0e2, + XMM7360_RPC_CALL_UTA_MS_NET_TIME_INFO_READ_REQ = 0x0e3, + XMM7360_RPC_CALL_UTA_MS_NET_SET_CSG_CONFIG_REQ = 0x0e6, + XMM7360_RPC_CALL_UTA_MS_NET_BAND_STATUS_REQ = 0x0e7, + XMM7360_RPC_CALL_UTA_MS_NET_GET_EXTENDED_RADIO_SIGNAL_INFO_REQ = 0x0ec, + XMM7360_RPC_CALL_UTA_MS_NET_DETACH_REQ = 0x0ef, + XMM7360_RPC_CALL_UTA_MS_NET_SELECT_GPRS_CLASS_REQ = 0x0f1, + XMM7360_RPC_CALL_UTA_MS_NET_GET_CSG_CONFIG_REQ = 0x0f3, + XMM7360_RPC_CALL_UTA_MS_NET_CS_SERVICE_NOTIFICATION_ACCEPT = 0x0f4, + XMM7360_RPC_CALL_UTA_MS_NET_SINGLE_SHOT_FD_REQ = 0x0f9, + XMM7360_RPC_CALL_UTA_MS_SIM_PB_LOCATION_REQ = 0x0fb, + XMM7360_RPC_CALL_UTA_MS_SIM_PB_READ_GAS_ENTRY_REQ = 0x0fd, + XMM7360_RPC_CALL_UTA_MS_SIM_PB_WRITE_ENTRY_REQ = 0x0ff, + XMM7360_RPC_CALL_UTA_MS_SIM_PB_GET_META_INFORMATION_REQ = 0x101, + XMM7360_RPC_CALL_UTA_MS_SIM_PB_USIM_PB_SELECT_REQ = 0x103, + XMM7360_RPC_CALL_UTA_MS_SIM_PB_GET_FREE_RECORDS_REQ = 0x105, + XMM7360_RPC_CALL_UTA_MS_SIM_CREATE_READ_BINARY_APDU = 0x10a, + XMM7360_RPC_CALL_UTA_MS_SIM_CREATE_UPDATE_BINARY_APDU = 0x10b, + XMM7360_RPC_CALL_UTA_MS_SIM_ANALYSE_READ_RESULT = 0x10c, + XMM7360_RPC_CALL_UTA_MS_SIM_SET_FDN_REQ = 0x10e, + XMM7360_RPC_CALL_SET_AP_SCREEN_STATE = 0x110, + XMM7360_RPC_CALL_UTA_IO_CTL = 0x111, + XMM7360_RPC_CALL_UTA_IDC_AP_MSG_SET_REQ = 0x114, + XMM7360_RPC_CALL_UTA_IDC_AP_MSG_GET_REQ = 0x115, + XMM7360_RPC_CALL_UTA_IDC_ENBLE_REQ = 0x116, + XMM7360_RPC_CALL_UTA_IDC_CWS_MSG_SET_REQ = 0x119, + XMM7360_RPC_CALL_UTA_IDC_CWS_MSG_GET_REQ = 0x11a, + XMM7360_RPC_CALL_UTA_IDC_SUBSCRIBE_INDICATIONS = 0x11c, + XMM7360_RPC_CALL_UTA_IDC_UNSUBSCRIBE_INDICATIONS = 0x11d, + XMM7360_RPC_CALL_UTA_BOOT_PREPARE_SHUTDOWN_REQ = 0x11f, + XMM7360_RPC_CALL_UTA_BOOT_SHUTDOWN_REQ = 0x120, + XMM7360_RPC_CALL_UTA_RF_MAX_TX_PWR_SET_2G = 0x121, + XMM7360_RPC_CALL_UTA_RF_MAX_TX_PWR_SET_3G = 0x122, + XMM7360_RPC_CALL_UTA_RF_MAX_TX_PWR_SET_4G = 0x123, + XMM7360_RPC_CALL_UTA_FREQ_INFO_ACTIVATE_REQ = 0x128, + XMM7360_RPC_CALL_UTA_FREQ_INFO_GET_FREQ_INFO_REQ = 0x129, + XMM7360_RPC_CALL_UTA_FREQ_INFO_DEACTIVATE_REQ = 0x12a, + XMM7360_RPC_CALL_UTA_FREQ_INFO_REGISTER_INDICATIONS = 0x12b, + XMM7360_RPC_CALL_UTA_FREQ_INFO_DEREGISTER_INDICATIONS = 0x12c, + XMM7360_RPC_CALL_UTA_MODE_SET_REQ = 0x12f, + XMM7360_RPC_CALL_UTA_NVM_FLUSH_SYNC = 0x130, + XMM7360_RPC_CALL_UTA_PROD_REGISTER_GTI_CALLBACK_FUNC = 0x132, + XMM7360_RPC_CALL_UTA_PROD_GTI_CMD_REQ = 0x133, + XMM7360_RPC_CALL_UTA_CELL_TIME_STAMP_REQ = 0x134, + XMM7360_RPC_CALL_UTA_MS_SS_LCS_INIT = 0x136, + XMM7360_RPC_CALL_UTA_MS_SS_LCS_MO_LOCATION_REQ = 0x137, + XMM7360_RPC_CALL_UTA_MS_SS_LCS_MTLR_NOTIFICATION_RSP = 0x139, + XMM7360_RPC_CALL_UTA_MS_CP_ASSISTANCE_DATA_INJECT_REQ = 0x13c, + XMM7360_RPC_CALL_UTA_MS_CP_RESET_ASSISTANCE_DATA = 0x13d, + XMM7360_RPC_CALL_UTA_MS_CP_POS_MEASUREMENT_REQ = 0x140, + XMM7360_RPC_CALL_UTA_MS_CP_POS_MEASUREMENT_ABORT_REQ = 0x142, + XMM7360_RPC_CALL_UTA_MS_CP_POS_ENABLE_MEASUREMENT_REPORT = 0x144, + XMM7360_RPC_CALL_UTA_MS_CP_POS_DISABLE_MEASUREMENT_REPORT = 0x145, + XMM7360_RPC_CALL_UTA_MS_SIM_TK_INIT = 0x146, + XMM7360_RPC_CALL_UTA_MS_SIM_TK_EXEC_SMS_PP_RSP = 0x148, + XMM7360_RPC_CALL_UTA_MS_SIM_TK_EXEC_SIM_INITIATED_CALL_RSP = 0x14a, + XMM7360_RPC_CALL_UTA_MS_SIM_TK_EXEC_SS_USSD_RSP = 0x14c, + XMM7360_RPC_CALL_UTA_MS_SIM_TK_EXEC_DTMF_RSP = 0x14e, + XMM7360_RPC_CALL_UTA_MS_SIM_TK_STOP_DTMF_REQ = 0x150, + XMM7360_RPC_CALL_UTA_MS_SIM_TK_REFRESH_CONFIRM_RSP = 0x152, + XMM7360_RPC_CALL_UTA_MS_SIM_TK_REFRESH_FCN_RSP = 0x154, + XMM7360_RPC_CALL_UTA_MS_SIM_TK_CONTROL_REQ = 0x155, + XMM7360_RPC_CALL_UTA_MS_SIM_TK_TERMINAL_PROFILE_DOWNLOAD_REQ = 0x157, + XMM7360_RPC_CALL_UTA_MS_3GPP2_SMS_SEND_REQ = 0x15a, + XMM7360_RPC_CALL_UTA_MS_3GPP2_SMS_SUBSCRIBE_INDICATIONS = 0x15c, + XMM7360_RPC_CALL_UTA_MS_3GPP2_SMS_UNSUBSCRIBE_INDICATIONS = 0x15d, + XMM7360_RPC_CALL_RPC_GET_REMOTE_VER_INFO = 0x15e, + XMM7360_RPC_CALL_UTA_MS_METRICS_REGISTER_HANDLER = 0x160, + XMM7360_RPC_CALL_UTA_MS_METRICS_DEREGISTER_HANDLER = 0x161, + XMM7360_RPC_CALL_UTA_MS_METRICS_SET_OPTIONS = 0x162, + XMM7360_RPC_CALL_UTA_MS_METRICS_TRIGGER = 0x163, + XMM7360_RPC_CALL_UTA_MS_EMBMS_INIT = 0x164, + XMM7360_RPC_CALL_UTA_MS_EMBMS_SET_SERVICE_REQ = 0x165, + XMM7360_RPC_CALL_UTA_MS_EMBMS_MBSFN_AREA_CONFIG_REQ = 0x166, + XMM7360_RPC_CALL_UTA_MS_EMBMS_SESSION_CONFIG_REQ = 0x167, + XMM7360_RPC_CALL_UTA_MS_EMBMS_SET_INTERESTED_TMGI_LIST_REQ = 0x168, + XMM7360_RPC_CALL_UTA_MS_EMBMS_SET_INTERESTED_SAI_FREQ_REQ = 0x169, + XMM7360_RPC_CALL_UTA_IMS_SUBSCRIBE_INDICATIONS = 0x176, + XMM7360_RPC_CALL_UTA_IMS_UNSUBSCRIBE_INDICATIONS = 0x177, + XMM7360_RPC_CALL_UTA_IMS_GET_FRAMEWORK_STATE = 0x178, + XMM7360_RPC_CALL_UTA_RTC_GET_DATETIME = 0x179, + XMM7360_RPC_CALL_UTA_MS_SIM_ANALYSE_SIM_APDU_RESULT = 0x17a, + XMM7360_RPC_CALL_UTA_MS_SIM_OPEN_CHANNEL_REQ = 0x17b, + XMM7360_RPC_CALL_UTA_MS_SIM_CLOSE_CHANNEL_REQ = 0x17d, + XMM7360_RPC_CALL_UTA_MS_SIM_SET_BDN_REQ = 0x17f, + XMM7360_RPC_CALL_UTA_MS_SET_SIM_STACK_MAPPING_REQ = 0x181, + XMM7360_RPC_CALL_UTA_MS_GET_SIM_STACK_MAPPING_REQ = 0x183, + XMM7360_RPC_CALL_UTA_MS_NET_SET_RADIO_SIGNAL_REPORTING_CONFIGURATION = 0x188, + XMM7360_RPC_CALL_UTA_PC_IE_ENUMERATIONEXT_TOUT = 0x189, + XMM7360_RPC_CALL_UTA_MS_SIM_TK_SET_TERMINAL_CAPABILITY_REQ = 0x18a, + XMM7360_RPC_CALL_UTA_MS_SIM_TK_READ_TERMINAL_CAPABILITY_REQ = 0x18c, + XMM7360_RPC_CALL_CSI_FCC_LOCK_QUERY_REQ = 0x18e, + XMM7360_RPC_CALL_CSI_FCC_LOCK_GEN_CHALLENGE_REQ = 0x190, + XMM7360_RPC_CALL_CSI_FCC_LOCK_VER_CHALLENGE_REQ = 0x192, + XMM7360_RPC_CALL_UTA_SENSOR_OPEN_REQ = 0x194, + XMM7360_RPC_CALL_UTA_SENSOR_CLOSE_EXT = 0x197, + XMM7360_RPC_CALL_UTA_SENSOR_START_EXT = 0x198, + XMM7360_RPC_CALL_UTA_SENSOR_SET_ALARM_PARAM_EXT = 0x199, + XMM7360_RPC_CALL_UTA_SENSOR_SET_SCHEDULER_PARAM_EXT = 0x19a, + XMM7360_RPC_CALL_CSI_SIO_IP_FILTER_CNTRL_SET_REQ = 0x19b, + XMM7360_RPC_CALL_UTA_MS_ACC_CURRENT_FREQ_INFO_REQ = 0x19d, + XMM7360_RPC_CALL_CSI_TRC_AT_CMND_REQ = 0x1a0, + XMM7360_RPC_CALL_UTA_MS_SIM_APDU_CMD_EXT_REQ = 0x1a2, + XMM7360_RPC_CALL_UTA_MS_NET_GET_PLMN_NAME_INFO_REQ = 0x1a4, + XMM7360_RPC_CALL_UTA_MS_NET_GET_COUNTRY_LIST_REQ = 0x1a7, + XMM7360_RPC_CALL_UTA_MS_NET_EXT_CONFIGURE_NETWORK_MODE_REQ = 0x1a9, + XMM7360_RPC_CALL_UTA_MS_NET_EXT_BAND_STATUS_REQ = 0x1ac, + XMM7360_RPC_CALL_UTA_MS_CALL_PS_ATTACH_APN_CONFIG_REQ = 0x1af, + XMM7360_RPC_CALL_CSI_MS_CALL_PS_INITIALIZE = 0x1b1, + XMM7360_RPC_CALL_UTA_AUDIO_ENABLE_SOURCE = 0x1b2, + XMM7360_RPC_CALL_UTA_AUDIO_DISABLE_SOURCE = 0x1b3, + XMM7360_RPC_CALL_UTA_AUDIO_CONFIGURE_DESTINATION_EXT = 0x1b4, + XMM7360_RPC_CALL_UTA_AUDIO_SET_DESTINATIONS_FOR_SOURCE = 0x1b5, + XMM7360_RPC_CALL_UTA_AUDIO_SET_VOLUME_FOR_SOURCE = 0x1b6, + XMM7360_RPC_CALL_UTA_AUDIO_SET_MUTE_FOR_SOURCE_EXT = 0x1b7, + XMM7360_RPC_CALL_UTA_AUDIO_SET_VOLUME_FOR_DESTINATION = 0x1b8, + XMM7360_RPC_CALL_UTA_AUDIO_SET_MUTE_FOR_DESTINATION_EXT = 0x1b9, + XMM7360_RPC_CALL_UTA_AUDIO_CONFIGURE_SOURCE_EXT = 0x1ba, + XMM7360_RPC_CALL_UTA_AUDIO_SET_DESTINATIONS_FOR_SOURCE_EXT = 0x1bb, + XMM7360_RPC_CALL_UTA_RPC_SCREEN_CONTROL_REQ = 0x1bc, + XMM7360_RPC_CALL_UTA_MS_CALL_PS_READ_CONTEXT_STATUS_REQ = 0x1bd, + XMM7360_RPC_CALL_CSI_MS_SIM_ACCESS_GET_SIM_STATE_INFO_REQ = 0x1bf, + XMM7360_RPC_CALL_CSI_MS_NET_GET_REGISTRATION_INFO_REQ = 0x1c1, + XMM7360_RPC_CALL_CSI_SIO_IP_FILTER_NEW_CNTRL_SET_REQ = 0x1c3, + XMM7360_RPC_CALL_CSI_MS_NET_LDR_GET_APN_PLMN_PARAMETER_LIST_REQ = 0x1c5, + XMM7360_RPC_CALL_RPC_GET_API_PARAM_CHANGED_BITMAP = 0x1c8, +} Xmm7360RpcCallId; + +typedef enum { + XMM7360_RPC_UNSOL_UTA_MS_SIM_APDU_CMD_RSP_CB = 0x003, + XMM7360_RPC_UNSOL_UTA_MS_SIM_APPLICATION_RSP_CB = 0x005, + XMM7360_RPC_UNSOL_UTA_MS_SIM_INFO_IND_CB = 0x007, + XMM7360_RPC_UNSOL_UTA_MS_SIM_INIT_IND_CB = 0x008, + XMM7360_RPC_UNSOL_UTA_MS_SIM_FULL_ACCESS_IND_CB = 0x009, + XMM7360_RPC_UNSOL_UTA_MS_SIM_ERROR_IND_CB = 0x00a, + XMM7360_RPC_UNSOL_UTA_MS_SIM_CARD_IND_CB = 0x00b, + XMM7360_RPC_UNSOL_UTA_MS_SIM_APPLICATION_IND_CB = 0x00c, + XMM7360_RPC_UNSOL_UTA_MS_SIM_PB_READ_ENTRY_RSP_CB = 0x00e, + XMM7360_RPC_UNSOL_UTA_MS_SIM_GEN_PIN_RSP_CB = 0x010, + XMM7360_RPC_UNSOL_UTA_MS_SIM_MODIFY_LOCK_RSP_CB = 0x012, + XMM7360_RPC_UNSOL_UTA_MS_SIM_LOCK_STATUS_IND_CB = 0x013, + XMM7360_RPC_UNSOL_UTA_MS_SIM_TK_MO_SMS_CONTROL_INFO_IND_CB = 0x014, + XMM7360_RPC_UNSOL_UTA_MS_SIM_TK_PROACTIVE_COMMAND_IND_CB = 0x015, + XMM7360_RPC_UNSOL_UTA_MS_SIM_TK_ENVELOPE_RES_IND_CB = 0x018, + XMM7360_RPC_UNSOL_UTA_MS_SIM_TK_TERMINAL_PROFILE_READ_RSP_CB = 0x01a, + XMM7360_RPC_UNSOL_UTA_SIM_TK_PROACTIVE_COMMAND_HANDLER_FUNC = 0x01b, + XMM7360_RPC_UNSOL_UTA_MS_CPS_SET_MODE_RSP = 0x01e, + XMM7360_RPC_UNSOL_UTA_MS_CPS_SET_MODE_IND_CB = 0x022, + XMM7360_RPC_UNSOL_UTA_MS_SS_NETWORK_ERROR_IND_CB = 0x02a, + XMM7360_RPC_UNSOL_UTA_MS_SS_NETWORK_REJECT_IND_CB = 0x02b, + XMM7360_RPC_UNSOL_UTA_MS_SS_NETWORK_GSM_CAUSE_IND_CB = 0x02c, + XMM7360_RPC_UNSOL_UTA_MS_SS_USSD_RSP_CB = 0x02d, + XMM7360_RPC_UNSOL_UTA_MS_SS_USSD_IND_CB = 0x02e, + XMM7360_RPC_UNSOL_UTA_MS_SS_END_IND_CB = 0x02f, + XMM7360_RPC_UNSOL_UTA_MS_SMS_INCOMING_IND_CB = 0x032, + XMM7360_RPC_UNSOL_UTA_MS_SMS_SEND_RSP_CB = 0x033, + XMM7360_RPC_UNSOL_UTA_MS_SMS_SET_MEMORY_AVAILABLE_RSP_CB = 0x035, + XMM7360_RPC_UNSOL_UTA_MS_SMS_SIM_MSG_CACHE_FINISHED_IND_CB = 0x037, + XMM7360_RPC_UNSOL_UTA_MS_SMS_SIM_MSG_COUNT_RSP_CB = 0x039, + XMM7360_RPC_UNSOL_UTA_MS_CALL_PS_DEFINE_PRIMARY_RSP_CB = 0x03e, + XMM7360_RPC_UNSOL_UTA_MS_CALL_PS_UNDEFINE_PRIMARY_RSP_CB = 0x040, + XMM7360_RPC_UNSOL_UTA_MS_CALL_PS_GET_PRIMARY_RSP_CB = 0x042, + XMM7360_RPC_UNSOL_UTA_MS_CALL_PS_SET_AUTHENTICATION_RSP_CB = 0x044, + XMM7360_RPC_UNSOL_UTA_MS_CALL_PS_SET_DNS_RSP_CB = 0x046, + XMM7360_RPC_UNSOL_UTA_MS_CALL_PS_GET_NEGOTIATED_DNS_RSP_CB = 0x048, + XMM7360_RPC_UNSOL_UTA_MS_CALL_PS_GET_NEG_IP_ADDR_RSP_CB = 0x04a, + XMM7360_RPC_UNSOL_UTA_MS_CALL_PS_ACTIVATE_RSP_CB = 0x04c, + XMM7360_RPC_UNSOL_UTA_MS_CALL_PS_ACTIVATE_STATUS_IND_CB = 0x04d, + XMM7360_RPC_UNSOL_UTA_MS_CALL_PS_DEACTIVATE_RSP_CB = 0x04f, + XMM7360_RPC_UNSOL_UTA_MS_CALL_PS_DEACTIVATE_IND_CB = 0x050, + XMM7360_RPC_UNSOL_UTA_MS_CALL_PS_CONNECT_RSP_CB = 0x052, + XMM7360_RPC_UNSOL_UTA_MS_NET_SINGLE_SHOT_RADIO_SIGNAL_REPORTING_RSP_CB = 0x056, + XMM7360_RPC_UNSOL_UTA_MS_NET_CELL_INFO_IND_CB = 0x057, + XMM7360_RPC_UNSOL_UTA_MS_NET_CONNECTION_INFO_IND_CB = 0x058, + XMM7360_RPC_UNSOL_UTA_MS_NET_HSPA_INFO_IND_CB = 0x059, + XMM7360_RPC_UNSOL_UTA_MS_NET_RADIO_SIGNAL_IND_CB = 0x05a, + XMM7360_RPC_UNSOL_UTA_MS_NET_CELL_CHANGE_IND_CB = 0x05b, + XMM7360_RPC_UNSOL_UTA_MS_NET_ATTACH_RSP_CB = 0x063, + XMM7360_RPC_UNSOL_UTA_MS_NET_PS_ATTACH_RSP_CB = 0x064, + XMM7360_RPC_UNSOL_UTA_MS_NET_PS_DETACH_RSP_CB = 0x065, + XMM7360_RPC_UNSOL_UTA_MS_NET_SCAN_RSP_CB = 0x066, + XMM7360_RPC_UNSOL_UTA_MS_NET_POWER_DOWN_RSP_CB = 0x067, + XMM7360_RPC_UNSOL_UTA_MS_NET_EXT_SCAN_RSP_CB = 0x068, + XMM7360_RPC_UNSOL_UTA_MS_NET_PS_ATTACH_IND_CB = 0x069, + XMM7360_RPC_UNSOL_UTA_MS_NET_PS_DETACH_IND_CB = 0x06a, + XMM7360_RPC_UNSOL_UTA_MS_NET_REGISTRATION_INFO_IND_CB = 0x06b, + XMM7360_RPC_UNSOL_UTA_MS_NET_IS_ATTACH_ALLOWED_IND_CB = 0x06c, + XMM7360_RPC_UNSOL_UTA_MS_NET_GPRS_CLASS_IND_CB = 0x06d, + XMM7360_RPC_UNSOL_UTA_MS_NET_SET_FD_CONFIG_RSP_CB = 0x06f, + XMM7360_RPC_UNSOL_UTA_MS_NET_FD_CONFIG_IND_CB = 0x070, + XMM7360_RPC_UNSOL_UTA_MS_NET_GET_FD_CONFIG_RSP_CB = 0x072, + XMM7360_RPC_UNSOL_UTA_MS_NET_CONFIGURE_NETWORK_MODE_RSP_CB = 0x074, + XMM7360_RPC_UNSOL_UTA_MS_NET_NETWORK_MODE_CHANGE_IND_CB = 0x075, + XMM7360_RPC_UNSOL_UTA_MS_NET_RAT_MODE_STATUS_RSP_CB = 0x077, + XMM7360_RPC_UNSOL_UTA_MS_NET_RAT_MODE_STATUS_IND_CB = 0x078, + XMM7360_RPC_UNSOL_UTA_MS_NET_DC_SET_VOICE_DOMAIN_PREFERENCE_CONFIG_RSP_CB = 0x081, + XMM7360_RPC_UNSOL_UTA_MS_CALL_CS_SETUP_CALL_RSP_CB = 0x083, + XMM7360_RPC_UNSOL_UTA_MS_CALL_CS_DIALING_IND_CB = 0x084, + XMM7360_RPC_UNSOL_UTA_MS_CALL_CS_ALERTING_IND_CB = 0x085, + XMM7360_RPC_UNSOL_UTA_MS_CALL_CS_CTM_INFO_IND_CB = 0x086, + XMM7360_RPC_UNSOL_UTA_MS_CALL_CS_CONNECTED_IND_CB = 0x087, + XMM7360_RPC_UNSOL_UTA_MS_CALL_CS_RELEASE_CALL_RSP_CB = 0x089, + XMM7360_RPC_UNSOL_UTA_MS_CALL_CS_DISCONNECTING_IND_CB = 0x08a, + XMM7360_RPC_UNSOL_UTA_MS_CALL_CS_DISCONNECTED_IND_CB = 0x08b, + XMM7360_RPC_UNSOL_UTA_MS_CALL_CS_INCOMING_CALL_IND_CB = 0x08c, + XMM7360_RPC_UNSOL_UTA_MS_CALL_CS_ACCEPT_CALL_RSP_CB = 0x08e, + XMM7360_RPC_UNSOL_UTA_MS_CALL_CS_PROGRESS_IND_CB = 0x08f, + XMM7360_RPC_UNSOL_UTA_MS_CALL_CS_SWAP_CALLS_RSP_CB = 0x091, + XMM7360_RPC_UNSOL_UTA_MS_CALL_CS_HOLD_CALL_RSP_CB = 0x093, + XMM7360_RPC_UNSOL_UTA_MS_CALL_CS_RETRIEVE_CALL_RSP_CB = 0x095, + XMM7360_RPC_UNSOL_UTA_MS_CALL_CS_SPLIT_MPTY_RSP_CB = 0x097, + XMM7360_RPC_UNSOL_UTA_MS_CALL_CS_JOIN_CALLS_RSP_CB = 0x099, + XMM7360_RPC_UNSOL_UTA_MS_CALL_CS_TRANSFER_CALLS_RSP_CB = 0x09b, + XMM7360_RPC_UNSOL_UTA_MS_CALL_CS_START_DTMF_RSP_CB = 0x09d, + XMM7360_RPC_UNSOL_UTA_MS_CALL_CS_STOP_DTMF_RSP_CB = 0x09f, + XMM7360_RPC_UNSOL_UTA_MS_CALL_CS_STOP_DTMF_EXT_RSP_CB = 0x0a0, + XMM7360_RPC_UNSOL_UTA_MS_CALL_CS_NOTIFICATION_IND_CB = 0x0a1, + XMM7360_RPC_UNSOL_UTA_MS_CALL_CS_CUG_INFO_IND_CB = 0x0a2, + XMM7360_RPC_UNSOL_UTA_MS_CALL_CS_CALLING_NAME_INFO_IND_CB = 0x0a3, + XMM7360_RPC_UNSOL_UTA_MS_CALL_CS_EMERGENCY_NUMBER_LIST_IND_CB = 0x0a4, + XMM7360_RPC_UNSOL_UTA_MS_CALL_CS_CALL_STATUS_IND_CB = 0x0a5, + XMM7360_RPC_UNSOL_UTA_CALL_MULTIMEDIA_GET_MEDIA_PROFILES_INFO_RSP_CB = 0x0a9, + XMM7360_RPC_UNSOL_UTA_MS_CALL_MULTIMEDIA_SETUP_CALL_RSP_CB = 0x0aa, + XMM7360_RPC_UNSOL_UTA_MS_CALL_MULTIMEDIA_UPDATE_CALL_RSP_CB = 0x0ab, + XMM7360_RPC_UNSOL_UTA_MS_CALL_CS_VOIMS_SRVCC_HO_STATUS_IND_CB = 0x0ae, + XMM7360_RPC_UNSOL_UTA_MS_CPS_SET_SIM_MODE_RSP = 0x0af, + XMM7360_RPC_UNSOL_UTA_MS_CPS_STARTUP_IND_CB = 0x0b1, + XMM7360_RPC_UNSOL_UTA_MS_SS_CALL_FORWARD_RSP_CB = 0x0b3, + XMM7360_RPC_UNSOL_UTA_MS_SS_CALL_WAITING_RSP_CB = 0x0b5, + XMM7360_RPC_UNSOL_UTA_MS_SS_CALL_BARRING_RSP_CB = 0x0b7, + XMM7360_RPC_UNSOL_UTA_MS_SS_IDENTIFICATION_RSP_CB = 0x0b9, + XMM7360_RPC_UNSOL_UTA_MS_SMS_DATA_DOWNLOAD_RSP_CB = 0x0bc, + XMM7360_RPC_UNSOL_UTA_MS_CALL_PS_GET_NEG_QOS_RSP_CB = 0x0bf, + XMM7360_RPC_UNSOL_UTA_MS_CALL_PS_GET_TFT_RSP_CB = 0x0c1, + XMM7360_RPC_UNSOL_UTA_MS_CALL_PS_SET_PCO_RSP_CB = 0x0c3, + XMM7360_RPC_UNSOL_UTA_MS_CALL_PS_GET_NW_PCO_RSP_CB = 0x0c5, + XMM7360_RPC_UNSOL_UTA_MS_CALL_PS_NW_ACTIVATE_IND_CB = 0x0c6, + XMM7360_RPC_UNSOL_UTA_MS_CALL_PS_NW_ACTIVATE_ACCEPT_RSP_CB = 0x0c8, + XMM7360_RPC_UNSOL_UTA_MS_CALL_PS_MODIFY_IND_CB = 0x0ca, + XMM7360_RPC_UNSOL_UTA_MS_CALL_PS_SUSPEND_IND_CB = 0x0cb, + XMM7360_RPC_UNSOL_UTA_MS_CALL_PS_RESUME_IND_CB = 0x0cc, + XMM7360_RPC_UNSOL_UTA_MS_CALL_PS_SET_DATA_PREF_RSP_CB = 0x0ce, + XMM7360_RPC_UNSOL_UTA_MS_CBS_START_RSP_CB = 0x0d1, + XMM7360_RPC_UNSOL_UTA_MS_CBS_STOP_RSP_CB = 0x0d2, + XMM7360_RPC_UNSOL_UTA_MS_CBS_NEW_MESSAGE_IND_CB = 0x0d5, + XMM7360_RPC_UNSOL_UTA_MS_CBS_ETWS_CONFIG_RSP_CB = 0x0d7, + XMM7360_RPC_UNSOL_UTA_MS_CBS_ETWS_START_RSP_CB = 0x0d9, + XMM7360_RPC_UNSOL_UTA_MS_CBS_ETWS_STOP_RSP_CB = 0x0db, + XMM7360_RPC_UNSOL_UTA_MS_CBS_ETWS_NOTIFY_PRIMARY_WARNING_IND = 0x0dc, + XMM7360_RPC_UNSOL_UTA_MS_CBS_ETWS_NOTIFY_SECONDARY_WARNING_IND = 0x0dd, + XMM7360_RPC_UNSOL_UTA_MS_NET_CONFIGURE_RX_DIVERSITY_DARP_IND_CB = 0x0e1, + XMM7360_RPC_UNSOL_UTA_MS_NET_TIME_INFO_READ_RSP_CB = 0x0e4, + XMM7360_RPC_UNSOL_UTA_MS_NET_TIME_INFO_IND_CB = 0x0e5, + XMM7360_RPC_UNSOL_UTA_MS_NET_BAND_STATUS_RSP_CB = 0x0e8, + XMM7360_RPC_UNSOL_UTA_MS_NET_BAND_STATUS_IND_CB = 0x0e9, + XMM7360_RPC_UNSOL_UTA_MS_NET_SET_CSG_CONFIG_RSP_CB = 0x0ea, + XMM7360_RPC_UNSOL_UTA_MS_NET_GET_CSG_CONFIG_RSP_CB = 0x0eb, + XMM7360_RPC_UNSOL_UTA_MS_NET_GET_EXTENDED_RADIO_SIGNAL_INFO_RSP_CB = 0x0ed, + XMM7360_RPC_UNSOL_UTA_MS_NET_NITZ_INFO_IND_CB = 0x0ee, + XMM7360_RPC_UNSOL_UTA_MS_NET_DETACH_RSP_CB = 0x0f0, + XMM7360_RPC_UNSOL_UTA_MS_NET_SELECT_GPRS_CLASS_RSP_CB = 0x0f2, + XMM7360_RPC_UNSOL_UTA_MS_NET_NETWORK_FEATURE_SUPPORT_INFO_IND_CB = 0x0f5, + XMM7360_RPC_UNSOL_UTA_MS_NET_EPS_NETWORK_FEATURE_SUPPORT_INFO_IND_CB = 0x0f6, + XMM7360_RPC_UNSOL_UTA_MS_NET_CS_SERVICE_NOTIFICATION_IND_CB = 0x0f7, + XMM7360_RPC_UNSOL_UTA_MS_NET_DUAL_SIM_SERVICE_IND_CB = 0x0f8, + XMM7360_RPC_UNSOL_UTA_MS_NET_SINGLE_SHOT_FD_RSP_CB = 0x0fa, + XMM7360_RPC_UNSOL_UTA_MS_SIM_PB_GET_LOCATION_RSP_CB = 0x0fc, + XMM7360_RPC_UNSOL_UTA_MS_SIM_PB_READ_GAS_ENTRY_RSP_CB = 0x0fe, + XMM7360_RPC_UNSOL_UTA_MS_SIM_PB_WRITE_ENTRY_RSP_CB = 0x100, + XMM7360_RPC_UNSOL_UTA_MS_SIM_PB_GET_META_INFORMATION_RSP_CB = 0x102, + XMM7360_RPC_UNSOL_UTA_MS_SIM_PB_USIM_PB_SELECT_RSP_CB = 0x104, + XMM7360_RPC_UNSOL_UTA_MS_SIM_PB_GET_FREE_RECORDS_RSP_CB = 0x106, + XMM7360_RPC_UNSOL_UTA_MS_SIM_PB_USIM_PB_READY_IND_CB = 0x107, + XMM7360_RPC_UNSOL_UTA_MS_SIM_PB_CACHE_LOAD_FINISHED_IND_CB = 0x108, + XMM7360_RPC_UNSOL_UTA_MS_SIM_PB_CACHE_LOAD_IND_CB = 0x109, + XMM7360_RPC_UNSOL_UTA_MS_SIM_GEN_PIN_IND_CB = 0x10d, + XMM7360_RPC_UNSOL_UTA_MS_SIM_FDN_STATE_IND_CB = 0x10f, + XMM7360_RPC_UNSOL_UTA_IDC_AP_MSG_SET_RSP_CB = 0x112, + XMM7360_RPC_UNSOL_UTA_IDC_AP_MSG_GET_RSP_CB = 0x113, + XMM7360_RPC_UNSOL_UTA_IDC_CWS_MSG_SET_RSP_CB = 0x117, + XMM7360_RPC_UNSOL_UTA_IDC_CWS_MSG_GET_RSP_CB = 0x118, + XMM7360_RPC_UNSOL_UTA_IDC_CWS_MSG_IND_CB = 0x11b, + XMM7360_RPC_UNSOL_UTA_BOOT_PREPARE_SHUTDOWN_RSP_CB = 0x11e, + XMM7360_RPC_UNSOL_UTA_FREQ_INFO_ACTIVATE_RSP_CB = 0x124, + XMM7360_RPC_UNSOL_UTA_FREQ_INFO_DEACTIVATE_RSP_CB = 0x125, + XMM7360_RPC_UNSOL_UTA_FREQ_INFO_GET_FREQ_INFO_RSP_CB = 0x126, + XMM7360_RPC_UNSOL_UTA_FREQ_INFO_INDICATION_CB = 0x127, + XMM7360_RPC_UNSOL_UTA_MODE_SET_RSP_CB = 0x12d, + XMM7360_RPC_UNSOL_UTA_MODE_STARTUP_IND_CB = 0x12e, + XMM7360_RPC_UNSOL_UTA_PROD_GTI_CMD_RSP_CB = 0x131, + XMM7360_RPC_UNSOL_UTA_CELL_TIME_STAMP_RSP_CB = 0x135, + XMM7360_RPC_UNSOL_UTA_MS_SS_LCS_MO_LOCATION_RSP_CB = 0x138, + XMM7360_RPC_UNSOL_UTA_MS_SS_LCS_CAPABILITIES_IND_CB = 0x13a, + XMM7360_RPC_UNSOL_UTA_MS_CP_ASSISTANCE_DATA_INJECT_RSP_CB = 0x13b, + XMM7360_RPC_UNSOL_UTA_MS_CP_ASSISTANCE_DATA_NEEDED_IND_CB = 0x13e, + XMM7360_RPC_UNSOL_UTA_MS_CP_POS_MEASUREMENT_RSP_CB = 0x13f, + XMM7360_RPC_UNSOL_UTA_MS_CP_POS_MEASUREMENT_ABORT_RSP_CB = 0x141, + XMM7360_RPC_UNSOL_UTA_MS_CP_POS_REPORT_MEASUREMENT_IND_CB = 0x143, + XMM7360_RPC_UNSOL_UTA_MS_SIM_TK_EXEC_SMS_PP_IND_CB = 0x147, + XMM7360_RPC_UNSOL_UTA_MS_SIM_TK_EXEC_SIM_INITIATED_CALL_IND_CB = 0x149, + XMM7360_RPC_UNSOL_UTA_MS_SIM_TK_EXEC_SS_USSD_IND_CB = 0x14b, + XMM7360_RPC_UNSOL_UTA_MS_SIM_TK_EXEC_DTMF_IND_CB = 0x14d, + XMM7360_RPC_UNSOL_UTA_MS_SIM_TK_EXEC_DTMF_END_IND_CB = 0x14f, + XMM7360_RPC_UNSOL_UTA_MS_SIM_TK_REFRESH_CONFIRM_IND_CB = 0x151, + XMM7360_RPC_UNSOL_UTA_MS_SIM_TK_REFRESH_FCN_IND_CB = 0x153, + XMM7360_RPC_UNSOL_UTA_MS_SIM_TK_CONTROL_RSP_CB = 0x156, + XMM7360_RPC_UNSOL_UTA_MS_SIM_TK_TERMINAL_PROFILE_DOWNLOAD_RSP_CB = 0x158, + XMM7360_RPC_UNSOL_UTA_MS_3GPP2_SMS_SEND_RSP_CB = 0x159, + XMM7360_RPC_UNSOL_UTA_MS_3GPP2_SMS_INCOMING_IND_CB = 0x15b, + XMM7360_RPC_UNSOL_UTA_METRICS_HANDLER_FUNCTION = 0x15f, + XMM7360_RPC_UNSOL_UTA_MS_EMBMS_SET_SERVICE_RSP_CB = 0x16a, + XMM7360_RPC_UNSOL_UTA_MS_EMBMS_MBSFN_AREA_CONFIG_RSP_CB = 0x16b, + XMM7360_RPC_UNSOL_UTA_MS_EMBMS_SESSION_CONFIG_RSP_CB = 0x16c, + XMM7360_RPC_UNSOL_UTA_MS_EMBMS_SET_INTERESTED_TMGI_LIST_RSP_CB = 0x16d, + XMM7360_RPC_UNSOL_UTA_MS_EMBMS_SET_INTERESTED_SAI_FREQ_RSP_CB = 0x16e, + XMM7360_RPC_UNSOL_UTA_MS_EMBMS_SERVICE_IND_CB = 0x16f, + XMM7360_RPC_UNSOL_UTA_MS_EMBMS_MBSFN_AREA_IND_CB = 0x170, + XMM7360_RPC_UNSOL_UTA_MS_EMBMS_SERVICES_LIST_IND_CB = 0x171, + XMM7360_RPC_UNSOL_UTA_MS_EMBMS_SAI_LIST_IND_CB = 0x172, + XMM7360_RPC_UNSOL_UTA_MS_EMBMS_MPS_INFO_IND_CB = 0x173, + XMM7360_RPC_UNSOL_UTA_IMS_STATE_CHANGED_IND_CB = 0x174, + XMM7360_RPC_UNSOL_UTA_IMS_SERVICE_STATE_CHANGED_IND_CB = 0x175, + XMM7360_RPC_UNSOL_UTA_MS_SIM_OPEN_CHANNEL_RSP_CB = 0x17c, + XMM7360_RPC_UNSOL_UTA_MS_SIM_CLOSE_CHANNEL_RSP_CB = 0x17e, + XMM7360_RPC_UNSOL_UTA_MS_SIM_BDN_STATE_IND_CB = 0x180, + XMM7360_RPC_UNSOL_UTA_MS_SET_SIM_STACK_MAPPING_RSP_CB = 0x182, + XMM7360_RPC_UNSOL_UTA_MS_GET_SIM_STACK_MAPPING_RSP_CB = 0x184, + XMM7360_RPC_UNSOL_UTA_MS_SIM_MCC_MNC_IND_CB = 0x185, + XMM7360_RPC_UNSOL_UTA_MS_SIM_TK_TERMINAL_RESPONSE_IND_CB = 0x186, + XMM7360_RPC_UNSOL_UTA_MS_NET_REGISTERED_PLMN_NAME_IND_CB = 0x187, + XMM7360_RPC_UNSOL_UTA_MS_SIM_TK_SET_TERMINAL_CAPABILITY_RSP_CB = 0x18b, + XMM7360_RPC_UNSOL_UTA_MS_SIM_TK_READ_TERMINAL_CAPABILITY_RSP_CB = 0x18d, + XMM7360_RPC_UNSOL_CSI_FCC_LOCK_QUERY_RSP_CB = 0x18f, + XMM7360_RPC_UNSOL_CSI_FCC_LOCK_GEN_CHALLENGE_RSP_CB = 0x191, + XMM7360_RPC_UNSOL_CSI_FCC_LOCK_VER_CHALLENGE_RSP_CB = 0x193, + XMM7360_RPC_UNSOL_UTA_SENSOR_OPEN_RSP_CB = 0x195, + XMM7360_RPC_UNSOL_UTA_SENSOR_MEAS_IND_CB = 0x196, + XMM7360_RPC_UNSOL_CSI_SIO_IP_FILTER_CNTRL_SET_RSP_CB = 0x19c, + XMM7360_RPC_UNSOL_CSI_SIO_IP_FILTER_NEW_CNTRL_SET_RSP_CB = 0x1c4, + XMM7360_RPC_UNSOL_UTA_MS_ACC_CURRENT_FREQ_INFO_RSP_CB = 0x19e, + XMM7360_RPC_UNSOL_UTA_MS_ACC_CURRENT_FREQ_INFO_IND_CB = 0x19f, + XMM7360_RPC_UNSOL_CSI_TRC_AT_CMND_RSP_CB = 0x1a1, + XMM7360_RPC_UNSOL_UTA_MS_SIM_APDU_CMD_EXT_RSP_CB = 0x1a3, + XMM7360_RPC_UNSOL_UTA_MS_NET_GET_PLMN_NAME_INFO_RSP_CB = 0x1a5, + XMM7360_RPC_UNSOL_UTA_MS_NET_SIB8_TIME_INFO_IND_CB = 0x1a6, + XMM7360_RPC_UNSOL_UTA_MS_NET_GET_COUNTRY_LIST_RSP_CB = 0x1a8, + XMM7360_RPC_UNSOL_UTA_MS_NET_EXT_CONFIGURE_NETWORK_MODE_RSP_CB = 0x1aa, + XMM7360_RPC_UNSOL_UTA_MS_NET_EXT_NETWORK_MODE_CHANGE_IND_CB = 0x1ab, + XMM7360_RPC_UNSOL_UTA_MS_NET_EXT_BAND_STATUS_RSP_CB = 0x1ad, + XMM7360_RPC_UNSOL_UTA_MS_NET_EXT_BAND_STATUS_IND_CB = 0x1ae, + XMM7360_RPC_UNSOL_UTA_MS_CALL_PS_ATTACH_APN_CONFIG_RSP_CB = 0x1b0, + XMM7360_RPC_UNSOL_UTA_MS_CALL_PS_READ_CONTEXT_STATUS_RSP_CB = 0x1be, + XMM7360_RPC_UNSOL_CSI_MS_SIM_ACCESS_GET_SIM_STATE_INFO_RSP_CB = 0x1c0, + XMM7360_RPC_UNSOL_CSI_MS_NET_GET_REGISTRATION_INFO_RSP_CB = 0x1c2, + XMM7360_RPC_UNSOL_CSI_MS_NET_LDR_GET_APN_PLMN_PARAMETER_LIST_RSP_CB = 0x1c6, + XMM7360_RPC_UNSOL_UTA_MS_NET_LDR_APN_PARAMETERS_CHANGE_IND_CB = 0x1c7, +} Xmm7360RpcUnsolId; + +typedef enum { + XMM7360_RPC_MSG_ARG_TYPE_BYTE, + XMM7360_RPC_MSG_ARG_TYPE_SHORT, + XMM7360_RPC_MSG_ARG_TYPE_LONG, + XMM7360_RPC_MSG_ARG_TYPE_STRING, + XMM7360_RPC_MSG_ARG_TYPE_UNKNOWN, +} Xmm7360RpcMsgArgType; + + +#define XMM7360_RPC_MSG_ARG_GET_INT(arg) \ + (arg->type == XMM7360_RPC_MSG_ARG_TYPE_BYTE ? arg->value.b \ + : arg->type == XMM7360_RPC_MSG_ARG_TYPE_SHORT ? arg->value.s \ + : arg->type == XMM7360_RPC_MSG_ARG_TYPE_LONG ? arg->value.l : -1) + +typedef struct { + Xmm7360RpcMsgArgType type; + union { + gint8 b; + gint16 s; + gint32 l; + const gchar *string; + } value; + gsize size; + gsize pad; +} Xmm7360RpcMsgArg; + +typedef enum { + XMM7360_RPC_RESPONSE_TYPE_RESPONSE, + XMM7360_RPC_RESPONSE_TYPE_ASYNC_ACK, + XMM7360_RPC_RESPONSE_TYPE_UNSOLICITED, + XMM7360_RPC_RESPONSE_TYPE_UNKNOWN, +} Xmm7360RpcResponseType; + +typedef struct { + guint32 id; + Xmm7360RpcResponseType type; + guint32 unsol_id; + GByteArray *body; + GPtrArray *content; /* contains (Xmm7360RpcMsgArgs *) */ +} Xmm7360RpcResponse; + +#define MM_TYPE_PORT_SERIAL_XMMRPC_XMM7360 (mm_port_serial_xmmrpc_xmm7360_get_type ()) +#define MM_PORT_SERIAL_XMMRPC_XMM7360(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MM_TYPE_PORT_SERIAL_XMMRPC_XMM7360, MMPortSerialXmmrpcXmm7360)) +#define MM_PORT_SERIAL_XMMRPC_XMM7360_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), MM_TYPE_PORT_SERIAL_XMMRPC_XMM7360, MMPortSerialXmmrpcXmm7360Class)) +#define MM_IS_PORT_SERIAL_XMMRPC_XMM7360(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MM_TYPE_PORT_SERIAL_XMMRPC_XMM7360)) +#define MM_IS_PORT_SERIAL_XMMRPC_XMM7360_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), MM_TYPE_PORT_SERIAL_XMMRPC_XMM7360)) +#define MM_PORT_SERIAL_XMMRPC_XMM7360_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), MM_TYPE_PORT_SERIAL_XMMRPC_XMM7360, MMPortSerialXmmrpcXmm7360Class)) + +typedef struct _MMPortSerialXmmrpcXmm7360 MMPortSerialXmmrpcXmm7360; +typedef struct _MMPortSerialXmmrpcXmm7360Class MMPortSerialXmmrpcXmm7360Class; +typedef struct _MMPortSerialXmmrpcXmm7360Private MMPortSerialXmmrpcXmm7360Private; + +typedef gboolean (*MMPortSerialXmmrpcXmm7360UnsolicitedMsgFn) (MMPortSerialXmmrpcXmm7360 *port, + Xmm7360RpcResponse *response, + gpointer user_data); + +struct _MMPortSerialXmmrpcXmm7360 { + MMPortSerial parent; + MMPortSerialXmmrpcXmm7360Private *priv; +}; + +struct _MMPortSerialXmmrpcXmm7360Class { + MMPortSerialClass parent; +}; + +GType mm_port_serial_xmmrpc_xmm7360_get_type (void); +G_DEFINE_AUTOPTR_CLEANUP_FUNC (MMPortSerialXmmrpcXmm7360, g_object_unref) + +MMPortSerialXmmrpcXmm7360 *mm_port_serial_xmmrpc_xmm7360_new (const char *name); + +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); + +void xmm7360_byte_array_append_asn_int4 (GByteArray *array, gint32 value); + +void xmm7360_rpc_response_free (Xmm7360RpcResponse *msg); + +G_DEFINE_AUTOPTR_CLEANUP_FUNC (Xmm7360RpcResponse, xmm7360_rpc_response_free); + +GByteArray *xmm7360_rpc_args_to_byte_array (const Xmm7360RpcMsgArg *args); + +Xmm7360RpcResponse *mm_port_serial_xmmrpc_xmm7360_command_finish (MMPortSerialXmmrpcXmm7360 *self, + GAsyncResult *res, + GError **error); + +guint mm_port_serial_xmmrpc_xmm7360_add_unsolicited_msg_handler (MMPortSerialXmmrpcXmm7360 *self, + MMPortSerialXmmrpcXmm7360UnsolicitedMsgFn callback, + gpointer user_data, + GDestroyNotify notify); + +void mm_port_serial_xmmrpc_xmm7360_enable_unsolicited_msg_handler (MMPortSerialXmmrpcXmm7360 *self, + guint handler_id, + gboolean enable); + +#endif /* MM_PORT_SERIAL_XMMRPC_XMM7360_H */ diff --git a/src/plugins/intel/mm-sim-xmm7360.c b/src/plugins/intel/mm-sim-xmm7360.c new file mode 100644 index 00000000..912e66e8 --- /dev/null +++ b/src/plugins/intel/mm-sim-xmm7360.c @@ -0,0 +1,445 @@ +/* -*- 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) 2024 Thomas Vogt + */ + +#include "mm-broadband-modem-xmm7360.h" +#include "mm-broadband-modem-xmm7360-rpc.h" +#include "mm-log-object.h" +#include "mm-sim-xmm7360.h" + +G_DEFINE_TYPE (MMSimXmm7360, mm_sim_xmm7360, MM_TYPE_BASE_SIM) + +#define PIN_PADDED { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff } +#define PIN_PADDED_SIZE 8 + +static void +copy_pin (const gchar *pin, gchar *pin_padded) +{ + gsize i; + + for (i = 0; pin[i] != 0 && i < PIN_PADDED_SIZE; i++) + pin_padded[i] = pin[i]; +} + +static void +update_modem_unlock_retries (MMSimXmm7360 *self, + MMModemLock lock_type, + guint32 remaining_attempts) +{ + g_autoptr(MMBaseModem) modem = NULL; + + g_object_get (G_OBJECT (self), + MM_BASE_SIM_MODEM, &modem, + NULL); + g_assert (MM_IS_BASE_MODEM (modem)); + + mm_broadband_modem_xmm7360_set_unlock_retries (MM_BROADBAND_MODEM_XMM7360 (modem), + lock_type, + remaining_attempts); + g_object_unref (modem); +} + +/*****************************************************************************/ +/* Enable PIN */ + +static gboolean +enable_pin_finish (MMBaseSim *self, + GAsyncResult *res, + GError **error) +{ + return g_task_propagate_boolean (G_TASK (res), error); +} + +static void +gen_pin_enable_ready (MMBroadbandModem *modem, + GAsyncResult *res, + GTask *task) +{ + MMSimXmm7360 *self; + GError *error = NULL; + g_autoptr(Xmm7360RpcResponse) response = NULL; + Xmm7360RpcMsgArg *arg; + guint8 err_code_1; + guint8 err_code_2; + guint32 remaining_attempts; + Xmm7360GenPinOp op; + MMModemLock lock_type; + + self = g_task_get_source_object (task); + + response = mm_broadband_modem_xmm7360_rpc_command_finish (MM_BROADBAND_MODEM_XMM7360 (modem), + res, + &error); + + if (error) { + g_task_return_error (task, error); + g_object_unref (task); + return; + } + + arg = (Xmm7360RpcMsgArg *) g_ptr_array_index (response->content, 2); + remaining_attempts = XMM7360_RPC_MSG_ARG_GET_INT (arg); + + arg = (Xmm7360RpcMsgArg *) g_ptr_array_index (response->content, 3); + err_code_1 = XMM7360_RPC_MSG_ARG_GET_INT (arg); + + arg = (Xmm7360RpcMsgArg *) g_ptr_array_index (response->content, 4); + err_code_2 = XMM7360_RPC_MSG_ARG_GET_INT (arg); + + arg = (Xmm7360RpcMsgArg *) g_ptr_array_index (response->content, 7); + op = (Xmm7360GenPinOp) XMM7360_RPC_MSG_ARG_GET_INT (arg); + + lock_type = remaining_attempts > 0 ? MM_MODEM_LOCK_SIM_PIN : MM_MODEM_LOCK_SIM_PUK; + update_modem_unlock_retries (self, lock_type, remaining_attempts); + + if (err_code_1 == 0x83 && err_code_2 == 0x69) { + g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, + "%s PIN failed (need to unlock PUK first)", + op == XMM7360_GEN_PIN_OP_ENABLE ? "Enabling" : "Disabling"); + g_object_unref (task); + return; + } else if (err_code_1 == 0x84 && err_code_2 == 0x69) { + g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, + "%s PIN failed (already %s)", + op == XMM7360_GEN_PIN_OP_ENABLE ? "Enabling" : "Disabling", + op == XMM7360_GEN_PIN_OP_ENABLE ? "enabled" : "disabled"); + g_object_unref (task); + return; + } else if (err_code_2 == 0x63 && (err_code_1 == 0xc2 || err_code_1 == 0xc1 || err_code_1 == 0xc0)) { + g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, + "%s PIN failed (wrong PIN, %d retries left)", + op == XMM7360_GEN_PIN_OP_ENABLE ? "Enabling" : "Disabling", + remaining_attempts); + g_object_unref (task); + return; + } else if (err_code_1 != 0x00 || err_code_2 != 0x90) { + g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, + "%s PIN failed (unknown error: 0x%02x, 0x%02x)", + op == XMM7360_GEN_PIN_OP_ENABLE ? "Enabling" : "Disabling", + err_code_1, err_code_2); + g_object_unref (task); + return; + } + + g_task_return_boolean (task, TRUE); + g_object_unref (task); +} + +static GByteArray * +padded_pack_gen_pin_enable (const gchar *pin_padded, const Xmm7360GenPinOp op) +{ + const Xmm7360RpcMsgArg args[] = { + { XMM7360_RPC_MSG_ARG_TYPE_BYTE, { .b = 0 } }, + { XMM7360_RPC_MSG_ARG_TYPE_LONG, { .l = op } }, + { XMM7360_RPC_MSG_ARG_TYPE_BYTE, { .b = 0 } }, + { XMM7360_RPC_MSG_ARG_TYPE_LONG, { .l = 1 } }, + { XMM7360_RPC_MSG_ARG_TYPE_STRING, { .string = pin_padded }, 8, 2 }, + { XMM7360_RPC_MSG_ARG_TYPE_LONG, { .l = 0 } }, + { XMM7360_RPC_MSG_ARG_TYPE_UNKNOWN }, + }; + return xmm7360_rpc_args_to_byte_array (args); +} + +static GByteArray * +pack_gen_pin_enable (const gchar *pin, gboolean enabled) +{ + gchar pin_padded[] = PIN_PADDED; + copy_pin (pin, pin_padded); + return padded_pack_gen_pin_enable ( + pin_padded, + enabled ? XMM7360_GEN_PIN_OP_ENABLE : XMM7360_GEN_PIN_OP_DISABLE + ); +} + +static void +enable_pin (MMBaseSim *self, + const gchar *pin, + gboolean enabled, + GAsyncReadyCallback callback, + gpointer user_data) +{ + GTask *task; + g_autoptr(MMBaseModem) modem = NULL; + MMPortSerialXmmrpcXmm7360 *port; + g_autoptr(GByteArray) body = NULL; + + task = g_task_new (self, NULL, callback, user_data); + + g_object_get (G_OBJECT (self), + MM_BASE_SIM_MODEM, &modem, + NULL); + g_assert (MM_IS_BASE_MODEM (modem)); + + port = mm_broadband_modem_xmm7360_peek_port_xmmrpc (MM_BROADBAND_MODEM_XMM7360 (modem)); + g_object_unref (modem); + + if (!port) { + g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, + "Couldn't peek XMMRPC port"); + g_object_unref (task); + return; + } + + body = pack_gen_pin_enable (pin, enabled); + + mm_broadband_modem_xmm7360_rpc_command_full (MM_BROADBAND_MODEM_XMM7360 (modem), + port, + XMM7360_RPC_CALL_UTA_MS_SIM_GEN_PIN_REQ, + TRUE, + body, + 3, + FALSE, + NULL, /* cancellable */ + (GAsyncReadyCallback)gen_pin_enable_ready, + task); +} + +/*****************************************************************************/ +/* Change PIN */ + +static gboolean +change_pin_finish (MMBaseSim *self, + GAsyncResult *res, + GError **error) +{ + return g_task_propagate_boolean (G_TASK (res), error); +} + +static void +gen_pin_change_ready (MMBroadbandModem *modem, + GAsyncResult *res, + GTask *task) +{ + MMSimXmm7360 *self; + GError *error = NULL; + g_autoptr(Xmm7360RpcResponse) response = NULL; + Xmm7360RpcMsgArg *arg; + guint8 err_code_1; + guint8 err_code_2; + guint32 remaining_attempts; + MMModemLock lock_type; + + self = g_task_get_source_object (task); + + response = mm_broadband_modem_xmm7360_rpc_command_finish (MM_BROADBAND_MODEM_XMM7360 (modem), + res, + &error); + + if (error) { + g_task_return_error (task, error); + g_object_unref (task); + return; + } + + if (response->content->len < 10) { + g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, + "Changing PIN failed (response too short)"); + g_object_unref (task); + return; + } + + arg = (Xmm7360RpcMsgArg *) g_ptr_array_index (response->content, 2); + remaining_attempts = XMM7360_RPC_MSG_ARG_GET_INT (arg); + + arg = (Xmm7360RpcMsgArg *) g_ptr_array_index (response->content, 3); + err_code_1 = XMM7360_RPC_MSG_ARG_GET_INT (arg); + + arg = (Xmm7360RpcMsgArg *) g_ptr_array_index (response->content, 4); + err_code_2 = XMM7360_RPC_MSG_ARG_GET_INT (arg); + + lock_type = remaining_attempts > 0 ? MM_MODEM_LOCK_SIM_PIN : MM_MODEM_LOCK_SIM_PUK; + update_modem_unlock_retries (self, lock_type, remaining_attempts); + + if (err_code_1 == 0x83 && err_code_2 == 0x69) { + g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, + "Changing PIN failed (need to unlock PUK first)"); + g_object_unref (task); + return; + } else if (err_code_1 == 0x84 && err_code_2 == 0x69) { + g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, + "Changing PIN failed (PIN lock is disabled)"); + g_object_unref (task); + return; + } else if (err_code_2 == 0x63 && (err_code_1 == 0xc2 || err_code_1 == 0xc1 || err_code_1 == 0xc0)) { + g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, + "Changing PIN failed (wrong PIN, %d retries left)", + remaining_attempts); + g_object_unref (task); + return; + } else if (err_code_1 != 0x00 || err_code_2 != 0x90) { + g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, + "Changing PIN failed (unknown error: 0x%02x, 0x%02x)", + err_code_1, err_code_2); + g_object_unref (task); + return; + } + + g_task_return_boolean (task, TRUE); + g_object_unref (task); +} + +static GByteArray * +padded_pack_gen_pin_change (const gchar *old_pin_padded, const gchar *new_pin_padded) +{ + const Xmm7360RpcMsgArg args[] = { + { XMM7360_RPC_MSG_ARG_TYPE_BYTE, { .b = 0 } }, + { XMM7360_RPC_MSG_ARG_TYPE_LONG, { .l = XMM7360_GEN_PIN_OP_CHANGE } }, + { XMM7360_RPC_MSG_ARG_TYPE_BYTE, { .b = 0 } }, + { XMM7360_RPC_MSG_ARG_TYPE_LONG, { .l = 1 } }, + { XMM7360_RPC_MSG_ARG_TYPE_STRING, { .string = new_pin_padded }, 8, 2}, + { XMM7360_RPC_MSG_ARG_TYPE_STRING, { .string = old_pin_padded }, 8, 2}, + { XMM7360_RPC_MSG_ARG_TYPE_LONG, { .l = 0 } }, + { XMM7360_RPC_MSG_ARG_TYPE_UNKNOWN }, + }; + return xmm7360_rpc_args_to_byte_array (args); +} + +static GByteArray * +pack_gen_pin_change (const gchar *old_pin, const gchar *new_pin) +{ + gchar old_pin_padded[] = PIN_PADDED; + gchar new_pin_padded[] = PIN_PADDED; + copy_pin (old_pin, old_pin_padded); + copy_pin (new_pin, new_pin_padded); + return padded_pack_gen_pin_change (old_pin_padded, new_pin_padded); +} + +static void +change_pin (MMBaseSim *self, + const gchar *old_pin, + const gchar *new_pin, + GAsyncReadyCallback callback, + gpointer user_data) +{ + GTask *task; + g_autoptr(MMBaseModem) modem = NULL; + MMPortSerialXmmrpcXmm7360 *port; + g_autoptr(GByteArray) body = NULL; + + task = g_task_new (self, NULL, callback, user_data); + + g_object_get (G_OBJECT (self), + MM_BASE_SIM_MODEM, &modem, + NULL); + g_assert (MM_IS_BASE_MODEM (modem)); + + port = mm_broadband_modem_xmm7360_peek_port_xmmrpc (MM_BROADBAND_MODEM_XMM7360 (modem)); + g_object_unref (modem); + + if (!port) { + g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, + "Couldn't peek XMMRPC port"); + g_object_unref (task); + return; + } + + body = pack_gen_pin_change (old_pin, new_pin); + + mm_broadband_modem_xmm7360_rpc_command_full (MM_BROADBAND_MODEM_XMM7360 (modem), + port, + XMM7360_RPC_CALL_UTA_MS_SIM_GEN_PIN_REQ, + TRUE, + body, + 3, + FALSE, + NULL, /* cancellable */ + (GAsyncReadyCallback)gen_pin_change_ready, + task); +} + +/*****************************************************************************/ + +MMBaseSim * +mm_sim_xmm7360_new_finish (GAsyncResult *res, + GError **error) +{ + GObject *source; + GObject *sim; + + source = g_async_result_get_source_object (res); + sim = g_async_initable_new_finish (G_ASYNC_INITABLE (source), res, error); + g_object_unref (source); + + if (!sim) + return NULL; + + /* Only export valid SIMs */ + mm_base_sim_export (MM_BASE_SIM (sim)); + + return MM_BASE_SIM (sim); +} + +void +mm_sim_xmm7360_new (MMBaseModem *modem, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data) +{ + g_async_initable_new_async (MM_TYPE_SIM_XMM7360, + G_PRIORITY_DEFAULT, + cancellable, + callback, + user_data, + MM_BASE_SIM_MODEM, modem, + "active", TRUE, /* by default always active */ + NULL); +} + +MMBaseSim * +mm_sim_xmm7360_new_initialized (MMBaseModem *modem, + guint slot_number, + gboolean active, + MMSimType sim_type, + MMSimEsimStatus esim_status, + const gchar *sim_identifier, + const gchar *imsi, + const gchar *eid, + const gchar *operator_identifier, + const gchar *operator_name, + const GStrv emergency_numbers) +{ + MMBaseSim *sim; + + sim = MM_BASE_SIM (g_object_new (MM_TYPE_SIM_XMM7360, + MM_BASE_SIM_MODEM, modem, + MM_BASE_SIM_SLOT_NUMBER, slot_number, + "active", active, + "sim-type", sim_type, + "esim-status", esim_status, + "sim-identifier", sim_identifier, + "eid", eid, + "operator-identifier", operator_identifier, + "operator-name", operator_name, + "emergency-numbers", emergency_numbers, + NULL)); + + mm_base_sim_export (sim); + return sim; +} + +static void +mm_sim_xmm7360_init (MMSimXmm7360 *self) +{ +} + +static void +mm_sim_xmm7360_class_init (MMSimXmm7360Class *klass) +{ + MMBaseSimClass *base_sim_class = MM_BASE_SIM_CLASS (klass); + + base_sim_class->enable_pin = enable_pin; + base_sim_class->enable_pin_finish = enable_pin_finish; + base_sim_class->change_pin = change_pin; + base_sim_class->change_pin_finish = change_pin_finish; +} diff --git a/src/plugins/intel/mm-sim-xmm7360.h b/src/plugins/intel/mm-sim-xmm7360.h new file mode 100644 index 00000000..c83a9fc5 --- /dev/null +++ b/src/plugins/intel/mm-sim-xmm7360.h @@ -0,0 +1,68 @@ +/* -*- 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) 2024 Thomas Vogt + */ + +#ifndef MM_SIM_XMM7360_H +#define MM_SIM_XMM7360_H + +#include <glib.h> +#include <glib-object.h> + +#include "mm-base-sim.h" + +typedef enum { + XMM7360_GEN_PIN_OP_DISABLE = 2, + XMM7360_GEN_PIN_OP_ENABLE = 3, + XMM7360_GEN_PIN_OP_CHANGE = 4, +} Xmm7360GenPinOp; + +#define MM_TYPE_SIM_XMM7360 (mm_sim_xmm7360_get_type ()) +#define MM_SIM_XMM7360(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MM_TYPE_SIM_XMM7360, MMSimXmm7360)) +#define MM_SIM_XMM7360_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), MM_TYPE_SIM_XMM7360, MMSimXmm7360Class)) +#define MM_IS_SIM_XMM7360(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MM_TYPE_SIM_XMM7360)) +#define MM_IS_SIM_XMM7360_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), MM_TYPE_SIM_XMM7360)) +#define MM_SIM_XMM7360_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), MM_TYPE_SIM_XMM7360, MMSimXmm7360Class)) + +typedef struct _MMSimXmm7360 MMSimXmm7360; +typedef struct _MMSimXmm7360Class MMSimXmm7360Class; + +struct _MMSimXmm7360 { + MMBaseSim parent; +}; + +struct _MMSimXmm7360Class { + MMBaseSimClass parent; +}; + +GType mm_sim_xmm7360_get_type (void); +G_DEFINE_AUTOPTR_CLEANUP_FUNC (MMSimXmm7360, g_object_unref) + +void mm_sim_xmm7360_new (MMBaseModem *modem, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data); +MMBaseSim *mm_sim_xmm7360_new_finish (GAsyncResult *res, + GError **error); +MMBaseSim *mm_sim_xmm7360_new_initialized (MMBaseModem *modem, + guint slot_number, + gboolean active, + MMSimType sim_type, + MMSimEsimStatus esim_status, + const gchar *sim_identifier, + const gchar *imsi, + const gchar *eid, + const gchar *operator_identifier, + const gchar *operator_name, + const GStrv emergency_numbers); +#endif /* MM_SIM_XMM7360_H */ diff --git a/src/plugins/meson.build b/src/plugins/meson.build index b21b5e36..b1b6bb22 100644 --- a/src/plugins/meson.build +++ b/src/plugins/meson.build @@ -582,8 +582,15 @@ endif # plugin: intel if plugins_options['intel'] + intel_inc = include_directories('intel') + sources = files( 'intel/mm-plugin-intel.c', + 'intel/mm-broadband-modem-xmm7360.c', + 'intel/mm-broadband-modem-xmm7360-rpc.c', + 'intel/mm-port-serial-xmmrpc-xmm7360.c', + 'intel/mm-bearer-xmm7360.c', + 'intel/mm-sim-xmm7360.c', ) if enable_mbim @@ -592,9 +599,40 @@ if plugins_options['intel'] common_c_args = '-DMM_MODULE_NAME="intel"' + headers = files('intel/mm-port-serial-xmmrpc-xmm7360.h') + + enums_types = 'mm-intel-enums-types' + + sources += custom_target( + enums_types + '.c', + input: headers, + output: enums_types + '.c', + command: [ + python, + mm_mkenums, + '--fhead', '#include "' + enums_types + '.h"\n', + '--template', files(templates_dir / enums_types + '.c.template'), + '@INPUT@'], + capture: true, + ) + + sources += custom_target( + enums_types + '.h', + input: headers, + output: enums_types + '.h', + command: [ + python, + mm_mkenums, + '--fhead', '#include "mm-port-serial-xmmrpc-xmm7360.h"\n#ifndef __MM_XMM7360_ENUMS_TYPES_H__\n#define __MM_XMM7360_ENUMS_TYPES_H__\n', + '--template', files(templates_dir / enums_types + '.h.template'), + '--ftail', '#endif /* __MM_XMM7360_ENUMS_TYPES_H__ */\n', + '@INPUT@'], + capture: true, + ) + plugins += {'plugin-intel': { 'plugin': true, - 'module': {'sources': sources, 'include_directories': plugins_incs + [xmm_inc], 'c_args': common_c_args}, + 'module': {'sources': sources, 'include_directories': plugins_incs + [xmm_inc, intel_inc], 'c_args': common_c_args}, }} endif |