diff options
Diffstat (limited to 'src/plugins/intel/mm-sim-xmm7360.c')
-rw-r--r-- | src/plugins/intel/mm-sim-xmm7360.c | 445 |
1 files changed, 445 insertions, 0 deletions
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; +} |