aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/mm-sim.c136
1 files changed, 136 insertions, 0 deletions
diff --git a/src/mm-sim.c b/src/mm-sim.c
index 4d238c95..782438d0 100644
--- a/src/mm-sim.c
+++ b/src/mm-sim.c
@@ -439,6 +439,127 @@ load_operator_identifier (MMSim *self,
}
/*****************************************************************************/
+/* Operator Name (Service Provider Name) */
+
+static gboolean
+parse_spn (MMSim *self,
+ gpointer none,
+ const gchar *command,
+ const gchar *response,
+ const GError *error,
+ GVariant **result,
+ GError **result_error)
+{
+ gint sw1;
+ gint sw2;
+ gboolean success = FALSE;
+ gchar hex[51];
+
+ if (error) {
+ *result_error = g_error_copy (error);
+ return FALSE;
+ }
+
+ memset (hex, 0, sizeof (hex));
+ if (sscanf (response, "+CRSM:%d,%d,\"%50c\"", &sw1, &sw2, (char *) &hex) == 3)
+ success = TRUE;
+ else {
+ /* May not include quotes... */
+ if (sscanf (response, "+CRSM:%d,%d,%50c", &sw1, &sw2, (char *) &hex) == 3)
+ success = TRUE;
+ }
+
+ if (!success) {
+ *result_error = g_error_new_literal (MM_CORE_ERROR,
+ MM_CORE_ERROR_FAILED,
+ "Could not parse the CRSM response");
+ return FALSE;
+ }
+
+ if ((sw1 == 0x90 && sw2 == 0x00) ||
+ (sw1 == 0x91) ||
+ (sw1 == 0x92) ||
+ (sw1 == 0x9f)) {
+ gsize buflen = 0;
+ gchar *bin;
+ gchar *utf8;
+
+ /* Make sure the buffer is only hex characters */
+ while (buflen < sizeof (hex) && hex[buflen]) {
+ if (!isxdigit (hex[buflen])) {
+ hex[buflen] = 0x0;
+ break;
+ }
+ buflen++;
+ }
+
+ /* Convert hex string to binary */
+ bin = utils_hexstr2bin (hex, &buflen);
+ if (!bin) {
+ *result_error = g_error_new (MM_CORE_ERROR,
+ MM_CORE_ERROR_FAILED,
+ "SIM returned malformed response '%s'",
+ hex);
+ return FALSE;
+ }
+
+ /* Remove the FF filler at the end */
+ while (buflen > 1 && bin[buflen - 1] == (char)0xff)
+ buflen--;
+
+ /* First byte is metadata; remainder is GSM-7 unpacked into octets; convert to UTF8 */
+ utf8 = (gchar *)mm_charset_gsm_unpacked_to_utf8 ((guint8 *)bin + 1, buflen - 1);
+ *result = g_variant_new_string (utf8);
+ g_free (utf8);
+ g_free (bin);
+ return TRUE;
+ }
+
+ *result_error = g_error_new (MM_CORE_ERROR,
+ MM_CORE_ERROR_FAILED,
+ "SIM failed to handle CRSM request (sw1 %d sw2 %d)",
+ sw1, sw2);
+ return FALSE;
+}
+
+static gchar *
+load_operator_name_finish (MMSim *self,
+ GAsyncResult *res,
+ GError **error)
+{
+ GVariant *result;
+ gchar *operator_name;
+
+ result = mm_at_command_finish (G_OBJECT (self), res, error);
+ if (!result)
+ return NULL;
+
+ operator_name = g_variant_dup_string (result, NULL);
+ g_variant_unref (result);
+ return operator_name;
+}
+
+static void
+load_operator_name (MMSim *self,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ mm_dbg ("loading Operator Name...");
+
+ /* READ BINARY of EFspn (Service Provider Name) ETSI 51.011 section 10.3.11 */
+ mm_at_command (G_OBJECT (self),
+ mm_base_modem_get_port_primary (MM_BASE_MODEM (self->priv->modem)),
+ "+CRSM=176,28486,0,0,17",
+ 3,
+ (MMAtResponseProcessor)parse_spn,
+ NULL, /* response_processor_context */
+ "s", /* spn */
+ NULL, /*TODO: cancellable */
+ callback,
+ user_data);
+}
+
+/*****************************************************************************/
typedef enum {
@@ -446,6 +567,7 @@ typedef enum {
INITIALIZATION_STEP_SIM_IDENTIFIER,
INITIALIZATION_STEP_IMSI,
INITIALIZATION_STEP_OPERATOR_ID,
+ INITIALIZATION_STEP_OPERATOR_NAME,
INITIALIZATION_STEP_LAST
} InitializationStep;
@@ -549,6 +671,7 @@ load_sim_identifier_ready (MMSim *self,
STR_REPLY_READY_FN (imsi, "IMSI")
STR_REPLY_READY_FN (operator_identifier, "Operator identifier")
+STR_REPLY_READY_FN (operator_name, "Operator name")
static void
interface_initialization_step (InitAsyncContext *ctx)
@@ -597,6 +720,19 @@ interface_initialization_step (InitAsyncContext *ctx)
}
break;
+ case INITIALIZATION_STEP_OPERATOR_NAME:
+ /* Operator Name is meant to be loaded only once during the whole
+ * lifetime of the modem. Therefore, if we already have them loaded,
+ * don't try to load them again. */
+ if (mm_gdbus_sim_get_operator_name (MM_GDBUS_SIM (ctx->self)) == NULL) {
+ load_operator_name (
+ ctx->self,
+ (GAsyncReadyCallback)load_operator_name_ready,
+ ctx);
+ return;
+ }
+ break;
+
case INITIALIZATION_STEP_LAST:
/* We are done without errors! */
g_simple_async_result_set_op_res_gboolean (ctx->result, TRUE);