new gemalto plugin
RFC PATCH 1/1 new gemalto plugin
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.
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
include/gemalto.h new file +27 -0
@@ -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-1301 USA +* +*/ + +enum auth_option { +GEMALTO_AUTH_DEFAULTS =0 , +GEMALTO_AUTH_USE_SGAUTH =1 <<0 , +GEMALTO_AUTH_ORDER_PWD_USR =1 <<1 , +GEMALTO_AUTH_ALWAYS_ALL_PARAMS =1 <<2 , +};
plugins/gemalto.c new file +64 -0
64 lines not on this page · raw message
// some models can use MBIM, but if the option is not included, // they fall back to PPP
+
+#include <drivers/qmimodem/qmi.h>
+#include <src/storage.h>
+#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 <stdio.h>
+#include <stdlib.h>
+#include <sys/wait.h>
+#include <unistd.h>
+
+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)]=0;
+ child_pid = fork();
+ if (!child_pid) {
+ dup2(2,1); // redirect output to stderr
+ fprintf(stdout,"stack trace for %s pid=%s\n",name_buf,pid_buf);
+ execlp("gdb", "gdb", "--batch", "-n", "-ex", "thread", "-ex", "bt", 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 = 1,
+ GEMALTO_CONNECTION_USB = 2,
+};
+
+enum gemalto_device_state {
+ STATE_ABSENT = 0,
+ STATE_PROBE = 1,
+ STATE_PRESENT = 2,
+};
+
+enum gprs_option {
+ NO_GPRS = 0,
+ USE_SWWAN = 1,
+ USE_CTX17 = 2,
+ USE_CTX3 = 3,
+ USE_PPP = 4,
+ USE_SWWAN_INV = 5, /* inverted syntax idx,act */
+ USE_CTX_INV = 6, /* inverted syntax idx,act */
+};
+
+static const char *none_prefix[] = { NULL };
+static const char *cfun_prefix[] = { "+CFUN:", NULL };
+static const char *sctm_prefix[] = { "^SCTM:", NULL };
+static const char *sbv_prefix[] = { "^SBV:", NULL };
+static const char *sqport_prefix[] = { "^SQPORT:", NULL };
+static const char *sgpsc_prefix[] = { "^SGPSC:", NULL };
+
+typedef void (*OpenResultFunc)(gboolean success, struct ofono_modem *modem); +
+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;
+
+ /* 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 = user_data;
+
+ ofono_info("%s%s", prefix, str);
+}
+
+static const char *gemalto_get_string(struct ofono_modem *modem, const char *k) +{
+ const char *v;
+
+ if (!modem || !k || !*k)
+ return NULL;
+
+ v = ofono_modem_get_string(modem, k);
+
+ if (!v || !*v)
+ return NULL;
+
+ return v;
+}
+
+static void gemalto_signal(const char *iface, const char *name,
+ const char *value, struct ofono_modem *modem)
+{
+ DBusMessageIter sub_iter,iter;
+ const char *path = ofono_modem_get_path(modem);
+ DBusConnection *conn = ofono_dbus_get_connection();
+
+ DBusMessage *signal = dbus_message_new_signal(path,
+ iface,
+ name);
+
+ DBG("");
+
+ if (signal == 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] = {0,0};
+
+ buf = g_strdup_printf("%s\r%s", command, argument);
+
+ if (strlen(argument)>=2 && g_str_equal(argument+strlen(argument)-2,
+ "^Z"))
+ sprintf(buf+strlen(buf)-2,"\x1a");
+
+ if (strlen(argument)>=2 && g_str_equal(argument+strlen(argument)-2,
+ "\\r"))
+ sprintf(buf+strlen(buf)-2,"\r");
+
+ expected_array[0]=prompt;
+ 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 = ofono_modem_get_data(modem);
+ const char *vid = gemalto_get_string(modem, "Vendor");
+ const char *pid = 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 = storage_open(NULL, store);
+
+ if (!f)
+ return;
+
+ for (index = 0; ; index++) {
+ sprintf(key, "command_%d", index);
+ command = 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 = 0; ; index++) {
+ sprintf(key, "command_%d", index);
+ command = g_key_file_get_string(f, "WithPrompt", key, NULL);
+ sprintf(key, "prompt_%d", index);
+ prompt = g_key_file_get_string(f, "WithPrompt", key, NULL);
+ sprintf(key, "argument_%d", index);
+ argument = 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);
+}
+ +/*******************************************************************************
+ * Hardware monitor interface
+ ******************************************************************************/ +
+#define HARDWARE_MONITOR_INTERFACE OFONO_SERVICE ".gemalto.HardwareMonitor" +#define CINTERION_LEGACY_HWMON_INTERFACE OFONO_SERVICE ".cinterion.HardwareMonitor" +
+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="Below low temperature alert limit";
+ break;
+ case 0:
+ val="Normal operating temperature";
+ break;
+ case 1:
+ val="Above upper temperature alert limit";
+ break;
+ case 2:
+ val="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 user_data) +{
+ struct gemalto_data *data = user_data;
+ DBusMessage *reply;
+ GAtResultIter iter;
+ DBusMessageIter dbus_iter;
+ DBusMessageIter dbus_dict;
+
+ if (data->hm_msg == 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 = 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 = 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 = ofono_modem_get_data(modem);
+
+ DBG("");
+
+ if (data->hm_msg != 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 = dbus_message_ref(msg);
+
+ return NULL;
+}
+
+static const GDBusMethodTable hardware_monitor_methods[] = {
+ { GDBUS_ASYNC_METHOD("GetStatistics",
+ NULL, GDBUS_ARGS({ "Statistics", "a{sv}" }),
+ hardware_monitor_get_statistics) },
+ {}
+};
+
+static const GDBusSignalTable hardware_monitor_signals[] = {
+ { 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 = ofono_modem_get_data(modem);
+ DBusConnection *conn = ofono_dbus_get_connection();
+ const char *path = 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=1,1", none_prefix, NULL, NULL, NULL);
+
+ 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 = ofono_modem_get_data(modem);
+ time_t t = time(NULL);
+ struct tm tm;
+ gchar cclk_cmd[32];
+
+ /* Set date and time */
+ tm = *localtime(&t);
+ strftime(cclk_cmd, 32, "AT+CCLK=\"%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[] = {
+ { GDBUS_ASYNC_METHOD("SetModemDatetime",
+ NULL, NULL, set_modem_datetime) },
+ {}
+};
+
+static const GDBusSignalTable gsmTime_signals[] = {
+ { GDBUS_SIGNAL("NitzUpdated",
+ GDBUS_ARGS({ "time", "a{sv}" }) )},
+ {}
+};
+
+static void gemalto_time_enable(struct ofono_modem *modem)
+{
+ DBusConnection *conn = ofono_dbus_get_connection();
+ const char *path = 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.CommandPassthrough" +
+static int command_passthrough_signal_answer(const char *answer,
+ gpointer user_data)
+{
+ struct ofono_modem *modem = user_data;
+ DBusConnection *conn = ofono_dbus_get_connection();
+ const char *path = ofono_modem_get_path(modem);
+ DBusMessage *signal;
+ DBusMessageIter iter;
+
+ if (!conn || !path)
+ return -1;
+
+ signal = 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 = 0;
+ char *answer;
+
+ g_at_result_iter_init(&iter, result);
+
+ while (g_at_result_iter_next(&iter, NULL)) {
+ len += strlen(g_at_result_iter_raw_line(&iter))+2;
+ }
+
+ len += strlen(g_at_result_final_response(result))+3;
+ answer = 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 = user_data;
+ struct gemalto_data *data = 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) != 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 = user_data;
+ struct gemalto_data *data = 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) != 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) != 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) != 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 = user_data;
+ struct gemalto_data *data = ofono_modem_get_data(modem);
+ GIOChannel *channel = 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[] = {
+ { 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[] = {
+ { GDBUS_SIGNAL("Answer",
+ GDBUS_ARGS({ "answer", "s" })) },
+ { }
+};
+
+static void gemalto_command_passthrough_enable(struct ofono_modem *modem) +{
+ DBusConnection *conn = ofono_dbus_get_connection();
+ const char *path = ofono_modem_get_path(modem);
+
+ /* Create Command Passthrough DBus interface */
+ if (!g_dbus_register_interface(conn, path, COMMAND_PASSTHROUGH_INTERFACE,
+ 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 = user_data;
+ struct gemalto_data *data = ofono_modem_get_data(modem);
+ const char *port = ofono_modem_get_string(modem, "GNSS");
+ GAtResultIter iter;
+ DBusMessage *reply;
+ DBusMessageIter dbusiter;
+ DBusMessageIter dict;
+
+ if (data->gnss_msg == NULL)
+ return;
+
+ if (!ok)
+ goto error;
+
+ reply = 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 = "";
+ const char *val = "";
+
+ 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 = user_data;
+ struct gemalto_data *data = ofono_modem_get_data(modem);
+
+ if (data->gnss_msg != 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 = dbus_message_ref(msg);
+
+ return NULL;
+}
+
+static void gnss_set_properties_cb(gboolean ok, GAtResult *result,
+ gpointer user_data)
+{
+ struct ofono_modem *modem = user_data;
+ struct gemalto_data *data = ofono_modem_get_data(modem);
+ DBusMessage *reply;
+
+ if (data->gnss_msg == NULL)
+ return;
+
+ if (!ok) {
+ __ofono_dbus_pending_reply(&data->gnss_msg,
+ __ofono_error_failed(data->gnss_msg));
+ return;
+ }
+
+ reply = 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 = user_data;
+ struct gemalto_data *data = ofono_modem_get_data(modem);
+ DBusMessageIter iter, var;
+ const char *name;
+ char *value;
+ char buf[256];
+
+ if (data->gnss_msg != NULL)
+ return __ofono_error_busy(msg);
+
+ if (dbus_message_iter_init(msg, &iter) == FALSE)
+ return __ofono_error_invalid_args(msg);
+
+ if (dbus_message_iter_get_arg_type(&iter) != 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) != DBUS_TYPE_VARIANT)
+ return __ofono_error_invalid_args(msg);
+
+ dbus_message_iter_recurse(&iter, &var);
+
+ if (dbus_message_iter_get_arg_type(&var) !=
+ DBUS_TYPE_STRING)
+ return __ofono_error_invalid_args(msg);
+
+ dbus_message_iter_get_basic(&var, &value);
+
+ snprintf(buf, sizeof(buf), "AT^SGPSC=\"%s\",\"%s\"", name, value);
+
+ if (!g_at_chat_send(data->app, buf, sgpsc_prefix,
+ gnss_set_properties_cb, modem, NULL))
+ return __ofono_error_failed(msg);
+
+ data->gnss_msg = dbus_message_ref(msg);
+ return NULL;
+}
+
+static const GDBusMethodTable gnss_methods[] = {
+ { 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 = ofono_modem_get_data(modem);
+ const char *vid = ofono_modem_get_string(modem, "Vendor");
+ const char *pid = 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 = storage_open(NULL, store);
+
+ if (!f)
+ return;
+
+ for (index=0;;index++) {
+ sprintf(key, "property_%d", index);
+ property = g_key_file_get_string(f, "Properties", key, NULL);
+
+ sprintf(key, "value_%d", index);
+ value = g_key_file_get_string(f, "Properties", key, NULL);
+
+ if(!property || !value)
+ break;
+
+ command = g_strdup_printf("AT^SGPSC=%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 = user_data;
+ DBusConnection *conn = ofono_dbus_get_connection();
+ const char *path = 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 = 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.HardwareControl" +
+static DBusMessage *hc_get_properties(DBusConnection *conn,
+ DBusMessage *msg, void *user_data)
+{
+ struct ofono_modem *modem = user_data;
+ struct gemalto_data *data = ofono_modem_get_data(modem);
+ DBusMessage *reply;
+ DBusMessageIter dbusiter;
+ DBusMessageIter dict;
+
+ reply = 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=AT+CFUN=7
+ * return:
+ * command_0=AT+CFUN=1
+ *
+ * powersave example for modules with GNSS (could also only stop the output):
+ * command_0=AT+CREG=0
+ * command_1=AT+CGREG=0
+ * command_2=AT+CEREG=0
+ * command_3=AT^SGPSC="Engine","0"
+ * command_4=AT^SGPSC="Power/Antenna","off"
+ * return:
+ * command_0=AT+CREG=2
+ * command_1=AT+CGREG=2
+ * command_2=AT+CEREG=2
+ * command_4=AT^SGPSC="Power/Antenna","on"
+ * command_3=AT^SGPSC="Engine","1"
+ */
+
+static void gemalto_powersave_cb(gboolean ok, GAtResult *result,
+ gpointer user_data)
+{
+ struct ofono_modem *modem = user_data;
+ struct gemalto_data *data = ofono_modem_get_data(modem);
+ DBusMessage *reply;
+
+ /* flip the state in any case */
+ data->powersave = !data->powersave;
+
+ if (data->hc_msg == NULL)
+ return;
+
+ reply = 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 = user_data;
+ struct gemalto_data *data = ofono_modem_get_data(modem);
+ DBusMessageIter iter, var;
+ const char *name;
+ gboolean enable;
+
+ if (data->hc_msg != NULL)
+ return __ofono_error_busy(msg);
+
+ if (dbus_message_iter_init(msg, &iter) == FALSE)
+ return __ofono_error_invalid_args(msg);
+
+ if (dbus_message_iter_get_arg_type(&iter) != 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) != DBUS_TYPE_VARIANT)
+ return __ofono_error_invalid_args(msg);
+
+ dbus_message_iter_recurse(&iter, &var);
+
+ if (dbus_message_iter_get_arg_type(&var) != DBUS_TYPE_BOOLEAN)
+ return __ofono_error_invalid_args(msg);
+
+ dbus_message_iter_get_basic(&var, &enable);
+
+ if (data->powersave == 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 = dbus_message_ref(msg);
+ return NULL;
+}
+
+static void gemalto_smso_cb(gboolean ok, GAtResult *result, gpointer user_data) +{
+ struct ofono_modem *modem = user_data;
+ struct gemalto_data *data = ofono_modem_get_data(modem);
+ DBusMessage *reply;
+
+ if (data->hc_msg == NULL)
+ return;
+
+ if (data->conn != GEMALTO_CONNECTION_SERIAL)
+ goto finished;
+
+ if (data->mdm)
+ g_at_chat_unref(data->mdm);
+ data->mdm = NULL;
+
+ if (data->app)
+ g_at_chat_unref(data->app);
+ data->app = NULL;
+
+ if (ok)
+ ofono_modem_set_powered(modem, FALSE);
+
+finished:
+ reply = 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 = user_data;
+ struct gemalto_data *data = ofono_modem_get_data(modem);
+
+ if (data->hc_msg != 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 = dbus_message_ref(msg);
+ return NULL;
+}
+
+static void gemalto_detect_sysstart(GAtResult *result, gpointer user_data); +
+static void gemalto_reset_cb(gboolean ok, GAtResult *result, gpointer user_data) +{
+ struct ofono_modem *modem = user_data;
+ struct gemalto_data *data = ofono_modem_get_data(modem);
+ DBusMessage *reply;
+
+ if (data->hc_msg == NULL)
+ return;
+
+ if (data->conn != GEMALTO_CONNECTION_SERIAL)
+ goto finished;
+
+ data->modem_ready_id = g_at_chat_register(data->app,
+ "^SYSSTART", gemalto_detect_sysstart, FALSE,
+ modem, NULL);
+
+finished:
+ reply = 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 = user_data;
+ struct gemalto_data *data = ofono_modem_get_data(modem);
+
+ if (data->hc_msg != NULL)
+ return __ofono_error_busy(msg);
+
+ if (!g_at_chat_send(data->app, "AT+CFUN=1,1", none_prefix,
+ gemalto_reset_cb, modem, NULL))
+ return __ofono_error_failed(msg);
+
+ data->hc_msg = dbus_message_ref(msg);
+ return NULL;
+}
+
+static const GDBusMethodTable hardware_control_methods[] = {
+ { 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 = ofono_dbus_get_connection();
+ const char *path = 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 *file) +{
+ void *data;
+ size_t len;
+ const struct mbim_desc *desc = NULL;
+ const struct mbim_extended_desc *ext_desc = NULL;
+
+ data = 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 = L_LE16_TO_CPU(desc->wMaxControlMessage);
+
+ if (ext_desc)
+ md->max_outstanding = ext_desc->bMaxOutstandingCommandMessages;
+
+ l_free(data);
+ return 0;
+}
+
+static int mbim_probe(struct ofono_modem *modem, struct gemalto_data *data) +{
+ const char *descriptors;
+ int err;
+
+ descriptors = gemalto_get_string(modem, "DescriptorFile");
+
+ if (!descriptors)
+ return -EINVAL;
+
+ data->max_outstanding = 1;
+
+ err = mbim_parse_descriptors(data, descriptors);
+ if (err < 0) {
+ DBG("Warning, unable to load descriptors, setting defaults");
+ data->max_segment = 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 = g_try_new0(struct gemalto_data, 1);
+ if (data == 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 = ofono_dbus_get_connection();
+ const char *path;
+
+ if (!modem)
+ return;
+
+ data = ofono_modem_get_data(modem);
+ path = ofono_modem_get_path(modem);
+
+ if (!data)
+ return;
+
+#ifdef HAVE_ELL
+ if (data->mbim == STATE_PRESENT) {
+ mbim_device_shutdown(data->device);
+ }
+#endif
+
+ if (data->qmi == STATE_PRESENT) {
+ qmi_device_unref(data->device);
+ }
+
+ if (data->app) {
+ /* Cleanup potential SIM state polling */
+ at_util_sim_state_query_free(data->sim_state_query);
+ data->sim_state_query = NULL;
+
+ g_at_chat_cancel_all(data->app);
+ g_at_chat_unregister_all(data->app);
+ g_at_chat_unref(data->app);
+ data->app = 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 = 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 == 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 = user_data;
+ struct gemalto_data *data = ofono_modem_get_data(modem);
+ struct ofono_sim *sim = data->sim;
+
+ at_util_sim_state_query_free(data->sim_state_query);
+ data->sim_state_query = 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 = ofono_modem_get_data(modem);
+ struct ofono_sim *sim = 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 = 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 = ofono_modem_get_data(modem);
+ const char *nitz_data;
+ char buf[32];
+
+ /* Example: +CIEV: nitz,<time>,<timezone>,<daylight> */
+ if (!g_at_result_iter_next_string(iter, &nitz_data))
+ return;
+
+ DBG("nitz_data %s", nitz_data);
+
+ sprintf(buf, "AT+CCLK=\"%s\"", nitz_data);
+ g_at_chat_send(data->app, buf, none_prefix, NULL, NULL, NULL);
+
+ gemalto_signal(GEMALTO_NITZ_TIME_INTERFACE, "NitzUpdated", nitz_data,
+ modem);
+}
+
+static void gemalto_ciev_notify(GAtResult *result, gpointer user_data) +{
+ struct ofono_modem *modem = user_data;
+
+ const char *sim_status = "simstatus";
+ const char *nitz_status = "nitz";
+ const char *ind_str;
+ GAtResultIter iter;
+
+ g_at_result_iter_init(&iter, result);
+
+ /* Example: +CIEV: simstatus,<status> */
+ if (!g_at_result_iter_next(&iter, "+CIEV:"))
+ return;
+
+ if (!g_at_result_iter_next_unquoted_string(&iter, &ind_str))
+ return;
+
+ if (g_str_equal(sim_status, ind_str)) {
+ gemalto_ciev_simstatus_notify(&iter, modem);
+ } else if (g_str_equal(nitz_status, ind_str)) {
+ gemalto_ciev_nitz_notify(&iter, modem);
+ }
+}
+
+static void sim_state_cb(gboolean present, gpointer user_data)
+{
+ struct ofono_modem *modem = user_data;
+ struct gemalto_data *data = ofono_modem_get_data(modem);
+
+ at_util_sim_state_query_free(data->sim_state_query);
+ data->sim_state_query = NULL;
+
+ data->have_sim = present;
+ ofono_modem_set_powered(modem, TRUE);
+
+ /* Register for specific sim status reports */
+ g_at_chat_register(data->app, "+CIEV:",
+ gemalto_ciev_notify, FALSE, modem, NULL);
+
+ g_at_chat_send(data->app, "AT^SIND=\"simstatus\",1", none_prefix,
+ NULL, NULL, NULL);
+ g_at_chat_send(data->app, "AT^SIND=\"nitz\",1", none_prefix,
+ NULL, NULL, NULL);
+}
+
+static void gemalto_exit_urc_notify(GAtResult *result, gpointer user_data) +{
+ GAtResultIter iter;
+ const char *error_message;
+
+ g_at_result_iter_init(&iter, result);
+ g_at_result_iter_next(&iter, "^EXIT:");
+ g_at_result_iter_next_unquoted_string(&iter, &error_message);
+ ofono_error("Modem exited! Cause: %s", error_message);
+}
+
+static void saic_probe(gboolean ok, GAtResult *result, gpointer user_data) +{
+ struct gemalto_data *data = ofono_modem_get_data(user_data);
+
+ if (ok)
+ data->voice_avail = TRUE;
+ else
+ data->voice_avail = FALSE;
+}
+
+static void sgauth_probe(gboolean ok, GAtResult *result, gpointer user_data) +{
+ struct gemalto_data *data = ofono_modem_get_data(user_data);
+
+ if (ok)
+ data->auth_syntax = GEMALTO_AUTH_USE_SGAUTH |
+ GEMALTO_AUTH_ORDER_PWD_USR;
+ else
+ data->auth_syntax = GEMALTO_AUTH_DEFAULTS;
+}
+
+static void gemalto_set_cfun_cb(gboolean ok, GAtResult *result,
+ gpointer user_data)
+{
+ struct ofono_modem *modem = user_data;
+ struct gemalto_data *data = ofono_modem_get_data(modem);
+
+ if (!ok || data->cfun == 41) {
+ g_at_chat_cancel_all(data->app);
+ ofono_modem_set_powered(modem, FALSE);
+ } else {
+ data->sim_state_query = at_util_sim_state_query_new(data->app,
+ 2, 20, sim_state_cb, modem, NULL);
+ }
+}
+
+static void gemalto_cfun_query(gboolean ok, GAtResult *result,
+ gpointer user_data)
+{
+ struct ofono_modem *modem = user_data;
+ struct gemalto_data *data = ofono_modem_get_data(user_data);
+ char buf[256];
+ GAtResultIter iter;
+ int mode;
+
+ sprintf(buf, "AT+CFUN=%d", data->cfun==41?4:data->cfun);
+
+ if (!ok)
+ goto error;
+
+ g_at_result_iter_init(&iter, result);
+
+ if (!g_at_result_iter_next(&iter, "+CFUN:"))
+ goto error;
+
+ if (!g_at_result_iter_next_number(&iter, &mode))
+ goto error;
+
+ if (mode == data->cfun)
+ sprintf(buf, "AT");
+
+error:
+ if (g_at_chat_send(data->app, buf, none_prefix, gemalto_set_cfun_cb,
+ modem, NULL) > 0)
+ return;
+
+ if (data->cfun == 41)
+ ofono_modem_set_powered(modem, FALSE);
+}
+
+static void gemalto_set_cfun(GAtChat *app, int mode, struct ofono_modem *modem) +{
+ struct gemalto_data *data = ofono_modem_get_data(modem);
+
+ data->cfun=mode;
+ g_at_chat_send(app, "AT+CFUN?", cfun_prefix, gemalto_cfun_query, modem, NULL);
+}
+
+static void gemalto_initialize(struct ofono_modem *modem)
+{
+ struct gemalto_data *data = ofono_modem_get_data(modem);
+ char *urcdest;
+ guint m = data->model;
+
+ DBG("app:%d, mdm:%d, mbim:%d, qmi:%d",
+ data->app!=NULL,
+ data->mdm!=NULL,
+ data->mbim == STATE_PRESENT,
+ data->qmi == STATE_PRESENT);
+
+ if (!data->app && !data->mdm) {
+ DBG("no AT interface available. Removing this device.");
+ ofono_modem_set_powered(modem, FALSE);
+ return;
+ }
+
+ urcdest = "AT^SCFG=\"URC/DstIfc\",\"app\"";
+
+ if (!data->app) {
+ data->use_mdm_for_app = TRUE;
+ data->app = data->mdm;
+ urcdest = "AT^SCFG=\"URC/DstIfc\",\"mdm\"";
+ }
+
+ if (!data->mdm && (data->gina == STATE_PRESENT)) {
+ /*these modems can start PPP from any port*/
+ data->mdm = data->app;
+ }
+
+ if (data->mdm && data->gprs_opt == NO_GPRS)
+ data->gprs_opt = USE_PPP;
+
+ g_at_chat_set_wakeup_command(data->app, "AT\r", 1000, 5000);
+
+ g_at_chat_send(data->app, "ATE0", none_prefix, NULL, NULL, NULL);
+
+ if (data->gina != STATE_PRESENT)
+ g_at_chat_send(data->app, urcdest, none_prefix, NULL, NULL,
+ NULL);
+
+ /* numeric error codes are interpreted by atmodem/atutil.c functions */
+ g_at_chat_send(data->app, "AT+CMEE=1", none_prefix, NULL, NULL, NULL);
+
+ if (data->mdm)
+ g_at_chat_send(data->mdm, "AT&C0", none_prefix, NULL, NULL,
+ NULL);
+
+ g_at_chat_send(data->app, "AT&C0", none_prefix, NULL, NULL, NULL);
+
+ /* watchdog */
+ g_at_chat_register(data->app, "^EXIT", gemalto_exit_urc_notify, FALSE,
+ modem, NULL);
+ ofono_devinfo_create(modem, OFONO_VENDOR_GEMALTO, "atmodem", data->app);
+ g_at_chat_send(data->app,
+ "AT^SCFG=\"MEopMode/PwrSave\",\"enabled\",52,50", none_prefix,
+ NULL, NULL, NULL);
// this command was in some gemalto.c versions, but it would better be // placed in one of the configuration files
+
+ if (m != 0x5b && m != 0x5c && m != 0x5d && m != 0xa0) {
+ g_at_chat_send(data->app, "AT^SGAUTH?", NULL, sgauth_probe,
+ modem, NULL);
+ }
+
+ g_at_chat_send(data->app, "AT^SAIC?", NULL, saic_probe, modem, NULL);
+
+ gemalto_exec_stored_cmd(modem, "enable");
+
+ gemalto_command_passthrough_enable(modem);
+ gemalto_hardware_monitor_enable(modem);
+ gemalto_time_enable(modem);
+ gemalto_gnss_enable(modem);
+ gemalto_hardware_control_enable(modem);
+
+ gemalto_set_cfun(data->app, 4, modem);
+ data->init_done = TRUE;
+}
+
+static gboolean gemalto_open_cb(GIOChannel *source, GIOCondition condition,
+ gpointer user_data)
+{
+ struct ofono_modem *modem = user_data;
+ struct gemalto_data *data = ofono_modem_get_data(modem);
+ GAtSyntax *syntax;
+
+ if (data->channel == NULL)
+ return TRUE;
+
+ if ((condition & G_IO_IN) == 0)
+ return TRUE;
+
+ g_source_remove(data->probing_timer);
+ data->probing_timer = 0;
+ g_source_remove(data->read_src);
+ g_io_channel_flush(data->channel, NULL);
+ /* reset channel defaults*/
+ g_io_channel_set_buffered(data->channel, TRUE);
+ g_io_channel_set_encoding(data->channel, "UTF-8", NULL);
+
+ syntax = g_at_syntax_new_gsm_permissive();
+ data->tmp_chat = g_at_chat_new(data->channel, syntax);
+ g_at_syntax_unref(syntax);
+
+ if (data->tmp_chat == NULL)
+ goto failed;
+
+ g_io_channel_unref(data->channel);
+ data->channel = NULL;
+ g_at_chat_set_debug(data->tmp_chat, gemalto_debug, "App: ");
+ data->open_cb(TRUE, modem);
+ return TRUE; // finished
+failed:
+ DBG("chat creation failed. aborting.");
+ g_io_channel_unref(data->channel);
+ data->channel = NULL;
+ DBG("aborted.");
+ data->open_cb(FALSE, modem);
+ return FALSE; // abort
+}
+
+static int gemalto_probe_device(void *user_data)
+{
+ struct ofono_modem *modem = user_data;
+ struct gemalto_data *data = ofono_modem_get_data(modem);
+ GIOStatus status;
+
+ if (data->channel==NULL)
+ return FALSE;
+
+ data->waiting_time++;
+ DBG("%d/%d", data->waiting_time, data->init_waiting_time);
+
+ if (data->waiting_time > data->init_waiting_time) {
+ data->waiting_time = 0;
+ goto failed;
+ }
+
+ status = g_io_channel_write_chars(data->channel, "AT\r", 3, NULL, NULL);
+
+ if (status != G_IO_STATUS_NORMAL && status != G_IO_STATUS_AGAIN)
+ goto failed;
+
+ return TRUE; // to be called again
+
+failed:
+ g_source_remove(data->probing_timer);
+ data->probing_timer = 0; /* remove the timer reference */
+ DBG("timeout: abort");
+ g_io_channel_unref(data->channel);
+ data->channel = NULL;
+ data->tmp_chat = NULL;
+ data->open_cb(FALSE, modem);
+ return FALSE; // abort
+}
+
+#include <asm/ioctls.h>
+#include <linux/serial.h>
+
+int ioctl(int, int, void *);
// these are temporarily in the middle. Discussed with Denis,
// maybe these and their use below is better placed in g_at_tty_open
+
+static void gemalto_open_device(const char *device,
+ OpenResultFunc func, struct ofono_modem *modem)
+{
+ struct gemalto_data *data = ofono_modem_get_data(modem);
+ GHashTable *options;
+ int fd;
+ struct serial_struct old, new;
+
+ if (!device || !*device) {
+ func(FALSE, modem);
+ return;
+ }
+
+ options = g_hash_table_new(g_str_hash, g_str_equal);
+ if (options == NULL) {
+ func(FALSE, modem);
+ return;
+ }
+
+ g_hash_table_insert(options, "Baud", "115200");
+ g_hash_table_insert(options, "StopBits", "1");
+ g_hash_table_insert(options, "DataBits", "8");
+ g_hash_table_insert(options, "Parity", "none");
+ g_hash_table_insert(options, "XonXoff", "off");
+ g_hash_table_insert(options, "RtsCts", "on");
+ g_hash_table_insert(options, "Local", "on");
+ g_hash_table_insert(options, "Read", "on");
+
+ DBG("Opening device %s", device);
+
+ data->channel = g_at_tty_open(device, options);
+ g_hash_table_destroy(options);
+
+ if (!data->channel) {
+ func(FALSE, modem);
+ return;
+ }
+
+ fd = g_io_channel_unix_get_fd(data->channel);
+ ioctl(fd, TIOCGSERIAL, &old);
+ new = old;
+ new.closing_wait = ASYNC_CLOSING_WAIT_NONE;
+ ioctl(fd, TIOCSSERIAL, &new);
// without this, even if the g_at_tty_open sets the port non-blocking, // it waits anyway 30 seconds if the port is not responding.
+
+ g_io_channel_flush(data->channel, NULL);
+ /* the channel is set by default to "UTF-8" and buffered */
+ g_io_channel_set_encoding(data->channel, NULL, NULL);
+ g_io_channel_set_buffered(data->channel, FALSE);
+ data->open_cb = func;
+ data->read_src = g_io_add_watch(data->channel, G_IO_IN, gemalto_open_cb,
+ modem);
+ data->probing_timer = g_timeout_add_seconds(1, gemalto_probe_device,
+ modem);
+}
// the 3 functions above:
// - open the channel as usual
// - test the port with AT once per second until timeout. Maybe this can be an
// additional wakeup function in GAtChat
// - attach a GAtChat to it or close it
+
+static void gemalto_enable_mdm_cb(gboolean success, struct ofono_modem *modem) +{
+ struct gemalto_data *data = ofono_modem_get_data(modem);
+
+ data->mdm = data->tmp_chat;
+ data->tmp_chat = NULL;
+ gemalto_initialize(modem);
+}
+
+static void gemalto_enable_app_cb(gboolean success, struct ofono_modem *modem) +{
+ struct gemalto_data *data = ofono_modem_get_data(modem);
+ const char *mdm = gemalto_get_string(modem, "Modem");
+
+ data->app = data->tmp_chat;
+ data->tmp_chat = NULL;
+ gemalto_open_device(mdm, gemalto_enable_mdm_cb, modem);
+}
+
+static int gemalto_enable_app(struct ofono_modem *modem)
+{
+ const char *app = gemalto_get_string(modem, "Application");
+
+ gemalto_open_device(app, gemalto_enable_app_cb, modem);
+ return -EINPROGRESS;
+}
+
+#ifdef HAVE_ELL
+static void mbim_device_caps_info_cb(struct mbim_message *message, void *user) +{
+ struct ofono_modem *modem = user;
+ struct gemalto_data *md = ofono_modem_get_data(modem);
+ uint32_t device_type;
+ uint32_t cellular_class;
+ uint32_t voice_class;
+ uint32_t sim_class;
+ uint32_t data_class;
+ uint32_t sms_caps;
+ uint32_t control_caps;
+ uint32_t max_sessions;
+ char *custom_data_class;
+ char *device_id;
+ char *firmware_info;
+ char *hardware_info;
+ bool r;
+
+ if (mbim_message_get_error(message) != 0)
+ goto error;
+
+ r = mbim_message_get_arguments(message, "uuuuuuuussss",
+ &device_type, &cellular_class,
+ &voice_class, &sim_class, &data_class,
+ &sms_caps, &control_caps, &max_sessions,
+ &custom_data_class, &device_id,
+ &firmware_info, &hardware_info);
+ if (!r)
+ goto error;
+
+ md->max_sessions = max_sessions;
+
+ DBG("DeviceId: %s", device_id);
+ DBG("FirmwareInfo: %s", firmware_info);
+ DBG("HardwareInfo: %s", hardware_info);
+
+ ofono_modem_set_string(modem, "DeviceId", device_id);
+ ofono_modem_set_string(modem, "FirmwareInfo", firmware_info);
+
+ l_free(custom_data_class);
+ l_free(device_id);
+ l_free(firmware_info);
+ l_free(hardware_info);
+
+ message = mbim_message_new(mbim_uuid_basic_connect,
+ MBIM_CID_DEVICE_SERVICE_SUBSCRIBE_LIST,
+ MBIM_COMMAND_TYPE_SET);
+
+ mbim_message_set_arguments(message, "av", 2,
+ "16yuuuuuuu",
+ mbim_uuid_basic_connect, 6,
+ MBIM_CID_SUBSCRIBER_READY_STATUS,
+ MBIM_CID_RADIO_STATE,
+ MBIM_CID_REGISTER_STATE,
+ MBIM_CID_PACKET_SERVICE,
+ MBIM_CID_SIGNAL_STATE,
+ MBIM_CID_CONNECT,
+ "16yuuuu", mbim_uuid_sms, 3,
+ MBIM_CID_SMS_CONFIGURATION,
+ MBIM_CID_SMS_READ,
+ MBIM_CID_SMS_MESSAGE_STORE_STATUS);
+
+ if (mbim_device_send(md->device, 0, message,
+ NULL, NULL, NULL)) {
+ md->mbim = STATE_PRESENT;
+ goto other_devices;
+ }
+
+
+error:
+ mbim_device_shutdown(md->device);
+
+other_devices:
+
+ if (md->init_done)
+ return;
+
+ gemalto_enable_app(modem); /* continue with mdm interface */
+}
+
+static void mbim_device_ready(void *user_data)
+{
+ struct ofono_modem *modem = user_data;
+ struct gemalto_data *md = ofono_modem_get_data(modem);
+ struct mbim_message *message =
+ mbim_message_new(mbim_uuid_basic_connect,
+ 1, MBIM_COMMAND_TYPE_QUERY);
+
+ mbim_message_set_arguments(message, "");
+ mbim_device_send(md->device, 0, message, mbim_device_caps_info_cb,
+ modem, NULL);
+}
+
+static void mbim_device_closed(void *user_data)
+{
+ struct ofono_modem *modem = user_data;
+ struct gemalto_data *md = ofono_modem_get_data(modem);
+
+ if (!md)
+ return;
+
+ /*
+ * if state=probe, it means that we are in the init phase
+ * and that we have failed the MBIM_OPEN
+ */
+ if (md->mbim == STATE_PROBE) {
+ DBG(REDCOLOR"MBIM OPEN failed!"NOCOLOR);
+ gemalto_enable_app(modem); /* continue with other interfaces */
+ }
+
+ /* reset the state for future attempts */
+ md->mbim = STATE_PROBE;
+
+ if(md->device)
+ mbim_device_unref(md->device);
+
+ md->device = NULL;
+}
+
+static int mbim_enable(struct ofono_modem *modem)
+{
+ const char *device;
+ int fd;
+ struct gemalto_data *md = ofono_modem_get_data(modem);
+
+ DBG("modem struct: %p", modem);
+
+ device = gemalto_get_string(modem, "NetworkControl");
+ if (!device)
+ goto other_devices;
+
+ DBG("modem device: %s", device);
+ fd = open(device, O_EXCL | O_NONBLOCK | O_RDWR);
+
+ if (fd < 0)
+ goto other_devices;
+
+ DBG("device: %s opened successfully", device);
+ md->device = mbim_device_new(fd, md->max_segment);
+ DBG("created new device %p", md->device);
+
+ mbim_device_set_close_on_unref(md->device, true);
+ mbim_device_set_max_outstanding(md->device, md->max_outstanding);
+ mbim_device_set_ready_handler(md->device,
+ mbim_device_ready, modem, NULL);
+ mbim_device_set_disconnect_handler(md->device,
+ mbim_device_closed, modem, NULL);
+ mbim_device_set_debug(md->device, gemalto_debug, "MBIM:", NULL);
+
+ return -EINPROGRESS;
+
+other_devices:
+
+ if (md->init_done)
+ return 0;
+
+ return gemalto_enable_app(modem);
+}
+#endif
+
+static void qmi_enable_cb(void *user_data)
+{
+ struct ofono_modem *modem = user_data;
+ struct gemalto_data *md = ofono_modem_get_data(modem);
+ md->qmi = STATE_PRESENT;
+ gemalto_enable_app(modem); /* qmi done, continue with app interface */
+}
+
+static int qmi_enable(struct ofono_modem *modem)
+{
+ struct gemalto_data *data = ofono_modem_get_data(modem);
+ const char *device;
+ int fd;
+
+ DBG("modem struct: %p", modem);
+
+ device = gemalto_get_string(modem, "NetworkControl");
+ if (!device)
+ return gemalto_enable_app(modem);
+
+ fd = open(device, O_RDWR | O_NONBLOCK | O_CLOEXEC);
+ if (fd < 0)
+ return gemalto_enable_app(modem);
+
+ data->device = qmi_device_new(fd);
+ if (!data->device) {
+ close(fd);
+ return gemalto_enable_app(modem);
+ }
+
+ qmi_device_set_close_on_unref(data->device, true);
+
+ qmi_device_set_debug(data->device, gemalto_debug, "QMI: ");
+
+ qmi_device_discover(data->device, qmi_enable_cb, modem, NULL);
+
+ return -EINPROGRESS;
+}
+
+static void set_from_model(struct gemalto_data *data) {
+ guint m = data->model;
+
+ data->has_lte = TRUE; /* default */
+
+ /* pre-configure non-MBIM network interfaces */
+ if (m != 0x62 && m != 0x5d && m != 0x65) {
+ /*
+ * note: we probe for ECM/NCM even if the port is not present
+ * (for serial connection type or serial-like)
+ */
+ if (m == 0x53 || m == 0x60 || m == 0x63)
+ data->qmi = STATE_PROBE;
+ /*these families have PPP only*/
+ else if (m != 0x58 && m != 0x47 && m != 0x54)
+ data->ecmncm = STATE_PROBE;
+ }
+
+ /* pre-configure SW features */
+ if (m == 0xa0) {
+ data->gprs_opt = USE_CTX3;
+ data->ecmncm = STATE_ABSENT;
+ }
+ if (m == 0x63 || m == 0x65 || m == 0x5b || m == 0x5c || m == 0x5d)
+ data->gina = STATE_PRESENT;
+
+ data->init_waiting_time = 30;
+
+ if (m == 0x55 || m == 0x47) {
+ data->has_lte = FALSE;
+ data->init_waiting_time = 5;
+ }
+
+ if (m == 0x58) {
+ data->has_lte = FALSE;
+ data->init_waiting_time = 15;
+ }
+
+ data->vts_with_quotes = TRUE;
+
+ if (m == 0x5b || m == 0x5c || m == 0x5d || m == 0xa0) {
+ data->vts_with_quotes = FALSE;
+ data->auth_syntax = GEMALTO_AUTH_USE_SGAUTH |
+ GEMALTO_AUTH_ALWAYS_ALL_PARAMS;
+ }
+}
+
+static void store_cgmm(gboolean ok, GAtResult *result, gpointer user_data) +{
+ struct ofono_modem *modem = user_data;
+ struct gemalto_data *data = ofono_modem_get_data(modem);
+ GAtResultIter iter;
+ char const *model;
+ char buf[16];
+
+ /* if no model, fallback to a basic 2G one */
+ data->model = 0x47;
+ strncpy(data->modelstr, "", sizeof(data->modelstr));
+
+ if (!ok)
+ return;
+
+ g_at_result_iter_init(&iter, result);
+
+ while (g_at_result_iter_next(&iter, NULL)) {
+ if (!g_at_result_iter_next_unquoted_string(&iter, &model))
+ continue;
+
+ if (model && *model) {
+ strncpy(data->modelstr, model, sizeof(data->modelstr));
+
+ if (g_ascii_strncasecmp(model, "TC", 2) == 0)
+ data->model = 0x47;
+ else if (g_ascii_strncasecmp(model, "MC", 2) == 0)
+ data->model = 0x47;
+ else if (g_ascii_strncasecmp(model, "AC", 2) == 0)
+ data->model = 0x47;
+ else if (g_ascii_strncasecmp(model, "HC", 2) == 0)
+ data->model = 0x47;
+ else if (g_ascii_strncasecmp(model, "HM", 2) == 0)
+ data->model = 0x47;
+ else if (g_ascii_strncasecmp(model, "XT", 2) == 0)
+ data->model = 0x47;
+ else if (g_ascii_strncasecmp(model, "AGS", 3) == 0)
+ data->model = 0x47;
+ else if (g_ascii_strncasecmp(model, "BGS", 3) == 0)
+ data->model = 0x47;
+ else if (g_ascii_strncasecmp(model, "AH3", 3) == 0)
+ data->model = 0x55;
+ else if (g_ascii_strncasecmp(model, "AHS", 3) == 0)
+ data->model = 0x55;
+ else if (g_ascii_strncasecmp(model, "PHS", 3) == 0)
+ data->model = 0x55;
+ else if (g_ascii_strncasecmp(model, "PH8", 3) == 0)
+ data->model = 0x55;
+ else if (g_ascii_strncasecmp(model, "AHS", 3) == 0)
+ data->model = 0x55;
+ else if (g_ascii_strncasecmp(model, "EHS", 3) == 0)
+ data->model = 0x58;
+ else if (g_ascii_strncasecmp(model, "ELS31-", 6) == 0)
+ data->model = 0xa0;
+ else if (g_ascii_strncasecmp(model, "ELS61-", 6) == 0)
+ data->model = 0x5b;
+ else if (g_ascii_strncasecmp(model, "PLS62-", 6) == 0)
+ data->model = 0x5b;
+ else if (g_ascii_strncasecmp(model, "PLS8-", 5) == 0)
+ data->model = 0x61;
+ else if (g_ascii_strncasecmp(model, "ALS3-", 5) == 0)
+ data->model = 0x61;
+ else if (g_ascii_strncasecmp(model, "ALAS5-", 6) == 0)
+ data->model = 0x65;
+ return;
+ }
+ }
+
+ sprintf(buf, "%04x", data->model);
+ ofono_modem_set_string(modem, "Model", buf);
+}
+
+static void store_sqport(gboolean ok, GAtResult *result, gpointer user_data) +{
+ struct ofono_modem *modem = user_data;
+ struct gemalto_data *data = ofono_modem_get_data(modem);
+ GAtResultIter iter;
+ char const *sqport;
+
+ /* in case of error, the port is of Modem type */
+ strncpy(data->sqport, "Modem", sizeof(data->sqport));
+
+ if (!ok)
+ goto done;
+
+ g_at_result_iter_init(&iter, result);
+
+ /* answer format: "^SQPORT: Application" */
+ if (!g_at_result_iter_next(&iter, "^SQPORT:"))
+ goto done;
+
+ if (!g_at_result_iter_next_unquoted_string(&iter, &sqport))
+ goto done;
+
+ if (!sqport || !*sqport)
+ goto done;
+
+ strncpy(data->sqport, sqport, sizeof(data->sqport));
+
+done:
+ /* select mdm, app or gina port type */
+ data->ecmncm = STATE_PROBE;
+
+ if (g_str_equal(sqport, "Modem")) {
+ data->mdm = data->app;
+ data->app = NULL;
+ data->ecmncm = STATE_ABSENT;
+ }
+
+ if ((*sqport >= '0' && *sqport <= '9'))
+ data->gina = STATE_PRESENT;
+
+ set_from_model(data);
+ gemalto_initialize(modem);
+}
+
+static void gemalto_detect_serial(gboolean success, struct ofono_modem *modem) +{
+ struct gemalto_data *data = ofono_modem_get_data(modem);
+
+ data->app = data->tmp_chat;
+ data->tmp_chat = NULL;
+
+ g_at_chat_send(data->app, "AT+CGMM", none_prefix, store_cgmm,
+ modem, NULL);
+ g_at_chat_send(data->app, "AT^SQPORT", sqport_prefix, store_sqport,
+ modem, NULL);
+}
+
+static void gemalto_detect_sysstart(GAtResult *result, gpointer user_data) +{
+ struct ofono_modem *modem = user_data;
+ struct gemalto_data *data = ofono_modem_get_data(modem);
+
+ if (data->modem_ready_id) {
+ g_at_chat_unregister(data->app, data->modem_ready_id);
+ data->modem_ready_id = 0;
+ }
+
+ data->tmp_chat = data->app;
+ gemalto_detect_serial(TRUE, modem);
+}
+
+static int gemalto_enable_serial(struct ofono_modem *modem)
+{
+ const char *device = ofono_modem_get_string(modem, "ATport");
+
+ if (!device)
+ return -EINVAL;
+
+ gemalto_open_device(device, gemalto_detect_serial, modem);
+ return -EINPROGRESS;
+}
+
+static int gemalto_enable(struct ofono_modem *modem)
+{
+ struct gemalto_data *data = ofono_modem_get_data(modem);
+ const char *model = gemalto_get_string(modem, "Model"),
+ *conn_type = gemalto_get_string(modem, "ConnType");
+ const char
+ *ctl = gemalto_get_string(modem, "NetworkControl"),
+ *net = gemalto_get_string(modem, "NetworkInterface");
+ guint m = 0;
+
+ if (!modem || !data)
+ return -EINVAL;
+
+ data->conn = g_str_equal(conn_type,"Serial") ? GEMALTO_CONNECTION_SERIAL
+ : GEMALTO_CONNECTION_USB;
+
+ if (data->conn == GEMALTO_CONNECTION_SERIAL)
+ return gemalto_enable_serial(modem);
+
+ DBG("modem struct: %p, gemalto_data: %p", modem, data);
+
+ if (data->init_done) {
+ gemalto_set_cfun(data->app, 4, modem);
+
+#ifdef HAVE_ELL
+ if (data->mbim != STATE_ABSENT)
+ mbim_enable(modem);
+#endif
+
+ return -EINPROGRESS;
+ }
+
+ if (model) {
+ data->model = strtoul(model, NULL, 16);
+ m = data->model;
+ }
+
+ /* single ACM interface 02: assign application to modem */
+ if (m == 0xa0) {
+ const char *app = gemalto_get_string(modem, "Application");
+ ofono_modem_set_string(modem, "Modem", app);
+ }
+
+ if (m == 0x60) {
+ const char *app = gemalto_get_string(modem, "Diag");
+ ofono_modem_set_string(modem, "Modem", app);
+ }
+
+ /* if single ACM interface, remove possible extra devices */
+ if (m == 0x58 || m == 0x47 || m == 0x54 || m == 0xa0 || m == 0x60) {
+ ofono_modem_set_string(modem, "Application", NULL);
+ ofono_modem_set_string(modem, "GNSS", NULL);
+ ofono_modem_set_string(modem, "RSA", NULL);
+ ofono_modem_set_string(modem, "Diag", NULL);
+ }
+
+#ifdef HAVE_ELL
+ /* pre-configure MBIM network interface */
+ if (m == 0x62 || m == 0x5d || m == 0x65) {
+ data->mbim = STATE_PROBE;
+ }
+#endif
+
+ set_from_model(data);
+
+#ifdef HAVE_ELL
+ if ((data->mbim == STATE_PROBE) && ctl && net) {
+ data->init_waiting_time = 3;
+ return mbim_enable(modem);
+ }
+#endif
+
+ if ((data->qmi == STATE_PROBE) && ctl && net) {
+ data->init_waiting_time = 10;
+ return qmi_enable(modem);
+ }
+
+ return gemalto_enable_app(modem);
+}
+
+#ifdef HAVE_ELL
+static int mbim_sim_probe(void *device)
+{
+ struct mbim_message *message;
+ /* SIM_GROUP is defined in mbimmodem.h that cannot be included */
+ uint32_t SIM_GROUP = 1;
+
+ message = mbim_message_new(mbim_uuid_basic_connect,
+ MBIM_CID_SUBSCRIBER_READY_STATUS,
+ MBIM_COMMAND_TYPE_QUERY);
+ if (!message)
+ return -ENOMEM;
+
+ mbim_message_set_arguments(message, "");
+
+ if (!mbim_device_send(device, SIM_GROUP, message,
+ NULL, NULL, NULL)) {
+ mbim_message_unref(message);
+ return -EIO;
+ }
+ return 0;
+}
+#endif
+
+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;
+
+ decode_at_error(&error, g_at_result_final_response(result));
+
+ cb(&error, cbd->data);
+}
+
+static void gemalto_set_online_serial(struct ofono_modem *modem,
+ ofono_bool_t online, ofono_modem_online_cb_t cb,
+ void *user_data)
+{
+ struct gemalto_data *data = ofono_modem_get_data(modem);
+ struct cb_data *cbd = cb_data_new(cb, user_data);
+ char const *command;
+
+ gemalto_exec_stored_cmd(modem, "set_online");
+
+ if (data->model == 0x47) {
+ command = online ? "AT^SCFG=\"MEopMode/Airplane\",\"off\"" :
+ "AT^SCFG=\"MEopMode/Airplane\",\"on\"";
+ } else {
+ command = online ? "AT+CFUN=1" : "AT+CFUN=4";
+ }
+
+ DBG("modem %p %s", modem, online ? "online" : "offline");
+
+ if (g_at_chat_send(data->app, command, NULL, set_online_cb, cbd,
+ g_free))
+ return;
+
+ CALLBACK_WITH_FAILURE(cb, cbd->data);
+ g_free(cbd);
+}
+
+static void gemalto_set_online(struct ofono_modem *modem, ofono_bool_t online,
+ ofono_modem_online_cb_t cb, void *user_data)
+{
+ struct gemalto_data *data = ofono_modem_get_data(modem);
+ struct cb_data *cbd = cb_data_new(cb, user_data);
+ char const *cmd = online ? "AT+CFUN=1" : "AT+CFUN=4";
+
+ if (data->conn == GEMALTO_CONNECTION_SERIAL) {
+ gemalto_set_online_serial(modem, online, cb, user_data);
+ return;
+ }
+
+ DBG("modem %p %s", modem, online ? "online" : "offline");
+
+ if (g_at_chat_send(data->app, cmd, NULL, set_online_cb, cbd, g_free))
+ return;
+
+ CALLBACK_WITH_FAILURE(cb, cbd->data);
+
+ g_free(cbd);
+}
+
+static void gemalto_pre_sim(struct ofono_modem *modem)
+{
+ struct gemalto_data *data = ofono_modem_get_data(modem);
+
+ DBG("%p", modem);
+
+ gemalto_exec_stored_cmd(modem, "pre_sim");
+
+ ofono_location_reporting_create(modem, 0, "gemaltomodem", data->app);
+
+ data->sim = ofono_sim_create(modem, OFONO_VENDOR_GEMALTO,
+ "atmodem", data->app);
+
+ if (data->sim && data->have_sim == TRUE)
+ ofono_sim_inserted_notify(data->sim, TRUE);
+}
+
+static void gemalto_post_sim(struct ofono_modem *modem)
+{
+ struct gemalto_data *data = ofono_modem_get_data(modem);
+
+ gemalto_exec_stored_cmd(modem, "post_sim");
+
+#ifdef HAVE_ELL
+ if (data->mbim == STATE_PRESENT) {
+ /* very important to set the interface ready */
+ mbim_sim_probe(data->device);
+ }
+#endif
+
+ ofono_phonebook_create(modem, 0, "atmodem", data->app);
+ ofono_modem_set_integer(modem, "GemaltoAuthType", data->auth_syntax);
+
+ if (data->has_lte)
+ ofono_lte_create(modem, 0, "gemaltomodem", data->app);
+}
+
+static void cgdcont17_probe(gboolean ok, GAtResult *result, gpointer user_data) +{
+ struct gemalto_data *data = ofono_modem_get_data(user_data);
+
+ if (ok)
+ data->gprs_opt = USE_CTX17;
+}
+
+static void swwan_probe(gboolean ok, GAtResult *result, gpointer user_data) +{
+ struct gemalto_data *data = ofono_modem_get_data(user_data);
+
+ if (ok)
+ data->gprs_opt = USE_SWWAN;
+}
+
+static void autoattach_probe_and_continue(gboolean ok, GAtResult *result,
+ gpointer user_data)
+{
+ struct ofono_modem* modem = user_data;
+ struct gemalto_data *data = ofono_modem_get_data(modem);
+ GAtResultIter iter;
+ struct ofono_message_waiting *mw;
+ struct ofono_gprs *gprs = NULL;
+ struct ofono_gprs_context *gc = NULL;
+
+ data->autoattach = FALSE;
+ ofono_modem_set_integer(modem, "GemaltoAutoAttach", 0);
+
+ if (ok) {
+ g_at_result_iter_init(&iter, result);
+ while (g_at_result_iter_next(&iter, NULL)) {
+ if (strstr(g_at_result_iter_raw_line(&iter),
+ "\"enabled\"")) {
+ data->autoattach = TRUE;
+ ofono_modem_set_integer(modem,
+ "GemaltoAutoAttach", 1);
+
+ }
+ }
+ }
+
+ // TODO: the ofono_gprs_create may require gemaltomodem instead of atmodem
// for now this code works with a modified version of atmodem/gprs.c, // where the ofono_gprs_set_cid_range is not called, so it can be called below +
+#ifdef HAVE_ELL
+ if (data->mbim == STATE_PRESENT) {
+ gprs = ofono_gprs_create(modem, OFONO_VENDOR_GEMALTO, "atmodem",
+ data->app);
+ ofono_gprs_set_cid_range(gprs, 0, data->max_sessions);
+ if (data->model == 0x62 || data->model == 0x65) {
+ struct gemalto_mbim_composite comp;
+ comp.device = data->device;
+ comp.chat = data->app;
+ comp.at_cid = 4;
+ gc = ofono_gprs_context_create(modem, 0, "gemaltomodemmbim", &comp);
+ } else /* model == 0x5d */
+ gc = ofono_gprs_context_create(modem, 0, "mbim", data->device);
+ } else
+#endif
+ if (data->qmi == STATE_PRESENT) {
+ gprs = ofono_gprs_create(modem, OFONO_VENDOR_GEMALTO, "atmodem",
+ data->app);
+ // TODO: verify if need be to create the contexts and auth params beforehand
+ // if so, may need a gprs-context gemaltomodem-at-qmi
+
+ /* on QMI devices, only a single context is supported */
+ // TODO: Is 1 ok for attach_APN != context_APN?
+ ofono_gprs_set_cid_range(gprs, 1, 1);
+ gc = ofono_gprs_context_create(modem, 0, "qmimodem",
+ data->device);
+ } else if (data->gprs_opt == USE_SWWAN || data->gprs_opt == USE_CTX17 ||
+ data->gprs_opt == USE_CTX3) {
+ ofono_modem_set_integer(modem, "GemaltoWwan",
+ data->gprs_opt == USE_SWWAN);
+ gprs = ofono_gprs_create(modem, OFONO_VENDOR_GEMALTO, "atmodem",
+ data->app);
+ if (data->gprs_opt == USE_CTX3)
+ ofono_gprs_set_cid_range(gprs, 3, 3);
+ else if (data->model == 0x5b)
+ /*
+ * limitation: same APN as for attach
+ * in this case create more contexts
+ */
+ ofono_gprs_set_cid_range(gprs, 1, 11);
+ else
+ ofono_gprs_set_cid_range(gprs, 4, 16);
+ // maybe rename the next to gemaltomodem-wwan
+ gc = ofono_gprs_context_create(modem, 0, "gemaltomodemswwan",
+ data->app);
+ } else if (data->gprs_opt == USE_PPP) {
+ /* plain PPP only works from mdm ports */
+ gprs = ofono_gprs_create(modem, OFONO_VENDOR_GEMALTO, "atmodem",
+ data->app);
+ if (data->model == 0x47)
+ ofono_gprs_set_cid_range(gprs, 1, 2);
+ else if (data->has_lte)
+ ofono_gprs_set_cid_range(gprs, 4, 16);
+ else
+ ofono_gprs_set_cid_range(gprs, 1, 16);
+
+ gc = ofono_gprs_context_create(modem, 0, "atmodem", data->mdm);
+
+ } /*
+ * in case of no match above, we have no gprs possibilities + * this is common when using the module through serial interfaces + * nevertheless other services (voice, gpio, gnss) could be available + */
+
+ if (gc)
+ ofono_gprs_context_set_type(gc,
+ OFONO_GPRS_CONTEXT_TYPE_INTERNET);
+
+ if (gprs && gc)
+ ofono_gprs_add_context(gprs, gc);
+
+ /* might have also without voicecall support */
+ ofono_ussd_create(modem, 0, "atmodem", data->app);
+
+ /*
+ * Call support is technically possible only after sim insertion
+ * with the module online. However the EMERGENCY_SETUP procedure of
+ * the 3GPP TS_24.008 is triggered by the same AT command,
+ * and namely 'ATD112;', 'ATD911;', etc.
+ * On the other hand, in airplane-mode it is not possible to do it, nor
+ * to create all relevant URCs for the atom.
+ *
+ * Ofono does not make a distinction between no-sim and
+ * airplane-mode scenarios, so we create the voicecall in post-online.
+ * This is half-compatible with the European directives that require
+ * a SIM inserted also for emergency setup.
+ */
// this needs further thinking
+
+ if (data->voice_avail) {
+ ofono_modem_set_integer(modem, "GemaltoVtsQuotes",
+ data->vts_with_quotes);
+ ofono_voicecall_create(modem, 0, "gemaltomodem", data->app);
+
+ ofono_call_forwarding_create(modem, 0, "atmodem", data->app);
+ ofono_call_settings_create(modem, 0, "atmodem", data->app);
+ ofono_call_meter_create(modem, 0, "atmodem", data->app);
+ ofono_call_barring_create(modem, 0, "atmodem", data->app);
+ }
+
+ /* modules require to be online to accept at+cnmi */
+ ofono_sms_create(modem, OFONO_VENDOR_GEMALTO, "atmodem", data->app);
+ mw = ofono_message_waiting_create(modem);
+
+ if (mw)
+ ofono_message_waiting_register(mw);
+
+ ofono_netreg_create(modem, OFONO_VENDOR_GEMALTO, "atmodem", data->app);
+}
+
+static int gemalto_post_online_delayed(void *modem)
+{
+ struct gemalto_data *data = ofono_modem_get_data(modem);
+
+ /*
+ * check module capabilities once online and SIM really ready.
+ *
+ * Note: the g_at_chat_send calls only insert the commands in a list:
+ * they are not executed synchronously
+ *
+ * Note: ofono executes each AT commands and the related callback before
+ * proceeding with the next. So continuing on the last AT command is all
+ * it takes
+ */
+
+ gemalto_exec_stored_cmd(modem, "post_online");
+
+ if (data->ecmncm == STATE_PROBE) {
+ data->gprs_opt = USE_PPP; /* fallback */
+ g_at_chat_send(data->app, "AT+CGDCONT=17", NULL,
+ cgdcont17_probe, modem, NULL);
+ g_at_chat_send(data->app, "AT^SWWAN?", NULL, swwan_probe, modem,
+ NULL);
+ }
+
+ g_at_chat_send(data->app, "AT^SCFG=\"GPRS/AutoAttach\"", NULL,
+ autoattach_probe_and_continue, modem, NULL);
+
+ return FALSE; /* to kill the timer */
+}
+
+static void gemalto_post_online(struct ofono_modem *modem)
+{
+ /*
+ * in this version of ofono we must wait for SIM 'really-ready'
+ * can be avoided when capturing the right URCs
// waiting for the URC (like '+PBREADY') is anyway not risk-free
// because if ofono is started when the module is already initialized // the URC is missing, and on some models cannot be queried.
// This needs further thinking.
+ */
+ g_timeout_add_seconds(5, gemalto_post_online_delayed, modem);
+}
+
+#ifdef HAVE_ELL
+static void mbim_radio_off_for_disable(struct mbim_message *message, void *user) +{
+ struct ofono_modem *modem = user;
+ struct gemalto_data *md = ofono_modem_get_data(modem);
+
+ DBG("%p", modem);
+
+ mbim_device_shutdown(md->device);
+}
+#endif
+
+static int gemalto_disable_serial(struct ofono_modem *modem)
+{
+ struct gemalto_data *data = ofono_modem_get_data(modem);
+
+ if (data->app != NULL) {
+ if (data->model == 0x47) {
+ g_at_chat_send(data->app,
+ "AT^SCFG=\"MEopMode/Airplane\",\"on\"",
+ NULL, NULL, NULL, NULL);
+ } else {
+ gemalto_set_cfun(data->app, 41, modem);
+ return -EINPROGRESS;
+ }
+ g_at_chat_cancel_all(data->app);
+ }
+
+ ofono_modem_set_powered(modem, FALSE);
+ return 0;
+}
+
+static int gemalto_disable(struct ofono_modem *modem)
+{
+ struct gemalto_data *data = ofono_modem_get_data(modem);
+#ifdef HAVE_ELL
+ struct mbim_message *message;
+#endif
+
+ DBG("%p", modem);
+
+ if (data->conn == GEMALTO_CONNECTION_SERIAL)
+ return gemalto_disable_serial(modem);
+
+#ifdef HAVE_ELL
+ if (data->mbim == STATE_PRESENT) {
+ message = mbim_message_new(mbim_uuid_basic_connect,
+ MBIM_CID_RADIO_STATE,
+ MBIM_COMMAND_TYPE_SET);
+ mbim_message_set_arguments(message, "u", 0);
+
+ if (mbim_device_send(data->device, 0, message,
+ mbim_radio_off_for_disable, modem, NULL)==0)
+ mbim_device_closed(modem);
+ }
+#endif
+
+ if (data->app == NULL)
+ return 0;
+
+ gemalto_exec_stored_cmd(modem, "disable");
+
+ gemalto_set_cfun(data->app, 41, modem);
+
+ return -EINPROGRESS;
+}
+
+static struct ofono_modem_driver gemalto_driver = {
+ .name = "gemalto",
+ .probe = gemalto_probe,
+ .remove = gemalto_remove,
+ .enable = gemalto_enable,
+ .disable = gemalto_disable,
+ .set_online = gemalto_set_online,
+ .pre_sim = gemalto_pre_sim,
+ .post_sim = gemalto_post_sim,
+ .post_online = gemalto_post_online,
+};
+
+static int gemalto_init(void)
+{
+ return ofono_modem_driver_register(&gemalto_driver);
+}
+
+static void gemalto_exit(void)
+{
+ ofono_modem_driver_unregister(&gemalto_driver);
+}
+
+OFONO_PLUGIN_DEFINE(gemalto, "Gemalto modem plugin", VERSION,
+ OFONO_PLUGIN_PRIORITY_DEFAULT, gemalto_init, gemalto_exit)
plugins/udevng.c +83 -44
151 lines not on this page · raw message
// not sure this logic of sorting the network interfaces is needed // but I don't see another way of being sure of having them in order
+ }
+ }
}
}
- DBG("application=%s gps=%s modem=%s network=%s qmi=%s",
- app, gps, mdm, net, qmi);
+ sprintf(descriptors, "%s/descriptors", modem->syspath);
+ ofono_modem_set_string(modem->modem, "ConnType", "Usb");
+ ofono_modem_set_string(modem->modem, "DescriptorFile", descriptors);
+ ofono_modem_set_string(modem->modem, "Vendor", modem->vendor);
+ ofono_modem_set_string(modem->modem, "Model", modem->model);
- if (app == NULL || mdm == NULL)
- return FALSE;
+ /*
+ * special cases.
+ * TODO: selected by external parameters:
+ * GEMALTO_CONFIG_MBIM_ONLY -> _set_driver(modem->modem, "mbim"); + * GEMALTO_CONFIG_QMI_ONLY -> _set_driver(modem->modem, "gobi"); + * ...
+ */
+ if (m==0x64) {
+ ofono_modem_set_string(modem->modem, "Device", ctl);
+ ofono_modem_set_string(modem->modem, "NetworkInterface", net);
+ ofono_modem_set_driver(modem->modem, "mbim");
+ return TRUE;
+ }
- ofono_modem_set_string(modem->modem, "Application", app);
- ofono_modem_set_string(modem->modem, "GPS", gps);
ofono_modem_set_string(modem->modem, "Modem", mdm);
- ofono_modem_set_string(modem->modem, "Device", qmi);
- ofono_modem_set_string(modem->modem, "Model", modem->model);
+ ofono_modem_set_string(modem->modem, "Application", app);
+ ofono_modem_set_string(modem->modem, "GNSS", gnss);
+ ofono_modem_set_string(modem->modem, "RSA", rsa);
+ ofono_modem_set_string(modem->modem, "Diag", diag);
+
+ ofono_modem_set_string(modem->modem, "NetworkControl", ctl);
ofono_modem_set_string(modem->modem, "NetworkInterface", net);
+ ofono_modem_set_string(modem->modem, "NetworkInterface2", net2);
+
+ ofono_modem_set_powered_timeout_hint(modem->modem, 60);
return TRUE;
}
@@ -1290,7 +1350,7 @@ static struct {
{ "quectel", setup_quectel },
{ "quectelqmi", setup_quectelqmi},
{ "ublox", setup_ublox },
- { "gemalto", setup_gemalto },
+ { "gemalto", setup_gemalto, "device/interface" },
{ "xmm7xxx", setup_xmm7xxx },
{ "mbim", setup_mbim },
/* Following are non-USB modems */
@@ -1298,12 +1358,12 @@ static struct {
{ "u8500", setup_isi_serial },
{ "n900", setup_isi_serial },
{ "calypso", setup_serial_modem },
- { "cinterion", setup_serial_modem },
+ { "cinterion", setup_gemalto },
{ "nokiacdma", setup_serial_modem },
{ "sim900", setup_serial_modem },
{ "wavecom", setup_wavecom },
- { "tc65", setup_tc65 },
- { "ehs6", setup_ehs6 },
+ { "tc65", setup_gemalto },
+ { "ehs6", setup_gemalto },
{ }
};
@@ -1464,11 +1524,10 @@ static void add_serial_device(struct udev_device *dev)
struct udev_device* mdev;
const char* driver;
+ /* discard if the device doesn't have the property OFONO_DRIVER */
mdev = get_serial_modem_device(dev);
- if (!mdev) {
- DBG("Device is missing required OFONO_DRIVER property");
+ if (!mdev)
// separate commit to remove this polluting message. On some systems // it can emit the line 256 times.
// By default a typical linux distro presents 64 ttySx ports, so even if a // few are used, there are too many unneeded notifications.
return;
- }
driver = udev_device_get_property_value(mdev, "OFONO_DRIVER");
@@ -1674,11 +1733,13 @@ static struct {
{ "ublox", "cdc_acm", "1546", "1102" },
{ "ublox", "rndis_host", "1546", "1146" },
{ "ublox", "cdc_acm", "1546", "1146" },
- { "gemalto", "option", "1e2d", "0053" },
- { "gemalto", "cdc_wdm", "1e2d", "0053" },
- { "gemalto", "qmi_wwan", "1e2d", "0053" },
- { "gemalto", "cdc_acm", "1e2d", "0061" },
- { "gemalto", "cdc_ether", "1e2d", "0061" },
+ { "gemalto", "cdc_acm", "1e2d" },
+ { "gemalto", "option", "1e2d" },
+ { "gemalto", "cdc_wdm", "1e2d" },
+ { "gemalto", "qmi_wwan", "1e2d" },
+ { "gemalto", "cdc_ether", "1e2d", },
+ { "gemalto", "cdc_mbim", "1e2d", },
+ { "gemalto", "cdc_ncm", "1e2d", },
{ "telit", "cdc_ncm", "1bc7", "0036" },
{ "telit", "cdc_acm", "1bc7", "0036" },
{ "xmm7xxx", "cdc_acm", "8087" },
@@ -1708,6 +1769,7 @@ static void check_usb_device(struct udev_device *device)
vendor = udev_device_get_property_value(usb_device, "ID_VENDOR_ID"); model = udev_device_get_property_value(usb_device, "ID_MODEL_ID");
+ /* for serial2usb-like devices, enum as usb/usb_device */
// separate commit or discard
driver = udev_device_get_property_value(usb_device, "OFONO_DRIVER"); if (!driver) {
struct udev_device *usb_interface =
@@ -1719,7 +1781,17 @@ static void check_usb_device(struct udev_device *device)
usb_interface, "OFONO_DRIVER");
}
- if (driver == NULL) {
+ if (!driver)
+ {
+ /* for serial2usb devices (enum as tty/generic) */
+ const char *serialdriver =
+ udev_device_get_property_value(device, "OFONO_DRIVER");
+ DBG("vId=%s, pId=%s, driver=%s", vendor, model, serialdriver);
+ if (serialdriver)
+ add_serial_device(device);
+ }
// separate commit to match the devices connected through an FDMI chip +
+ if (!driver) { /* use linux driver */
const char *drv;
unsigned int i;
@@ -1772,6 +1844,7 @@ static void check_usb_device(struct udev_device *device)
static void check_device(struct udev_device *device)
{
const char *bus;
+ const char *ofono_ignore_device;
bus = udev_device_get_property_value(device, "ID_BUS"); if (bus == NULL) {
@@ -1780,6 +1853,11 @@ static void check_device(struct udev_device *device)
return;
}
+ ofono_ignore_device = udev_device_get_property_value(device,
+ "OFONO_IGNORE_DEVICE");
+ if (ofono_ignore_device)
+ return; /* skip this device */
+
// separate commit to ignore some devices
if ((g_str_equal(bus, "usb") == TRUE) ||
(g_str_equal(bus, "usbmisc") == TRUE))
check_usb_device(device);
Signature · 1 line
2.17.1
Hi Giacinto,
Quoted 5 lines · Giacinto Cifelli
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.
Quoted 234 lines
I have included the totality of file
plugins/gemalto.cbecause 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.hfile, 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 100644plugins/gemalto.cinclude/gemalto.h new file +27
-0@@ -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-1301 USA +* +*/ + +enum auth_option { +GEMALTO_AUTH_DEFAULTS =0 , +GEMALTO_AUTH_USE_SGAUTH =1 <<0 , +GEMALTO_AUTH_ORDER_PWD_USR =1 <<1 , +GEMALTO_AUTH_ALWAYS_ALL_PARAMS =1 <<2 , +};plugins/gemalto.c new file +64
-0@@ -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-1301 USA +* +*/ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <errno.h> +#include <fcntl.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <sys/mman.h> +#include <linux/types.h> +#include <unistd.h> +#include <stdlib.h> +#include <stdio.h> +#include <stdint.h> +#include <glib.h> +#include <gatchat.h> +#include <gattty.h> +#include <gdbus.h> +#include "ofono.h" +#define OFONO_API_SUBJECT_TO_CHANGE +#include <ofono/dbus.h> +#include <ofono/plugin.h> +#include <ofono/log.h> +#include <ofono/modem.h> +#include <ofono/devinfo.h> +#include <ofono/netreg.h> +#include <ofono/phonebook.h> +#include <ofono/sim.h> +#include <ofono/sms.h> +#include <ofono/gprs.h> +#include <ofono/gprs-context.h> +#include <ofono/location-reporting.h> +#include <drivers/atmodem/atutil.h> +#include <drivers/atmodem/vendor.h> +#include <string.h> + +#ifdef HAVE_ELL +#include <ell/ell.h> +#include <drivers/mbimmodem/mbim.h> +#include <drivers/mbimmodem/mbim-message.h> +#include <drivers/mbimmodem/mbim-desc.h> +#endif // some models can use MBIM, but if the option is not included, // they fall back to PPP
plugins/gemalto.c new file +5
-0+ +#include <drivers/qmimodem/qmi.h> +#include <src/storage.h> +#include "gemalto.h" +// the next part will not be in the official commit
plugins/gemalto.c new file +97
-0+/* debug utilities - begin */ + +#define REDCOLOR "\x1b\x5b\x30\x31\x3b\x33\x31\x6d" +#define NOCOLOR "\x1b\x5b\x30\x30\x6d" + +#include <stdio.h> +#include <stdlib.h> +#include <sys/wait.h> +#include <unistd.h> + +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 )]=0 ; + child_pid =fork (); +if (!child_pid) { +dup2 (2 ,1 );// redirect output to stderr +fprintf (stdout,"stack trace for %s pid=%s\n" ,name_buf,pid_buf); +execlp ("gdb" ,"gdb" ,"--batch" ,"-n" ,"-ex" ,"thread" ,"-ex" ,"bt" , 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 =1 , +GEMALTO_CONNECTION_USB =2 , +}; + +enum gemalto_device_state { +STATE_ABSENT =0 , +STATE_PROBE =1 , +STATE_PRESENT =2 , +}; + +enum gprs_option { +NO_GPRS =0 , +USE_SWWAN =1 , +USE_CTX17 =2 , +USE_CTX3 =3 , +USE_PPP =4 , +USE_SWWAN_INV =5 ,/* inverted syntax idx,act */ +USE_CTX_INV =6 ,/* inverted syntax idx,act */ +}; + +static const char *none_prefix[] = {NULL }; +static const char *cfun_prefix[] = {"+CFUN:" ,NULL }; +static const char *sctm_prefix[] = {"^SCTM:" ,NULL }; +static const char *sbv_prefix[] = {"^SBV:" ,NULL }; +static const char *sqport_prefix[] = {"^SQPORT:" ,NULL }; +static const char *sgpsc_prefix[] = {"^SGPSC:" ,NULL }; + +typedef void (*OpenResultFunc )(gboolean success,struct ofono_modem *modem); + +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;
plugins/gemalto.c
Quoted 10 lines
plugins/gemalto.c new file +10
-0+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?
Quoted 26 lines
plugins/gemalto.c new file +26
-0+/* 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 = user_data; + +ofono_info ("%s%s" , prefix, str); +} + +static const char *gemalto_get_string (struct ofono_modem *modem,const char *k) +{ +const char *v; +
plugins/gemalto.c
Quoted 10 lines
plugins/gemalto.c new file +10
-0+if (!modem || !k || !*k) +return NULL ; + + v =ofono_modem_get_string (modem, k); + +if (!v || !*v) +return NULL ; + +return v; +}
Uhh, why?
Quoted 92 lines
plugins/gemalto.c new file +92
-0+ +static void gemalto_signal (const char *iface,const char *name, +const char *value,struct ofono_modem *modem) +{ +DBusMessageIter sub_iter,iter; +const char *path =ofono_modem_get_path (modem); +DBusConnection *conn =ofono_dbus_get_connection (); + +DBusMessage *signal =dbus_message_new_signal (path, + iface, + name); + +DBG ("" ); + +if (signal ==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 ] = {0 ,0 }; + + buf =g_strdup_printf ("%s\r%s" , command, argument); + +if (strlen (argument)>=2 &&g_str_equal (argument+strlen (argument)-2 , +"^Z" )) +sprintf (buf+strlen (buf)-2 ,"\x1a" ); + +if (strlen (argument)>=2 &&g_str_equal (argument+strlen (argument)-2 , +"\\r" )) +sprintf (buf+strlen (buf)-2 ,"\r" ); + + expected_array[0 ]=prompt; +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 =ofono_modem_get_data (modem); +const char *vid =gemalto_get_string (modem,"Vendor" ); +const char *pid =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 =storage_open (NULL , store); + +if (!f) +return ; + +for (index =0 ; ; index++) { +sprintf (key,"command_%d" , index); + command =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 =0 ; ; index++) { +sprintf (key,"command_%d" , index); + command =g_key_file_get_string (f,"WithPrompt" , key,NULL ); +sprintf (key,"prompt_%d" , index); + prompt =g_key_file_get_string (f,"WithPrompt" , key,NULL ); +sprintf (key,"argument_%d" , index); + argument =g_key_file_get_string (f,"WithPrompt" , key,NULL ); +
plugins/gemalto.c
Quoted 10 lines
plugins/gemalto.c new file +10
-0+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?
Quoted 225 lines
plugins/gemalto.c new file +225
-0+ +/******************************************************************************* +* Hardware monitor interface +******************************************************************************/ + +#define HARDWARE_MONITOR_INTERFACE OFONO_SERVICE ".gemalto.HardwareMonitor" +#define CINTERION_LEGACY_HWMON_INTERFACE OFONO_SERVICE ".cinterion.HardwareMonitor" + +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="Below low temperature alert limit" ; +break ; +case 0 : + val="Normal operating temperature" ; +break ; +case 1 : + val="Above upper temperature alert limit" ; +break ; +case 2 : + val="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 user_data) +{ +struct gemalto_data *data = user_data; +DBusMessage *reply; +GAtResultIter iter; +DBusMessageIter dbus_iter; +DBusMessageIter dbus_dict; + +if (data->hm_msg ==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 =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 = 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 =ofono_modem_get_data (modem); + +DBG ("" ); + +if (data->hm_msg !=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 =dbus_message_ref (msg); + +return NULL ; +} + +static const GDBusMethodTable hardware_monitor_methods[] = { + {GDBUS_ASYNC_METHOD ("GetStatistics" , + NULL, GDBUS_ARGS({ "Statistics", "a{sv}" }), + hardware_monitor_get_statistics) }, + {} +}; + +static const GDBusSignalTable hardware_monitor_signals[] = { + {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 =ofono_modem_get_data (modem); +DBusConnection *conn =ofono_dbus_get_connection (); +const char *path =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=1,1" , none_prefix,NULL ,NULL ,NULL ); + +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 =ofono_modem_get_data (modem); +time_t t =time (NULL ); +struct tm tm; +gchar cclk_cmd[32 ]; + +/* Set date and time */ + tm = *localtime (&t); +strftime (cclk_cmd,32 ,"AT+CCLK=\"%y/%m/%d,%T\"" , &tm);
plugins/gemalto.c
Quoted 10 lines
plugins/gemalto.c new file +10
-0+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[] = { + {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?
plugins/gemalto.c
Quoted 5 lines
plugins/gemalto.c new file +5
-0+static const GDBusSignalTable gsmTime_signals[] = { + {GDBUS_SIGNAL ("NitzUpdated" , + GDBUS_ARGS({ "time", "a{sv}" }) )}, + {} +};
You don't like ofono_netreg_time_notify?
Quoted 17 lines
plugins/gemalto.c new file +17
-0+ +static void gemalto_time_enable (struct ofono_modem *modem) +{ +DBusConnection *conn =ofono_dbus_get_connection (); +const char *path =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 ; + }
plugins/gemalto.c
Quoted 10 lines
plugins/gemalto.c new file +10
-0+ +ofono_modem_add_interface (modem,GEMALTO_NITZ_TIME_INTERFACE ); +} + +/******************************************************************************* +* Command passtrhough interface +******************************************************************************/ + +#define COMMAND_PASSTHROUGH_INTERFACE OFONO_SERVICE ".gemalto.CommandPassthrough" +
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.
Quoted 308 lines
plugins/gemalto.c new file +308
-0+static int command_passthrough_signal_answer (const char *answer, +gpointer user_data) +{ +struct ofono_modem *modem = user_data; +DBusConnection *conn =ofono_dbus_get_connection (); +const char *path =ofono_modem_get_path (modem); +DBusMessage *signal; +DBusMessageIter iter; + +if (!conn || !path) +return -1 ; + + signal =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 =0 ; +char *answer; + +g_at_result_iter_init (&iter, result); + +while (g_at_result_iter_next (&iter,NULL )) { + len +=strlen (g_at_result_iter_raw_line (&iter))+2 ; + } + + len +=strlen (g_at_result_final_response (result))+3 ; + answer =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 = user_data; +struct gemalto_data *data =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) !=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 = user_data; +struct gemalto_data *data =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) !=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) !=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) !=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 = user_data; +struct gemalto_data *data =ofono_modem_get_data (modem); +GIOChannel *channel =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[] = { + {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[] = { + {GDBUS_SIGNAL ("Answer" , + GDBUS_ARGS({ "answer", "s" })) }, + { } +}; + +static void gemalto_command_passthrough_enable (struct ofono_modem *modem) +{ +DBusConnection *conn =ofono_dbus_get_connection (); +const char *path =ofono_modem_get_path (modem); + +/* Create Command Passthrough DBus interface */ +if (!g_dbus_register_interface (conn, path,COMMAND_PASSTHROUGH_INTERFACE , + 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 = user_data; +struct gemalto_data *data =ofono_modem_get_data (modem); +const char *port =ofono_modem_get_string (modem,"GNSS" ); +GAtResultIter iter; +DBusMessage *reply; +DBusMessageIter dbusiter; +DBusMessageIter dict; + +if (data->gnss_msg ==NULL ) +return ; + +if (!ok) + goto error; + + reply =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 ="" ; +const char *val ="" ; + +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 = user_data; +struct gemalto_data *data =ofono_modem_get_data (modem); + +if (data->gnss_msg !=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 =dbus_message_ref (msg); + +return NULL ; +} + +static void gnss_set_properties_cb (gboolean ok,GAtResult *result, +gpointer user_data) +{ +struct ofono_modem *modem = user_data; +struct gemalto_data *data =ofono_modem_get_data (modem); +DBusMessage *reply; + +if (data->gnss_msg ==NULL ) +return ; + +if (!ok) { +__ofono_dbus_pending_reply (&data->gnss_msg, +__ofono_error_failed (data->gnss_msg)); +return ; + } + + reply =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 = user_data; +struct gemalto_data *data =ofono_modem_get_data (modem); +DBusMessageIter iter, var; +const char *name; +char *value; +char buf[256 ]; + +if (data->gnss_msg !=NULL ) +return __ofono_error_busy (msg); + +if (dbus_message_iter_init (msg, &iter) == FALSE) +return __ofono_error_invalid_args (msg); + +if (dbus_message_iter_get_arg_type (&iter) !=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) !=DBUS_TYPE_VARIANT ) +return __ofono_error_invalid_args (msg); +
plugins/gemalto.c
Quoted 10 lines
plugins/gemalto.c new file +10
-0+dbus_message_iter_recurse (&iter, &var); + +if (dbus_message_iter_get_arg_type (&var) != +DBUS_TYPE_STRING ) +return __ofono_error_invalid_args (msg); + +dbus_message_iter_get_basic (&var, &value); + +snprintf (buf,sizeof (buf),"AT^SGPSC=\"%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 ?
Quoted 127 lines
plugins/gemalto.c new file +127
-0+if (!g_at_chat_send (data->app, buf, sgpsc_prefix, + gnss_set_properties_cb, modem,NULL )) +return __ofono_error_failed (msg); + + data->gnss_msg =dbus_message_ref (msg); +return NULL ; +} + +static const GDBusMethodTable gnss_methods[] = { + {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 =ofono_modem_get_data (modem); +const char *vid =ofono_modem_get_string (modem,"Vendor" ); +const char *pid =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 =storage_open (NULL , store); + +if (!f) +return ; + +for (index=0 ;;index++) { +sprintf (key,"property_%d" , index); + property =g_key_file_get_string (f,"Properties" , key,NULL ); + +sprintf (key,"value_%d" , index); + value =g_key_file_get_string (f,"Properties" , key,NULL ); + +if (!property || !value) +break ; + + command =g_strdup_printf ("AT^SGPSC=%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 = user_data; +DBusConnection *conn =ofono_dbus_get_connection (); +const char *path =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 =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.HardwareControl" + +static DBusMessage *hc_get_properties (DBusConnection *conn, +DBusMessage *msg,void *user_data) +{ +struct ofono_modem *modem = user_data; +struct gemalto_data *data =ofono_modem_get_data (modem); +DBusMessage *reply; +DBusMessageIter dbusiter; +DBusMessageIter dict; + + reply =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=AT+CFUN=7 +* return: +* command_0=AT+CFUN=1 +* +* powersave example for modules with GNSS (could also only stop the output): +* command_0=AT+CREG=0 +* command_1=AT+CGREG=0
plugins/gemalto.c
Quoted 10 lines
plugins/gemalto.c new file +10
-0+* command_2=AT+CEREG=0 +* command_3=AT^SGPSC="Engine","0" +* command_4=AT^SGPSC="Power/Antenna","off" +* return: +* command_0=AT+CREG=2 +* command_1=AT+CGREG=2 +* command_2=AT+CEREG=2 +* command_4=AT^SGPSC="Power/Antenna","on" +* command_3=AT^SGPSC="Engine","1" +*/
How is 'powersave' useful?
Quoted 185 lines
plugins/gemalto.c new file +185
-0+ +static void gemalto_powersave_cb (gboolean ok,GAtResult *result, +gpointer user_data) +{ +struct ofono_modem *modem = user_data; +struct gemalto_data *data =ofono_modem_get_data (modem); +DBusMessage *reply; + +/* flip the state in any case */ + data->powersave = !data->powersave; + +if (data->hc_msg ==NULL ) +return ; + + reply =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 = user_data; +struct gemalto_data *data =ofono_modem_get_data (modem); +DBusMessageIter iter, var; +const char *name; +gboolean enable; + +if (data->hc_msg !=NULL ) +return __ofono_error_busy (msg); + +if (dbus_message_iter_init (msg, &iter) == FALSE) +return __ofono_error_invalid_args (msg); + +if (dbus_message_iter_get_arg_type (&iter) !=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) !=DBUS_TYPE_VARIANT ) +return __ofono_error_invalid_args (msg); + +dbus_message_iter_recurse (&iter, &var); + +if (dbus_message_iter_get_arg_type (&var) !=DBUS_TYPE_BOOLEAN ) +return __ofono_error_invalid_args (msg); + +dbus_message_iter_get_basic (&var, &enable); + +if (data->powersave == 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 =dbus_message_ref (msg); +return NULL ; +} + +static void gemalto_smso_cb (gboolean ok,GAtResult *result,gpointer user_data) +{ +struct ofono_modem *modem = user_data; +struct gemalto_data *data =ofono_modem_get_data (modem); +DBusMessage *reply; + +if (data->hc_msg ==NULL ) +return ; + +if (data->conn !=GEMALTO_CONNECTION_SERIAL ) + goto finished; + +if (data->mdm) +g_at_chat_unref (data->mdm); + data->mdm =NULL ; + +if (data->app) +g_at_chat_unref (data->app); + data->app =NULL ; + +if (ok) +ofono_modem_set_powered (modem, FALSE); + +finished: + reply =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 = user_data; +struct gemalto_data *data =ofono_modem_get_data (modem); + +if (data->hc_msg !=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 =dbus_message_ref (msg); +return NULL ; +} + +static void gemalto_detect_sysstart (GAtResult *result,gpointer user_data); + +static void gemalto_reset_cb (gboolean ok,GAtResult *result,gpointer user_data) +{ +struct ofono_modem *modem = user_data; +struct gemalto_data *data =ofono_modem_get_data (modem); +DBusMessage *reply; + +if (data->hc_msg ==NULL ) +return ; + +if (data->conn !=GEMALTO_CONNECTION_SERIAL ) + goto finished; + + data->modem_ready_id =g_at_chat_register (data->app, +"^SYSSTART" , gemalto_detect_sysstart, FALSE, + modem,NULL ); + +finished: + reply =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 = user_data; +struct gemalto_data *data =ofono_modem_get_data (modem); + +if (data->hc_msg !=NULL ) +return __ofono_error_busy (msg); + +if (!g_at_chat_send (data->app,"AT+CFUN=1,1" , none_prefix, + gemalto_reset_cb, modem,NULL )) +return __ofono_error_failed (msg); + + data->hc_msg =dbus_message_ref (msg); +return NULL ; +} + +static const GDBusMethodTable hardware_control_methods[] = { + {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 =ofono_dbus_get_connection (); +const char *path =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 ; + }
plugins/gemalto.c
Quoted 10 lines
plugins/gemalto.c new file +10
-0+ +ofono_modem_add_interface (modem,HARDWARE_CONTROL_INTERFACE ); +} + +/******************************************************************************* +* modem plugin +******************************************************************************/ + +#ifdef HAVE_ELL +static int mbim_parse_descriptors (struct gemalto_data *md,const char *file)
Why is MBIM driver not enough?
Quoted 82 lines
plugins/gemalto.c new file +82
-0+{ +void *data; +size_t len; +const struct mbim_desc *desc =NULL ; +const struct mbim_extended_desc *ext_desc =NULL ; + + data =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 =L_LE16_TO_CPU (desc->wMaxControlMessage); + +if (ext_desc) + md->max_outstanding = ext_desc->bMaxOutstandingCommandMessages; + +l_free (data); +return 0 ; +} + +static int mbim_probe (struct ofono_modem *modem,struct gemalto_data *data) +{ +const char *descriptors; +int err; + + descriptors =gemalto_get_string (modem,"DescriptorFile" ); + +if (!descriptors) +return -EINVAL ; + + data->max_outstanding =1 ; + + err =mbim_parse_descriptors (data, descriptors); +if (err <0 ) { +DBG ("Warning, unable to load descriptors, setting defaults" ); + data->max_segment =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 = g_try_new0(struct gemalto_data, 1); +if (data ==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 =ofono_dbus_get_connection (); +const char *path; + +if (!modem) +return ; + + data =ofono_modem_get_data (modem); + path =ofono_modem_get_path (modem); + +if (!data) +return ; +
plugins/gemalto.c
Quoted 10 lines
plugins/gemalto.c new file +10
-0+#ifdef HAVE_ELL +if (data->mbim ==STATE_PRESENT ) { +mbim_device_shutdown (data->device); + } +#endif + +if (data->qmi ==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.
Quoted 961 lines
plugins/gemalto.c new file +320
-0+if (data->app) { +/* Cleanup potential SIM state polling */ +at_util_sim_state_query_free (data->sim_state_query); + data->sim_state_query =NULL ; + +g_at_chat_cancel_all (data->app); +g_at_chat_unregister_all (data->app); +g_at_chat_unref (data->app); + data->app =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 =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 == 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 = user_data; +struct gemalto_data *data =ofono_modem_get_data (modem); +struct ofono_sim *sim = data->sim; + +at_util_sim_state_query_free (data->sim_state_query); + data->sim_state_query =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 =ofono_modem_get_data (modem); +struct ofono_sim *sim = 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 =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 =ofono_modem_get_data (modem); +const char *nitz_data; +char buf[32 ]; + +/* Example: +CIEV: nitz,<time>,<timezone>,<daylight> */ +if (!g_at_result_iter_next_string (iter, &nitz_data)) +return ; + +DBG ("nitz_data %s" , nitz_data); + +sprintf (buf,"AT+CCLK=\"%s\"" , nitz_data); +g_at_chat_send (data->app, buf, none_prefix,NULL ,NULL ,NULL ); + +gemalto_signal (GEMALTO_NITZ_TIME_INTERFACE ,"NitzUpdated" , nitz_data, + modem); +} + +static void gemalto_ciev_notify (GAtResult *result,gpointer user_data) +{ +struct ofono_modem *modem = user_data; + +const char *sim_status ="simstatus" ; +const char *nitz_status ="nitz" ; +const char *ind_str; +GAtResultIter iter; + +g_at_result_iter_init (&iter, result); + +/* Example: +CIEV: simstatus,<status> */ +if (!g_at_result_iter_next (&iter,"+CIEV:" )) +return ; + +if (!g_at_result_iter_next_unquoted_string (&iter, &ind_str)) +return ; + +if (g_str_equal (sim_status, ind_str)) { +gemalto_ciev_simstatus_notify (&iter, modem); + }else if (g_str_equal (nitz_status, ind_str)) { +gemalto_ciev_nitz_notify (&iter, modem); + } +} + +static void sim_state_cb (gboolean present,gpointer user_data) +{ +struct ofono_modem *modem = user_data; +struct gemalto_data *data =ofono_modem_get_data (modem); + +at_util_sim_state_query_free (data->sim_state_query); + data->sim_state_query =NULL ; + + data->have_sim = present; +ofono_modem_set_powered (modem, TRUE); + +/* Register for specific sim status reports */ +g_at_chat_register (data->app,"+CIEV:" , + gemalto_ciev_notify, FALSE, modem,NULL ); + +g_at_chat_send (data->app,"AT^SIND=\"simstatus\",1" , none_prefix, +NULL ,NULL ,NULL ); +g_at_chat_send (data->app,"AT^SIND=\"nitz\",1" , none_prefix, +NULL ,NULL ,NULL ); +} + +static void gemalto_exit_urc_notify (GAtResult *result,gpointer user_data) +{ +GAtResultIter iter; +const char *error_message; + +g_at_result_iter_init (&iter, result); +g_at_result_iter_next (&iter,"^EXIT:" ); +g_at_result_iter_next_unquoted_string (&iter, &error_message); +ofono_error ("Modem exited! Cause: %s" , error_message); +} + +static void saic_probe (gboolean ok,GAtResult *result,gpointer user_data) +{ +struct gemalto_data *data =ofono_modem_get_data (user_data); + +if (ok) + data->voice_avail = TRUE; +else + data->voice_avail = FALSE; +} + +static void sgauth_probe (gboolean ok,GAtResult *result,gpointer user_data) +{ +struct gemalto_data *data =ofono_modem_get_data (user_data); + +if (ok) + data->auth_syntax =GEMALTO_AUTH_USE_SGAUTH | +GEMALTO_AUTH_ORDER_PWD_USR ; +else + data->auth_syntax =GEMALTO_AUTH_DEFAULTS ; +} + +static void gemalto_set_cfun_cb (gboolean ok,GAtResult *result, +gpointer user_data) +{ +struct ofono_modem *modem = user_data; +struct gemalto_data *data =ofono_modem_get_data (modem); + +if (!ok || data->cfun ==41 ) { +g_at_chat_cancel_all (data->app); +ofono_modem_set_powered (modem, FALSE); + }else { + data->sim_state_query =at_util_sim_state_query_new (data->app, +2 ,20 , sim_state_cb, modem,NULL ); + } +} + +static void gemalto_cfun_query (gboolean ok,GAtResult *result, +gpointer user_data) +{ +struct ofono_modem *modem = user_data; +struct gemalto_data *data =ofono_modem_get_data (user_data); +char buf[256 ]; +GAtResultIter iter; +int mode; + +sprintf (buf,"AT+CFUN=%d" , data->cfun==41 ?4 :data->cfun); + +if (!ok) + goto error; + +g_at_result_iter_init (&iter, result); + +if (!g_at_result_iter_next (&iter,"+CFUN:" )) + goto error; + +if (!g_at_result_iter_next_number (&iter, &mode)) + goto error; + +if (mode == data->cfun) +sprintf (buf,"AT" ); + +error: +if (g_at_chat_send (data->app, buf, none_prefix, gemalto_set_cfun_cb, + modem,NULL ) >0 ) +return ; + +if (data->cfun ==41 ) +ofono_modem_set_powered (modem, FALSE); +} + +static void gemalto_set_cfun (GAtChat *app,int mode,struct ofono_modem *modem) +{ +struct gemalto_data *data =ofono_modem_get_data (modem); + + data->cfun=mode; +g_at_chat_send (app,"AT+CFUN?" , cfun_prefix, gemalto_cfun_query, modem,NULL ); +} + +static void gemalto_initialize (struct ofono_modem *modem) +{ +struct gemalto_data *data =ofono_modem_get_data (modem); +char *urcdest; +guint m = data->model; + +DBG ("app:%d, mdm:%d, mbim:%d, qmi:%d" , + data->app!=NULL , + data->mdm!=NULL , + data->mbim ==STATE_PRESENT , + data->qmi ==STATE_PRESENT ); + +if (!data->app && !data->mdm) { +DBG ("no AT interface available. Removing this device." ); +ofono_modem_set_powered (modem, FALSE); +return ; + } + + urcdest ="AT^SCFG=\"URC/DstIfc\",\"app\"" ; + +if (!data->app) { + data->use_mdm_for_app = TRUE; + data->app = data->mdm; + urcdest ="AT^SCFG=\"URC/DstIfc\",\"mdm\"" ; + } + +if (!data->mdm && (data->gina ==STATE_PRESENT )) { +/*these modems can start PPP from any port*/ + data->mdm = data->app; + } + +if (data->mdm && data->gprs_opt ==NO_GPRS ) + data->gprs_opt =USE_PPP ; + +g_at_chat_set_wakeup_command (data->app,"AT\r" ,1000 ,5000 ); + +g_at_chat_send (data->app,"ATE0" , none_prefix,NULL ,NULL ,NULL ); + +if (data->gina !=STATE_PRESENT ) +g_at_chat_send (data->app, urcdest, none_prefix,NULL ,NULL , +NULL ); + +/* numeric error codes are interpreted by atmodem/atutil.c functions */ +g_at_chat_send (data->app,"AT+CMEE=1" , none_prefix,NULL ,NULL ,NULL ); + +if (data->mdm) +g_at_chat_send (data->mdm,"AT&C0" , none_prefix,NULL ,NULL , +NULL ); + +g_at_chat_send (data->app,"AT&C0" , none_prefix,NULL ,NULL ,NULL ); + +/* watchdog */ +g_at_chat_register (data->app,"^EXIT" , gemalto_exit_urc_notify, FALSE, + modem,NULL ); +ofono_devinfo_create (modem,OFONO_VENDOR_GEMALTO ,"atmodem" , data->app); +g_at_chat_send (data->app, +"AT^SCFG=\"MEopMode/PwrSave\",\"enabled\",52,50" , none_prefix, +NULL ,NULL ,NULL );// this command was in some
gemalto.cversions, but it would better be // placed in one of the configuration filesplugins/gemalto.c new file +101
-0+ +if (m !=0x5b && m !=0x5c && m !=0x5d && m !=0xa0 ) { +g_at_chat_send (data->app,"AT^SGAUTH?" ,NULL , sgauth_probe, + modem,NULL ); + } + +g_at_chat_send (data->app,"AT^SAIC?" ,NULL , saic_probe, modem,NULL ); + +gemalto_exec_stored_cmd (modem,"enable" ); + +gemalto_command_passthrough_enable (modem); +gemalto_hardware_monitor_enable (modem); +gemalto_time_enable (modem); +gemalto_gnss_enable (modem); +gemalto_hardware_control_enable (modem); + +gemalto_set_cfun (data->app,4 , modem); + data->init_done = TRUE; +} + +static gboolean gemalto_open_cb (GIOChannel *source,GIOCondition condition, +gpointer user_data) +{ +struct ofono_modem *modem = user_data; +struct gemalto_data *data =ofono_modem_get_data (modem); +GAtSyntax *syntax; + +if (data->channel ==NULL ) +return TRUE; + +if ((condition &G_IO_IN ) ==0 ) +return TRUE; + +g_source_remove (data->probing_timer); + data->probing_timer =0 ; +g_source_remove (data->read_src); +g_io_channel_flush (data->channel,NULL ); +/* reset channel defaults*/ +g_io_channel_set_buffered (data->channel, TRUE); +g_io_channel_set_encoding (data->channel,"UTF-8" ,NULL ); + + syntax =g_at_syntax_new_gsm_permissive (); + data->tmp_chat =g_at_chat_new (data->channel, syntax); +g_at_syntax_unref (syntax); + +if (data->tmp_chat ==NULL ) + goto failed; + +g_io_channel_unref (data->channel); + data->channel =NULL ; +g_at_chat_set_debug (data->tmp_chat, gemalto_debug,"App: " ); + data->open_cb (TRUE, modem); +return TRUE;// finished +failed: +DBG ("chat creation failed. aborting." ); +g_io_channel_unref (data->channel); + data->channel =NULL ; +DBG ("aborted." ); + data->open_cb (FALSE, modem); +return FALSE;// abort +} + +static int gemalto_probe_device (void *user_data) +{ +struct ofono_modem *modem = user_data; +struct gemalto_data *data =ofono_modem_get_data (modem); +GIOStatus status; + +if (data->channel==NULL ) +return FALSE; + + data->waiting_time++; +DBG ("%d/%d" , data->waiting_time, data->init_waiting_time); + +if (data->waiting_time > data->init_waiting_time) { + data->waiting_time =0 ; + goto failed; + } + + status =g_io_channel_write_chars (data->channel,"AT\r" ,3 ,NULL ,NULL ); + +if (status !=G_IO_STATUS_NORMAL && status !=G_IO_STATUS_AGAIN ) + goto failed; + +return TRUE;// to be called again + +failed: +g_source_remove (data->probing_timer); + data->probing_timer =0 ;/* remove the timer reference */ +DBG ("timeout: abort" ); +g_io_channel_unref (data->channel); + data->channel =NULL ; + data->tmp_chat =NULL ; + data->open_cb (FALSE, modem); +return FALSE;// abort +} + +#include <asm/ioctls.h> +#include <linux/serial.h> + +int ioctl (int ,int ,void *);// these are temporarily in the middle. Discussed with Denis, // maybe these and their use below is better placed in
g_at_tty_openplugins/gemalto.c new file +44
-0+ +static void gemalto_open_device (const char *device, +OpenResultFunc func,struct ofono_modem *modem) +{ +struct gemalto_data *data =ofono_modem_get_data (modem); +GHashTable *options; +int fd; +struct serial_struct old, new; + +if (!device || !*device) { +func (FALSE, modem); +return ; + } + + options =g_hash_table_new (g_str_hash, g_str_equal); +if (options ==NULL ) { +func (FALSE, modem); +return ; + } + +g_hash_table_insert (options,"Baud" ,"115200" ); +g_hash_table_insert (options,"StopBits" ,"1" ); +g_hash_table_insert (options,"DataBits" ,"8" ); +g_hash_table_insert (options,"Parity" ,"none" ); +g_hash_table_insert (options,"XonXoff" ,"off" ); +g_hash_table_insert (options,"RtsCts" ,"on" ); +g_hash_table_insert (options,"Local" ,"on" ); +g_hash_table_insert (options,"Read" ,"on" ); + +DBG ("Opening device %s" , device); + + data->channel =g_at_tty_open (device, options); +g_hash_table_destroy (options); + +if (!data->channel) { +func (FALSE, modem); +return ; + } + + fd =g_io_channel_unix_get_fd (data->channel); +ioctl (fd,TIOCGSERIAL , &old); + new = old; + new.closing_wait =ASYNC_CLOSING_WAIT_NONE ; +ioctl (fd,TIOCSSERIAL , &new);// without this, even if the
g_at_tty_opensets the port non-blocking, // it waits anyway 30 seconds if the port is not responding.plugins/gemalto.c new file +11
-0+ +g_io_channel_flush (data->channel,NULL ); +/* the channel is set by default to "UTF-8" and buffered */ +g_io_channel_set_encoding (data->channel,NULL ,NULL ); +g_io_channel_set_buffered (data->channel, FALSE); + data->open_cb = func; + data->read_src =g_io_add_watch (data->channel,G_IO_IN , gemalto_open_cb, + modem); + data->probing_timer =g_timeout_add_seconds (1 , gemalto_probe_device, + modem); +}// the 3 functions above:
// - open the channel as usual
// - test the port with AT once per second until timeout. Maybe this can be an
// additional wakeup function in GAtChat
// - attach a GAtChat to it or close itplugins/gemalto.c new file +474
-0+ +static void gemalto_enable_mdm_cb (gboolean success,struct ofono_modem *modem) +{ +struct gemalto_data *data =ofono_modem_get_data (modem); + + data->mdm = data->tmp_chat; + data->tmp_chat =NULL ; +gemalto_initialize (modem); +} + +static void gemalto_enable_app_cb (gboolean success,struct ofono_modem *modem) +{ +struct gemalto_data *data =ofono_modem_get_data (modem); +const char *mdm =gemalto_get_string (modem,"Modem" ); + + data->app = data->tmp_chat; + data->tmp_chat =NULL ; +gemalto_open_device (mdm, gemalto_enable_mdm_cb, modem); +} + +static int gemalto_enable_app (struct ofono_modem *modem) +{ +const char *app =gemalto_get_string (modem,"Application" ); + +gemalto_open_device (app, gemalto_enable_app_cb, modem); +return -EINPROGRESS ; +} + +#ifdef HAVE_ELL +static void mbim_device_caps_info_cb (struct mbim_message *message,void *user) +{ +struct ofono_modem *modem = user; +struct gemalto_data *md =ofono_modem_get_data (modem); +uint32_t device_type; +uint32_t cellular_class; +uint32_t voice_class; +uint32_t sim_class; +uint32_t data_class; +uint32_t sms_caps; +uint32_t control_caps; +uint32_t max_sessions; +char *custom_data_class; +char *device_id; +char *firmware_info; +char *hardware_info; +bool r; + +if (mbim_message_get_error (message) !=0 ) + goto error; + + r =mbim_message_get_arguments (message,"uuuuuuuussss" , + &device_type, &cellular_class, + &voice_class, &sim_class, &data_class, + &sms_caps, &control_caps, &max_sessions, + &custom_data_class, &device_id, + &firmware_info, &hardware_info); +if (!r) + goto error; + + md->max_sessions = max_sessions; + +DBG ("DeviceId: %s" , device_id); +DBG ("FirmwareInfo: %s" , firmware_info); +DBG ("HardwareInfo: %s" , hardware_info); + +ofono_modem_set_string (modem,"DeviceId" , device_id); +ofono_modem_set_string (modem,"FirmwareInfo" , firmware_info); + +l_free (custom_data_class); +l_free (device_id); +l_free (firmware_info); +l_free (hardware_info); + + message =mbim_message_new (mbim_uuid_basic_connect, +MBIM_CID_DEVICE_SERVICE_SUBSCRIBE_LIST , +MBIM_COMMAND_TYPE_SET ); + +mbim_message_set_arguments (message,"av" ,2 , +"16yuuuuuuu" , + mbim_uuid_basic_connect,6 , +MBIM_CID_SUBSCRIBER_READY_STATUS , +MBIM_CID_RADIO_STATE , +MBIM_CID_REGISTER_STATE , +MBIM_CID_PACKET_SERVICE , +MBIM_CID_SIGNAL_STATE , +MBIM_CID_CONNECT , +"16yuuuu" , mbim_uuid_sms,3 , +MBIM_CID_SMS_CONFIGURATION , +MBIM_CID_SMS_READ , +MBIM_CID_SMS_MESSAGE_STORE_STATUS ); + +if (mbim_device_send (md->device,0 , message, +NULL ,NULL ,NULL )) { + md->mbim =STATE_PRESENT ; + goto other_devices; + } + + +error: +mbim_device_shutdown (md->device); + +other_devices: + +if (md->init_done) +return ; + +gemalto_enable_app (modem);/* continue with mdm interface */ +} + +static void mbim_device_ready (void *user_data) +{ +struct ofono_modem *modem = user_data; +struct gemalto_data *md =ofono_modem_get_data (modem); +struct mbim_message *message = +mbim_message_new (mbim_uuid_basic_connect, +1 ,MBIM_COMMAND_TYPE_QUERY ); + +mbim_message_set_arguments (message,"" ); +mbim_device_send (md->device,0 , message, mbim_device_caps_info_cb, + modem,NULL ); +} + +static void mbim_device_closed (void *user_data) +{ +struct ofono_modem *modem = user_data; +struct gemalto_data *md =ofono_modem_get_data (modem); + +if (!md) +return ; + +/* +* if state=probe, it means that we are in the init phase +* and that we have failed the MBIM_OPEN +*/ +if (md->mbim ==STATE_PROBE ) { +DBG (REDCOLOR "MBIM OPEN failed!" NOCOLOR ); +gemalto_enable_app (modem);/* continue with other interfaces */ + } + +/* reset the state for future attempts */ + md->mbim =STATE_PROBE ; + +if (md->device) +mbim_device_unref (md->device); + + md->device =NULL ; +} + +static int mbim_enable (struct ofono_modem *modem) +{ +const char *device; +int fd; +struct gemalto_data *md =ofono_modem_get_data (modem); + +DBG ("modem struct: %p" , modem); + + device =gemalto_get_string (modem,"NetworkControl" ); +if (!device) + goto other_devices; + +DBG ("modem device: %s" , device); + fd =open (device,O_EXCL |O_NONBLOCK |O_RDWR ); + +if (fd <0 ) + goto other_devices; + +DBG ("device: %s opened successfully" , device); + md->device =mbim_device_new (fd, md->max_segment); +DBG ("created new device %p" , md->device); + +mbim_device_set_close_on_unref (md->device, true); +mbim_device_set_max_outstanding (md->device, md->max_outstanding); +mbim_device_set_ready_handler (md->device, + mbim_device_ready, modem,NULL ); +mbim_device_set_disconnect_handler (md->device, + mbim_device_closed, modem,NULL ); +mbim_device_set_debug (md->device, gemalto_debug,"MBIM:" ,NULL ); + +return -EINPROGRESS ; + +other_devices: + +if (md->init_done) +return 0 ; + +return gemalto_enable_app (modem); +} +#endif + +static void qmi_enable_cb (void *user_data) +{ +struct ofono_modem *modem = user_data; +struct gemalto_data *md =ofono_modem_get_data (modem); + md->qmi =STATE_PRESENT ; +gemalto_enable_app (modem);/* qmi done, continue with app interface */ +} + +static int qmi_enable (struct ofono_modem *modem) +{ +struct gemalto_data *data =ofono_modem_get_data (modem); +const char *device; +int fd; + +DBG ("modem struct: %p" , modem); + + device =gemalto_get_string (modem,"NetworkControl" ); +if (!device) +return gemalto_enable_app (modem); + + fd =open (device,O_RDWR |O_NONBLOCK |O_CLOEXEC ); +if (fd <0 ) +return gemalto_enable_app (modem); + + data->device =qmi_device_new (fd); +if (!data->device) { +close (fd); +return gemalto_enable_app (modem); + } + +qmi_device_set_close_on_unref (data->device, true); + +qmi_device_set_debug (data->device, gemalto_debug,"QMI: " ); + +qmi_device_discover (data->device, qmi_enable_cb, modem,NULL ); + +return -EINPROGRESS ; +} + +static void set_from_model (struct gemalto_data *data) { +guint m = data->model; + + data->has_lte = TRUE;/* default */ + +/* pre-configure non-MBIM network interfaces */ +if (m !=0x62 && m !=0x5d && m !=0x65 ) { +/* +* note: we probe for ECM/NCM even if the port is not present +* (for serial connection type or serial-like) +*/ +if (m ==0x53 || m ==0x60 || m ==0x63 ) + data->qmi =STATE_PROBE ; +/*these families have PPP only*/ +else if (m !=0x58 && m !=0x47 && m !=0x54 ) + data->ecmncm =STATE_PROBE ; + } + +/* pre-configure SW features */ +if (m ==0xa0 ) { + data->gprs_opt =USE_CTX3 ; + data->ecmncm =STATE_ABSENT ; + } +if (m ==0x63 || m ==0x65 || m ==0x5b || m ==0x5c || m ==0x5d ) + data->gina =STATE_PRESENT ; + + data->init_waiting_time =30 ; + +if (m ==0x55 || m ==0x47 ) { + data->has_lte = FALSE; + data->init_waiting_time =5 ; + } + +if (m ==0x58 ) { + data->has_lte = FALSE; + data->init_waiting_time =15 ; + } + + data->vts_with_quotes = TRUE; + +if (m ==0x5b || m ==0x5c || m ==0x5d || m ==0xa0 ) { + data->vts_with_quotes = FALSE; + data->auth_syntax =GEMALTO_AUTH_USE_SGAUTH | +GEMALTO_AUTH_ALWAYS_ALL_PARAMS ; + } +} + +static void store_cgmm (gboolean ok,GAtResult *result,gpointer user_data) +{ +struct ofono_modem *modem = user_data; +struct gemalto_data *data =ofono_modem_get_data (modem); +GAtResultIter iter; +char const *model; +char buf[16 ]; + +/* if no model, fallback to a basic 2G one */ + data->model =0x47 ; +strncpy (data->modelstr,"" ,sizeof (data->modelstr)); + +if (!ok) +return ; + +g_at_result_iter_init (&iter, result); + +while (g_at_result_iter_next (&iter,NULL )) { +if (!g_at_result_iter_next_unquoted_string (&iter, &model)) +continue ; + +if (model && *model) { +strncpy (data->modelstr, model,sizeof (data->modelstr)); + +if (g_ascii_strncasecmp (model,"TC" ,2 ) ==0 ) + data->model =0x47 ; +else if (g_ascii_strncasecmp (model,"MC" ,2 ) ==0 ) + data->model =0x47 ; +else if (g_ascii_strncasecmp (model,"AC" ,2 ) ==0 ) + data->model =0x47 ; +else if (g_ascii_strncasecmp (model,"HC" ,2 ) ==0 ) + data->model =0x47 ; +else if (g_ascii_strncasecmp (model,"HM" ,2 ) ==0 ) + data->model =0x47 ; +else if (g_ascii_strncasecmp (model,"XT" ,2 ) ==0 ) + data->model =0x47 ; +else if (g_ascii_strncasecmp (model,"AGS" ,3 ) ==0 ) + data->model =0x47 ; +else if (g_ascii_strncasecmp (model,"BGS" ,3 ) ==0 ) + data->model =0x47 ; +else if (g_ascii_strncasecmp (model,"AH3" ,3 ) ==0 ) + data->model =0x55 ; +else if (g_ascii_strncasecmp (model,"AHS" ,3 ) ==0 ) + data->model =0x55 ; +else if (g_ascii_strncasecmp (model,"PHS" ,3 ) ==0 ) + data->model =0x55 ; +else if (g_ascii_strncasecmp (model,"PH8" ,3 ) ==0 ) + data->model =0x55 ; +else if (g_ascii_strncasecmp (model,"AHS" ,3 ) ==0 ) + data->model =0x55 ; +else if (g_ascii_strncasecmp (model,"EHS" ,3 ) ==0 ) + data->model =0x58 ; +else if (g_ascii_strncasecmp (model,"ELS31-" ,6 ) ==0 ) + data->model =0xa0 ; +else if (g_ascii_strncasecmp (model,"ELS61-" ,6 ) ==0 ) + data->model =0x5b ; +else if (g_ascii_strncasecmp (model,"PLS62-" ,6 ) ==0 ) + data->model =0x5b ; +else if (g_ascii_strncasecmp (model,"PLS8-" ,5 ) ==0 ) + data->model =0x61 ; +else if (g_ascii_strncasecmp (model,"ALS3-" ,5 ) ==0 ) + data->model =0x61 ; +else if (g_ascii_strncasecmp (model,"ALAS5-" ,6 ) ==0 ) + data->model =0x65 ; +return ; + } + } + +sprintf (buf,"%04x" , data->model); +ofono_modem_set_string (modem,"Model" , buf); +} + +static void store_sqport (gboolean ok,GAtResult *result,gpointer user_data) +{ +struct ofono_modem *modem = user_data; +struct gemalto_data *data =ofono_modem_get_data (modem); +GAtResultIter iter; +char const *sqport; + +/* in case of error, the port is of Modem type */ +strncpy (data->sqport,"Modem" ,sizeof (data->sqport)); + +if (!ok) + goto done; + +g_at_result_iter_init (&iter, result); + +/* answer format: "^SQPORT: Application" */ +if (!g_at_result_iter_next (&iter,"^SQPORT:" )) + goto done; + +if (!g_at_result_iter_next_unquoted_string (&iter, &sqport)) + goto done; + +if (!sqport || !*sqport) + goto done; + +strncpy (data->sqport, sqport,sizeof (data->sqport)); + +done: +/* select mdm, app or gina port type */ + data->ecmncm =STATE_PROBE ; + +if (g_str_equal (sqport,"Modem" )) { + data->mdm = data->app; + data->app =NULL ; + data->ecmncm =STATE_ABSENT ; + } + +if ((*sqport >='0' && *sqport <='9' )) + data->gina =STATE_PRESENT ; + +set_from_model (data); +gemalto_initialize (modem); +} + +static void gemalto_detect_serial (gboolean success,struct ofono_modem *modem) +{ +struct gemalto_data *data =ofono_modem_get_data (modem); + + data->app = data->tmp_chat; + data->tmp_chat =NULL ; + +g_at_chat_send (data->app,"AT+CGMM" , none_prefix, store_cgmm, + modem,NULL ); +g_at_chat_send (data->app,"AT^SQPORT" , sqport_prefix, store_sqport, + modem,NULL ); +} + +static void gemalto_detect_sysstart (GAtResult *result,gpointer user_data) +{ +struct ofono_modem *modem = user_data; +struct gemalto_data *data =ofono_modem_get_data (modem); + +if (data->modem_ready_id) { +g_at_chat_unregister (data->app, data->modem_ready_id); + data->modem_ready_id =0 ; + } + + data->tmp_chat = data->app; +gemalto_detect_serial (TRUE, modem); +} + +static int gemalto_enable_serial (struct ofono_modem *modem) +{ +const char *device =ofono_modem_get_string (modem,"ATport" ); + +if (!device) +return -EINVAL ; + +gemalto_open_device (device, gemalto_detect_serial, modem); +return -EINPROGRESS ; +} + +static int gemalto_enable (struct ofono_modem *modem) +{ +struct gemalto_data *data =ofono_modem_get_data (modem); +const char *model =gemalto_get_string (modem,"Model" ), + *conn_type =gemalto_get_string (modem,"ConnType" ); +const char + *ctl =gemalto_get_string (modem,"NetworkControl" ), + *net =gemalto_get_string (modem,"NetworkInterface" ); +guint m =0 ; + +if (!modem || !data) +return -EINVAL ; + + data->conn =g_str_equal (conn_type,"Serial" ) ?GEMALTO_CONNECTION_SERIAL + :GEMALTO_CONNECTION_USB ; + +if (data->conn ==GEMALTO_CONNECTION_SERIAL ) +return gemalto_enable_serial (modem); + +DBG ("modem struct: %p, gemalto_data: %p" , modem, data); + +if (data->init_done) { +gemalto_set_cfun (data->app,4 , modem); + +#ifdef HAVE_ELL +if (data->mbim !=STATE_ABSENT ) +mbim_enable (modem); +#endif + +return -EINPROGRESS ; + } + +if (model) { + data->model =strtoul (model,NULL ,16 ); + m = data->model; + } + +/* single ACM interface 02: assign application to modem */ +if (m ==0xa0 ) { +const char *app =gemalto_get_string (modem,"Application" ); +ofono_modem_set_string (modem,"Modem" , app); + } + +if (m ==0x60 ) { +const char *app =gemalto_get_string (modem,"Diag" );
plugins/gemalto.c
Quoted 10 lines
plugins/gemalto.c new file +10
-0+ofono_modem_set_string (modem,"Modem" , app); + } + +/* if single ACM interface, remove possible extra devices */ +if (m ==0x58 || m ==0x47 || m ==0x54 || m ==0xa0 || m ==0x60 ) { +ofono_modem_set_string (modem,"Application" ,NULL ); +ofono_modem_set_string (modem,"GNSS" ,NULL ); +ofono_modem_set_string (modem,"RSA" ,NULL ); +ofono_modem_set_string (modem,"Diag" ,NULL ); + }
This really belongs in udevng...
Quoted 392 lines
plugins/gemalto.c new file +189
-0+ +#ifdef HAVE_ELL +/* pre-configure MBIM network interface */ +if (m ==0x62 || m ==0x5d || m ==0x65 ) { + data->mbim =STATE_PROBE ; + } +#endif + +set_from_model (data); + +#ifdef HAVE_ELL +if ((data->mbim ==STATE_PROBE ) && ctl && net) { + data->init_waiting_time =3 ; +return mbim_enable (modem); + } +#endif + +if ((data->qmi ==STATE_PROBE ) && ctl && net) { + data->init_waiting_time =10 ; +return qmi_enable (modem); + } + +return gemalto_enable_app (modem); +} + +#ifdef HAVE_ELL +static int mbim_sim_probe (void *device) +{ +struct mbim_message *message; +/* SIM_GROUP is defined in mbimmodem.h that cannot be included */ +uint32_t SIM_GROUP =1 ; + + message =mbim_message_new (mbim_uuid_basic_connect, +MBIM_CID_SUBSCRIBER_READY_STATUS , +MBIM_COMMAND_TYPE_QUERY ); +if (!message) +return -ENOMEM ; + +mbim_message_set_arguments (message,"" ); + +if (!mbim_device_send (device,SIM_GROUP , message, +NULL ,NULL ,NULL )) { +mbim_message_unref (message); +return -EIO ; + } +return 0 ; +} +#endif + +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; + +decode_at_error (&error,g_at_result_final_response (result)); + +cb (&error, cbd->data); +} + +static void gemalto_set_online_serial (struct ofono_modem *modem, +ofono_bool_t online,ofono_modem_online_cb_t cb, +void *user_data) +{ +struct gemalto_data *data =ofono_modem_get_data (modem); +struct cb_data *cbd =cb_data_new (cb, user_data); +char const *command; + +gemalto_exec_stored_cmd (modem,"set_online" ); + +if (data->model ==0x47 ) { + command = online ?"AT^SCFG=\"MEopMode/Airplane\",\"off\"" : +"AT^SCFG=\"MEopMode/Airplane\",\"on\"" ; + }else { + command = online ?"AT+CFUN=1" :"AT+CFUN=4" ; + } + +DBG ("modem %p %s" , modem, online ?"online" :"offline" ); + +if (g_at_chat_send (data->app, command,NULL , set_online_cb, cbd, + g_free)) +return ; + +CALLBACK_WITH_FAILURE (cb, cbd->data); +g_free (cbd); +} + +static void gemalto_set_online (struct ofono_modem *modem,ofono_bool_t online, +ofono_modem_online_cb_t cb,void *user_data) +{ +struct gemalto_data *data =ofono_modem_get_data (modem); +struct cb_data *cbd =cb_data_new (cb, user_data); +char const *cmd = online ?"AT+CFUN=1" :"AT+CFUN=4" ; + +if (data->conn ==GEMALTO_CONNECTION_SERIAL ) { +gemalto_set_online_serial (modem, online, cb, user_data); +return ; + } + +DBG ("modem %p %s" , modem, online ?"online" :"offline" ); + +if (g_at_chat_send (data->app, cmd,NULL , set_online_cb, cbd, g_free)) +return ; + +CALLBACK_WITH_FAILURE (cb, cbd->data); + +g_free (cbd); +} + +static void gemalto_pre_sim (struct ofono_modem *modem) +{ +struct gemalto_data *data =ofono_modem_get_data (modem); + +DBG ("%p" , modem); + +gemalto_exec_stored_cmd (modem,"pre_sim" ); + +ofono_location_reporting_create (modem,0 ,"gemaltomodem" , data->app); + + data->sim =ofono_sim_create (modem,OFONO_VENDOR_GEMALTO , +"atmodem" , data->app); + +if (data->sim && data->have_sim == TRUE) +ofono_sim_inserted_notify (data->sim, TRUE); +} + +static void gemalto_post_sim (struct ofono_modem *modem) +{ +struct gemalto_data *data =ofono_modem_get_data (modem); + +gemalto_exec_stored_cmd (modem,"post_sim" ); + +#ifdef HAVE_ELL +if (data->mbim ==STATE_PRESENT ) { +/* very important to set the interface ready */ +mbim_sim_probe (data->device); + } +#endif + +ofono_phonebook_create (modem,0 ,"atmodem" , data->app); +ofono_modem_set_integer (modem,"GemaltoAuthType" , data->auth_syntax); + +if (data->has_lte) +ofono_lte_create (modem,0 ,"gemaltomodem" , data->app); +} + +static void cgdcont17_probe (gboolean ok,GAtResult *result,gpointer user_data) +{ +struct gemalto_data *data =ofono_modem_get_data (user_data); + +if (ok) + data->gprs_opt =USE_CTX17 ; +} + +static void swwan_probe (gboolean ok,GAtResult *result,gpointer user_data) +{ +struct gemalto_data *data =ofono_modem_get_data (user_data); + +if (ok) + data->gprs_opt =USE_SWWAN ; +} + +static void autoattach_probe_and_continue (gboolean ok,GAtResult *result, +gpointer user_data) +{ +struct ofono_modem * modem = user_data; +struct gemalto_data *data =ofono_modem_get_data (modem); +GAtResultIter iter; +struct ofono_message_waiting *mw; +struct ofono_gprs *gprs =NULL ; +struct ofono_gprs_context *gc =NULL ; + + data->autoattach = FALSE; +ofono_modem_set_integer (modem,"GemaltoAutoAttach" ,0 ); + +if (ok) { +g_at_result_iter_init (&iter, result); +while (g_at_result_iter_next (&iter,NULL )) { +if (strstr (g_at_result_iter_raw_line (&iter), +"\"enabled\"" )) { + data->autoattach = TRUE; +ofono_modem_set_integer (modem, +"GemaltoAutoAttach" ,1 ); + + } + } + } + +// TODO: the ofono_gprs_create may require gemaltomodem instead of atmodem // for now this code works with a modified version of
atmodem/gprs.c, // where theofono_gprs_set_cid_rangeis not called, so it can be called belowplugins/gemalto.c new file +88
-0+ +#ifdef HAVE_ELL +if (data->mbim ==STATE_PRESENT ) { + gprs =ofono_gprs_create (modem,OFONO_VENDOR_GEMALTO ,"atmodem" , + data->app); +ofono_gprs_set_cid_range (gprs,0 , data->max_sessions); +if (data->model ==0x62 || data->model ==0x65 ) { +struct gemalto_mbim_composite comp; + comp.device = data->device; + comp.chat = data->app; + comp.at_cid =4 ; + gc =ofono_gprs_context_create (modem,0 ,"gemaltomodemmbim" , &comp); + }else /* model == 0x5d */ + gc =ofono_gprs_context_create (modem,0 ,"mbim" , data->device); + } else +#endif +if (data->qmi ==STATE_PRESENT ) { + gprs =ofono_gprs_create (modem,OFONO_VENDOR_GEMALTO ,"atmodem" , + data->app); +// TODO: verify if need be to create the contexts and auth params beforehand +// if so, may need a gprs-context gemaltomodem-at-qmi + +/* on QMI devices, only a single context is supported */ +// TODO: Is 1 ok for attach_APN != context_APN? +ofono_gprs_set_cid_range (gprs,1 ,1 ); + gc =ofono_gprs_context_create (modem,0 ,"qmimodem" , + data->device); + }else if (data->gprs_opt ==USE_SWWAN || data->gprs_opt ==USE_CTX17 || + data->gprs_opt ==USE_CTX3 ) { +ofono_modem_set_integer (modem,"GemaltoWwan" , + data->gprs_opt ==USE_SWWAN ); + gprs =ofono_gprs_create (modem,OFONO_VENDOR_GEMALTO ,"atmodem" , + data->app); +if (data->gprs_opt ==USE_CTX3 ) +ofono_gprs_set_cid_range (gprs,3 ,3 ); +else if (data->model ==0x5b ) +/* +* limitation: same APN as for attach +* in this case create more contexts +*/ +ofono_gprs_set_cid_range (gprs,1 ,11 ); +else +ofono_gprs_set_cid_range (gprs,4 ,16 ); +// maybe rename the next to gemaltomodem-wwan + gc =ofono_gprs_context_create (modem,0 ,"gemaltomodemswwan" , + data->app); + }else if (data->gprs_opt ==USE_PPP ) { +/* plain PPP only works from mdm ports */ + gprs =ofono_gprs_create (modem,OFONO_VENDOR_GEMALTO ,"atmodem" , + data->app); +if (data->model ==0x47 ) +ofono_gprs_set_cid_range (gprs,1 ,2 ); +else if (data->has_lte) +ofono_gprs_set_cid_range (gprs,4 ,16 ); +else +ofono_gprs_set_cid_range (gprs,1 ,16 ); + + gc =ofono_gprs_context_create (modem,0 ,"atmodem" , data->mdm); + + }/* +* in case of no match above, we have no gprs possibilities +* this is common when using the module through serial interfaces +* nevertheless other services (voice, gpio, gnss) could be available +*/ + +if (gc) +ofono_gprs_context_set_type (gc, +OFONO_GPRS_CONTEXT_TYPE_INTERNET ); + +if (gprs && gc) +ofono_gprs_add_context (gprs, gc); + +/* might have also without voicecall support */ +ofono_ussd_create (modem,0 ,"atmodem" , data->app); + +/* +* Call support is technically possible only after sim insertion +* with the module online. However the EMERGENCY_SETUP procedure of +* the 3GPP TS_24.008 is triggered by the same AT command, +* and namely 'ATD112;', 'ATD911;', etc. +* On the other hand, in airplane-mode it is not possible to do it, nor +* to create all relevant URCs for the atom. +* +* Ofono does not make a distinction between no-sim and +* airplane-mode scenarios, so we create the voicecall in post-online. +* This is half-compatible with the European directives that require +* a SIM inserted also for emergency setup. +*/ // this needs further thinking
plugins/gemalto.c new file +58
-0+ +if (data->voice_avail) { +ofono_modem_set_integer (modem,"GemaltoVtsQuotes" , + data->vts_with_quotes); +ofono_voicecall_create (modem,0 ,"gemaltomodem" , data->app); + +ofono_call_forwarding_create (modem,0 ,"atmodem" , data->app); +ofono_call_settings_create (modem,0 ,"atmodem" , data->app); +ofono_call_meter_create (modem,0 ,"atmodem" , data->app); +ofono_call_barring_create (modem,0 ,"atmodem" , data->app); + } + +/* modules require to be online to accept at+cnmi */ +ofono_sms_create (modem,OFONO_VENDOR_GEMALTO ,"atmodem" , data->app); + mw =ofono_message_waiting_create (modem); + +if (mw) +ofono_message_waiting_register (mw); + +ofono_netreg_create (modem,OFONO_VENDOR_GEMALTO ,"atmodem" , data->app); +} + +static int gemalto_post_online_delayed (void *modem) +{ +struct gemalto_data *data =ofono_modem_get_data (modem); + +/* +* check module capabilities once online and SIM really ready. +* +* Note: the g_at_chat_send calls only insert the commands in a list: +* they are not executed synchronously +* +* Note: ofono executes each AT commands and the related callback before +* proceeding with the next. So continuing on the last AT command is all +* it takes +*/ + +gemalto_exec_stored_cmd (modem,"post_online" ); + +if (data->ecmncm ==STATE_PROBE ) { + data->gprs_opt =USE_PPP ;/* fallback */ +g_at_chat_send (data->app,"AT+CGDCONT=17" ,NULL , + cgdcont17_probe, modem,NULL ); +g_at_chat_send (data->app,"AT^SWWAN?" ,NULL , swwan_probe, modem, +NULL ); + } + +g_at_chat_send (data->app,"AT^SCFG=\"GPRS/AutoAttach\"" ,NULL , + autoattach_probe_and_continue, modem,NULL ); + +return FALSE;/* to kill the timer */ +} + +static void gemalto_post_online (struct ofono_modem *modem) +{ +/* +* in this version of ofono we must wait for SIM 'really-ready' +* can be avoided when capturing the right URCs // waiting for the URC (like '+PBREADY') is anyway not risk-free // because if ofono is started when the module is already initialized // the URC is missing, and on some models cannot be queried. // This needs further thinking.
plugins/gemalto.c new file +50
-0+*/ +g_timeout_add_seconds (5 , gemalto_post_online_delayed, modem); +} + +#ifdef HAVE_ELL +static void mbim_radio_off_for_disable (struct mbim_message *message,void *user) +{ +struct ofono_modem *modem = user; +struct gemalto_data *md =ofono_modem_get_data (modem); + +DBG ("%p" , modem); + +mbim_device_shutdown (md->device); +} +#endif + +static int gemalto_disable_serial (struct ofono_modem *modem) +{ +struct gemalto_data *data =ofono_modem_get_data (modem); + +if (data->app !=NULL ) { +if (data->model ==0x47 ) { +g_at_chat_send (data->app, +"AT^SCFG=\"MEopMode/Airplane\",\"on\"" , +NULL ,NULL ,NULL ,NULL ); + }else { +gemalto_set_cfun (data->app,41 , modem); +return -EINPROGRESS ; + } +g_at_chat_cancel_all (data->app); + } + +ofono_modem_set_powered (modem, FALSE); +return 0 ; +} + +static int gemalto_disable (struct ofono_modem *modem) +{ +struct gemalto_data *data =ofono_modem_get_data (modem); +#ifdef HAVE_ELL +struct mbim_message *message; +#endif + +DBG ("%p" , modem); + +if (data->conn ==GEMALTO_CONNECTION_SERIAL ) +return gemalto_disable_serial (modem); + +#ifdef HAVE_ELL +if (data->mbim ==STATE_PRESENT ) {
plugins/gemalto.c
Quoted 10 lines
plugins/gemalto.c new file +10
-0+ message =mbim_message_new (mbim_uuid_basic_connect, +MBIM_CID_RADIO_STATE , +MBIM_COMMAND_TYPE_SET ); +mbim_message_set_arguments (message,"u" ,0 ); + +if (mbim_device_send (data->device,0 , message, + mbim_radio_off_for_disable, modem,NULL )==0 ) + mbim_device_closed(modem); + } +#endif
All these #ifdefs need to go away. The only place that should be using HAVE_ELL is main.c. All the other mbim stuff needs to be merged into the mbim driver.
Quoted 353 lines
plugins/gemalto.c new file +35
-0+ +if (data->app ==NULL ) +return 0 ; + +gemalto_exec_stored_cmd (modem,"disable" ); + +gemalto_set_cfun (data->app,41 , modem); + +return -EINPROGRESS ; +} + +static struct ofono_modem_driver gemalto_driver = { + .name ="gemalto" , + .probe = gemalto_probe, + .remove = gemalto_remove, + .enable = gemalto_enable, + .disable = gemalto_disable, + .set_online = gemalto_set_online, + .pre_sim = gemalto_pre_sim, + .post_sim = gemalto_post_sim, + .post_online = gemalto_post_online, +}; + +static int gemalto_init (void ) +{ +return ofono_modem_driver_register (&gemalto_driver); +} + +static void gemalto_exit (void ) +{ +ofono_modem_driver_unregister (&gemalto_driver); +} + +OFONO_PLUGIN_DEFINE (gemalto,"Gemalto modem plugin" ,VERSION , +OFONO_PLUGIN_PRIORITY_DEFAULT , gemalto_init, gemalto_exit)plugins/udevng.c +112
-52@@ -3,6 +3,7 @@* oFono - Open Source Telephony * * Copyright (C) 2008-2011 Intel Corporation. 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 @@ -979,20 +980,6 @@ static gboolean setup_serial_modem(struct modem_info* modem)return TRUE; } -static gboolean setup_tc65 (struct modem_info * modem) -{ -ofono_modem_set_driver (modem->modem,"cinterion" ); - -return setup_serial_modem (modem); -} - -static gboolean setup_ehs6 (struct modem_info * modem) -{ -ofono_modem_set_driver (modem->modem,"cinterion" ); - -return setup_serial_modem (modem); -} -static gboolean setup_ifx (struct modem_info * modem) {struct serial_device_info * info;@@ -1121,60 +1108,133 @@ static gboolean setup_ublox(struct modem_info *modem)static gboolean setup_gemalto (struct modem_info * modem) { -const char *app =NULL , *gps =NULL , *mdm =NULL , - *net =NULL , *qmi =NULL ; +const char *mdm =NULL , *app =NULL , *gnss =NULL , *rsa =NULL , + *diag =NULL , + *ctl =NULL , *net =NULL , *net2 =NULL ; +char descriptors[PATH_MAX ]; +guint m; +const char *prevNet =NULL ;GSList *list; -DBG ("%s" , modem->syspath); +DBG ("type:%d, syspath:%s [%s:%s]" , modem->type, modem->syspath, + modem->vendor, modem->model); + +if (modem->type==MODEM_TYPE_SERIAL ) { +struct serial_device_info * info = modem->serial; +ofono_modem_set_string (modem->modem,"ConnType" ,"Serial" ); +ofono_modem_set_string (modem->modem,"ATport" , info->devnode); +ofono_modem_set_string (modem->modem,"Vendor" ,"gemalto" ); +ofono_modem_set_string (modem->modem,"Model" ,"0000" ); +return TRUE; + + } + + m =strtoul (modem->model,NULL ,16 ); +/* map devices */ for (list = modem->devices; list; list = list->next) {struct device_info *info = list->data; -DBG ("%s %s %s %s %s" , info->devnode, info->interface, - info->number, info->label, info->subsystem); - -/* PHS8-P */ -if (g_strcmp0 (info->interface,"255/255/255" ) ==0 ) { -if (g_strcmp0 (info->number,"01" ) ==0 ) - gps = info->devnode; -else if (g_strcmp0 (info->number,"02" ) ==0 ) - app = info->devnode; -else if (g_strcmp0 (info->number,"03" ) ==0 ) - mdm = info->devnode; -else if (g_strcmp0 (info->subsystem,"net" ) ==0 ) - net = info->devnode; -else if (g_strcmp0 (info->subsystem,"usbmisc" ) ==0 ) - qmi = info->devnode; +DBG ("node:%s, sub:%s, interface:%s, number:%s, sysattr:%s" , + info->devnode, info->subsystem, info->interface, + info->number, info->sysattr); + +if (g_str_equal (info->subsystem,"tty" )) { +/* option devices (ttyUSBx) */ +/* note: option devices order in VMs under windows may vary */ +if (g_str_equal (info->interface,"255/255/255" ) || +g_str_equal (info->interface,"255/66/1" ) || +g_str_equal (info->interface,"255/0/0" )) { +if (g_str_equal (info->number,"00" )) { + app = info->devnode; + }else if (g_str_equal (info->number,"01" )) { + gnss = rsa = info->devnode; + }else if (g_str_equal (info->number,"02" )) { + diag = info->devnode; + }else if (g_str_equal (info->number,"03" )) { + mdm = info->devnode; + } +/* cdc-acm devices (ttyACMx) */ + }else if (g_str_equal (info->interface,"2/2/0" ) || +g_str_equal (info->interface,"2/2/1" )) { +if (g_str_equal (info->number,"00" )) { + mdm = info->devnode; + }else if (g_str_equal (info->number,"02" )) { + app = info->devnode; + }else if (g_str_equal (info->number,"04" )) { + gnss = info->devnode; + }else if (g_str_equal (info->number,"06" )) { + rsa = info->devnode; + }else if (g_str_equal (info->number,"08" )) { + diag = info->devnode; + } + } } -/* Cinterion ALS3, PLS8-E, PLS8-X */ -if (g_strcmp0 (info->interface,"2/2/1" ) ==0 ) { -if (g_strcmp0 (info->number,"00" ) ==0 ) - mdm = info->devnode; -else if (g_strcmp0 (info->number,"02" ) ==0 ) - app = info->devnode; -else if (g_strcmp0 (info->number,"04" ) ==0 ) - gps = info->devnode; - } -if (g_strcmp0 (info->interface,"2/6/0" ) ==0 ) { -if (g_strcmp0 (info->subsystem,"net" ) ==0 ) - net = info->devnode; +if (g_str_equal (info->subsystem,"usbmisc" )) +/* control device for qmi/mbim: /dev/cdc-wdmX */ + ctl = info->devnode; + +/* network interfaces */ +if (g_str_equal (info->subsystem,"net" )) { +/* cdc-mbim */ +if (g_str_equal (info->interface,"2/14/0" ) || +/* qmi-wwan */ +g_str_equal (info->interface,"255/255/255" )) { + net = info->devnode;/* wwanX */ +/* cdc-ecm */ + }else if (g_str_equal (info->interface,"2/6/0" ) || +/* cdc-ncm */ +g_str_equal (info->interface,"2/13/0" )) { +/* +* there could be 2 interfaces in this case, +* and need to have them sorted to be sure to +* report the right interface to activate +*/ +if (!prevNet || +g_strcmp0 (info->number,prevNet)<0 ) { + prevNet = info->number; + net2 = net;/* can be NULL, ok */ + net = info->devnode;/* usbX */ + }else {// invert + net2 = info->devnode;/* usbX */ // not sure this logic of sorting the network interfaces is needed // but I don't see another way of being sure of having them in order + } + } } } -DBG ("application=%s gps=%s modem=%s network=%s qmi=%s" , - app, gps, mdm, net, qmi); +sprintf (descriptors,"%s/descriptors" , modem->syspath); +ofono_modem_set_string (modem->modem,"ConnType" ,"Usb" ); +ofono_modem_set_string (modem->modem,"DescriptorFile" , descriptors); +ofono_modem_set_string (modem->modem,"Vendor" , modem->vendor); +ofono_modem_set_string (modem->modem,"Model" , modem->model); -if (app ==NULL || mdm ==NULL ) -return FALSE; +/* +* special cases. +* TODO: selected by external parameters: +* GEMALTO_CONFIG_MBIM_ONLY -> _set_driver(modem->modem, "mbim"); +* GEMALTO_CONFIG_QMI_ONLY -> _set_driver(modem->modem, "gobi"); +* ... +*/ +if (m==0x64 ) { +ofono_modem_set_string (modem->modem,"Device" , ctl); +ofono_modem_set_string (modem->modem,"NetworkInterface" , net); +ofono_modem_set_driver (modem->modem,"mbim" ); +return TRUE; + } -ofono_modem_set_string (modem->modem,"Application" , app); -ofono_modem_set_string (modem->modem,"GPS" , gps);ofono_modem_set_string (modem->modem,"Modem" , mdm); -ofono_modem_set_string (modem->modem,"Device" , qmi); -ofono_modem_set_string (modem->modem,"Model" , modem->model); +ofono_modem_set_string (modem->modem,"Application" , app); +ofono_modem_set_string (modem->modem,"GNSS" , gnss); +ofono_modem_set_string (modem->modem,"RSA" , rsa); +ofono_modem_set_string (modem->modem,"Diag" , diag); + +ofono_modem_set_string (modem->modem,"NetworkControl" , ctl);ofono_modem_set_string (modem->modem,"NetworkInterface" , net); +ofono_modem_set_string (modem->modem,"NetworkInterface2" , net2); + +ofono_modem_set_powered_timeout_hint (modem->modem,60 );return TRUE; }plugins/udevng.c +14
-12@@ -1290,7 +1350,7 @@ static struct { { "quectel", setup_quectel }, { "quectelqmi", setup_quectelqmi}, { "ublox", setup_ublox }, - { "gemalto", setup_gemalto }, + { "gemalto", setup_gemalto, "device/interface" }, { "xmm7xxx", setup_xmm7xxx }, { "mbim", setup_mbim }, /* Following are non-USB modems */@@ -1298,12 +1358,12 @@ static struct { { "u8500", setup_isi_serial }, { "n900", setup_isi_serial }, { "calypso", setup_serial_modem }, - { "cinterion", setup_serial_modem }, + { "cinterion", setup_gemalto }, { "nokiacdma", setup_serial_modem }, { "sim900", setup_serial_modem }, { "wavecom", setup_wavecom }, - { "tc65", setup_tc65 }, - { "ehs6", setup_ehs6 }, + { "tc65", setup_gemalto }, + { "ehs6", setup_gemalto }, { } };@@ -1464,11 +1524,10 @@ static void add_serial_device(struct udev_device *dev)struct udev_device * mdev;const char * driver; +/* discard if the device doesn't have the property OFONO_DRIVER */ mdev =get_serial_modem_device (dev); -if (!mdev) { -DBG ("Device is missing required OFONO_DRIVER property" ); +if (!mdev)// separate commit to remove this polluting message. On some systems // it can emit the line 256 times. // By default a typical linux distro presents 64 ttySx ports, so even if a // few are used, there are too many unneeded notifications. return ; - } driver =udev_device_get_property_value (mdev,"OFONO_DRIVER" );@@ -1674,11 +1733,13 @@ static struct { { "ublox", "cdc_acm", "1546", "1102" }, { "ublox", "rndis_host", "1546", "1146" }, { "ublox", "cdc_acm", "1546", "1146" }, - { "gemalto", "option", "1e2d", "0053" }, - { "gemalto", "cdc_wdm", "1e2d", "0053" }, - { "gemalto", "qmi_wwan", "1e2d", "0053" }, - { "gemalto", "cdc_acm", "1e2d", "0061" }, - { "gemalto", "cdc_ether", "1e2d", "0061" }, + { "gemalto", "cdc_acm", "1e2d" }, + { "gemalto", "option", "1e2d" }, + { "gemalto", "cdc_wdm", "1e2d" }, + { "gemalto", "qmi_wwan", "1e2d" }, + { "gemalto", "cdc_ether", "1e2d", }, + { "gemalto", "cdc_mbim", "1e2d", }, + { "gemalto", "cdc_ncm", "1e2d", }, { "telit", "cdc_ncm", "1bc7", "0036" }, { "telit", "cdc_acm", "1bc7", "0036" }, { "xmm7xxx", "cdc_acm", "8087" },@@ -1708,6 +1769,7 @@ static void check_usb_device(struct udev_device *device) vendor =udev_device_get_property_value (usb_device,"ID_VENDOR_ID" ); model =udev_device_get_property_value (usb_device,"ID_MODEL_ID" ); +/* for serial2usb-like devices, enum as usb/usb_device */ // separate commit or discard Lines of this file are not shown. View the raw message.
The rest of this diff is not shown. View the raw message.
struct
udev_device*usb_interface=plugins/udevng.c +17
-1Lines of this file are not shown. View the raw message.
The rest of this diff is not shown. View the raw message.
Quoted 2 lines
check_usb_device(device);
Regards,
-Denis
Part of the diff is not shown here. View the raw message.
Hi Denis,
Thank you for taking the time to look into this.
Quoted 10 lines · Denis Kenzior
Hi Giacinto,
Quoted 5 lines · Giacinto Cifelli
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.
Quoted 4 lines
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.
In your existing code, there are a few models from each vendor, partially supported, with hardcoded choices for several options.
Quoted 5 lines
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.
Several modems are either qmi+at or mbim+at, while others are simply at (for example with ppp, ecm, ncm networking).
The qmi and mbim part is limited to initialization and gprs-context, the rest is common.
Shall I duplicate everything for this?
The #ifdefs are a choice of the project for the ELL, otherwise it would be simply, well-integrated ifs (like for qmi).
I understand that the use of ELL will be extended in the future, why not go all the way, remove the #ifdefs, and declare a full dependency?
Quoted 4 lines
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.
Yes
Quoted 9 lines
Review
Quoted 6 lines
(quoted diff) +6
-0+ + /* mbim data */ + uint16_t max_segment; + uint8_t max_outstanding; + uint8_t max_sessions; +The mbim plugin is not sufficient for this?
there are no mbim-only gemalto modems, only mbim+at. And so, it is not suffient, I had to duplicate all this.
Quoted 9 lines
Quoted 9 lines
(quoted diff) +9
-0+ +static const char *gemalto_get_string(struct ofono_modem *modem, const char *k) +{ + const char *v; + + if (!modem || !k || !*k) + return NULL; + + v = ofono_modem_get_string(modem, k);
Quoted 8 lines
plugins/udevng.c static gboolean setup_ublox(struct modem_info *modem) · line 1131
Quoted 6 lines
plugins/udevng.c +6
-0+ +if (!v || !*v) +return NULL ; + +return v; +}Uhh, why?
to have a simple compare for !var in the code. Because it is not possible to remove a variable once set, only to set it to an empty string.
Maybe I should add instead an ofono_modem_unset_string ?
Quoted 41 lines
Quoted 41 lines
(quoted diff) +41
-0+static void gemalto_exec_stored_cmd(struct ofono_modem *modem, + const char *filename) +{ + struct gemalto_data *data = ofono_modem_get_data(modem); + const char *vid = gemalto_get_string(modem, "Vendor"); + const char *pid = 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 = storage_open(NULL, store); + + if (!f) + return; + + for (index = 0; ; index++) { + sprintf(key, "command_%d", index); + command = 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 = 0; ; index++) { + sprintf(key, "command_%d", index); + command = g_key_file_get_string(f, "WithPrompt", key, NULL); + sprintf(key, "prompt_%d", index); + prompt = g_key_file_get_string(f, "WithPrompt", key, NULL); + sprintf(key, "argument_%d", index); + argument = g_key_file_get_string(f, "WithPrompt", key, NULL); + + if (!command || !prompt || !argument) + break; + + DBG("executing stored command with prompt: %s", command);
Quoted 8 lines
plugins/udevng.c static gboolean setup_ublox(struct modem_info *modem) · line 1157
Quoted 6 lines
plugins/udevng.c +6
-0+executeWithPrompt (data->app, command, prompt, argument, +NULL ,NULL ,NULL ); + } + +storage_close (NULL , store, f, FALSE); +}What is this doing actually?
Reading stored commands and executing them.
It is intended to configure the modem for a specific application. There are hooks for each state of the modem state machine.
There isn't much interest in passing these commands through an interface, because each application has its own configuration and sends a different set of commands.
And it is modem-specific, so it is stored by USB VID-PID.
Quoted 24 lines
Quoted 24 lines
(quoted diff) +24
-0+/******************************************************************************* + * 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 = ofono_modem_get_data(modem); + time_t t = time(NULL); + struct tm tm; + gchar cclk_cmd[32]; + + /* Set date and time */ + tm = *localtime(&t); + strftime(cclk_cmd, 32, "AT+CCLK=\"%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[] = { + { GDBUS_ASYNC_METHOD("SetModemDatetime",
Quoted 8 lines
Review
Quoted 4 lines
(quoted diff) +4
-0+ 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.
Some systems care: this comes in fact from an application (and will be committed mentioning a co-author).
And it helps for timestamps in case of stack logs.
Quoted 2 lines
Secondly, why not do this as an actual core atom?
Not sure of the general interest, but yes can do.
Quoted 8 lines
Review
Quoted 6 lines
(quoted diff) +5
-0+static const GDBusSignalTable gsmTime_signals[] = { + { GDBUS_SIGNAL("NitzUpdated", + GDBUS_ARGS({ "time", "a{sv}" }) )}, + {} +};You don't like
ofono_netreg_time_notify?
I will look into this.
Quoted 25 lines
Quoted 25 lines
(quoted diff) +24
-0+ +static void gemalto_time_enable(struct ofono_modem *modem) +{ + DBusConnection *conn = ofono_dbus_get_connection(); + const char *path = 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 + ******************************************************************************/
Quoted 8 lines
plugins/gemalto.c line 26
Quoted 3 lines
plugins/gemalto.c new file +3
-0+ +#define COMMAND_PASSTHROUGH_INTERFACE OFONO_SERVICE ".gemalto.CommandPassthrough" +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.
I may have misunderstood your comments the one time we had the conversation. I don't understand your blind refusal: cluttering the interface with every single command that an application may need for some special events doesn't seem that brilliant.
Quoted 128 lines
Quoted 128 lines
(quoted diff) +128
-0+/******************************************************************************* + * 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 = user_data; + struct gemalto_data *data = ofono_modem_get_data(modem); + const char *port = ofono_modem_get_string(modem, "GNSS"); + GAtResultIter iter; + DBusMessage *reply; + DBusMessageIter dbusiter; + DBusMessageIter dict; + + if (data->gnss_msg == NULL) + return; + + if (!ok) + goto error; + + reply = 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 = ""; + const char *val = ""; + + 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 = user_data; + struct gemalto_data *data = ofono_modem_get_data(modem); + + if (data->gnss_msg != 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 = dbus_message_ref(msg); + + return NULL; +} + +static void gnss_set_properties_cb(gboolean ok, GAtResult *result, + gpointer user_data) +{ + struct ofono_modem *modem = user_data; + struct gemalto_data *data = ofono_modem_get_data(modem); + DBusMessage *reply; + + if (data->gnss_msg == NULL) + return; + + if (!ok) { + __ofono_dbus_pending_reply(&data->gnss_msg, + __ofono_error_failed(data->gnss_msg)); + return; + } + + reply = 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 = user_data; + struct gemalto_data *data = ofono_modem_get_data(modem); + DBusMessageIter iter, var; + const char *name; + char *value; + char buf[256]; + + if (data->gnss_msg != NULL) + return __ofono_error_busy(msg); + + if (dbus_message_iter_init(msg, &iter) == FALSE) + return __ofono_error_invalid_args(msg); + + if (dbus_message_iter_get_arg_type(&iter) != 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) != DBUS_TYPE_VARIANT) + return __ofono_error_invalid_args(msg); + + dbus_message_iter_recurse(&iter, &var); + + if (dbus_message_iter_get_arg_type(&var) != + DBUS_TYPE_STRING)
Quoted 8 lines
plugins/gemalto.c line 58
Quoted 6 lines
plugins/gemalto.c new file +6
-0+return __ofono_error_invalid_args (msg); + +dbus_message_iter_get_basic (&var, &value); + +snprintf (buf,sizeof (buf),"AT^SGPSC=\"%s\",\"%s\"" , name, value); +So the modem is doing the property validation? Yeah, not happening ;)
The name and number of properties vary by the models.
There isn't much to validate in a general way. Null perhaps, or an arbitrary maximum length.
This interface is supposed to use GetProperties and then can change some of them.
That the set property is in the list returned by GetProperties can be verified.
Quoted 2 lines
Why don't you just configure the device into NMEA mode and use
location_reportingatom ?
that atom is not compatible with gpsd and takes arbitrary choices on how to handle the receiver, it is incomplete and adds constrains on the dbus version.
For example, for the gemalto atom, it starts the engine in assisted-gps mode, without loading the corresponding data files.
Quoted 20 lines
Quoted 18 lines
+/*
+ * powersave for older modules:
+ * command_0=AT+CFUN=7
+ * return:
+ * command_0=AT+CFUN=1
+ *
+ * powersave example for modules with GNSS (could also only stop the output):+ * command_0=AT+CREG=0 + * command_1=AT+CGREG=0 + * command_2=AT+CEREG=0 + * command_3=AT^SGPSC="Engine","0" + * command_4=AT^SGPSC="Power/Antenna","off" + * return: + * command_0=AT+CREG=2 + * command_1=AT+CGREG=2 + * command_2=AT+CEREG=2 + * command_4=AT^SGPSC="Power/Antenna","on" + * command_3=AT^SGPSC="Engine","1"Quoted 1 line
+ */
Quoted 1 line
How is 'powersave' useful?
If you go on holiday for 1 week, you may like that your car still starts when you come back.
The modem in this kind of applications is always on, for anti-theft of simply so that you can blink the lights when coming out of a supermarket.
But when parked, the modem remains listening for network events, and only ping the application (and ofono) in case of sms, for example (up to the specific application how to come back from sleep). Above, a common setting: no reg events (if we lose coverage we don't react instead of re-powering) and stop the gnss engine.
Quoted 5 lines
plugins/gemalto.c
Quoted 3 lines
plugins/gemalto.c new file +3
-0+ +#ifdef HAVE_ELL +static int mbim_parse_descriptors (struct gemalto_data *md,const char *file)Why is MBIM driver not enough?
for this yes, pure duplication. It would be good to have this code in an atom or in common.c or similar.
Otherwise, all Gemalto modems are MBIM+AT, so the pure mbim is not enough.
Quoted 22 lines
Quoted 22 lines
(quoted diff) +22
-0+ +static void gemalto_remove(struct ofono_modem *modem) +{ + struct gemalto_data *data; + DBusConnection *conn = ofono_dbus_get_connection(); + const char *path; + + if (!modem) + return; + + data = ofono_modem_get_data(modem); + path = ofono_modem_get_path(modem); + + if (!data) + return; + +#ifdef HAVE_ELL + if (data->mbim == STATE_PRESENT) { + mbim_device_shutdown(data->device); + } +#endif +
Quoted 8 lines
plugins/udevng.c static gboolean setup_ublox(struct modem_info *modem) · line 1132
Quoted 4 lines
plugins/udevng.c +4
-0+if (data->qmi ==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.
again, the modems are all mixed-mode with AT.
To support them properly, I need both views.
But there is no mbim+qmi combination.
Is there a way to support mbim+at today?
What do you suggest?
Regards,
Giacinto
Hi Giacinto,
Quoted 8 lines
Quoted 4 lines
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.
In your existing code, there are a few models from each vendor, partially supported, with hardcoded choices for several options.
We can only do so much without docs. Remember, much of these drivers were community contributed based on reverse engineering efforts or if lucky, leaked docs which were frequently incomplete. So let me flip this around and ask where were you all this time? ;)
Quoted 8 lines
Quoted 5 lines
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.
Several modems are either qmi+at or mbim+at, while others are simply at (for example with ppp, ecm, ncm networking).
So you might need to expand on this some more. What is QMI+AT or MBIM+AT actually doing? Is there a single AT port? Multiple? What is the AT port being used for, just vendor specific APIs or something more?
Quoted 4 lines
The qmi and mbim part is limited to initialization and gprs-context, the rest is common.
Shall I duplicate everything for this?
I don't really have a recommendation yet as I don't have enough info. But, in general, we prefer duplication over convoluted code. This is because you can turn features off via configure / plugin blacklisting. If you have one giant unified driver, then your hands are tied.
Quoted 5 lines
The #ifdefs are a choice of the project for the ELL, otherwise it would be simply, well-integrated ifs (like for qmi).
I understand that the use of ELL will be extended in the future, why not go all the way, remove the #ifdefs, and declare a full dependency?
We can do that. But that won't really help you in the grand scheme of things. My point still stands though, we cannot have a bazillion if conditions in the driver with dozens/hundreds of different forks. That just does not lead to maintainable code. Lets work smarter, not harder here.
Quoted 10 lines
Reading stored commands and executing them.
It is intended to configure the modem for a specific application. There are hooks for each state of the modem state machine.There isn't much interest in passing these commands through an interface, because each application has its own configuration and sends a different set of commands.
And it is modem-specific, so it is stored by USB VID-PID.
Since it sounds like this is a very esoteric use case, you might want to schedule this last. Right now it just distracts from the core discussion.
Quoted 9 lines
Quoted 4 lines
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.
Some systems care: this comes in fact from an application (and will be committed mentioning a co-author).
And it helps for timestamps in case of stack logs.
So who do you think is going to be responsible for setting the time appropriately in the modem? Remember, oFono is a user-centric API. We don't expose stuff that is not somehow visible to the user.
Have you considered just having your modem driver auto-magically setting the time into the modem and forgetting all this API business?
Quoted 10 lines
Quoted 5 lines
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.
I may have misunderstood your comments the one time we had the conversation. I don't understand your blind refusal: cluttering the interface with every single command that an application may need for some special events doesn't seem that brilliant.
As I said, we're a user-centric and use-case centric API. If a typical user doesn't see this or doesn't know what to do with something, then don't expose it. If you can't explain a use case for something, don't expose it. No typical user is going to send arbitrary AT commands and 'application may need special events' is also not a valid use case.
Also, there are security and interference aspects to consider. One can send some AT command that interferes with the functioning of an atom driver for example and then your entire system breaks. Trust me, it is just not a good idea. If you want to shoot yourself in the foot, be my guest. But it isn't going upstream ;)
Quoted 9 lines
Quoted 1 line
So the modem is doing the property validation? Yeah, not happening ;)
The name and number of properties vary by the models.
There isn't much to validate in a general way. Null perhaps, or an arbitrary maximum length.
This interface is supposed to use GetProperties and then can change some of them.
That the set property is in the list returned by GetProperties can be verified.
Yeah, again. Not a valid usecase. We're not here to half-ass expose every Gemalto feature via D-Bus. This is not what this project is about.
Quoted 4 lines
Quoted 2 lines
Why don't you just configure the device into NMEA mode and use
location_reportingatom ?that atom is not compatible with gpsd and takes arbitrary choices on
So fix gpsd. We're not taking upstream a vendor specific NMEA API when location-reporting already exists.
Quoted 1 line
how to handle the receiver, it is incomplete and adds constrains on
So fix the driver and set appropriately sane defaults?
Quoted 1 line
the dbus version.
What now? The file-descriptor passing has been in DBus for like 8 years now. I do not buy this at all.
Quoted 3 lines
For example, for the gemalto atom, it starts the engine in assisted-gps mode, without loading the corresponding data files.
So again, fix the driver?
Quoted 13 lines
Quoted 1 line
How is 'powersave' useful?
If you go on holiday for 1 week, you may like that your car still starts when you come back.
The modem in this kind of applications is always on, for anti-theft of simply so that you can blink the lights when coming out of a supermarket.
But when parked, the modem remains listening for network events, and only ping the application (and ofono) in case of sms, for example (up to the specific application how to come back from sleep). Above, a common setting: no reg events (if we lose coverage we don't react instead of re-powering) and stop the gnss engine.
Okay, lets deal with this later though.
Quoted 8 lines
Quoted 4 lines
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.
again, the modems are all mixed-mode with AT.
To support them properly, I need both views.
But there is no mbim+qmi combination.
Yes, but others do support MBIM + QMI.
Quoted 3 lines
Is there a way to support mbim+at today?
What do you suggest?
So I think we have two options we can pursue:
1. Instead of jumbling all the MBIM/QMI logic into the gemalto plugin, let the mbim/gobi driver take care of it. It should be 100% the same anyway. Have udevng set some attributes specific for Gemalto modems. Then add another plugin that registers a modem watch and gets notified of any modems added to the system. If the modem has a set of magic attributes (e.g. ofono_modem_get_string, etc) then you know it is a Gemalto modem and you can bolt on additional capabilities to it. So for example if all you're doing is running vendor AT command extensions over the AT port, you can simply have the plugin open the AT port and add any additional atoms / vendor APIs to the modem. Look at plugins/smart-messaging.c or hfp_ag_bluez5.c for some ideas.
2. Split the single monolithic gemalto driver into 3+ drivers. One for MBIM+AT, one for QMI+AT, one for Serial/USB AT. Or alternatively extend plugins/gobi or plugins/mbim with Gemalto specific model quirks.
Regards,
-Denis
Hi Denis,
Quoted 8 lines · Denis Kenzior
Hi Giacinto,
Quoted 5 lines
Quoted 4 lines
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.
Quoted 8 lines
Quoted 3 lines
In your existing code, there are a few models from each vendor, partially supported, with hardcoded choices for several options.
We can only do so much without docs. Remember, much of these drivers were community contributed based on reverse engineering efforts or if lucky, leaked docs which were frequently incomplete. So let me flip this around and ask where were you all this time? ;)
The point here is that it is the first time a large scale operation is attempted.
I find it normal to have all kind of options for all the models. It won't be easy to maintain, I agree.
Below I see that your main concerns are about mixed-mode drivers, which make only a part of the if's, all the branching is at the moment limited to the initialization.
Quoted 7 lines
Quoted 7 lines
Quoted 6 lines
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.
Quoted 6 lines
Quoted 2 lines
Several modems are either qmi+at or mbim+at, while others are simply at (for example with ppp, ecm, ncm networking).
So you might need to expand on this some more. What is QMI+AT or MBIM+AT actually doing? Is there a single AT port? Multiple? What is the AT port being used for, just vendor specific APIs or something more?
MBIM and QMI are actually used only for the gprs-context atom. The modems can work with the mbim or gobi plugin if they support the respective protocols (I have tested them with changes in udevng), but then we only get the bare minimum: no configuration options, hardware monitor, low-power mode, gnss and its options, and the rest. So, given that we need the AT interface anyway, everything is done through it. It is also easier to debug than the big binary blog (admittedly, wireshark helps a lot for mbim, but not sure for qmi).
Quoted 10 lines
Quoted 5 lines
The qmi and mbim part is limited to initialization and gprs-context, the rest is common.
Shall I duplicate everything for this?I don't really have a recommendation yet as I don't have enough info. But, in general, we prefer duplication over convoluted code. This is because you can turn features off via configure / plugin blacklisting. If you have one giant unified driver, then your hands are tied.
The plugin works this way: if mbim is detected, it is initialized, and then up to 2 AT interfaces are probed.
The same for qmi: if qmi is detected, it is initialized, and then up to 2 AT interfaces are probed.
If ELL is not used, mbim is skipped and the modem will use PPP from atmodem/gprs-context.
The same would be true for qmi, but there is no ifdef, so there is no need for fallback options.
Apart from its length, the code is quite simple. When the mbim or qmi initialization is finished, successfully or not, the plugin moves to the AT interfaces.
QMI support is quite straightforward: only a quick initialization, basically to make sure it is there. It doesn't need all the gobi plugin init sequence.
MBIM is more elaborate, it needs to exchange a few frames for a proper initialization, but then again, it finishes here. This initialization follows the mbim plugin up to a point, but does not need the entire mbim plugin enable sequence.
MBIM support works a bit differently depending on the chipset manufacturer. The drivers/mbim/gprs-context works out of the box for some models, while for others it is necessary to create the PDP context with AT+CGDCONT. But this is dealt with a dedicated atom, only the atom selection is visible in the plugin.
enough for the detour. Back to the plug-in initialization: the different models have zillion of differences. Whenever possible, I prefer to ask the modem, otherwise it is if/switch depending on the model.
Example of the first case: all Gemalto modems support the AT^SAIC command if there is voice support (I checked down to the MC55 that were like rel.98 or earlier).
All switches are independent from each other, working on a simple principle, like for voice: ascertain if the feature is present and with which options, then select the right atom and atom options. There are quite a few, but maintainable thanks to that. Unfortunately not all features can be probed at startup because some require to be online, so the checks are split in 3 parts: pre-defined with PID, tested during the enable phase (could be shifted to pre-sim to have a faster enable), and in post-online.
And this is the part to maintain. If I split the plugins in 3 independent ones, I need to duplicate and maintain this.
Quoted 7 lines
Quoted 6 lines
The #ifdefs are a choice of the project for the ELL, otherwise it would be simply, well-integrated ifs (like for qmi).
I understand that the use of ELL will be extended in the future, why not go all the way, remove the #ifdefs, and declare a full dependency?
Quoted 6 lines
We can do that. But that won't really help you in the grand scheme of things. My point still stands though, we cannot have a bazillion if conditions in the driver with dozens/hundreds of different forks. That just does not lead to maintainable code. Lets work smarter, not harder here.
as explained, the bazillion conditions will remain. maybe only mbim/qmi/plainAt will disappear. And all the vendor-specific atoms will have to be duplicated too.
Quoted 5 lines
Quoted 5 lines
Reading stored commands and executing them.
It is intended to configure the modem for a specific application. There are hooks for each state of the modem state machine.
Quoted 8 lines
Quoted 5 lines
There isn't much interest in passing these commands through an interface, because each application has its own configuration and sends a different set of commands.
And it is modem-specific, so it is stored by USB VID-PID.Since it sounds like this is a very esoteric use case, you might want to schedule this last. Right now it just distracts from the core discussion.
ok for having it last, but it is not so esoteric.
Take for example the audio settings (which most likely exist for all other vendors): you can select the type of interface (analog/digital/usb), the port to use in case there is more than one, suboptions like I2C or PCM, master/slave, and quite some other. They could be set in a quite large vendor-specific interface (I saw some tried it in an atom), but the configuration is fixed for the end application, and - as you explained - will raise more problems than it solves exposing these settings through dbus.
So the best is to run a couple of commands to set the interface properly for the product and that's it.
Quoted 11 lines
Quoted 10 lines
Quoted 5 lines
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.
Some systems care: this comes in fact from an application (and will be committed mentioning a co-author).
And it helps for timestamps in case of stack logs.
Quoted 7 lines
So who do you think is going to be responsible for setting the time appropriately in the modem? Remember, oFono is a user-centric API. We don't expose stuff that is not somehow visible to the user.
Have you considered just having your modem driver auto-magically setting the time into the modem and forgetting all this API business?
I'll look into this with the customer who proposed it, most likely reading this conversation already.
Quoted 17 lines
Quoted 10 lines
Quoted 5 lines
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.
I may have misunderstood your comments the one time we had the conversation. I don't understand your blind refusal: cluttering the interface with every single command that an application may need for some special events doesn't seem that brilliant.
As I said, we're a user-centric and use-case centric API. If a typical user doesn't see this or doesn't know what to do with something, then don't expose it. If you can't explain a use case for something, don't expose it. No typical user is going to send arbitrary AT commands and 'application may need special events' is also not a valid use case.
Quoted 5 lines
Also, there are security and interference aspects to consider. One can send some AT command that interferes with the functioning of an atom driver for example and then your entire system breaks. Trust me, it is just not a good idea. If you want to shoot yourself in the foot, be my guest. But it isn't going upstream ;)
it's ok, GPLv2 just requires me to publish the changes I have done to the code, not to fight to get them upstream.
I have posted this special case, it is public, so I won't discuss it further here.
If Gemalto users want it, we will give it to them.
Nor I will go into all the use cases of ofono today. There is some truth in what you say as the initial vision for the project, but apparently it is growing out of that.
Quoted 11 lines
Quoted 10 lines
Quoted 2 lines
So the modem is doing the property validation? Yeah, not happening ;)
The name and number of properties vary by the models.
There isn't much to validate in a general way. Null perhaps, or an arbitrary maximum length.
This interface is supposed to use GetProperties and then can change some of them.
That the set property is in the list returned by GetProperties can be verified.
Quoted 3 lines
Yeah, again. Not a valid usecase. We're not here to half-ass expose every Gemalto feature via D-Bus. This is not what this project is about.
ditto for this atom: I have fulfilled the GPLv2 requirements.
Most of the options would make sense in a generic atom (like: which satellites constellations to use for the fix (GPS, GLONASS, GALILEO, Beidou, QZSS, ...), which to output, how to output them, NMEA version to use, start/stop the receiver, start/stop the output, etc. But some others would still require a vendor-specific plug-in, like on which port to output the NMEA sentences, whether to exclude satellites below a given angle, etc.
Quoted 7 lines
Quoted 4 lines
Quoted 2 lines
Why don't you just configure the device into NMEA mode and use
location_reportingatom ?that atom is not compatible with gpsd and takes arbitrary choices on
So fix gpsd. We're not taking upstream a vendor specific NMEA API when location-reporting already exists.
Changing it would make the dbus interface incompatible. gpsd takes a device, not an fd, but the atom initialize the receiver and opens the port.
This kind of linked mechanisms make policies, and are quite impossible to break once done.
In my opinion, it is a bad design for linux, because the policies should be a step higher, but it wasn't detected in time and now it has to be like that.
You may have noticed that I didn't remove this atom from the plugin, because there are now users for it.
And by the way: passing the fd most likely means that each client is implementing its own NMEA parser, with risks of bugs, duplication of effort and lack of standardization. The atom encourages bad practices.
Quoted 29 lines
Quoted 2 lines
how to handle the receiver, it is incomplete and adds constrains on
So fix the driver and set appropriately sane defaults?
Quoted 1 line
the dbus version.
What now? The file-descriptor passing has been in DBus for like 8 years now. I do not buy this at all.
Quoted 3 lines
For example, for the gemalto atom, it starts the engine in assisted-gps mode, without loading the corresponding data files.
So again, fix the driver?
Quoted 13 lines
Quoted 1 line
How is 'powersave' useful?
If you go on holiday for 1 week, you may like that your car still starts when you come back.
The modem in this kind of applications is always on, for anti-theft of simply so that you can blink the lights when coming out of a supermarket.
But when parked, the modem remains listening for network events, and only ping the application (and ofono) in case of sms, for example (up to the specific application how to come back from sleep). Above, a common setting: no reg events (if we lose coverage we don't react instead of re-powering) and stop the gnss engine.
Quoted 1 line
Okay, lets deal with this later though.
ok
Quoted 11 lines
Quoted 9 lines
Quoted 5 lines
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.
again, the modems are all mixed-mode with AT.
To support them properly, I need both views.
But there is no mbim+qmi combination.Yes, but others do support MBIM + QMI.
this might be a different use case, like all communication is qmi and only carried over mbim frames. This would require a different plugin, but I am not in this case.
Quoted 19 lines
Quoted 4 lines
Is there a way to support mbim+at today?
What do you suggest?So I think we have two options we can pursue:
1. Instead of jumbling all the MBIM/QMI logic into the gemalto plugin, let the mbim/gobi driver take care of it. It should be 100% the same anyway. Have udevng set some attributes specific for Gemalto modems. Then add another plugin that registers a modem watch and gets notified of any modems added to the system. If the modem has a set of magic attributes (e.g.
ofono_modem_get_string, etc) then you know it is a Gemalto modem and you can bolt on additional capabilities to it. So for example if all you're doing is running vendor AT command extensions over the AT port, you can simply have the plugin open the AT port and add any additional atoms / vendor APIs to the modem. Look atplugins/smart-messaging.corhfp_ag_bluez5.cfor some ideas.
Quoted 3 lines
2. Split the single monolithic gemalto driver into 3+ drivers. One for MBIM+AT, one for QMI+AT, one for Serial/USB AT. Or alternatively extend plugins/gobi or plugins/mbim with Gemalto specific model quirks.
the second option is more interesting, but would make the behavior switches in the plugin duplicated.
What about extracting the qmi and mbim initialization in a separate file, so not to distract the reader?
All modems are, after all, AT modems, with special initialization for dedicated interfaces. It initializes mbim just like it does for gnss, only this code takes more space.
Quoted 3 lines
Regards,
-Denis
Regards,
Giacinto
Hi!
Quoted 8 lines
Quoted 5 lines
Also, there are security and interference aspects to consider. One can send some AT command that interferes with the functioning of an atom driver for example and then your entire system breaks. Trust me, it is just not a good idea. If you want to shoot yourself in the foot, be my guest. But it isn't going upstream ;)
it's ok, GPLv2 just requires me to publish the changes I have done to the code, not to fight to get them upstream.
Actually, GPLv2 does not you require to publish your changes.
It just (approximately) means that if you give binaries to someone, you give them sources, too...
Best regards,
Pavel
Signature · 2 lines
(english) http://www.livejournal.com/~pavelmachek (cesky, pictures) http://atrey.karlin.mff.cuni.cz/~pavel/picture/horses/blog.html
H Pavel,
Quoted 9 lines · Pavel Machek
Hi!
Quoted 6 lines
Quoted 5 lines
Also, there are security and interference aspects to consider. One can send some AT command that interferes with the functioning of an atom driver for example and then your entire system breaks. Trust me, it is just not a good idea. If you want to shoot yourself in the foot, be my guest. But it isn't going upstream ;)
Quoted 8 lines
Quoted 2 lines
it's ok, GPLv2 just requires me to publish the changes I have done to the code, not to fight to get them upstream.
Actually, GPLv2 does not you require to publish your changes.
It just (approximately) means that if you give binaries to someone, you give them sources, too...
even better!
Quoted 6 lines
Best regards,
Pavel--
(english) http://www.livejournal.com/~pavelmachek
(cesky, pictures) http://atrey.karlin.mff.cuni.cz/~pavel/picture/horses/blog.html
Regards,
Giacinto
Hi Giacinto,
Quoted 4 lines
Quoted 3 lines
So you might need to expand on this some more. What is QMI+AT or MBIM+AT actually doing? Is there a single AT port? Multiple? What is the AT port being used for, just vendor specific APIs or something more?
Quoted 9 lines
MBIM and QMI are actually used only for the gprs-context atom. The modems can work with the mbim or gobi plugin if they support the respective protocols (I have tested them with changes in udevng), but then we only get the bare minimum: no configuration options, hardware monitor, low-power mode, gnss and its options, and the rest. So, given that we need the AT interface anyway, everything is done through it. It is also easier to debug than the big binary blog (admittedly, wireshark helps a lot for mbim, but not sure for qmi).
You really need to decide what you want to do here. If your modem is MBIM, then MBIM driver needs to be used. I really don't want to get into situations where we build some Frankenstein driver with gprs-context being driven via MBIM and netreg being driven via AT commands.
I can understand if you have MBIM + a GPS/GNSS port and want to expose location-reporting on that. Ok, fine, I get that. I can also understand if you want to use the AT command port for some vendor specific extensions not available via MBIM. But I would need serious convincing of anything beyond this.
Quoted 5 lines
MBIM support works a bit differently depending on the chipset manufacturer. The
drivers/mbim/gprs-contextworks out of the box for some models, while for others it is necessary to create the PDP context with AT+CGDCONT. But this is dealt with a dedicated atom, only the atom selection is visible in the plugin.
So explain that one to me? You send all context details in MBIM_CID_CONNECT. So why would a CGDCONT be needed?
Quoted 8 lines
enough for the detour. Back to the plug-in initialization: the different models have zillion of differences. Whenever possible, I prefer to ask the modem, otherwise it is if/switch depending on the model.
Example of the first case: all Gemalto modems support the AT^SAIC command if there is voice support (I checked down to the MC55 that were like rel.98 or earlier).
You do realize that all this probing can be done in the individual atom driver, right? Even as far as the driver being able to call ofono_foo_remove if support is lacking.
Quoted 8 lines
All switches are independent from each other, working on a simple principle, like for voice: ascertain if the feature is present and with which options, then select the right atom and atom options. There are quite a few, but maintainable thanks to that. Unfortunately not all features can be probed at startup because some require to be online, so the checks are split in 3 parts: pre-defined with PID, tested during the enable phase (could be shifted to pre-sim to have a faster enable), and in post-online.
We've been through this, no AT commands should be executed in pre_sim, post_sim, post_online hooks...
Quoted 2 lines
And this is the part to maintain. If I split the plugins in 3 independent ones, I need to duplicate and maintain this.
No, you really don't. It might take a while for you to be convinced though.
Quoted 2 lines
mbim/qmi/plainAt will disappear. And all the vendor-specific atoms will have to be duplicated too.
Again, they really don't need to be duplicated...
Quoted 14 lines
Quoted 2 lines
Since it sounds like this is a very esoteric use case, you might want to schedule this last. Right now it just distracts from the core discussion.
ok for having it last, but it is not so esoteric.
Take for example the audio settings (which most likely exist for all other vendors): you can select the type of interface (analog/digital/usb), the port to use in case there is more than one, suboptions like I2C or PCM, master/slave, and quite some other. They could be set in a quite large vendor-specific interface (I saw some tried it in an atom), but the configuration is fixed for the end application, and - as you explained - will raise more problems than it solves exposing these settings through dbus.
So the best is to run a couple of commands to set the interface properly for the product and that's it.
I understand. But this is also why we have audio-settings atom. So all such details can be easily put in there and its relevant driver. If you want to have some simple configuration file that is read by your driver where the user can select between some common configurations that is fine as well.
One can also add additional drivers / plugins out-of-tree that would be dynamically loaded and do whatever they desire. But reading a file and blindly executing AT commands in a daemon that is running as root is just asking for trouble. I don't really care what you do in your products, but I'm not taking security holes upstream.
Quoted 8 lines
Quoted 4 lines
Have you considered just having your modem driver auto-magically setting the time into the modem and forgetting all this API business?
I'll look into this with the customer who proposed it, most likely reading this conversation already.
So as a hint, your customer might want to start engaging with us directly and skip the middle men. We do have some experience with integration given that between oFono and its sister projects we control the Bluetooth stack, WiFi stack, cellular stack, NFC stack and the overall network management ;)
Quoted 6 lines
it's ok, GPLv2 just requires me to publish the changes I have done to the code, not to fight to get them upstream.
I have posted this special case, it is public, so I won't discuss it further here.
If Gemalto users want it, we will give it to them.
I can only advise you what is acceptable to the oFono upstream project. What you do in the end is really up to you and your customers. But we do have certain standards with respect to security and code quality which we are not going to compromise on.
Quoted 5 lines
Most of the options would make sense in a generic atom (like: which satellites constellations to use for the fix (GPS, GLONASS, GALILEO, Beidou, QZSS, ...), which to output, how to output them, NMEA version to use, start/stop the receiver, start/stop the output, etc.
So maybe you want to start there and propose some improvements to the location reporting APIs?
Quoted 3 lines
But some others would still require a vendor-specific plug-in, like on which port to output the NMEA sentences, whether to exclude satellites below a given angle, etc.
It seems like this is a platform specific configuration and really doesn't belong being exported over D-Bus anyway.
Quoted 6 lines
Quoted 2 lines
So fix gpsd. We're not taking upstream a vendor specific NMEA API when location-reporting already exists.
Changing it would make the dbus interface incompatible. gpsd takes a device, not an fd, but the atom initialize the receiver and opens the port.
So? You can always come up with a simple pseudo-tty proxy? That's like 20 minutes of work. Or maybe 5 in Python.
Quoted 11 lines
This kind of linked mechanisms make policies, and are quite impossible to break once done.
In my opinion, it is a bad design for linux, because the policies should be a step higher, but it wasn't detected in time and now it has to be like that.
You may have noticed that I didn't remove this atom from the plugin, because there are now users for it.
And by the way: passing the fd most likely means that each client is implementing its own NMEA parser, with risks of bugs, duplication of effort and lack of standardization. The atom encourages bad practices.
Ok, thanks for the rant, but I will have to disagree completely here. LocationReporting was done the way it was for very specific reasons. We can't assume that the GPS port will be on one of the USB TTYs. It may be this way on your products, but it definitely is not this way on others. For example, it is exported through phonet or QMI on some devices.
Many modems also need magic commands / setup prior to being able to use the GPS receiver. So the LocationReporting API takes care of that. This also allows us to keep track of client lifetime. So that if/when the client dies we can automatically turn off the GPS unit and save some battery.
Honestly I'd really like to see GPS integrated properly into the kernel, with the ability for userspace daemons like oFono to provide drivers/devices to the kernel. Maybe Pavel has some additional ideas on how we could accomplish that.
Quoted 4 lines
Quoted 1 line
Yes, but others do support MBIM + QMI.
this might be a different use case, like all communication is qmi and only carried over mbim frames. This would require a different plugin,
Not all, but some, yes.
Quoted 1 line
but I am not in this case.
<snip>
Quoted 9 lines
Quoted 4 lines
2. Split the single monolithic gemalto driver into 3+ drivers. One for MBIM+AT, one for QMI+AT, one for Serial/USB AT. Or alternatively extend plugins/gobi or plugins/mbim with Gemalto specific model quirks.
the second option is more interesting, but would make the behavior switches in the plugin duplicated.
What about extracting the qmi and mbim initialization in a separate file, so not to distract the reader?
As mentioned earlier, we're not doing any Frankenstein drivers.
Quoted 3 lines
All modems are, after all, AT modems, with special initialization for dedicated interfaces. It initializes mbim just like it does for gnss, only this code takes more space.
Are you talking about your modems here? Because certainly not 'all' modems are AT modems. Our modems for example are binary protocol based with AT commands bolted on top. Also, many vendors do not provide AT ports for MBIM/QMI, etc based devices.
Regards,
-Denis
Hi Denis,
Quoted 12 lines
Quoted 11 lines
Quoted 3 lines
So you might need to expand on this some more. What is QMI+AT or MBIM+AT actually doing? Is there a single AT port? Multiple? What is the AT port being used for, just vendor specific APIs or something more?
MBIM and QMI are actually used only for the gprs-context atom. The modems can work with the mbim or gobi plugin if they support the respective protocols (I have tested them with changes in udevng), but then we only get the bare minimum: no configuration options, hardware monitor, low-power mode, gnss and its options, and the rest. So, given that we need the AT interface anyway, everything is done through it. It is also easier to debug than the big binary blog (admittedly, wireshark helps a lot for mbim, but not sure for qmi).
Quoted 3 lines
You really need to decide what you want to do here. If your modem is MBIM, then MBIM driver needs to be used. I really don't want to get into situations where we build some Frankenstein driver with gprs-context being driven via MBIM and netreg being driven via AT commands.
I can understand if you have MBIM + a GPS/GNSS port and want to expose location-reporting on that. Ok, fine, I get that. I can also understand if you want to use the AT command port for some vendor specific extensions not available via MBIM. But I would need serious convincing of anything beyond this.
I think that MBIM (and even QMI) have AT passthrough options. So by all means, the main transport suppose to be MBIM here. I always prefer if only one hardware port is opened and there is no need to open more.
Regards
Marcel
Hi Marcel,
Quoted 2 lines
I think that MBIM (and even QMI) have AT passthrough options. So by all means, the main transport suppose to be MBIM here. I always prefer if only one hardware port is opened and there is no need to open more.
I've seen QMI over MBIM some vendor extended service UIDs. Not sure about AT over MBIM. If someone knows more, feel free to educate me.
Isn't AT over QMI also possible? And in theory AT over QMI over MBIM, lol.
Regards,
-Denis
Hi,
Quoted 11 lines · Denis Kenzior
Hi Marcel,
Quoted 2 lines
I think that MBIM (and even QMI) have AT passthrough options. So by all means, the main transport suppose to be MBIM here. I always prefer if only one hardware port is opened and there is no need to open more.
I've seen QMI over MBIM some vendor extended service UIDs. Not sure about AT over MBIM. If someone knows more, feel free to educate me.
Isn't AT over QMI also possible? And in theory AT over QMI over MBIM, lol.
AT over MBIM is possible, but we decided not to implement it.
The big advantage of MBIM is a ready-made driver on Windows and on MM/ofono. Adding AT over it would mean reworking the driver, so no more advantages.
Over QMI I don't know.
QMI over MBIM is also possible, but with the same disadvantage as AT over MBIM.
Quoted 2 lines
Regards,
-Denis
Hi Denis,
Quoted 204 lines · Denis Kenzior
Hi Giacinto,
Quoted 4 lines
Quoted 3 lines
So you might need to expand on this some more. What is QMI+AT or MBIM+AT actually doing? Is there a single AT port? Multiple? What is the AT port being used for, just vendor specific APIs or something more?
Quoted 9 lines
MBIM and QMI are actually used only for the gprs-context atom. The modems can work with the mbim or gobi plugin if they support the respective protocols (I have tested them with changes in udevng), but then we only get the bare minimum: no configuration options, hardware monitor, low-power mode, gnss and its options, and the rest. So, given that we need the AT interface anyway, everything is done through it. It is also easier to debug than the big binary blog (admittedly, wireshark helps a lot for mbim, but not sure for qmi).
You really need to decide what you want to do here. If your modem is MBIM, then MBIM driver needs to be used. I really don't want to get into situations where we build some Frankenstein driver with gprs-context being driven via MBIM and netreg being driven via AT commands.
I can understand if you have MBIM + a GPS/GNSS port and want to expose location-reporting on that. Ok, fine, I get that. I can also understand if you want to use the AT command port for some vendor specific extensions not available via MBIM. But I would need serious convincing of anything beyond this.
Quoted 5 lines
MBIM support works a bit differently depending on the chipset manufacturer. The
drivers/mbim/gprs-contextworks out of the box for some models, while for others it is necessary to create the PDP context with AT+CGDCONT. But this is dealt with a dedicated atom, only the atom selection is visible in the plugin.So explain that one to me? You send all context details in
MBIM_CID_CONNECT. So why would a CGDCONT be needed?Quoted 8 lines
enough for the detour. Back to the plug-in initialization: the different models have zillion of differences. Whenever possible, I prefer to ask the modem, otherwise it is if/switch depending on the model.
Example of the first case: all Gemalto modems support the AT^SAIC command if there is voice support (I checked down to the MC55 that were like rel.98 or earlier).You do realize that all this probing can be done in the individual atom driver, right? Even as far as the driver being able to call
ofono_foo_removeif support is lacking.Quoted 8 lines
All switches are independent from each other, working on a simple principle, like for voice: ascertain if the feature is present and with which options, then select the right atom and atom options. There are quite a few, but maintainable thanks to that. Unfortunately not all features can be probed at startup because some require to be online, so the checks are split in 3 parts: pre-defined with PID, tested during the enable phase (could be shifted to pre-sim to have a faster enable), and in post-online.
We've been through this, no AT commands should be executed in
pre_sim,post_sim,post_onlinehooks...Quoted 2 lines
And this is the part to maintain. If I split the plugins in 3 independent ones, I need to duplicate and maintain this.
No, you really don't. It might take a while for you to be convinced though.
Quoted 2 lines
mbim/qmi/plainAt will disappear. And all the vendor-specific atoms will have to be duplicated too.
Again, they really don't need to be duplicated...
Quoted 14 lines
Quoted 2 lines
Since it sounds like this is a very esoteric use case, you might want to schedule this last. Right now it just distracts from the core discussion.
ok for having it last, but it is not so esoteric.
Take for example the audio settings (which most likely exist for all other vendors): you can select the type of interface (analog/digital/usb), the port to use in case there is more than one, suboptions like I2C or PCM, master/slave, and quite some other. They could be set in a quite large vendor-specific interface (I saw some tried it in an atom), but the configuration is fixed for the end application, and - as you explained - will raise more problems than it solves exposing these settings through dbus.
So the best is to run a couple of commands to set the interface properly for the product and that's it.I understand. But this is also why we have audio-settings atom. So all such details can be easily put in there and its relevant driver. If you want to have some simple configuration file that is read by your driver where the user can select between some common configurations that is fine as well.
One can also add additional drivers / plugins out-of-tree that would be dynamically loaded and do whatever they desire. But reading a file and blindly executing AT commands in a daemon that is running as root is just asking for trouble. I don't really care what you do in your products, but I'm not taking security holes upstream.
Quoted 8 lines
Quoted 4 lines
Have you considered just having your modem driver auto-magically setting the time into the modem and forgetting all this API business?
I'll look into this with the customer who proposed it, most likely reading this conversation already.
So as a hint, your customer might want to start engaging with us directly and skip the middle men. We do have some experience with integration given that between oFono and its sister projects we control the Bluetooth stack, WiFi stack, cellular stack, NFC stack and the overall network management ;)
Quoted 6 lines
it's ok, GPLv2 just requires me to publish the changes I have done to the code, not to fight to get them upstream.
I have posted this special case, it is public, so I won't discuss it further here.
If Gemalto users want it, we will give it to them.I can only advise you what is acceptable to the oFono upstream project. What you do in the end is really up to you and your customers. But we do have certain standards with respect to security and code quality which we are not going to compromise on.
Quoted 5 lines
Most of the options would make sense in a generic atom (like: which satellites constellations to use for the fix (GPS, GLONASS, GALILEO, Beidou, QZSS, ...), which to output, how to output them, NMEA version to use, start/stop the receiver, start/stop the output, etc.
So maybe you want to start there and propose some improvements to the location reporting APIs?
Quoted 3 lines
But some others would still require a vendor-specific plug-in, like on which port to output the NMEA sentences, whether to exclude satellites below a given angle, etc.
It seems like this is a platform specific configuration and really doesn't belong being exported over D-Bus anyway.
Quoted 6 lines
Quoted 2 lines
So fix gpsd. We're not taking upstream a vendor specific NMEA API when location-reporting already exists.
Changing it would make the dbus interface incompatible. gpsd takes a device, not an fd, but the atom initialize the receiver and opens the port.
So? You can always come up with a simple pseudo-tty proxy? That's like 20 minutes of work. Or maybe 5 in Python.
Quoted 11 lines
This kind of linked mechanisms make policies, and are quite impossible to break once done.
In my opinion, it is a bad design for linux, because the policies should be a step higher, but it wasn't detected in time and now it has to be like that.
You may have noticed that I didn't remove this atom from the plugin, because there are now users for it.
And by the way: passing the fd most likely means that each client is implementing its own NMEA parser, with risks of bugs, duplication of effort and lack of standardization. The atom encourages bad practices.Ok, thanks for the rant, but I will have to disagree completely here. LocationReporting was done the way it was for very specific reasons. We can't assume that the GPS port will be on one of the USB TTYs. It may be this way on your products, but it definitely is not this way on others. For example, it is exported through phonet or QMI on some devices.
Many modems also need magic commands / setup prior to being able to use the GPS receiver. So the LocationReporting API takes care of that. This also allows us to keep track of client lifetime. So that if/when the client dies we can automatically turn off the GPS unit and save some battery.
Honestly I'd really like to see GPS integrated properly into the kernel, with the ability for userspace daemons like oFono to provide
drivers/devicesto the kernel. Maybe Pavel has some additional ideas on how we could accomplish that.Quoted 4 lines
Quoted 1 line
Yes, but others do support MBIM + QMI.
this might be a different use case, like all communication is qmi and only carried over mbim frames. This would require a different plugin,
Not all, but some, yes.
Quoted 1 line
but I am not in this case.
<snip>
Quoted 9 lines
Quoted 4 lines
2. Split the single monolithic gemalto driver into 3+ drivers. One for MBIM+AT, one for QMI+AT, one for Serial/USB AT. Or alternatively extend plugins/gobi or plugins/mbim with Gemalto specific model quirks.
the second option is more interesting, but would make the behavior switches in the plugin duplicated.
What about extracting the qmi and mbim initialization in a separate file, so not to distract the reader?As mentioned earlier, we're not doing any Frankenstein drivers.
Quoted 9 lines
Quoted 3 lines
All modems are, after all, AT modems, with special initialization for dedicated interfaces. It initializes mbim just like it does for gnss, only this code takes more space.
Are you talking about your modems here? Because certainly not 'all' modems are AT modems. Our modems for example are binary protocol based with AT commands bolted on top. Also, many vendors do not provide AT ports for MBIM/QMI, etc based devices.
I am talking about the Gemalto modems in this plugin. They are all AT modems, with extensions for the gprs-context.
Reading the ofono documentation, I kind of understood that the very structure of ofono is so done that a plug-in can call the atoms that it needs.
Was that all just advertisement?
And the Frankenstein idea was good, it is called also cherry-picking. The drama that it got out of control is just... drama.
Gemalto prefers to use the AT interface because it is well understood, easy to format, and has an excellent test coverage.
So it will remain the preferred interface when there is a choice between it and something else.
MBIM is not even supported through the glib, but through the ell library that is itself quite fresh (and for which there is little advantage instead of the glib. Maybe because using glib you have to listen to someone else's opinion for pushing changes).
I will think about the entire conversation and see how to proceed.
Quoted 2 lines
Regards,
-Denis
Regards,
Giacinto
Hi Giacinto,
Quoted 5 lines
MBIM is not even supported through the glib, but through the ell library that is itself quite fresh (and for which there is little advantage instead of the glib. Maybe because using glib you have to listen to someone else's opinion for pushing changes).
Look, I understand you're unhappy, but please don't make stuff up. We are unhappy with glib due to its size (including the GObject stuff) and not for any other reason you speculate.
And ell is now almost 7 years old and used by multiple serious projects.
It is now also a dependency for oFono starting with the next release.
So please, stop the negativity. It is uncalled for.
Regards,
-Denis
Hi Giacinto,
Quoted 227 lines
Quoted 201 lines
Quoted 4 lines
Quoted 3 lines
So you might need to expand on this some more. What is QMI+AT or MBIM+AT actually doing? Is there a single AT port? Multiple? What is the AT port being used for, just vendor specific APIs or something more?
Quoted 9 lines
MBIM and QMI are actually used only for the gprs-context atom. The modems can work with the mbim or gobi plugin if they support the respective protocols (I have tested them with changes in udevng), but then we only get the bare minimum: no configuration options, hardware monitor, low-power mode, gnss and its options, and the rest. So, given that we need the AT interface anyway, everything is done through it. It is also easier to debug than the big binary blog (admittedly, wireshark helps a lot for mbim, but not sure for qmi).
You really need to decide what you want to do here. If your modem is MBIM, then MBIM driver needs to be used. I really don't want to get into situations where we build some Frankenstein driver with gprs-context being driven via MBIM and netreg being driven via AT commands.
I can understand if you have MBIM + a GPS/GNSS port and want to expose location-reporting on that. Ok, fine, I get that. I can also understand if you want to use the AT command port for some vendor specific extensions not available via MBIM. But I would need serious convincing of anything beyond this.
Quoted 5 lines
MBIM support works a bit differently depending on the chipset manufacturer. The
drivers/mbim/gprs-contextworks out of the box for some models, while for others it is necessary to create the PDP context with AT+CGDCONT. But this is dealt with a dedicated atom, only the atom selection is visible in the plugin.So explain that one to me? You send all context details in
MBIM_CID_CONNECT. So why would a CGDCONT be needed?Quoted 8 lines
enough for the detour. Back to the plug-in initialization: the different models have zillion of differences. Whenever possible, I prefer to ask the modem, otherwise it is if/switch depending on the model.
Example of the first case: all Gemalto modems support the AT^SAIC command if there is voice support (I checked down to the MC55 that were like rel.98 or earlier).You do realize that all this probing can be done in the individual atom driver, right? Even as far as the driver being able to call
ofono_foo_removeif support is lacking.Quoted 8 lines
All switches are independent from each other, working on a simple principle, like for voice: ascertain if the feature is present and with which options, then select the right atom and atom options. There are quite a few, but maintainable thanks to that. Unfortunately not all features can be probed at startup because some require to be online, so the checks are split in 3 parts: pre-defined with PID, tested during the enable phase (could be shifted to pre-sim to have a faster enable), and in post-online.
We've been through this, no AT commands should be executed in
pre_sim,post_sim,post_onlinehooks...Quoted 2 lines
And this is the part to maintain. If I split the plugins in 3 independent ones, I need to duplicate and maintain this.
No, you really don't. It might take a while for you to be convinced though.
Quoted 2 lines
mbim/qmi/plainAt will disappear. And all the vendor-specific atoms will have to be duplicated too.
Again, they really don't need to be duplicated...
Quoted 14 lines
Quoted 2 lines
Since it sounds like this is a very esoteric use case, you might want to schedule this last. Right now it just distracts from the core discussion.
ok for having it last, but it is not so esoteric.
Take for example the audio settings (which most likely exist for all other vendors): you can select the type of interface (analog/digital/usb), the port to use in case there is more than one, suboptions like I2C or PCM, master/slave, and quite some other. They could be set in a quite large vendor-specific interface (I saw some tried it in an atom), but the configuration is fixed for the end application, and - as you explained - will raise more problems than it solves exposing these settings through dbus.
So the best is to run a couple of commands to set the interface properly for the product and that's it.I understand. But this is also why we have audio-settings atom. So all such details can be easily put in there and its relevant driver. If you want to have some simple configuration file that is read by your driver where the user can select between some common configurations that is fine as well.
One can also add additional drivers / plugins out-of-tree that would be dynamically loaded and do whatever they desire. But reading a file and blindly executing AT commands in a daemon that is running as root is just asking for trouble. I don't really care what you do in your products, but I'm not taking security holes upstream.
Quoted 8 lines
Quoted 4 lines
Have you considered just having your modem driver auto-magically setting the time into the modem and forgetting all this API business?
I'll look into this with the customer who proposed it, most likely reading this conversation already.
So as a hint, your customer might want to start engaging with us directly and skip the middle men. We do have some experience with integration given that between oFono and its sister projects we control the Bluetooth stack, WiFi stack, cellular stack, NFC stack and the overall network management ;)
Quoted 6 lines
it's ok, GPLv2 just requires me to publish the changes I have done to the code, not to fight to get them upstream.
I have posted this special case, it is public, so I won't discuss it further here.
If Gemalto users want it, we will give it to them.I can only advise you what is acceptable to the oFono upstream project. What you do in the end is really up to you and your customers. But we do have certain standards with respect to security and code quality which we are not going to compromise on.
Quoted 5 lines
Most of the options would make sense in a generic atom (like: which satellites constellations to use for the fix (GPS, GLONASS, GALILEO, Beidou, QZSS, ...), which to output, how to output them, NMEA version to use, start/stop the receiver, start/stop the output, etc.
So maybe you want to start there and propose some improvements to the location reporting APIs?
Quoted 3 lines
But some others would still require a vendor-specific plug-in, like on which port to output the NMEA sentences, whether to exclude satellites below a given angle, etc.
It seems like this is a platform specific configuration and really doesn't belong being exported over D-Bus anyway.
Quoted 6 lines
Quoted 2 lines
So fix gpsd. We're not taking upstream a vendor specific NMEA API when location-reporting already exists.
Changing it would make the dbus interface incompatible. gpsd takes a device, not an fd, but the atom initialize the receiver and opens the port.
So? You can always come up with a simple pseudo-tty proxy? That's like 20 minutes of work. Or maybe 5 in Python.
Quoted 11 lines
This kind of linked mechanisms make policies, and are quite impossible to break once done.
In my opinion, it is a bad design for linux, because the policies should be a step higher, but it wasn't detected in time and now it has to be like that.
You may have noticed that I didn't remove this atom from the plugin, because there are now users for it.
And by the way: passing the fd most likely means that each client is implementing its own NMEA parser, with risks of bugs, duplication of effort and lack of standardization. The atom encourages bad practices.Ok, thanks for the rant, but I will have to disagree completely here. LocationReporting was done the way it was for very specific reasons. We can't assume that the GPS port will be on one of the USB TTYs. It may be this way on your products, but it definitely is not this way on others. For example, it is exported through phonet or QMI on some devices.
Many modems also need magic commands / setup prior to being able to use the GPS receiver. So the LocationReporting API takes care of that. This also allows us to keep track of client lifetime. So that if/when the client dies we can automatically turn off the GPS unit and save some battery.
Honestly I'd really like to see GPS integrated properly into the kernel, with the ability for userspace daemons like oFono to provide
drivers/devicesto the kernel. Maybe Pavel has some additional ideas on how we could accomplish that.Quoted 4 lines
Quoted 1 line
Yes, but others do support MBIM + QMI.
this might be a different use case, like all communication is qmi and only carried over mbim frames. This would require a different plugin,
Not all, but some, yes.
Quoted 1 line
but I am not in this case.
<snip>
Quoted 9 lines
Quoted 4 lines
2. Split the single monolithic gemalto driver into 3+ drivers. One for MBIM+AT, one for QMI+AT, one for Serial/USB AT. Or alternatively extend plugins/gobi or plugins/mbim with Gemalto specific model quirks.
the second option is more interesting, but would make the behavior switches in the plugin duplicated.
What about extracting the qmi and mbim initialization in a separate file, so not to distract the reader?As mentioned earlier, we're not doing any Frankenstein drivers.
Quoted 9 lines
Quoted 3 lines
All modems are, after all, AT modems, with special initialization for dedicated interfaces. It initializes mbim just like it does for gnss, only this code takes more space.
Are you talking about your modems here? Because certainly not 'all' modems are AT modems. Our modems for example are binary protocol based with AT commands bolted on top. Also, many vendors do not provide AT ports for MBIM/QMI, etc based devices.
I am talking about the Gemalto modems in this plugin. They are all AT modems, with extensions for the gprs-context.
Reading the ofono documentation, I kind of understood that the very structure of ofono is so done that a plug-in can call the atoms that it needs.
Was that all just advertisement?And the Frankenstein idea was good, it is called also cherry-picking. The drama that it got out of control is just... drama.
Gemalto prefers to use the AT interface because it is well understood, easy to format, and has an excellent test coverage.
So it will remain the preferred interface when there is a choice between it and something else.
Quoted 4 lines
MBIM is not even supported through the glib, but through the ell library that is itself quite fresh (and for which there is little advantage instead of the glib. Maybe because using glib you have to listen to someone else's opinion for pushing changes).
ELL is actually pretty old since most of its original code is from 2011 and most of the parts that oFono uses have been in ELL since a long time now. We now have an option in oFono to use internal ELL or external ELL. So that will make things even easier since you can just default to the internal version if you want to.
We started to dislike GLib since it started to grow out of control from a memory usage point of view. And we really only used a tiny portion of it. When we started ELL, we looked at BlueZ, oFono, ConnMan etc and checked what parts of GLib they actually used and that was the design factor behind ELL.
Keep in mind that we only require GLib >= 2.32 and I think the latest version is 2.58 or something like that. So pushing changes into GLib was never needed.
Regards
Marcel
Hi Marcel, Denis,
Quoted 235 lines · Marcel Holtmann
Hi Giacinto,
Quoted 227 lines
Quoted 201 lines
Quoted 4 lines
Quoted 3 lines
So you might need to expand on this some more. What is QMI+AT or MBIM+AT actually doing? Is there a single AT port? Multiple? What is the AT port being used for, just vendor specific APIs or something more?
Quoted 9 lines
MBIM and QMI are actually used only for the gprs-context atom. The modems can work with the mbim or gobi plugin if they support the respective protocols (I have tested them with changes in udevng), but then we only get the bare minimum: no configuration options, hardware monitor, low-power mode, gnss and its options, and the rest. So, given that we need the AT interface anyway, everything is done through it. It is also easier to debug than the big binary blog (admittedly, wireshark helps a lot for mbim, but not sure for qmi).
You really need to decide what you want to do here. If your modem is MBIM, then MBIM driver needs to be used. I really don't want to get into situations where we build some Frankenstein driver with gprs-context being driven via MBIM and netreg being driven via AT commands.
I can understand if you have MBIM + a GPS/GNSS port and want to expose location-reporting on that. Ok, fine, I get that. I can also understand if you want to use the AT command port for some vendor specific extensions not available via MBIM. But I would need serious convincing of anything beyond this.
Quoted 5 lines
MBIM support works a bit differently depending on the chipset manufacturer. The
drivers/mbim/gprs-contextworks out of the box for some models, while for others it is necessary to create the PDP context with AT+CGDCONT. But this is dealt with a dedicated atom, only the atom selection is visible in the plugin.So explain that one to me? You send all context details in
MBIM_CID_CONNECT. So why would a CGDCONT be needed?Quoted 8 lines
enough for the detour. Back to the plug-in initialization: the different models have zillion of differences. Whenever possible, I prefer to ask the modem, otherwise it is if/switch depending on the model.
Example of the first case: all Gemalto modems support the AT^SAIC command if there is voice support (I checked down to the MC55 that were like rel.98 or earlier).You do realize that all this probing can be done in the individual atom driver, right? Even as far as the driver being able to call
ofono_foo_removeif support is lacking.Quoted 8 lines
All switches are independent from each other, working on a simple principle, like for voice: ascertain if the feature is present and with which options, then select the right atom and atom options. There are quite a few, but maintainable thanks to that. Unfortunately not all features can be probed at startup because some require to be online, so the checks are split in 3 parts: pre-defined with PID, tested during the enable phase (could be shifted to pre-sim to have a faster enable), and in post-online.
We've been through this, no AT commands should be executed in
pre_sim,post_sim,post_onlinehooks...Quoted 2 lines
And this is the part to maintain. If I split the plugins in 3 independent ones, I need to duplicate and maintain this.
No, you really don't. It might take a while for you to be convinced though.
Quoted 2 lines
mbim/qmi/plainAt will disappear. And all the vendor-specific atoms will have to be duplicated too.
Again, they really don't need to be duplicated...
Quoted 14 lines
Quoted 2 lines
Since it sounds like this is a very esoteric use case, you might want to schedule this last. Right now it just distracts from the core discussion.
ok for having it last, but it is not so esoteric.
Take for example the audio settings (which most likely exist for all other vendors): you can select the type of interface (analog/digital/usb), the port to use in case there is more than one, suboptions like I2C or PCM, master/slave, and quite some other. They could be set in a quite large vendor-specific interface (I saw some tried it in an atom), but the configuration is fixed for the end application, and - as you explained - will raise more problems than it solves exposing these settings through dbus.
So the best is to run a couple of commands to set the interface properly for the product and that's it.I understand. But this is also why we have audio-settings atom. So all such details can be easily put in there and its relevant driver. If you want to have some simple configuration file that is read by your driver where the user can select between some common configurations that is fine as well.
One can also add additional drivers / plugins out-of-tree that would be dynamically loaded and do whatever they desire. But reading a file and blindly executing AT commands in a daemon that is running as root is just asking for trouble. I don't really care what you do in your products, but I'm not taking security holes upstream.
Quoted 8 lines
Quoted 4 lines
Have you considered just having your modem driver auto-magically setting the time into the modem and forgetting all this API business?
I'll look into this with the customer who proposed it, most likely reading this conversation already.
So as a hint, your customer might want to start engaging with us directly and skip the middle men. We do have some experience with integration given that between oFono and its sister projects we control the Bluetooth stack, WiFi stack, cellular stack, NFC stack and the overall network management ;)
Quoted 6 lines
it's ok, GPLv2 just requires me to publish the changes I have done to the code, not to fight to get them upstream.
I have posted this special case, it is public, so I won't discuss it further here.
If Gemalto users want it, we will give it to them.I can only advise you what is acceptable to the oFono upstream project. What you do in the end is really up to you and your customers. But we do have certain standards with respect to security and code quality which we are not going to compromise on.
Quoted 5 lines
Most of the options would make sense in a generic atom (like: which satellites constellations to use for the fix (GPS, GLONASS, GALILEO, Beidou, QZSS, ...), which to output, how to output them, NMEA version to use, start/stop the receiver, start/stop the output, etc.
So maybe you want to start there and propose some improvements to the location reporting APIs?
Quoted 3 lines
But some others would still require a vendor-specific plug-in, like on which port to output the NMEA sentences, whether to exclude satellites below a given angle, etc.
It seems like this is a platform specific configuration and really doesn't belong being exported over D-Bus anyway.
Quoted 6 lines
Quoted 2 lines
So fix gpsd. We're not taking upstream a vendor specific NMEA API when location-reporting already exists.
Changing it would make the dbus interface incompatible. gpsd takes a device, not an fd, but the atom initialize the receiver and opens the port.
So? You can always come up with a simple pseudo-tty proxy? That's like 20 minutes of work. Or maybe 5 in Python.
Quoted 11 lines
This kind of linked mechanisms make policies, and are quite impossible to break once done.
In my opinion, it is a bad design for linux, because the policies should be a step higher, but it wasn't detected in time and now it has to be like that.
You may have noticed that I didn't remove this atom from the plugin, because there are now users for it.
And by the way: passing the fd most likely means that each client is implementing its own NMEA parser, with risks of bugs, duplication of effort and lack of standardization. The atom encourages bad practices.Ok, thanks for the rant, but I will have to disagree completely here. LocationReporting was done the way it was for very specific reasons. We can't assume that the GPS port will be on one of the USB TTYs. It may be this way on your products, but it definitely is not this way on others. For example, it is exported through phonet or QMI on some devices.
Many modems also need magic commands / setup prior to being able to use the GPS receiver. So the LocationReporting API takes care of that. This also allows us to keep track of client lifetime. So that if/when the client dies we can automatically turn off the GPS unit and save some battery.
Honestly I'd really like to see GPS integrated properly into the kernel, with the ability for userspace daemons like oFono to provide
drivers/devicesto the kernel. Maybe Pavel has some additional ideas on how we could accomplish that.Quoted 4 lines
Quoted 1 line
Yes, but others do support MBIM + QMI.
this might be a different use case, like all communication is qmi and only carried over mbim frames. This would require a different plugin,
Not all, but some, yes.
Quoted 1 line
but I am not in this case.
<snip>
Quoted 9 lines
Quoted 4 lines
2. Split the single monolithic gemalto driver into 3+ drivers. One for MBIM+AT, one for QMI+AT, one for Serial/USB AT. Or alternatively extend plugins/gobi or plugins/mbim with Gemalto specific model quirks.
the second option is more interesting, but would make the behavior switches in the plugin duplicated.
What about extracting the qmi and mbim initialization in a separate file, so not to distract the reader?As mentioned earlier, we're not doing any Frankenstein drivers.
Quoted 9 lines
Quoted 3 lines
All modems are, after all, AT modems, with special initialization for dedicated interfaces. It initializes mbim just like it does for gnss, only this code takes more space.
Are you talking about your modems here? Because certainly not 'all' modems are AT modems. Our modems for example are binary protocol based with AT commands bolted on top. Also, many vendors do not provide AT ports for MBIM/QMI, etc based devices.
I am talking about the Gemalto modems in this plugin. They are all AT modems, with extensions for the gprs-context.
Reading the ofono documentation, I kind of understood that the very structure of ofono is so done that a plug-in can call the atoms that it needs.
Was that all just advertisement?And the Frankenstein idea was good, it is called also cherry-picking. The drama that it got out of control is just... drama.
Gemalto prefers to use the AT interface because it is well understood, easy to format, and has an excellent test coverage.
So it will remain the preferred interface when there is a choice between it and something else.Quoted 4 lines
MBIM is not even supported through the glib, but through the ell library that is itself quite fresh (and for which there is little advantage instead of the glib. Maybe because using glib you have to listen to someone else's opinion for pushing changes).
Quoted 6 lines
ELL is actually pretty old since most of its original code is from 2011 and most of the parts that oFono uses have been in ELL since a long time now. We now have an option in oFono to use internal ELL or external ELL. So that will make things even easier since you can just default to the internal version if you want to.
We started to dislike GLib since it started to grow out of control from a memory usage point of view. And we really only used a tiny portion of it. When we started ELL, we looked at BlueZ, oFono, ConnMan etc and checked what parts of GLib they actually used and that was the design factor behind ELL.
Keep in mind that we only require GLib >= 2.32 and I think the latest version is 2.58 or something like that. So pushing changes into GLib was never needed.
Thank you for the explanation. Is the glib going to be removed completely then, or both will coexist?
However I have not seen a real reason not to use AT+MBIM together, nor a convenient way to do it.
Denis pointed that the atom-configuration tests can be done in the atoms themselves, except that then I am asked to maintain my own atom when I want to add more than a couple lines (see the lte atom story). Nevertheless this is an excellent suggestion to reduce the complexity in the plugin.
Often MBIM can be turned on or off or changed for some other interface for the same (Gemalto) modem, depending on the application wishes, so if the plugin is split, all its settings are just duplicated.
Quoted 4 lines
Regards
Marcel
Regards,
Giacinto
Hi Giacinto,
Quoted 3 lines
Thank you for the explanation. Is the glib going to be removed completely then, or both will coexist?
oFono is a large project, so eradicating use of glib cannot happen overnight. For the foreseeable future we will have both coexist. I will start 'encouraging' people to start using ell instead of glib. Especially for new code that does not depend on GAtChat, etc.
Quoted 5 lines
Denis pointed that the atom-configuration tests can be done in the atoms themselves, except that then I am asked to maintain my own atom when I want to add more than a couple lines (see the lte atom story). Nevertheless this is an excellent suggestion to reduce the complexity in the plugin.
You will end up writing many of your own atom drivers anyway. voicecall for example. For others we will figure this out, I've some ideas I need to play with.
Quoted 5 lines
Often MBIM can be turned on or off or changed for some other interface for the same (Gemalto) modem, depending on the application wishes, so if the plugin is split, all its settings are just duplicated.
This also implies having the modem reconfigure itself on the USB interface, no? E.g. a hot-unplug and hot-plug. I doubt many users will be doing this anyway.
If you're worried about duplicating vendor specific API code between multiple modem drivers, then don't. This code can be shared easily and I've already given you a few ideas.
Regards,
-Denis