|
|
|
@ -59,9 +59,12 @@
|
|
|
|
|
|
|
|
|
|
#include "ofono.h"
|
|
|
|
|
#include "gdbus.h"
|
|
|
|
|
#include "util.h"
|
|
|
|
|
#include "dbus.h"
|
|
|
|
|
|
|
|
|
|
#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 +76,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;
|
|
|
|
@ -108,8 +113,380 @@ struct xmm7xxx_data {
|
|
|
|
|
ofono_bool_t have_sim;
|
|
|
|
|
unsigned int netreg_watch;
|
|
|
|
|
int xsim_status;
|
|
|
|
|
ofono_bool_t stk_enable;
|
|
|
|
|
ofono_bool_t enable_euicc;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
/* 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);
|
|
|
|
|
|
|
|
|
|
if (euicc->pending)
|
|
|
|
|
dbus_message_unref(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;
|
|
|
|
|
euicc->length = 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void euicc_pending_reply(struct xmm7xxx_euicc *euicc,
|
|
|
|
|
const char *resp)
|
|
|
|
|
{
|
|
|
|
|
DBusMessage *reply;
|
|
|
|
|
DBusMessageIter iter, array;
|
|
|
|
|
unsigned char *response = NULL;
|
|
|
|
|
long length;
|
|
|
|
|
int bufferBytesSize = strlen(resp) / 2;
|
|
|
|
|
|
|
|
|
|
reply = dbus_message_new_method_return(euicc->pending);
|
|
|
|
|
if (reply == NULL)
|
|
|
|
|
goto done;
|
|
|
|
|
|
|
|
|
|
response = g_new0(unsigned char, bufferBytesSize);
|
|
|
|
|
decode_hex_own_buf(resp, strlen(resp), &length, '\0', response );
|
|
|
|
|
|
|
|
|
|
dbus_message_iter_init_append(reply, &iter);
|
|
|
|
|
dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY,
|
|
|
|
|
DBUS_TYPE_BYTE_AS_STRING, &array);
|
|
|
|
|
dbus_message_iter_append_fixed_array(&array, DBUS_TYPE_BYTE,
|
|
|
|
|
&response, length);
|
|
|
|
|
dbus_message_iter_close_container(&iter, &array);
|
|
|
|
|
|
|
|
|
|
g_free(response);
|
|
|
|
|
done:
|
|
|
|
|
__ofono_dbus_pending_reply(&euicc->pending, reply);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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 DBusMessage *euicc_select_isdr_req(DBusConnection *conn,
|
|
|
|
|
DBusMessage *msg, void *data);
|
|
|
|
|
static DBusMessage *euicc_release_isdr_req(DBusConnection *conn,
|
|
|
|
|
DBusMessage *msg, void *data);
|
|
|
|
|
|
|
|
|
|
static const GDBusMethodTable euicc_methods[] = {
|
|
|
|
|
{ GDBUS_ASYNC_METHOD("TransmitLpaApdu",
|
|
|
|
|
GDBUS_ARGS({ "pdu", "ay" }),
|
|
|
|
|
GDBUS_ARGS({ "pdu", "ay" }),
|
|
|
|
|
euicc_transmit_pdu) },
|
|
|
|
|
{ GDBUS_ASYNC_METHOD("SelectISDR",
|
|
|
|
|
NULL, NULL, euicc_select_isdr_req) },
|
|
|
|
|
{ GDBUS_ASYNC_METHOD("ReleaseISDR",
|
|
|
|
|
NULL, NULL, euicc_release_isdr_req) },
|
|
|
|
|
{ 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("euicc_register");
|
|
|
|
|
|
|
|
|
|
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);
|
|
|
|
|
|
|
|
|
|
if (!ok) {
|
|
|
|
|
g_free(euicc->command);
|
|
|
|
|
|
|
|
|
|
if (!euicc->is_registered) {
|
|
|
|
|
g_free(euicc->eid);
|
|
|
|
|
g_free(euicc);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
DBG("Success");
|
|
|
|
|
|
|
|
|
|
g_at_result_iter_init(&iter, result);
|
|
|
|
|
DBG("Iter init");
|
|
|
|
|
|
|
|
|
|
if (!g_at_result_iter_next(&iter, "+CGLA:"))
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
DBG("CGLA");
|
|
|
|
|
|
|
|
|
|
if (!g_at_result_iter_next_number(&iter, &length))
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
DBG("length = %d", length);
|
|
|
|
|
|
|
|
|
|
if (!g_at_result_iter_next_string(&iter, &resp))
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
DBG("resp = %s", resp);
|
|
|
|
|
|
|
|
|
|
if (!euicc->is_registered) {
|
|
|
|
|
g_free(euicc->eid);
|
|
|
|
|
euicc->eid = g_strdup(resp+10);
|
|
|
|
|
euicc_release_isdr(euicc);
|
|
|
|
|
|
|
|
|
|
/* eid is present register interface*/
|
|
|
|
|
euicc_register(euicc);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
DBG("pending = %p", euicc->pending);
|
|
|
|
|
|
|
|
|
|
if (euicc->pending)
|
|
|
|
|
euicc_pending_reply(euicc, resp);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void euicc_send_cmd(struct xmm7xxx_euicc *euicc)
|
|
|
|
|
{
|
|
|
|
|
char *buff = g_new0(char, euicc->length + 20);
|
|
|
|
|
|
|
|
|
|
sprintf(buff, "AT+CGLA=%u,%u,\"%s\"",
|
|
|
|
|
euicc->channel, euicc->length, euicc->command);
|
|
|
|
|
|
|
|
|
|
g_at_chat_send(euicc->chat, buff, cgla_prefix,
|
|
|
|
|
euicc_send_cmd_cb, euicc, NULL);
|
|
|
|
|
|
|
|
|
|
g_free(buff);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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);
|
|
|
|
|
|
|
|
|
|
if (!euicc->is_registered)
|
|
|
|
|
euicc_send_cmd(euicc);
|
|
|
|
|
|
|
|
|
|
if (euicc->pending)
|
|
|
|
|
__ofono_dbus_pending_reply(&euicc->pending,
|
|
|
|
|
dbus_message_new_method_return(euicc->pending));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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, array;
|
|
|
|
|
const unsigned char *command;
|
|
|
|
|
int length;
|
|
|
|
|
|
|
|
|
|
DBG("euicc_transmit_pdu");
|
|
|
|
|
|
|
|
|
|
if (euicc->pending)
|
|
|
|
|
return __ofono_error_busy(msg);
|
|
|
|
|
|
|
|
|
|
if (euicc->channel < 0)
|
|
|
|
|
return __ofono_error_not_available(msg);
|
|
|
|
|
|
|
|
|
|
if (!dbus_message_iter_init(msg, &iter))
|
|
|
|
|
return __ofono_error_invalid_args(msg);
|
|
|
|
|
|
|
|
|
|
if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY)
|
|
|
|
|
return __ofono_error_invalid_args(msg);
|
|
|
|
|
|
|
|
|
|
dbus_message_iter_recurse(&iter, &array);
|
|
|
|
|
|
|
|
|
|
if (dbus_message_iter_get_arg_type(&array) != DBUS_TYPE_BYTE)
|
|
|
|
|
return __ofono_error_invalid_args(msg);
|
|
|
|
|
|
|
|
|
|
dbus_message_iter_get_fixed_array(&array, &command, &length);
|
|
|
|
|
|
|
|
|
|
g_free(euicc->command);
|
|
|
|
|
euicc->length = length * 2;
|
|
|
|
|
euicc->command = g_new0(char, euicc->length + 1);
|
|
|
|
|
encode_hex_own_buf(command,(long)length,0, euicc->command);
|
|
|
|
|
euicc->pending = dbus_message_ref(msg);
|
|
|
|
|
|
|
|
|
|
euicc_send_cmd(euicc);
|
|
|
|
|
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static DBusMessage *euicc_select_isdr_req(DBusConnection *conn,
|
|
|
|
|
DBusMessage *msg, void *data)
|
|
|
|
|
{
|
|
|
|
|
struct xmm7xxx_euicc *euicc = data;
|
|
|
|
|
|
|
|
|
|
DBG("euicc_select_isdr_req");
|
|
|
|
|
|
|
|
|
|
if (euicc->pending)
|
|
|
|
|
return __ofono_error_busy(msg);
|
|
|
|
|
|
|
|
|
|
euicc_select_isdr(euicc);
|
|
|
|
|
|
|
|
|
|
euicc->pending = dbus_message_ref(msg);
|
|
|
|
|
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static DBusMessage *euicc_release_isdr_req(DBusConnection *conn,
|
|
|
|
|
DBusMessage *msg, void *data)
|
|
|
|
|
{
|
|
|
|
|
struct xmm7xxx_euicc *euicc = data;
|
|
|
|
|
|
|
|
|
|
DBG("euicc_release_isdr_req");
|
|
|
|
|
|
|
|
|
|
if (euicc->pending)
|
|
|
|
|
return __ofono_error_busy(msg);
|
|
|
|
|
|
|
|
|
|
euicc_release_isdr(euicc);
|
|
|
|
|
|
|
|
|
|
return dbus_message_new_method_return(msg);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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("euicc 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,
|
|
|
|
@ -999,6 +1376,9 @@ static void switch_sim_state_status(struct ofono_modem *modem, int status)
|
|
|
|
|
|
|
|
|
|
ofono_sim_initialized_notify(data->sim);
|
|
|
|
|
break;
|
|
|
|
|
case 18:
|
|
|
|
|
data->enable_euicc = TRUE;
|
|
|
|
|
break;
|
|
|
|
|
default:
|
|
|
|
|
ofono_warn("Unknown SIM state %d received", status);
|
|
|
|
|
break;
|
|
|
|
@ -1176,6 +1556,7 @@ static int xmm7xxx_disable(struct ofono_modem *modem)
|
|
|
|
|
data->netreg_watch = 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
xmm_euicc_disable(modem);
|
|
|
|
|
return -EINPROGRESS;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
@ -1188,6 +1569,8 @@ 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);
|
|
|
|
|
ofono_stk_create(modem, 0, "atmodem", data->chat);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void set_online_cb(gboolean ok, GAtResult *result, gpointer user_data)
|
|
|
|
@ -1195,8 +1578,15 @@ static void set_online_cb(gboolean ok, GAtResult *result, gpointer user_data)
|
|
|
|
|
struct cb_data *cbd = user_data;
|
|
|
|
|
ofono_modem_online_cb_t cb = cbd->cb;
|
|
|
|
|
struct ofono_error error;
|
|
|
|
|
struct ofono_modem *modem = cbd->data;
|
|
|
|
|
struct xmm7xxx_data *data = ofono_modem_get_data(modem);
|
|
|
|
|
|
|
|
|
|
decode_at_error(&error, g_at_result_final_response(result));
|
|
|
|
|
|
|
|
|
|
if (data->enable_euicc == TRUE && data->stk_enable == TRUE)
|
|
|
|
|
g_at_chat_send(data->chat, "AT+CFUN=16", none_prefix,
|
|
|
|
|
NULL, NULL, NULL);
|
|
|
|
|
|
|
|
|
|
cb(&error, cbd->data);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
@ -1208,6 +1598,7 @@ static void xmm7xxx_set_online(struct ofono_modem *modem, ofono_bool_t online,
|
|
|
|
|
char const *command = online ? "AT+CFUN=1" : "AT+CFUN=4";
|
|
|
|
|
|
|
|
|
|
DBG("modem %p %s", modem, online ? "online" : "offline");
|
|
|
|
|
data->stk_enable = online;
|
|
|
|
|
|
|
|
|
|
if (g_at_chat_send(data->chat, command, none_prefix,
|
|
|
|
|
set_online_cb, cbd, g_free) > 0)
|
|
|
|
|