xmm7xxx modem plugin will support Gemalto's eUICC LPA functionality,
which includes opening a logical channel to ISDR applet in eUICC and
transmitting APDU command for Profile Download, Profile Activate etc.
from LPA to the eUICC and deliver the response from eUICC to LPA.
---
plugins/xmm7xxx.c | 313 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 313 insertions(+)
diff --git a/plugins/xmm7xxx.c b/plugins/xmm7xxx.c
index a544798..cb0a587 100644
--- a/plugins/xmm7xxx.c
+++ b/plugins/xmm7xxx.c
@@ -62,6 +62,7 @@
#define OFONO_COEX_INTERFACE OFONO_SERVICE ".intel.LteCoexistence"
#define OFONO_COEX_AGENT_INTERFACE OFONO_SERVICE ".intel.LteCoexistenceAgent"
+#define OFONO_EUICC_LPA_INTERFACE OFONO_SERVICE ".intel.EuiccLpa"
#define NET_BAND_LTE_INVALID 0
#define NET_BAND_LTE_1 101
@@ -73,6 +74,8 @@
static const char *none_prefix[] = { NULL };
static const char *xsimstate_prefix[] = { "+XSIMSTATE:", NULL };
static const char *xnvmplmn_prefix[] = { "+XNVMPLMN:", NULL };
+static const char *ccho_prefix[] = { "+CCHO:", NULL };
+static const char *cgla_prefix[] = { "+CGLA:", NULL };
struct bt_coex_info {
int safe_tx_min;
@@ -110,6 +113,312 @@ struct xmm7xxx_data {
unsigned int netreg_watch;
};
+/* eUICC Implementation */
+#define EUICC_EID_CMD "80e2910006BF3E035C015A00"
+#define EUICC_ISDR_AID "A0000005591010FFFFFFFF8900000100"
+
+struct xmm7xxx_euicc {
+ GAtChat *chat;
+ struct ofono_modem *modem;
+
+ char *eid;
+ int channel;
+ char *command;
+ int length;
+ DBusMessage *pending;
+ ofono_bool_t is_registered;
+};
+
+static void euicc_cleanup(void *data)
+{
+ struct xmm7xxx_euicc *euicc = data;
+
+ g_free(euicc->command);
+ g_free(euicc->eid);
+ g_free(euicc->pending);
+ g_free(euicc);
+}
+
+static void euicc_release_isdr(struct xmm7xxx_euicc *euicc)
+{
+ char buff[20];
+
+ snprintf(buff, sizeof(buff), "AT+CCHC=%u", euicc->channel);
+
+ g_at_chat_send(euicc->chat, buff, none_prefix, NULL, NULL, NULL);
+
+ euicc->channel = -1;
+ g_free(euicc->command);
+ euicc->command = NULL;
+}
+
+static void euicc_pending_reply(struct xmm7xxx_euicc *euicc,
+ const char *resp)
+{
+ DBusMessage *reply;
+ DBusConnection *conn = ofono_dbus_get_connection();
+
+ reply = dbus_message_new_method_return(euicc->pending);
+ if (reply == NULL)
+ return;
+
+ dbus_message_append_args(reply, DBUS_TYPE_STRING, &resp,
+ DBUS_TYPE_INVALID);
+
+ dbus_connection_flush(conn);
+
+ if (dbus_connection_send(conn, reply, NULL) == FALSE)
+ return;
+
+ dbus_message_unref(reply);
+ dbus_message_unref(euicc->pending);
+ euicc->pending = NULL;
+}
+
+static DBusMessage *euicc_get_properties(DBusConnection *conn,
+ DBusMessage *msg, void *data)
+{
+ struct xmm7xxx_euicc *euicc = data;
+ DBusMessage *reply;
+ DBusMessageIter iter;
+ DBusMessageIter dict;
+ const char *eid = NULL;
+
+ reply = dbus_message_new_method_return(msg);
+ if (reply == NULL)
+ return NULL;
+
+ dbus_message_iter_init_append(reply, &iter);
+
+ dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY,
+ OFONO_PROPERTIES_ARRAY_SIGNATURE,
+ &dict);
+
+ eid = euicc->eid;
+ ofono_dbus_dict_append(&dict, "EID", DBUS_TYPE_STRING, &eid);
+
+ dbus_message_iter_close_container(&iter, &dict);
+
+ return reply;
+}
+
+static DBusMessage *euicc_transmit_pdu(DBusConnection *conn,
+ DBusMessage *msg, void *data);
+
+static const GDBusMethodTable euicc_methods[] = {
+ { GDBUS_ASYNC_METHOD("TransmitLpaApdu",
+ GDBUS_ARGS({ "length", "i" }, { "pdu", "s"
}),
+ GDBUS_ARGS({ "pdu", "s" }),
+ euicc_transmit_pdu) },
+ { GDBUS_ASYNC_METHOD("GetProperties",
+ NULL, GDBUS_ARGS({ "properties", "a{sv}" }),
+ euicc_get_properties) },
+ { }
+};
+
+static const GDBusSignalTable euicc_signals[] = {
+ { GDBUS_SIGNAL("PropertyChanged",
+ GDBUS_ARGS({ "name", "s" }, { "value", "v" }))
},
+ { }
+};
+
+static void euicc_register(struct xmm7xxx_euicc *euicc)
+{
+ DBusConnection *conn = ofono_dbus_get_connection();
+ const char *path = ofono_modem_get_path(euicc->modem);
+
+ DBG("");
+ if (!g_dbus_register_interface(conn, path, OFONO_EUICC_LPA_INTERFACE,
+ euicc_methods,
+ euicc_signals,
+ NULL, euicc, euicc_cleanup)) {
+ ofono_error("Could not register %s interface under %s",
+ OFONO_EUICC_LPA_INTERFACE, path);
+ return;
+ }
+
+ ofono_modem_add_interface(euicc->modem, OFONO_EUICC_LPA_INTERFACE);
+
+ euicc->is_registered = TRUE;
+
+ ofono_dbus_signal_property_changed(conn, path,
+ OFONO_EUICC_LPA_INTERFACE, "EID",
+ DBUS_TYPE_STRING, &euicc->eid);
+}
+
+static void euicc_send_cmd_cb(gboolean ok, GAtResult *result,
+ gpointer user_data)
+{
+ struct xmm7xxx_euicc *euicc = user_data;
+ GAtResultIter iter;
+ int length;
+ const char *resp;
+
+ DBG("ok %d", ok);
+
+ euicc_release_isdr(euicc);
+
+ if (!ok) {
+ g_free(euicc->command);
+
+ if (!euicc->is_registered) {
+ g_free(euicc->eid);
+ g_free(euicc);
+ }
+
+ return;
+ }
+
+ g_at_result_iter_init(&iter, result);
+
+ if (!g_at_result_iter_next(&iter, "+CGLA:"))
+ return;
+
+ if (!g_at_result_iter_next_number(&iter, &length))
+ return;
+
+ if (!g_at_result_iter_next_string(&iter, &resp))
+ return;
+
+ if (!euicc->is_registered) {
+ g_free(euicc->eid);
+ euicc->eid = g_strdup(resp+10);
+
+ /* eid is present register interface*/
+ euicc_register(euicc);
+ }
+
+ if (euicc->pending)
+ euicc_pending_reply(euicc, resp);
+}
+
+static int euicc_send_cmd(struct xmm7xxx_euicc *euicc)
+{
+ char buff[100];
+
+ snprintf(buff, sizeof(buff), "AT+CGLA=%u,%u,\"%s\"",
+ euicc->channel, euicc->length, euicc->command);
+
+ if (g_at_chat_send(euicc->chat, buff, cgla_prefix,
+ euicc_send_cmd_cb, euicc, NULL))
+ return 0;
+
+ return -1;
+}
+
+static void euicc_select_isdr_cb(gboolean ok, GAtResult *result,
+ gpointer user_data)
+{
+ struct xmm7xxx_euicc *euicc = user_data;
+ GAtResultIter iter;
+
+ DBG("ok %d", ok);
+
+ if (!ok) {
+ g_free (euicc->command);
+
+ if (!euicc->is_registered) {
+ g_free(euicc->eid);
+ g_free(euicc);
+ }
+
+ return;
+ }
+
+ g_at_result_iter_init(&iter, result);
+
+ if (!g_at_result_iter_next(&iter, "+CCHO:"))
+ return;
+
+ g_at_result_iter_next_number(&iter, &euicc->channel);
+
+ euicc_send_cmd(euicc);
+}
+
+static void euicc_select_isdr(struct xmm7xxx_euicc *euicc)
+{
+ char buff[50];
+
+ snprintf(buff, sizeof(buff), "AT+CCHO=\"%s\"", EUICC_ISDR_AID);
+
+ g_at_chat_send(euicc->chat, buff, ccho_prefix,
+ euicc_select_isdr_cb, euicc, NULL);
+}
+
+static DBusMessage *euicc_transmit_pdu(DBusConnection *conn,
+ DBusMessage *msg, void *data)
+{
+ struct xmm7xxx_euicc *euicc = data;
+ DBusMessageIter iter;
+ DBusMessageIter var;
+ const char *command;
+
+ DBG("");
+ if (euicc->pending)
+ return __ofono_error_busy(msg);
+
+ if (!dbus_message_iter_init(msg, &iter))
+ return __ofono_error_invalid_args(msg);
+
+ if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_INT32)
+ return __ofono_error_invalid_args(msg);
+
+ dbus_message_iter_get_basic(&iter, &euicc->length);
+
+ dbus_message_iter_next(&iter);
+
+ if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_STRING)
+ return __ofono_error_invalid_args(msg);
+
+ dbus_message_iter_get_basic(&iter, &command);
+ g_free(euicc->command);
+ euicc->command = g_strdup(command);
+
+ euicc->pending = dbus_message_ref(msg);
+
+ euicc_select_isdr(euicc);
+
+ return NULL;
+}
+
+static void euicc_update_eid(struct xmm7xxx_euicc *euicc)
+{
+ g_free(euicc->command);
+ euicc->command = g_strdup(EUICC_EID_CMD);
+ euicc->length = sizeof(EUICC_EID_CMD) - 1;
+
+ euicc_select_isdr(euicc);
+}
+
+static void xmm_euicc_enable(struct ofono_modem *modem, void *data)
+{
+ struct xmm7xxx_euicc *euicc = g_new0(struct xmm7xxx_euicc, 1);
+
+ DBG("coex enable");
+
+ euicc->chat = data;
+ euicc->modem = modem;
+ euicc->eid = g_strdup("INVALID");
+ euicc->channel = -1;
+ euicc->command = NULL;
+ euicc->pending = NULL;
+ euicc->is_registered = FALSE;
+
+ euicc_update_eid(euicc);
+}
+
+static void xmm_euicc_disable(struct ofono_modem *modem)
+{
+ DBusConnection *conn = ofono_dbus_get_connection();
+ const char *path = ofono_modem_get_path(modem);
+
+ if (g_dbus_unregister_interface(conn, path,
+ OFONO_EUICC_LPA_INTERFACE))
+ ofono_modem_remove_interface(modem,
+ OFONO_EUICC_LPA_INTERFACE);
+}
+/* eUICC Implementation Ends */
+
/* Coex Implementation */
enum wlan_bw {
WLAN_BW_UNSUPPORTED = -1,
@@ -1181,6 +1490,8 @@ static int xmm7xxx_disable(struct ofono_modem *modem)
data->netreg_watch = 0;
}
+ xmm_euicc_disable(modem);
+
return -EINPROGRESS;
}
@@ -1193,6 +1504,7 @@ static void xmm7xxx_pre_sim(struct ofono_modem *modem)
ofono_devinfo_create(modem, OFONO_VENDOR_IFX, "atmodem", data->chat);
data->sim = ofono_sim_create(modem, OFONO_VENDOR_XMM, "atmodem",
data->chat);
+ xmm_euicc_enable(modem, data->chat);
}
static void set_online_cb(gboolean ok, GAtResult *result, gpointer user_data)
@@ -1290,6 +1602,7 @@ static int xmm7xxx_probe(struct ofono_modem *modem)
DBG("%p", modem);
data = g_try_new0(struct xmm7xxx_data, 1);
+
if (data == NULL)
return -ENOMEM;
--
1.9.1