From mboxrd@z Thu Jan 1 00:00:00 1970 Content-Type: multipart/mixed; boundary="===============0283468069608977877==" MIME-Version: 1.0 From: Denis Kenzior Subject: Re: [RFC PATCH] new gemalto plugin Date: Mon, 29 Oct 2018 14:58:44 -0500 Message-ID: In-Reply-To: <20181026061055.18669-1-gciofono@gmail.com> List-Id: To: ofono@ofono.org --===============0283468069608977877== Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Hi Giacinto, On 10/26/2018 01:10 AM, Giacinto Cifelli wrote: > I would like to submit to your attention the new version of the Gemalto > plugin. It is not ready, but would already benefit from some feedback. > The purpose of this new plugin is to address most of the Gemalto modules > (excluding perhaps some LTE CatM and CatNB), interfaced either via USB > or RS232 interface. So just a cursory look through this, but overall my impression is that = this code would be utterly unmaintainable. You need to split this up = into something without a bazillion if conditions and #ifdefs in it. = Notice how none of our existing driver code uses #ifdefs. That means MBIM/QMI/AT logic needs to be separated into separate = drivers. If you have a weird QMI/AT or MBIM/AT or QMI over MBIM = combination stuff going on, then these all need to be separate drivers. You may want to basically start with the basics. Get the initial driver = separation figured out, then you can add fancy stuff like vendor = specific APIs, etc. Right now they just clutter the code and are not = really useful to a wide audience anyway. > = > I have included the totality of file plugins/gemalto.c because it is > quite different from the current one. > = > I would appreciate a generic comment on how to split it in commits > this file. > = > I have added an include/gemalto.h file, as suggested by Jonas Bonn. > There isn't much in it yet, but some additional code should be moved into > it. > = > The gprs and gprs-context's are perhaps not finished. > = > In udevng there are 3 additional commits, please just comment if you > like and find them useful and I will submit separately. > = > There are some review comments, in // format, not part of the code. > = > --- > include/gemalto.h | 27 + > plugins/gemalto.c | 2715 +++++++++++++++++++++++++++++++++++++++++++++ > plugins/udevng.c | 208 ++-- > 3 files changed, 2885 insertions(+), 65 deletions(-) > create mode 100644 include/gemalto.h > create mode 100644 plugins/gemalto.c > = > diff --git a/include/gemalto.h b/include/gemalto.h > new file mode 100644 > index 00000000..26165eb1 > --- /dev/null > +++ b/include/gemalto.h > @@ -0,0 +1,27 @@ > +/* > + * > + * oFono - Open Source Telephony > + * > + * Copyright (C) 2018 Gemalto M2M > + * > + * This program is free software; you can redistribute it and/or modify > + * it under the terms of the GNU General Public License version 2 as > + * published by the Free Software Foundation. > + * > + * 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. > + * > + * You should have received a copy of the GNU General Public License > + * along with this program; if not, write to the Free Software > + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-130= 1 USA > + * > + */ > + > +enum auth_option { > + GEMALTO_AUTH_DEFAULTS =3D 0, > + GEMALTO_AUTH_USE_SGAUTH =3D 1<<0, > + GEMALTO_AUTH_ORDER_PWD_USR =3D 1<<1, > + GEMALTO_AUTH_ALWAYS_ALL_PARAMS =3D 1<<2, > +}; > diff --git a/plugins/gemalto.c b/plugins/gemalto.c > new file mode 100644 > index 00000000..6b208572 > --- /dev/null > +++ b/plugins/gemalto.c > @@ -0,0 +1,2715 @@ > +/* > + * > + * oFono - Open Source Telephony > + * > + * Copyright (C) 2017 Vincent Cesson. All rights reserved. > + * Copyright (C) 2018 Gemalto M2M > + * > + * This program is free software; you can redistribute it and/or modify > + * it under the terms of the GNU General Public License version 2 as > + * published by the Free Software Foundation. > + * > + * 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. > + * > + * You should have received a copy of the GNU General Public License > + * along with this program; if not, write to the Free Software > + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-130= 1 USA > + * > + */ > + > +#ifdef HAVE_CONFIG_H > +#include > +#endif > + > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include "ofono.h" > +#define OFONO_API_SUBJECT_TO_CHANGE > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > + > +#ifdef HAVE_ELL > +#include > +#include > +#include > +#include > +#endif > // some models can use MBIM, but if the option is not included, > // they fall back to PPP > + > +#include > +#include > +#include "gemalto.h" > + > // the next part will not be in the official commit > +/* debug utilities - begin */ > + > +#define REDCOLOR "\x1b\x5b\x30\x31\x3b\x33\x31\x6d" > +#define NOCOLOR "\x1b\x5b\x30\x30\x6d" > + > +#include > +#include > +#include > +#include > + > +void print_trace(); > + > +void print_trace() { > + char pid_buf[30]; > + char name_buf[512]; > + int child_pid; > + sprintf(pid_buf, "%d", getpid()); > + name_buf[readlink("/proc/self/exe", name_buf, 511)]=3D0; > + child_pid =3D fork(); > + if (!child_pid) { > + dup2(2,1); // redirect output to stderr > + fprintf(stdout,"stack trace for %s pid=3D%s\n",name_buf,pid_buf); > + execlp("gdb", "gdb", "--batch", "-n", "-ex", "thread", "-ex", "b= t", name_buf, pid_buf, NULL); > + abort(); /* If gdb failed to start */ > + } else { > + waitpid(child_pid,NULL,0); > + } > +} > + > +/* debug utilities - end */ > + > +enum gemalto_connection_type { > + GEMALTO_CONNECTION_SERIAL =3D 1, > + GEMALTO_CONNECTION_USB =3D 2, > +}; > + > +enum gemalto_device_state { > + STATE_ABSENT =3D 0, > + STATE_PROBE =3D 1, > + STATE_PRESENT =3D 2, > +}; > + > +enum gprs_option { > + NO_GPRS =3D 0, > + USE_SWWAN =3D 1, > + USE_CTX17 =3D 2, > + USE_CTX3 =3D 3, > + USE_PPP =3D 4, > + USE_SWWAN_INV =3D 5, /* inverted syntax idx,act */ > + USE_CTX_INV =3D 6, /* inverted syntax idx,act */ > +}; > + > +static const char *none_prefix[] =3D { NULL }; > +static const char *cfun_prefix[] =3D { "+CFUN:", NULL }; > +static const char *sctm_prefix[] =3D { "^SCTM:", NULL }; > +static const char *sbv_prefix[] =3D { "^SBV:", NULL }; > +static const char *sqport_prefix[] =3D { "^SQPORT:", NULL }; > +static const char *sgpsc_prefix[] =3D { "^SGPSC:", NULL }; > + > +typedef void (*OpenResultFunc)(gboolean success, struct ofono_modem *mod= em); > + > +struct gemalto_data { > + gboolean init_done; > + GIOChannel *channel; > + GAtChat *tmp_chat; > + OpenResultFunc open_cb; > + guint read_src; > + GAtChat *app; > + GAtChat *mdm; > + int cfun; > + > + struct ofono_sim *sim; > + gboolean have_sim; > + struct at_util_sim_state_query *sim_state_query; > + guint modem_ready_id; > + > + char modelstr[32]; > + char sqport[32]; > + > + guint model; > + guint probing_timer; > + guint init_waiting_time; > + guint waiting_time; > + > + enum gemalto_connection_type conn; > + enum gemalto_device_state mbim; > + enum gemalto_device_state qmi; > + enum gemalto_device_state ecmncm; > + enum gemalto_device_state gina; > + gboolean inverse_enum; > + gboolean use_mdm_for_app; > + gboolean voice_avail; > + enum auth_option auth_syntax; > + enum gprs_option gprs_opt; > + gboolean has_lte; > + gboolean autoattach; > + gboolean autoconfig; > + gboolean autoactivation; > + gboolean vts_with_quotes; > + > + void *device; /* struct mbim_device* or struct qmi_device* */ > + > + /* mbim data */ > + uint16_t max_segment; > + uint8_t max_outstanding; > + uint8_t max_sessions; > + The mbim plugin is not sufficient for this? > + /* hardware monitor variables */ > + DBusMessage *hm_msg; > + int32_t temperature; > + int32_t voltage; > + /* gnss variables */ > + DBusMessage *gnss_msg; > + /* hardware control variables */ > + DBusMessage *hc_msg; > + gboolean powersave; > +}; > + > +/***********************************************************************= ******** > + * Generic functions > + ***********************************************************************= *******/ > + > +static void gemalto_debug(const char *str, void *user_data) > +{ > + const char *prefix =3D user_data; > + > + ofono_info("%s%s", prefix, str); > +} > + > +static const char *gemalto_get_string(struct ofono_modem *modem, const c= har *k) > +{ > + const char *v; > + > + if (!modem || !k || !*k) > + return NULL; > + > + v =3D ofono_modem_get_string(modem, k); > + > + if (!v || !*v) > + return NULL; > + > + return v; > +} Uhh, why? > + > +static void gemalto_signal(const char *iface, const char *name, > + const char *value, struct ofono_modem *modem) > +{ > + DBusMessageIter sub_iter,iter; > + const char *path =3D ofono_modem_get_path(modem); > + DBusConnection *conn =3D ofono_dbus_get_connection(); > + > + DBusMessage *signal =3D dbus_message_new_signal(path, > + iface, > + name); > + > + DBG(""); > + > + if (signal =3D=3D NULL) { > + DBG("Cannot create new signal message"); > + return; > + } > + > + dbus_message_iter_init_append(signal, &iter); > + dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, > + "s", &sub_iter); > + if (!dbus_message_iter_append_basic(&sub_iter, > + DBUS_TYPE_STRING, &value)) { > + DBG("Out of memory!"); > + return; > + } > + > + dbus_message_iter_close_container(&iter, &sub_iter); > + g_dbus_send_message(conn, signal); > +} > + > +static void executeWithPrompt(GAtChat *port, const char *command, > + const char *prompt, const char *argument, void *cb, > + void *cbd, void *freecall) > +{ > + char *buf; > + const char *expected_array[2] =3D {0,0}; > + > + buf =3D g_strdup_printf("%s\r%s", command, argument); > + > + if (strlen(argument)>=3D2 && g_str_equal(argument+strlen(argument)-2, > + "^Z")) > + sprintf(buf+strlen(buf)-2,"\x1a"); > + > + if (strlen(argument)>=3D2 && g_str_equal(argument+strlen(argument)-2, > + "\\r")) > + sprintf(buf+strlen(buf)-2,"\r"); > + > + expected_array[0]=3Dprompt; > + g_at_chat_send_and_expect_short_prompt(port, buf, expected_array, > + cb, cbd, freecall); > + free(buf); > +} > + > +static void gemalto_exec_stored_cmd(struct ofono_modem *modem, > + const char *filename) > +{ > + struct gemalto_data *data =3D ofono_modem_get_data(modem); > + const char *vid =3D gemalto_get_string(modem, "Vendor"); > + const char *pid =3D gemalto_get_string(modem, "Model"); > + char store[64]; > + int index; > + char *command, *prompt, *argument; > + char key[32]; > + GKeyFile *f; > + > + sprintf(store,"%s-%s/%s", vid, pid, filename); > + f =3D storage_open(NULL, store); > + > + if (!f) > + return; > + > + for (index =3D 0; ; index++) { > + sprintf(key, "command_%d", index); > + command =3D g_key_file_get_string(f, "Simple", key, NULL); > + > + if (!command) > + break; > + > + DBG(REDCOLOR"executing stored command simple: %s"NOCOLOR, command); > + g_at_chat_send(data->app, command, NULL, NULL, NULL, NULL); > + } > + > + for (index =3D 0; ; index++) { > + sprintf(key, "command_%d", index); > + command =3D g_key_file_get_string(f, "WithPrompt", key, NULL); > + sprintf(key, "prompt_%d", index); > + prompt =3D g_key_file_get_string(f, "WithPrompt", key, NULL); > + sprintf(key, "argument_%d", index); > + argument =3D g_key_file_get_string(f, "WithPrompt", key, NULL); > + > + if (!command || !prompt || !argument) > + break; > + > + DBG("executing stored command with prompt: %s", command); > + executeWithPrompt(data->app, command, prompt, argument, > + NULL, NULL, NULL); > + } > + > + storage_close(NULL, store, f, FALSE); > +} What is this doing actually? > + > +/***********************************************************************= ******** > + * Hardware monitor interface > + ***********************************************************************= *******/ > + > +#define HARDWARE_MONITOR_INTERFACE OFONO_SERVICE ".gemalto.HardwareMonit= or" > +#define CINTERION_LEGACY_HWMON_INTERFACE OFONO_SERVICE ".cinterion.Hardw= areMonitor" > + > +static void gemalto_sctmb_notify(GAtResult *result, gpointer user_data) > +{ > + GAtResultIter iter; > + gint value; > + char *val; > + > + g_at_result_iter_init(&iter, result); > + g_at_result_iter_next(&iter, "^SCTM_B:"); > + g_at_result_iter_next_number(&iter, &value); > + > + switch(value) { > + case -1: > + val=3D"Below low temperature alert limit"; > + break; > + case 0: > + val=3D"Normal operating temperature"; > + break; > + case 1: > + val=3D"Above upper temperature alert limit"; > + break; > + case 2: > + val=3D"Above uppermost temperature limit"; > + break; > + default: /* unvalid value, do not output signal*/ > + return; > + } > + > + gemalto_signal(HARDWARE_MONITOR_INTERFACE, "CriticalTemperature", val, > + user_data); > +} > + > +static void gemalto_sbc_notify(GAtResult *result, gpointer user_data) > +{ > + GAtResultIter iter; > + const char *value; > + > + g_at_result_iter_init(&iter, result); > + g_at_result_iter_next(&iter, "^SBC:"); > + g_at_result_iter_next_unquoted_string(&iter, &value); > + gemalto_signal(HARDWARE_MONITOR_INTERFACE, "CriticalVoltage", value, > + user_data); > +} > + > +static void gemalto_sctm_cb(gboolean ok, GAtResult *result, gpointer use= r_data) > +{ > + struct gemalto_data *data =3D user_data; > + DBusMessage *reply; > + GAtResultIter iter; > + DBusMessageIter dbus_iter; > + DBusMessageIter dbus_dict; > + > + if (data->hm_msg =3D=3D NULL) > + return; > + > + if (!ok) > + goto error; > + > + g_at_result_iter_init(&iter, result); > + > + if (!g_at_result_iter_next(&iter, "^SCTM:")) > + goto error; > + > + if (!g_at_result_iter_skip_next(&iter)) > + goto error; > + > + if (!g_at_result_iter_skip_next(&iter)) > + goto error; > + > + if (!g_at_result_iter_next_number(&iter, &data->temperature)) > + goto error; > + > + reply =3D dbus_message_new_method_return(data->hm_msg); > + > + dbus_message_iter_init_append(reply, &dbus_iter); > + > + dbus_message_iter_open_container(&dbus_iter, DBUS_TYPE_ARRAY, > + OFONO_PROPERTIES_ARRAY_SIGNATURE, > + &dbus_dict); > + > + ofono_dbus_dict_append(&dbus_dict, "Temperature", > + DBUS_TYPE_INT32, &data->temperature); > + > + ofono_dbus_dict_append(&dbus_dict, "Voltage", > + DBUS_TYPE_UINT32, &data->voltage); > + > + dbus_message_iter_close_container(&dbus_iter, &dbus_dict); > + > + __ofono_dbus_pending_reply(&data->hm_msg, reply); > + > + return; > + > +error: > + __ofono_dbus_pending_reply(&data->hm_msg, > + __ofono_error_failed(data->hm_msg)); > +} > + > +static void gemalto_sbv_cb(gboolean ok, GAtResult *result, gpointer user= _data) > +{ > + struct gemalto_data *data =3D user_data; > + GAtResultIter iter; > + > + if (!ok) > + goto error; > + > + g_at_result_iter_init(&iter, result); > + > + if (!g_at_result_iter_next(&iter, "^SBV:")) > + goto error; > + > + if (!g_at_result_iter_next_number(&iter, &data->voltage)) > + goto error; > + > + if (g_at_chat_send(data->app, "AT^SCTM?", sctm_prefix, gemalto_sctm_cb, > + data, NULL) > 0) > + return; > + > +error: > + __ofono_dbus_pending_reply(&data->hm_msg, > + __ofono_error_failed(data->hm_msg)); > +} > + > +static DBusMessage *hardware_monitor_get_statistics(DBusConnection *conn, > + DBusMessage *msg, > + void *modem) > +{ > + struct gemalto_data *data =3D ofono_modem_get_data(modem); > + > + DBG(""); > + > + if (data->hm_msg !=3D NULL) > + return __ofono_error_busy(msg); > + > + if (!g_at_chat_send(data->app, "AT^SBV", sbv_prefix, gemalto_sbv_cb, > + data, NULL)) > + return __ofono_error_failed(msg); > + > + data->hm_msg =3D dbus_message_ref(msg); > + > + return NULL; > +} > + > +static const GDBusMethodTable hardware_monitor_methods[] =3D { > + { GDBUS_ASYNC_METHOD("GetStatistics", > + NULL, GDBUS_ARGS({ "Statistics", "a{sv}" }), > + hardware_monitor_get_statistics) }, > + {} > +}; > + > +static const GDBusSignalTable hardware_monitor_signals[] =3D { > + { GDBUS_SIGNAL("CriticalTemperature", > + GDBUS_ARGS({ "temperature", "a{sv}" }) )}, > + { GDBUS_SIGNAL("CriticalVoltage", > + GDBUS_ARGS({ "voltage", "a{sv}" }) )}, > + {} > +}; > + > +static void gemalto_hardware_monitor_enable(struct ofono_modem *modem) > +{ > + struct gemalto_data *data =3D ofono_modem_get_data(modem); > + DBusConnection *conn =3D ofono_dbus_get_connection(); > + const char *path =3D ofono_modem_get_path(modem); > + > + /* Listen to over/undertemperature URCs (activated with AT^SCTM) */ > + g_at_chat_register(data->app, "^SCTM_B:", > + gemalto_sctmb_notify, FALSE, NULL, NULL); > + /* Listen to over/under voltage URCs (automatic URC) */ > + g_at_chat_register(data->app, "^SBC:", > + gemalto_sbc_notify, FALSE, NULL, NULL); > + /* Enable temperature URC and value output */ > + g_at_chat_send(data->app, "AT^SCTM=3D1,1", none_prefix, NULL, NULL, NUL= L); > + > + if (!g_dbus_register_interface(conn, path, HARDWARE_MONITOR_INTERFACE, > + hardware_monitor_methods, > + hardware_monitor_signals, > + NULL, > + modem, > + NULL)) { > + ofono_error("Could not register %s interface under %s", > + HARDWARE_MONITOR_INTERFACE, path); > + return; > + } > + > + ofono_modem_add_interface(modem, HARDWARE_MONITOR_INTERFACE); > + > + if (!g_dbus_register_interface(conn, path, > + CINTERION_LEGACY_HWMON_INTERFACE, > + hardware_monitor_methods, > + NULL, > + NULL, > + modem, > + NULL)) { > + ofono_error("Could not register %s interface under %s", > + CINTERION_LEGACY_HWMON_INTERFACE, path); > + return; > + } > + > + ofono_modem_add_interface(modem, CINTERION_LEGACY_HWMON_INTERFACE); > +} > + > +/***********************************************************************= ******** > + * Time services interface > + ***********************************************************************= *******/ > + > +#define GEMALTO_NITZ_TIME_INTERFACE OFONO_SERVICE ".gemalto.TimeServices" > + > +static DBusMessage *set_modem_datetime(DBusConnection *conn, > + DBusMessage *msg, > + void *modem) > +{ > + struct gemalto_data *data =3D ofono_modem_get_data(modem); > + time_t t =3D time(NULL); > + struct tm tm; > + gchar cclk_cmd[32]; > + > + /* Set date and time */ > + tm =3D *localtime(&t); > + strftime(cclk_cmd, 32, "AT+CCLK=3D\"%y/%m/%d,%T\"", &tm); > + g_at_chat_send(data->app, cclk_cmd, none_prefix, NULL, NULL, NULL); > + return dbus_message_new_method_return(msg); > +} > + > +static const GDBusMethodTable gsmTime_methods[] =3D { > + { GDBUS_ASYNC_METHOD("SetModemDatetime", > + NULL, NULL, set_modem_datetime) }, > + {} > +}; > + So first question is, why would you want to do this? These days most = systems use the time on the Application processor. Who cares what the = modem thinks the time is. Secondly, why not do this as an actual core atom? > +static const GDBusSignalTable gsmTime_signals[] =3D { > + { GDBUS_SIGNAL("NitzUpdated", > + GDBUS_ARGS({ "time", "a{sv}" }) )}, > + {} > +}; You don't like ofono_netreg_time_notify? > + > +static void gemalto_time_enable(struct ofono_modem *modem) > +{ > + DBusConnection *conn =3D ofono_dbus_get_connection(); > + const char *path =3D ofono_modem_get_path(modem); > + > + if (!g_dbus_register_interface(conn, path, > + GEMALTO_NITZ_TIME_INTERFACE, > + gsmTime_methods, > + gsmTime_signals, > + NULL, > + modem, > + NULL)) { > + ofono_error("Could not register %s interface under %s", > + GEMALTO_NITZ_TIME_INTERFACE, path); > + return; > + } > + > + ofono_modem_add_interface(modem, GEMALTO_NITZ_TIME_INTERFACE); > +} > + > +/***********************************************************************= ******** > + * Command passtrhough interface > + ***********************************************************************= *******/ > + > +#define COMMAND_PASSTHROUGH_INTERFACE OFONO_SERVICE ".gemalto.CommandPas= sthrough" > + No AT command pass through in oFono upstream ;) We've had this = conversation many times, if there are useful AT commands that can be = sent via this interface, then they should be abstracted behind a proper = API instead. > +static int command_passthrough_signal_answer(const char *answer, > + gpointer user_data) > +{ > + struct ofono_modem *modem =3D user_data; > + DBusConnection *conn =3D ofono_dbus_get_connection(); > + const char *path =3D ofono_modem_get_path(modem); > + DBusMessage *signal; > + DBusMessageIter iter; > + > + if (!conn || !path) > + return -1; > + > + signal =3D dbus_message_new_signal(path, COMMAND_PASSTHROUGH_INTERFACE, > + "Answer"); > + if (!signal) { > + ofono_error("Unable to allocate new %s.PropertyChanged signal", > + COMMAND_PASSTHROUGH_INTERFACE); > + return -1; > + } > + > + dbus_message_iter_init_append(signal, &iter); > + > + dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, &answer); > + > + DBG(""); > + > + return g_dbus_send_message(conn, signal); > +} > + > +static void command_passthrough_cb(gboolean ok, GAtResult *result, > + gpointer user_data) > +{ > + GAtResultIter iter; > + guint len =3D 0; > + char *answer; > + > + g_at_result_iter_init(&iter, result); > + > + while (g_at_result_iter_next(&iter, NULL)) { > + len +=3D strlen(g_at_result_iter_raw_line(&iter))+2; > + } > + > + len +=3D strlen(g_at_result_final_response(result))+3; > + answer =3D g_new0(char, len); > + g_at_result_iter_init(&iter, result); > + > + while (g_at_result_iter_next(&iter, NULL)) { > + sprintf(answer+strlen(answer),"%s\r\n", > + g_at_result_iter_raw_line(&iter)); > + } > + > + sprintf(answer+strlen(answer),"%s\r\n", > + g_at_result_final_response(result)); > + > + DBG("answer_len: %u, answer_string: %s", len, answer); > + command_passthrough_signal_answer(answer, user_data); > + > + g_free(answer); > +} > + > +static DBusMessage *command_passthrough_simple(DBusConnection *conn, > + DBusMessage *msg, > + void *user_data) > +{ > + struct ofono_modem *modem =3D user_data; > + struct gemalto_data *data =3D ofono_modem_get_data(modem); > + DBusMessageIter iter; > + const char *command; > + > + if (!dbus_message_iter_init(msg, &iter)) > + return g_dbus_create_error(msg, DBUS_ERROR_INVALID_ARGS, > + "No arguments given"); > + > + if (dbus_message_iter_get_arg_type(&iter) !=3D DBUS_TYPE_STRING) > + return g_dbus_create_error(msg, DBUS_ERROR_INVALID_ARGS, > + "Invalid argument type: '%c'", > + dbus_message_iter_get_arg_type(&iter)); > + > + dbus_message_iter_get_basic(&iter, &command); > + > + g_at_chat_send(data->app, command, NULL, command_passthrough_cb, > + modem, NULL); > + > + return dbus_message_new_method_return(msg); > +} > + > +static DBusMessage *command_passthrough_with_prompt(DBusConnection *conn, > + DBusMessage *msg, > + void *user_data) > +{ > + struct ofono_modem *modem =3D user_data; > + struct gemalto_data *data =3D ofono_modem_get_data(modem); > + DBusMessageIter iter; > + const char *command, *prompt, *argument; > + > + if (!dbus_message_iter_init(msg, &iter)) > + return g_dbus_create_error(msg, DBUS_ERROR_INVALID_ARGS, > + "No arguments given"); > + > + if (dbus_message_iter_get_arg_type(&iter) !=3D DBUS_TYPE_STRING) > + return g_dbus_create_error(msg, DBUS_ERROR_INVALID_ARGS, > + "Invalid argument type: '%c'", > + dbus_message_iter_get_arg_type(&iter)); > + > + dbus_message_iter_get_basic(&iter, &command); > + dbus_message_iter_next(&iter); > + > + if (dbus_message_iter_get_arg_type(&iter) !=3D DBUS_TYPE_STRING) > + return g_dbus_create_error(msg, DBUS_ERROR_INVALID_ARGS, > + "Invalid argument type: '%c'", > + dbus_message_iter_get_arg_type(&iter)); > + > + dbus_message_iter_get_basic(&iter, &prompt); > + dbus_message_iter_next(&iter); > + > + if (dbus_message_iter_get_arg_type(&iter) !=3D DBUS_TYPE_STRING) > + return g_dbus_create_error(msg, DBUS_ERROR_INVALID_ARGS, > + "Invalid argument type: '%c'", > + dbus_message_iter_get_arg_type(&iter)); > + > + dbus_message_iter_get_basic(&iter, &argument); > + > + executeWithPrompt(data->app, command, prompt, argument, > + command_passthrough_cb, modem, NULL); > + > + return dbus_message_new_method_return(msg); > +} > + > +static DBusMessage *command_passthrough_send_break(DBusConnection *conn, > + DBusMessage *msg, > + void *user_data) > +{ > + struct ofono_modem *modem =3D user_data; > + struct gemalto_data *data =3D ofono_modem_get_data(modem); > + GIOChannel *channel =3D g_at_chat_get_channel(data->app); > + > + g_io_channel_write_chars(channel, "\r", 1, NULL, NULL); > + > + return dbus_message_new_method_return(msg); > +} > + > +static const GDBusMethodTable command_passthrough_methods[] =3D { > + { GDBUS_ASYNC_METHOD("Simple", > + GDBUS_ARGS({ "command", "s" }), > + NULL, > + command_passthrough_simple) }, > + { GDBUS_ASYNC_METHOD("WithPrompt", > + GDBUS_ARGS({ "command", "s" }, { "prompt", "s" }, > + { "argument", "s" }), > + NULL, > + command_passthrough_with_prompt) }, > + { GDBUS_ASYNC_METHOD("SendBreak", > + NULL, > + NULL, > + command_passthrough_send_break) }, > + {} > +}; > + > +static const GDBusSignalTable command_passthrough_signals[] =3D { > + { GDBUS_SIGNAL("Answer", > + GDBUS_ARGS({ "answer", "s" })) }, > + { } > +}; > + > +static void gemalto_command_passthrough_enable(struct ofono_modem *modem) > +{ > + DBusConnection *conn =3D ofono_dbus_get_connection(); > + const char *path =3D ofono_modem_get_path(modem); > + > + /* Create Command Passthrough DBus interface */ > + if (!g_dbus_register_interface(conn, path, COMMAND_PASSTHROUGH_INTERFAC= E, > + command_passthrough_methods, > + command_passthrough_signals, > + NULL, > + modem, > + NULL)) { > + ofono_error("Could not register %s interface under %s", > + COMMAND_PASSTHROUGH_INTERFACE, path); > + return; > + } > + > + ofono_modem_add_interface(modem, COMMAND_PASSTHROUGH_INTERFACE); > +} > + > +/***********************************************************************= ******** > + * GNSS interface > + ***********************************************************************= *******/ > + > +#define GNSS_INTERFACE OFONO_SERVICE ".gemalto.GNSS" > + > +static void gnss_get_properties_cb(gboolean ok, GAtResult *result, > + gpointer user_data) > +{ > + struct ofono_modem *modem =3D user_data; > + struct gemalto_data *data =3D ofono_modem_get_data(modem); > + const char *port =3D ofono_modem_get_string(modem, "GNSS"); > + GAtResultIter iter; > + DBusMessage *reply; > + DBusMessageIter dbusiter; > + DBusMessageIter dict; > + > + if (data->gnss_msg =3D=3D NULL) > + return; > + > + if (!ok) > + goto error; > + > + reply =3D dbus_message_new_method_return(data->gnss_msg); > + dbus_message_iter_init_append(reply, &dbusiter); > + dbus_message_iter_open_container(&dbusiter, DBUS_TYPE_ARRAY, > + OFONO_PROPERTIES_ARRAY_SIGNATURE, > + &dict); > + g_at_result_iter_init(&iter, result); > + > + /* supported format: ^SGPSC: "Nmea/Output","off" */ > + while (g_at_result_iter_next(&iter, "^SGPSC:")) { > + const char *name =3D ""; > + const char *val =3D ""; > + > + if (!g_at_result_iter_next_string(&iter, &name)) > + continue; > + > + /* > + * skip the "Info" property: > + * different line format and different usage > + */ > + if (g_str_equal(name,"Info")) > + continue; > + > + if (!g_at_result_iter_next_string(&iter, &val)) > + continue; > + > + ofono_dbus_dict_append(&dict, name, DBUS_TYPE_STRING, &val); > + } > + > + ofono_dbus_dict_append(&dict, "Port", DBUS_TYPE_STRING, &port); > + dbus_message_iter_close_container(&dbusiter, &dict); > + __ofono_dbus_pending_reply(&data->gnss_msg, reply); > + return; > + > +error: > + __ofono_dbus_pending_reply(&data->gnss_msg, > + __ofono_error_failed(data->gnss_msg)); > +} > + > +static DBusMessage *gnss_get_properties(DBusConnection *conn, > + DBusMessage *msg, void *user_data) > +{ > + struct ofono_modem *modem =3D user_data; > + struct gemalto_data *data =3D ofono_modem_get_data(modem); > + > + if (data->gnss_msg !=3D NULL) > + return __ofono_error_busy(msg); > + > + if (!g_at_chat_send(data->app, "AT^SGPSC?", sgpsc_prefix, > + gnss_get_properties_cb, modem, NULL)) > + return __ofono_error_failed(msg); > + > + data->gnss_msg =3D dbus_message_ref(msg); > + > + return NULL; > +} > + > +static void gnss_set_properties_cb(gboolean ok, GAtResult *result, > + gpointer user_data) > +{ > + struct ofono_modem *modem =3D user_data; > + struct gemalto_data *data =3D ofono_modem_get_data(modem); > + DBusMessage *reply; > + > + if (data->gnss_msg =3D=3D NULL) > + return; > + > + if (!ok) { > + __ofono_dbus_pending_reply(&data->gnss_msg, > + __ofono_error_failed(data->gnss_msg)); > + return; > + } > + > + reply =3D dbus_message_new_method_return(data->gnss_msg); > + __ofono_dbus_pending_reply(&data->gnss_msg, reply); > +} > + > +static DBusMessage *gnss_set_property(DBusConnection *conn, > + DBusMessage *msg, void *user_data) > +{ > + struct ofono_modem *modem =3D user_data; > + struct gemalto_data *data =3D ofono_modem_get_data(modem); > + DBusMessageIter iter, var; > + const char *name; > + char *value; > + char buf[256]; > + > + if (data->gnss_msg !=3D NULL) > + return __ofono_error_busy(msg); > + > + if (dbus_message_iter_init(msg, &iter) =3D=3D FALSE) > + return __ofono_error_invalid_args(msg); > + > + if (dbus_message_iter_get_arg_type(&iter) !=3D DBUS_TYPE_STRING) > + return __ofono_error_invalid_args(msg); > + > + dbus_message_iter_get_basic(&iter, &name); > + dbus_message_iter_next(&iter); > + > + if (dbus_message_iter_get_arg_type(&iter) !=3D DBUS_TYPE_VARIANT) > + return __ofono_error_invalid_args(msg); > + > + dbus_message_iter_recurse(&iter, &var); > + > + if (dbus_message_iter_get_arg_type(&var) !=3D > + DBUS_TYPE_STRING) > + return __ofono_error_invalid_args(msg); > + > + dbus_message_iter_get_basic(&var, &value); > + > + snprintf(buf, sizeof(buf), "AT^SGPSC=3D\"%s\",\"%s\"", name, value); > + So the modem is doing the property validation? Yeah, not happening ;) = Why don't you just configure the device into NMEA mode and use = location_reporting atom ? > + if (!g_at_chat_send(data->app, buf, sgpsc_prefix, > + gnss_set_properties_cb, modem, NULL)) > + return __ofono_error_failed(msg); > + > + data->gnss_msg =3D dbus_message_ref(msg); > + return NULL; > +} > + > +static const GDBusMethodTable gnss_methods[] =3D { > + { GDBUS_ASYNC_METHOD("GetProperties", > + NULL, GDBUS_ARGS({ "properties", "a{sv}" }), > + gnss_get_properties) }, > + { GDBUS_ASYNC_METHOD("SetProperty", > + GDBUS_ARGS({ "property", "s" }, { "value", "v" }), > + NULL, gnss_set_property) }, > + { } > +}; > + > +static void gnss_exec_stored_param(struct ofono_modem *modem, > + const char *filename) { > + struct gemalto_data *data =3D ofono_modem_get_data(modem); > + const char *vid =3D ofono_modem_get_string(modem, "Vendor"); > + const char *pid =3D ofono_modem_get_string(modem, "Model"); > + char store[64]; > + int index; > + char *property, *value; > + char key[32]; > + GKeyFile *f; > + char *command; > + > + sprintf(store,"%s-%s/%s", vid, pid, filename); > + f =3D storage_open(NULL, store); > + > + if (!f) > + return; > + > + for (index=3D0;;index++) { > + sprintf(key, "property_%d", index); > + property =3D g_key_file_get_string(f, "Properties", key, NULL); > + > + sprintf(key, "value_%d", index); > + value =3D g_key_file_get_string(f, "Properties", key, NULL); > + > + if(!property || !value) > + break; > + > + command =3D g_strdup_printf("AT^SGPSC=3D%s,%s", property, value); > + DBG(REDCOLOR"setting GNSS property: %sNOCOLOR", command); > + g_at_chat_send(data->app, command, NULL, NULL, NULL, NULL); > + free(command); > + } > + > + storage_close(NULL, store, f, FALSE); > +} > + > +static void gemalto_gnss_enable_cb(gboolean ok, GAtResult *result, > + gpointer user_data) > +{ > + struct ofono_modem *modem =3D user_data; > + DBusConnection *conn =3D ofono_dbus_get_connection(); > + const char *path =3D ofono_modem_get_path(modem); > + > + if (!ok) > + return; /* the module does not support GNSS */ > + > + gnss_exec_stored_param(modem, "gnss_startup"); > + > + /* Create GNSS DBus interface */ > + if (!g_dbus_register_interface(conn, path, GNSS_INTERFACE, > + gnss_methods, > + NULL, > + NULL, > + modem, > + NULL)) { > + ofono_error("Could not register %s interface under %s", > + GNSS_INTERFACE, path); > + return; > + } > + > + ofono_modem_add_interface(modem, GNSS_INTERFACE); > +} > + > +static void gemalto_gnss_enable(struct ofono_modem *modem) > +{ > + struct gemalto_data *data =3D ofono_modem_get_data(modem); > + > + g_at_chat_send(data->app, "AT^SGPSC?", sgpsc_prefix, > + gemalto_gnss_enable_cb, modem, NULL); > +} > + > +/***********************************************************************= ******** > + * Hardware control interface > + ***********************************************************************= *******/ > + > +#define HARDWARE_CONTROL_INTERFACE OFONO_SERVICE ".gemalto.HardwareContr= ol" > + > +static DBusMessage *hc_get_properties(DBusConnection *conn, > + DBusMessage *msg, void *user_data) > +{ > + struct ofono_modem *modem =3D user_data; > + struct gemalto_data *data =3D ofono_modem_get_data(modem); > + DBusMessage *reply; > + DBusMessageIter dbusiter; > + DBusMessageIter dict; > + > + reply =3D dbus_message_new_method_return(msg); > + dbus_message_iter_init_append(reply, &dbusiter); > + dbus_message_iter_open_container(&dbusiter, DBUS_TYPE_ARRAY, > + OFONO_PROPERTIES_ARRAY_SIGNATURE, > + &dict); > + > + ofono_dbus_dict_append(&dict, "Powersave", DBUS_TYPE_BOOLEAN, > + &data->powersave); > + dbus_message_iter_close_container(&dbusiter, &dict); > + > + return reply; > +} > + > +/* > + * powersave for older modules: > + * command_0=3DAT+CFUN=3D7 > + * return: > + * command_0=3DAT+CFUN=3D1 > + * > + * powersave example for modules with GNSS (could also only stop the out= put): > + * command_0=3DAT+CREG=3D0 > + * command_1=3DAT+CGREG=3D0 > + * command_2=3DAT+CEREG=3D0 > + * command_3=3DAT^SGPSC=3D"Engine","0" > + * command_4=3DAT^SGPSC=3D"Power/Antenna","off" > + * return: > + * command_0=3DAT+CREG=3D2 > + * command_1=3DAT+CGREG=3D2 > + * command_2=3DAT+CEREG=3D2 > + * command_4=3DAT^SGPSC=3D"Power/Antenna","on" > + * command_3=3DAT^SGPSC=3D"Engine","1" > + */ How is 'powersave' useful? > + > +static void gemalto_powersave_cb(gboolean ok, GAtResult *result, > + gpointer user_data) > +{ > + struct ofono_modem *modem =3D user_data; > + struct gemalto_data *data =3D ofono_modem_get_data(modem); > + DBusMessage *reply; > + > + /* flip the state in any case */ > + data->powersave =3D !data->powersave; > + > + if (data->hc_msg =3D=3D NULL) > + return; > + > + reply =3D dbus_message_new_method_return(data->hc_msg); > + __ofono_dbus_pending_reply(&data->hc_msg, reply); > +} > + > +static DBusMessage *hc_set_property(DBusConnection *conn, > + DBusMessage *msg, void *user_data) > +{ > + struct ofono_modem *modem =3D user_data; > + struct gemalto_data *data =3D ofono_modem_get_data(modem); > + DBusMessageIter iter, var; > + const char *name; > + gboolean enable; > + > + if (data->hc_msg !=3D NULL) > + return __ofono_error_busy(msg); > + > + if (dbus_message_iter_init(msg, &iter) =3D=3D FALSE) > + return __ofono_error_invalid_args(msg); > + > + if (dbus_message_iter_get_arg_type(&iter) !=3D DBUS_TYPE_STRING) > + return __ofono_error_invalid_args(msg); > + > + dbus_message_iter_get_basic(&iter, &name); > + > + if (!g_str_equal(name, "Powersave")) > + return __ofono_error_invalid_args(msg); > + > + dbus_message_iter_next(&iter); > + > + if (dbus_message_iter_get_arg_type(&iter) !=3D DBUS_TYPE_VARIANT) > + return __ofono_error_invalid_args(msg); > + > + dbus_message_iter_recurse(&iter, &var); > + > + if (dbus_message_iter_get_arg_type(&var) !=3D DBUS_TYPE_BOOLEAN) > + return __ofono_error_invalid_args(msg); > + > + dbus_message_iter_get_basic(&var, &enable); > + > + if (data->powersave =3D=3D enable) > + return dbus_message_new_method_return(msg); > + > + gemalto_exec_stored_cmd(modem, enable ? "power_mode_powersave" : > + "power_mode_normal"); > + > + gnss_exec_stored_param(modem, enable ? "gnss_powersave" : > + "gnss_normal"); > + > + if (!g_at_chat_send(data->app, "AT", none_prefix, > + gemalto_powersave_cb, modem, NULL)) > + return __ofono_error_failed(msg); > + > + data->hc_msg =3D dbus_message_ref(msg); > + return NULL; > +} > + > +static void gemalto_smso_cb(gboolean ok, GAtResult *result, gpointer use= r_data) > +{ > + struct ofono_modem *modem =3D user_data; > + struct gemalto_data *data =3D ofono_modem_get_data(modem); > + DBusMessage *reply; > + > + if (data->hc_msg =3D=3D NULL) > + return; > + > + if (data->conn !=3D GEMALTO_CONNECTION_SERIAL) > + goto finished; > + > + if (data->mdm) > + g_at_chat_unref(data->mdm); > + data->mdm =3D NULL; > + > + if (data->app) > + g_at_chat_unref(data->app); > + data->app =3D NULL; > + > + if (ok) > + ofono_modem_set_powered(modem, FALSE); > + > +finished: > + reply =3D dbus_message_new_method_return(data->hc_msg); > + __ofono_dbus_pending_reply(&data->hc_msg, reply); > +} > + > +static DBusMessage *hc_shutdown(DBusConnection *conn, > + DBusMessage *msg, void *user_data) > +{ > + struct ofono_modem *modem =3D user_data; > + struct gemalto_data *data =3D ofono_modem_get_data(modem); > + > + if (data->hc_msg !=3D NULL) > + return __ofono_error_busy(msg); > + > + if (!g_at_chat_send(data->app, "AT^SMSO", none_prefix, > + gemalto_smso_cb, modem, NULL)) > + return __ofono_error_failed(msg); > + > + data->hc_msg =3D dbus_message_ref(msg); > + return NULL; > +} > + > +static void gemalto_detect_sysstart(GAtResult *result, gpointer user_dat= a); > + > +static void gemalto_reset_cb(gboolean ok, GAtResult *result, gpointer us= er_data) > +{ > + struct ofono_modem *modem =3D user_data; > + struct gemalto_data *data =3D ofono_modem_get_data(modem); > + DBusMessage *reply; > + > + if (data->hc_msg =3D=3D NULL) > + return; > + > + if (data->conn !=3D GEMALTO_CONNECTION_SERIAL) > + goto finished; > + > + data->modem_ready_id =3D g_at_chat_register(data->app, > + "^SYSSTART", gemalto_detect_sysstart, FALSE, > + modem, NULL); > + > +finished: > + reply =3D dbus_message_new_method_return(data->hc_msg); > + __ofono_dbus_pending_reply(&data->hc_msg, reply); > +} > + > +static DBusMessage *hc_reset(DBusConnection *conn, > + DBusMessage *msg, void *user_data) > +{ > + struct ofono_modem *modem =3D user_data; > + struct gemalto_data *data =3D ofono_modem_get_data(modem); > + > + if (data->hc_msg !=3D NULL) > + return __ofono_error_busy(msg); > + > + if (!g_at_chat_send(data->app, "AT+CFUN=3D1,1", none_prefix, > + gemalto_reset_cb, modem, NULL)) > + return __ofono_error_failed(msg); > + > + data->hc_msg =3D dbus_message_ref(msg); > + return NULL; > +} > + > +static const GDBusMethodTable hardware_control_methods[] =3D { > + { GDBUS_ASYNC_METHOD("GetProperties", > + NULL, GDBUS_ARGS({ "properties", "a{sv}" }), > + hc_get_properties) }, > + { GDBUS_ASYNC_METHOD("SetProperty", > + GDBUS_ARGS({ "property", "s" }, { "value", "v" }), > + NULL, hc_set_property) }, > + { GDBUS_ASYNC_METHOD("Shutdown", > + NULL, NULL, hc_shutdown) }, > + { GDBUS_ASYNC_METHOD("Reset", > + NULL, NULL, hc_reset) }, > + { } > +}; > + > +static void gemalto_hardware_control_enable(struct ofono_modem *modem) > +{ > + DBusConnection *conn =3D ofono_dbus_get_connection(); > + const char *path =3D ofono_modem_get_path(modem); > + > + /* Create Hardware Control DBus interface */ > + if (!g_dbus_register_interface(conn, path, HARDWARE_CONTROL_INTERFACE, > + hardware_control_methods, > + NULL, > + NULL, > + modem, > + NULL)) { > + ofono_error("Could not register %s interface under %s", > + HARDWARE_CONTROL_INTERFACE, path); > + return; > + } > + > + ofono_modem_add_interface(modem, HARDWARE_CONTROL_INTERFACE); > +} > + > +/***********************************************************************= ******** > + * modem plugin > + ***********************************************************************= *******/ > + > +#ifdef HAVE_ELL > +static int mbim_parse_descriptors(struct gemalto_data *md, const char *f= ile) Why is MBIM driver not enough? > +{ > + void *data; > + size_t len; > + const struct mbim_desc *desc =3D NULL; > + const struct mbim_extended_desc *ext_desc =3D NULL; > + > + data =3D l_file_get_contents(file, &len); > + if (!data) > + return -EIO; > + > + if (!mbim_find_descriptors(data, len, &desc, &ext_desc)) { > + l_free(data); > + return -ENOENT; > + } > + > + if (desc) > + md->max_segment =3D L_LE16_TO_CPU(desc->wMaxControlMessage); > + > + if (ext_desc) > + md->max_outstanding =3D ext_desc->bMaxOutstandingCommandMessages; > + > + l_free(data); > + return 0; > +} > + > +static int mbim_probe(struct ofono_modem *modem, struct gemalto_data *da= ta) > +{ > + const char *descriptors; > + int err; > + > + descriptors =3D gemalto_get_string(modem, "DescriptorFile"); > + > + if (!descriptors) > + return -EINVAL; > + > + data->max_outstanding =3D 1; > + > + err =3D mbim_parse_descriptors(data, descriptors); > + if (err < 0) { > + DBG("Warning, unable to load descriptors, setting defaults"); > + data->max_segment =3D 512; > + } > + > + DBG("MaxSegment: %d, MaxOutstanding: %d", > + data->max_segment, data->max_outstanding); > + > + return 0; > +} > +#endif > + > +static int gemalto_probe(struct ofono_modem *modem) > +{ > + struct gemalto_data *data; > + > + data =3D g_try_new0(struct gemalto_data, 1); > + if (data =3D=3D NULL) > + return -ENOMEM; > + > +#ifdef HAVE_ELL > + mbim_probe(modem, data); > +#endif > + > + ofono_modem_set_data(modem, data); > + > + return 0; > +} > + > +static void gemalto_remove(struct ofono_modem *modem) > +{ > + struct gemalto_data *data; > + DBusConnection *conn =3D ofono_dbus_get_connection(); > + const char *path; > + > + if (!modem) > + return; > + > + data =3D ofono_modem_get_data(modem); > + path =3D ofono_modem_get_path(modem); > + > + if (!data) > + return; > + > +#ifdef HAVE_ELL > + if (data->mbim =3D=3D STATE_PRESENT) { > + mbim_device_shutdown(data->device); > + } > +#endif > + > + if (data->qmi =3D=3D STATE_PRESENT) { > + qmi_device_unref(data->device); > + } > + No, we're not doing MBIM/QMI/AT all in one driver. These should = basically be 3 different drivers or merged into plugins/mbim = plugins/gobi or whatever. > + if (data->app) { > + /* Cleanup potential SIM state polling */ > + at_util_sim_state_query_free(data->sim_state_query); > + data->sim_state_query =3D NULL; > + > + g_at_chat_cancel_all(data->app); > + g_at_chat_unregister_all(data->app); > + g_at_chat_unref(data->app); > + data->app =3D NULL; > + } > + > + if (data->mdm) { > + g_at_chat_cancel_all(data->app); > + g_at_chat_unregister_all(data->mdm); > + g_at_chat_unref(data->mdm); > + data->mdm =3D NULL; > + } > + > + if (conn && path) { > + if (g_dbus_unregister_interface(conn, path, > + HARDWARE_MONITOR_INTERFACE)) > + ofono_modem_remove_interface(modem, > + HARDWARE_MONITOR_INTERFACE); > + > + if (g_dbus_unregister_interface(conn, path, > + CINTERION_LEGACY_HWMON_INTERFACE)) > + ofono_modem_remove_interface(modem, > + CINTERION_LEGACY_HWMON_INTERFACE); > + > + if (g_dbus_unregister_interface(conn, path, > + GEMALTO_NITZ_TIME_INTERFACE)) > + ofono_modem_remove_interface(modem, > + GEMALTO_NITZ_TIME_INTERFACE); > + > + if (g_dbus_unregister_interface(conn, path, > + COMMAND_PASSTHROUGH_INTERFACE)) > + ofono_modem_remove_interface(modem, > + COMMAND_PASSTHROUGH_INTERFACE); > + > + if (g_dbus_unregister_interface(conn, path, > + GNSS_INTERFACE)) > + ofono_modem_remove_interface(modem, > + GNSS_INTERFACE); > + > + if (g_dbus_unregister_interface(conn, path, > + HARDWARE_CONTROL_INTERFACE)) > + ofono_modem_remove_interface(modem, > + HARDWARE_CONTROL_INTERFACE); > + } > + > + //if (data->conn =3D=3D GEMALTO_CONNECTION_SERIAL) > + // return; > + > + ofono_modem_set_data(modem, NULL); > + g_free(data); > +} > + > +static void sim_ready_cb(gboolean present, gpointer user_data) > +{ > + struct ofono_modem *modem =3D user_data; > + struct gemalto_data *data =3D ofono_modem_get_data(modem); > + struct ofono_sim *sim =3D data->sim; > + > + at_util_sim_state_query_free(data->sim_state_query); > + data->sim_state_query =3D NULL; > + > + DBG("sim present: %d", present); > + > + ofono_sim_inserted_notify(sim, present); > +} > + > +static void gemalto_ciev_simstatus_notify(GAtResultIter *iter, > + struct ofono_modem *modem) > +{ > + struct gemalto_data *data =3D ofono_modem_get_data(modem); > + struct ofono_sim *sim =3D data->sim; > + int status; > + > + DBG("sim status %d", status); > + > + if (!g_at_result_iter_next_number(iter, &status)) > + return; > + > + switch (status) { > + /* SIM is removed from the holder */ > + case 0: > + ofono_sim_inserted_notify(sim, FALSE); > + break; > + > + /* SIM is inserted inside the holder */ > + case 1: > + /* The SIM won't be ready yet */ > + data->sim_state_query =3D at_util_sim_state_query_new(data->app, > + 1, 20, sim_ready_cb, modem, > + NULL); > + break; > + > + /* USIM initialization completed. UE has finished reading USIM data. */ > + case 5: > + ofono_sim_initialized_notify(sim); > + break; > + > + default: > + break; > + } > +} > + > +static void gemalto_ciev_nitz_notify(GAtResultIter *iter, > + struct ofono_modem *modem) > +{ > + struct gemalto_data *data =3D ofono_modem_get_data(modem); > + const char *nitz_data; > + char buf[32]; > + > + /* Example: +CIEV: nitz,