diff --git a/net-misc/eg25-manager/eg25-manager-0.3.0.ebuild b/net-misc/eg25-manager/eg25-manager-0.3.0.ebuild new file mode 100644 index 0000000..a1d10e0 --- /dev/null +++ b/net-misc/eg25-manager/eg25-manager-0.3.0.ebuild @@ -0,0 +1,44 @@ +# Copyright 2020 Gentoo Authors +# Distributed under the terms of the GNU General Public License v2 + +EAPI=7 + +inherit meson git-r3 systemd + +DESCRIPTION="Daemon for managing the Quectel EG25 modem" +HOMEPAGE="https://gitlab.com/mobian1/devices/eg25-manager" + + +KEYWORDS="~arm64" + +EGIT_REPO_URI="${HOMEPAGE}.git" + +if [[ ${PV} != 9999 ]]; then + EGIT_COMMIT="73e16f76994b1d3c587796a35766cc668e30c0cd" +else + KEYWORDS="" +fi + +LICENSE="GPL-3" +SLOT="0" + +DEPEND=" + dev-libs/libgpiod + virtual/libusb:1 + net-misc/modemmanager + " +RDEPEND="${DEPEND}" + +PATCHES=( + ${FILESDIR}/18.patch + ${FILESDIR}/15.patch +) + +src_install() { + meson_src_install + systemd_dounit "${FILESDIR}"/eg25-manager.service +} + +pkg_postinst() { + systemd_reenable --all eg25-manager +} diff --git a/net-misc/eg25-manager/files/0001-suspend-add-boot-timer.patch b/net-misc/eg25-manager/files/0001-suspend-add-boot-timer.patch new file mode 100644 index 0000000..7f59592 --- /dev/null +++ b/net-misc/eg25-manager/files/0001-suspend-add-boot-timer.patch @@ -0,0 +1,297 @@ +From b8d269cf2f5aed4ca0e0bcb9702b65897fda18bc Mon Sep 17 00:00:00 2001 +From: Dylan Van Assche +Date: Sat, 6 Feb 2021 07:52:32 +0100 +Subject: [PATCH] suspend: add boot timer + +The EG25 modem needs at least 2 minutes after indicating 'RDY' +to be fully operational. If the modem is suspended before that, +calls or texts may not be seen by the userspace. +This mostly occurs when a full reboot or poweroff/poweron +sequence of the phone is performed. + +: +--- + src/at.c | 4 +- + src/manager.c | 5 +- + src/manager.h | 4 +- + src/suspend.c | 126 +++++++++++++++++++++++++++++++++++++++----------- + src/suspend.h | 2 +- + 5 files changed, 108 insertions(+), 33 deletions(-) + +diff --git a/src/at.c b/src/at.c +index 39a857a..661098a 100644 +--- a/src/at.c ++++ b/src/at.c +@@ -202,8 +202,10 @@ static gboolean modem_response(gint fd, + + g_message("Response: [%s]", response); + +- if (strcmp(response, "RDY") == 0) ++ if (strcmp(response, "RDY") == 0) { ++ suspend_inhibit(manager, TRUE, TRUE); + manager->modem_state = EG25_STATE_STARTED; ++ } + else if (strstr(response, "ERROR")) + retry_at_command(manager); + else if (strstr(response, "OK")) +diff --git a/src/manager.c b/src/manager.c +index 6445e0f..912a095 100644 +--- a/src/manager.c ++++ b/src/manager.c +@@ -178,7 +178,7 @@ void modem_suspend_post(struct EG25Manager *manager) + { + gpio_sequence_suspend(manager); + g_message("suspend sequence is over, drop inhibitor"); +- suspend_inhibit(manager, FALSE); ++ suspend_inhibit(manager, FALSE, FALSE); + } + + void modem_resume_pre(struct EG25Manager *manager) +@@ -205,7 +205,8 @@ int main(int argc, char *argv[]) + + memset(&manager, 0, sizeof(manager)); + manager.at_fd = -1; +- manager.suspend_inhibit_fd = -1; ++ manager.delay_inhibit_fd = -1; ++ manager.block_inhibit_fd = -1; + + opt_context = g_option_context_new ("- Power management for the Quectel EG25 modem"); + g_option_context_add_main_entries (opt_context, options, NULL); +diff --git a/src/manager.h b/src/manager.h +index f6351be..21b3b67 100644 +--- a/src/manager.h ++++ b/src/manager.h +@@ -43,8 +43,10 @@ struct EG25Manager { + MMModem *mm_modem; + + GDBusProxy *suspend_proxy; +- int suspend_inhibit_fd; ++ int delay_inhibit_fd; ++ int block_inhibit_fd; + guint suspend_timer; ++ guint boot_timer; + + GUdevClient *udev; + +diff --git a/src/suspend.c b/src/suspend.c +index 4b1a026..622cf00 100644 +--- a/src/suspend.c ++++ b/src/suspend.c +@@ -13,9 +13,10 @@ + + #include + +-#define SD_NAME "org.freedesktop.login1" +-#define SD_PATH "/org/freedesktop/login1" +-#define SD_INTERFACE "org.freedesktop.login1.Manager" ++#define SD_NAME "org.freedesktop.login1" ++#define SD_PATH "/org/freedesktop/login1" ++#define SD_INTERFACE "org.freedesktop.login1.Manager" ++#define FULL_BOOT_DELAY 120 + + static gboolean check_modem_resume(struct EG25Manager *manager) + { +@@ -26,18 +27,28 @@ static gboolean check_modem_resume(struct EG25Manager *manager) + return FALSE; + } + +-static gboolean drop_inhibitor(struct EG25Manager *manager) ++static gboolean drop_inhibitor(struct EG25Manager *manager, gboolean block) + { +- if (manager->suspend_inhibit_fd >= 0) { +- g_message("dropping systemd sleep inhibitor"); +- close(manager->suspend_inhibit_fd); +- manager->suspend_inhibit_fd = -1; +- return TRUE; ++ if (block) { ++ if (manager->block_inhibit_fd >= 0) { ++ g_message("dropping systemd sleep block inhibitor"); ++ close(manager->block_inhibit_fd); ++ manager->block_inhibit_fd = -1; ++ return TRUE; ++ } ++ } ++ else { ++ if (manager->delay_inhibit_fd >= 0) { ++ g_message("dropping systemd sleep delay inhibitor"); ++ close(manager->delay_inhibit_fd); ++ manager->delay_inhibit_fd = -1; ++ return TRUE; ++ } + } + return FALSE; + } + +-static void inhibit_done(GObject *source, ++static void inhibit_done_delay(GObject *source, + GAsyncResult *result, + gpointer user_data) + { +@@ -54,27 +65,81 @@ static void inhibit_done(GObject *source, + if (!fd_list || g_unix_fd_list_get_length(fd_list) != 1) + g_warning("didn't get a single fd back"); + +- manager->suspend_inhibit_fd = g_unix_fd_list_get(fd_list, 0, NULL); ++ manager->delay_inhibit_fd = g_unix_fd_list_get(fd_list, 0, NULL); + +- g_message("inhibitor fd is %d", manager->suspend_inhibit_fd); ++ g_message("inhibitor sleep fd is %d", manager->delay_inhibit_fd); + g_object_unref(fd_list); + g_variant_unref(res); + } + } + +-static void take_inhibitor(struct EG25Manager *manager) ++static void inhibit_done_block(GObject *source, ++ GAsyncResult *result, ++ gpointer user_data) ++{ ++ GDBusProxy *suspend_proxy = G_DBUS_PROXY(source); ++ struct EG25Manager *manager = user_data; ++ g_autoptr (GError) error = NULL; ++ GVariant *res; ++ GUnixFDList *fd_list; ++ ++ res = g_dbus_proxy_call_with_unix_fd_list_finish(suspend_proxy, &fd_list, result, &error); ++ if (!res) { ++ g_warning("inhibit failed: %s", error->message); ++ } else { ++ if (!fd_list || g_unix_fd_list_get_length(fd_list) != 1) ++ g_warning("didn't get a single fd back"); ++ ++ manager->block_inhibit_fd = g_unix_fd_list_get(fd_list, 0, NULL); ++ ++ g_message("inhibitor block fd is %d", manager->block_inhibit_fd); ++ g_object_unref(fd_list); ++ g_variant_unref(res); ++ } ++} ++ ++/* ++ * After the EG25 modem sends 'RDY', it takes up to 2 minutes before all ++ * capabilities are operational. If the modem is suspended before that, ++ * calls and texts may be not recognized properly. ++ */ ++static gboolean modem_fully_booted(struct EG25Manager *manager) ++{ ++ g_message("Modem is up for %d seconds and fully ready", FULL_BOOT_DELAY); ++ manager->boot_timer = 0; ++ drop_inhibitor(manager, TRUE); ++ ++ return FALSE; ++} ++ ++static void take_inhibitor(struct EG25Manager *manager, gboolean block) + { + GVariant *variant_arg; + +- if(manager->suspend_inhibit_fd != -1) +- drop_inhibitor(manager); ++ if (block) { ++ if(manager->block_inhibit_fd != -1) ++ drop_inhibitor(manager, TRUE); + +- variant_arg = g_variant_new ("(ssss)", "sleep", "eg25manager", +- "eg25manager needs to prepare modem for sleep", "delay"); ++ variant_arg = g_variant_new ("(ssss)", "sleep", "eg25manager", ++ "eg25manager needs to wait for modem to be fully booted", "block"); + +- g_message("taking systemd sleep inhibitor"); +- g_dbus_proxy_call_with_unix_fd_list(manager->suspend_proxy, "Inhibit", variant_arg, +- 0, G_MAXINT, NULL, NULL, inhibit_done, manager); ++ g_message("taking systemd sleep block inhibitor"); ++ g_dbus_proxy_call_with_unix_fd_list(manager->suspend_proxy, "Inhibit", variant_arg, ++ 0, G_MAXINT, NULL, NULL, inhibit_done_block, manager); ++ g_message("scheduling block inhibitor release"); ++ manager->boot_timer = g_timeout_add_seconds(FULL_BOOT_DELAY, G_SOURCE_FUNC(modem_fully_booted), manager); ++ } ++ else { ++ if(manager->delay_inhibit_fd != -1) ++ drop_inhibitor(manager, FALSE); ++ ++ variant_arg = g_variant_new ("(ssss)", "sleep", "eg25manager", ++ "eg25manager needs to prepare modem for sleep", "delay"); ++ ++ g_message("taking systemd sleep delay inhibitor"); ++ g_dbus_proxy_call_with_unix_fd_list(manager->suspend_proxy, "Inhibit", variant_arg, ++ 0, G_MAXINT, NULL, NULL, inhibit_done_delay, manager); ++ } + } + + static void signal_cb(GDBusProxy *proxy, +@@ -97,7 +162,7 @@ static void signal_cb(GDBusProxy *proxy, + modem_suspend_pre(manager); + } else { + g_message("system is resuming"); +- take_inhibitor(manager); ++ take_inhibitor(manager, FALSE); + modem_resume_pre(manager); + if (manager->mm_modem) { + /* +@@ -126,10 +191,10 @@ static void name_owner_cb(GObject *object, + + owner = g_dbus_proxy_get_name_owner(proxy); + if (owner) { +- take_inhibitor(manager); ++ take_inhibitor(manager, FALSE); + g_free(owner); + } else { +- drop_inhibitor(manager); ++ drop_inhibitor(manager, FALSE); + } + } + +@@ -151,7 +216,7 @@ static void on_proxy_acquired(GObject *object, + + owner = g_dbus_proxy_get_name_owner(manager->suspend_proxy); + if (owner) { +- take_inhibitor(manager); ++ take_inhibitor(manager, FALSE); + g_free(owner); + } + } +@@ -167,21 +232,26 @@ void suspend_init(struct EG25Manager *manager) + + void suspend_destroy(struct EG25Manager *manager) + { +- drop_inhibitor(manager); ++ drop_inhibitor(manager, FALSE); ++ drop_inhibitor(manager, TRUE); + if (manager->suspend_timer) { + g_source_remove(manager->suspend_timer); + manager->suspend_timer = 0; + } ++ if (manager->boot_timer) { ++ g_source_remove(manager->boot_timer); ++ manager->boot_timer = 0; ++ } + if (manager->suspend_proxy) { + g_object_unref(manager->suspend_proxy); + manager->suspend_proxy = NULL; + } + } + +-void suspend_inhibit(struct EG25Manager *manager, gboolean inhibit) ++void suspend_inhibit(struct EG25Manager *manager, gboolean inhibit, gboolean block) + { + if (inhibit) +- take_inhibitor(manager); ++ take_inhibitor(manager, block); + else +- drop_inhibitor(manager); ++ drop_inhibitor(manager, block); + } +diff --git a/src/suspend.h b/src/suspend.h +index 39832aa..e82eeff 100644 +--- a/src/suspend.h ++++ b/src/suspend.h +@@ -11,4 +11,4 @@ + void suspend_init (struct EG25Manager *data); + void suspend_destroy (struct EG25Manager *data); + +-void suspend_inhibit (struct EG25Manager *data, gboolean inhibit); ++void suspend_inhibit (struct EG25Manager *data, gboolean inhibit, gboolean block); +-- +GitLab + + diff --git a/net-misc/eg25-manager/files/10.patch b/net-misc/eg25-manager/files/10.patch new file mode 100644 index 0000000..ea5925b --- /dev/null +++ b/net-misc/eg25-manager/files/10.patch @@ -0,0 +1,57 @@ +From 04eed2496d2a02bb23b4eae91be0264417fb28c6 Mon Sep 17 00:00:00 2001 +From: Biktor +Date: Wed, 24 Feb 2021 12:32:46 +0100 +Subject: [PATCH] Set URC config to 'all' instead of 'usbat' dquote> When using + a custom kernel, if this setting is set to 'usbat' only, no RING urc is + reported on any interface. Changing QURCCFG to 'all' makes the modem report + RINGs on all supported interfaces, making receiving calls possible when using + a custom firmware + +--- + data/pine64,pinephone-1.0.toml | 2 +- + data/pine64,pinephone-1.1.toml | 2 +- + data/pine64,pinephone-1.2.toml | 2 +- + 3 files changed, 3 insertions(+), 3 deletions(-) + +diff --git a/data/pine64,pinephone-1.0.toml b/data/pine64,pinephone-1.0.toml +index c0e9192..7287298 100644 +--- a/data/pine64,pinephone-1.0.toml ++++ b/data/pine64,pinephone-1.0.toml +@@ -39,7 +39,7 @@ configure = [ + { cmd = "QCFG", subcmd = "urc/ri/smsincoming", expect = "\"pulse\",2000" }, + { cmd = "QCFG", subcmd = "urc/ri/other", expect = "\"off\",1" }, + { cmd = "QCFG", subcmd = "urc/delay", expect = "1" }, +- { cmd = "QURCCFG", subcmd = "urcport", expect = "\"usbat\"" }, ++ { cmd = "QURCCFG", subcmd = "urcport", expect = "\"all\"" }, + { cmd = "QGPS", value = "1" }, + { cmd = "QSCLK", value = "1" }, + { cmd = "QCFG", subcmd = "urc/cache", value = "0" } +diff --git a/data/pine64,pinephone-1.1.toml b/data/pine64,pinephone-1.1.toml +index c0e9192..7287298 100644 +--- a/data/pine64,pinephone-1.1.toml ++++ b/data/pine64,pinephone-1.1.toml +@@ -39,7 +39,7 @@ configure = [ + { cmd = "QCFG", subcmd = "urc/ri/smsincoming", expect = "\"pulse\",2000" }, + { cmd = "QCFG", subcmd = "urc/ri/other", expect = "\"off\",1" }, + { cmd = "QCFG", subcmd = "urc/delay", expect = "1" }, +- { cmd = "QURCCFG", subcmd = "urcport", expect = "\"usbat\"" }, ++ { cmd = "QURCCFG", subcmd = "urcport", expect = "\"all\"" }, + { cmd = "QGPS", value = "1" }, + { cmd = "QSCLK", value = "1" }, + { cmd = "QCFG", subcmd = "urc/cache", value = "0" } +diff --git a/data/pine64,pinephone-1.2.toml b/data/pine64,pinephone-1.2.toml +index 566ac80..2effb30 100644 +--- a/data/pine64,pinephone-1.2.toml ++++ b/data/pine64,pinephone-1.2.toml +@@ -32,7 +32,7 @@ configure = [ + { cmd = "QCFG", subcmd = "risignaltype", expect = "\"physical\"" }, + { cmd = "QCFG", subcmd = "ims", expect = "1" }, + { cmd = "QCFG", subcmd = "apready", expect = "1,0,500" }, +- { cmd = "QURCCFG", subcmd = "urcport", expect = "\"usbat\"" }, ++ { cmd = "QURCCFG", subcmd = "urcport", expect = "\"all\"" }, + { cmd = "QGPS", value = "1" }, + { cmd = "QSCLK", value = "1" }, + { cmd = "QCFG", subcmd = "urc/cache", value = "0" } +-- +GitLab + diff --git a/net-misc/eg25-manager/files/11.patch b/net-misc/eg25-manager/files/11.patch new file mode 100644 index 0000000..21cf06d --- /dev/null +++ b/net-misc/eg25-manager/files/11.patch @@ -0,0 +1,49 @@ +From d40bb283101fd9cf702e4944865aebef52c34185 Mon Sep 17 00:00:00 2001 +From: Djhg2000 +Date: Thu, 4 Mar 2021 20:08:12 +0100 +Subject: [PATCH] Add a 60ms delay before PWRKEY sequence + +This brings the power on sequence in line with the EG25-G Hardware Design +datasheet, which states we need to wait at least 30 ms after VBAT becomes stable +before pulling PWRKEY low (first action of the power on sequene). + +After this change the sequence becomes as follows: +- Set RESET_N high +- Wait 60 ms (double 30 ms) +- Execute PWRKEY sequence + +60 ms was choosen because we don't know when VBAT becomes stable, but it should +be much less than an additional 30 ms. Empirical evidence suggests PinePhone +units with a healthy battery do not see serious side effects from not doing +this, while the modem will fail to boot and/or throw random errors on boot with +a worn out battery. +--- + src/manager.c | 5 +++++ + 1 file changed, 5 insertions(+) + +diff --git a/src/manager.c b/src/manager.c +index aaa3905..a992572 100644 +--- a/src/manager.c ++++ b/src/manager.c +@@ -27,6 +27,9 @@ + #define EG25_DATADIR "/usr/share/eg25-manager" + #endif + ++// Delay between setting GPIO and PWRKEY sequence (60ms) ++#define MODEM_POWERON_DELAY 60000 ++ + static gboolean quit_app(struct EG25Manager *manager) + { + int i; +@@ -86,6 +89,8 @@ static gboolean modem_start(struct EG25Manager *manager) + + if (should_boot) { + g_message("Starting modem..."); ++ // Modem might crash on boot (especially with worn battery) if we don't delay here ++ usleep(MODEM_POWERON_DELAY); + gpio_sequence_poweron(manager); + manager->modem_state = EG25_STATE_POWERED; + } else { +-- +GitLab + diff --git a/net-misc/eg25-manager/files/12.patch b/net-misc/eg25-manager/files/12.patch new file mode 100644 index 0000000..e4fbd43 --- /dev/null +++ b/net-misc/eg25-manager/files/12.patch @@ -0,0 +1,65 @@ +From 84a0ae603df95311a5d5cae9ca04b69fd6784827 Mon Sep 17 00:00:00 2001 +From: Bhushan Shah +Date: Fri, 26 Mar 2021 13:56:37 +0530 +Subject: [PATCH] udev: use the udev rules directly to set attr + +We don't need complicated script for this, we can just set required +attributes using udev rules. +--- + udev/80-modem-eg25.rules | 6 +++++- + udev/eg25-configure-usb | 21 --------------------- + udev/meson.build | 1 - + 3 files changed, 5 insertions(+), 23 deletions(-) + delete mode 100755 udev/eg25-configure-usb + +diff --git a/udev/80-modem-eg25.rules b/udev/80-modem-eg25.rules +index 0ea9e6f..fc3b78f 100644 +--- a/udev/80-modem-eg25.rules ++++ b/udev/80-modem-eg25.rules +@@ -1 +1,5 @@ +-ACTION=="add", SUBSYSTEM=="tty", ATTRS{idVendor}=="2c7c", ATTRS{idProduct}=="0125", ENV{MINOR}=="0", RUN+="/usr/bin/eg25-configure-usb %p" ++ACTION=="add", SUBSYSTEM=="usb", DRIVERS=="usb", ATTRS{idVendor}=="2c7c", ATTRS{idProduct}=="0125", ATTR{power/control}="auto" ++ACTION=="add", SUBSYSTEM=="usb", DRIVERS=="usb", ATTRS{idVendor}=="2c7c", ATTRS{idProduct}=="0125", ATTR{power/autosuspend_delay_ms}="3000" ++ACTION=="add", SUBSYSTEM=="usb", DRIVERS=="usb", ATTRS{idVendor}=="2c7c", ATTRS{idProduct}=="0125", ATTR{power/wakeup}="enabled" ++ACTION=="add", SUBSYSTEM=="usb", DRIVERS=="usb", ATTRS{idVendor}=="2c7c", ATTRS{idProduct}=="0125", ATTR{avoid_reset_quirk}="1" ++ACTION=="add", SUBSYSTEM=="usb", DRIVERS=="usb", ATTRS{idVendor}=="2c7c", ATTRS{idProduct}=="0125", ATTR{power/persist}="0" +diff --git a/udev/eg25-configure-usb b/udev/eg25-configure-usb +deleted file mode 100755 +index 9972af7..0000000 +--- a/udev/eg25-configure-usb ++++ /dev/null +@@ -1,21 +0,0 @@ +-#!/bin/sh +- +-DEVPATH=/sys/$1 +-USB_ID= +- +-[ -d ${DEVPATH} ] || exit 1 +- +-while [ ! "${USB_ID}" ]; do +- if [ -f ${DEVPATH}/avoid_reset_quirk ]; then +- USB_ID=$(basename ${DEVPATH}) +- break +- fi +- DEVPATH=$(dirname ${DEVPATH}) +-done +- +-# Avoid USB resets +-echo "auto" > /sys/bus/usb/devices/${USB_ID}/power/control +-echo "3000" > /sys/bus/usb/devices/${USB_ID}/power/autosuspend_delay_ms +-echo "enabled" > /sys/bus/usb/devices/${USB_ID}/power/wakeup +-echo "1" > /sys/bus/usb/devices/${USB_ID}/avoid_reset_quirk +-echo "0" > /sys/bus/usb/devices/${USB_ID}/power/persist +diff --git a/udev/meson.build b/udev/meson.build +index fe558fc..1488660 100644 +--- a/udev/meson.build ++++ b/udev/meson.build +@@ -4,5 +4,4 @@ + # SPDX-License-Identifier: GPL-3.0-or-later + # + +-install_data ('eg25-configure-usb', install_dir: bindir) + install_data ('80-modem-eg25.rules', install_dir: udevrulesdir) +-- +GitLab + diff --git a/net-misc/eg25-manager/files/15.patch b/net-misc/eg25-manager/files/15.patch new file mode 100644 index 0000000..0f07657 --- /dev/null +++ b/net-misc/eg25-manager/files/15.patch @@ -0,0 +1,1262 @@ +From e6d410275ccf193e1a6dd21bb2200443e41ca03b Mon Sep 17 00:00:00 2001 +From: Dylan Van Assche +Date: Wed, 12 May 2021 16:41:48 +0200 +Subject: [PATCH 1/5] at: g_free doesn't require NULL checking + +From the docs: If mem is NULL it simply returns, so there is no need to check mem against NULL before calling this function. +--- + src/at.c | 15 +++++---------- + 1 file changed, 5 insertions(+), 10 deletions(-) + +diff --git a/src/at.c b/src/at.c +index a34025f..c6a90d4 100644 +--- a/src/at.c ++++ b/src/at.c +@@ -104,14 +104,10 @@ static void next_at_command(struct EG25Manager *manager) + if (!at_cmd) + return; + +- if (at_cmd->cmd) +- g_free(at_cmd->cmd); +- if (at_cmd->subcmd) +- g_free(at_cmd->subcmd); +- if (at_cmd->value) +- g_free(at_cmd->value); +- if (at_cmd->expected) +- g_free(at_cmd->expected); ++ g_free(at_cmd->cmd); ++ g_free(at_cmd->subcmd); ++ g_free(at_cmd->value); ++ g_free(at_cmd->expected); + g_free(at_cmd); + manager->at_cmds = g_list_remove(manager->at_cmds, at_cmd); + +@@ -142,8 +138,7 @@ static void process_at_result(struct EG25Manager *manager, char *response) + return; + + if (at_cmd->expected && !strstr(response, at_cmd->expected)) { +- if (at_cmd->value) +- g_free(at_cmd->value); ++ g_free(at_cmd->value); + at_cmd->value = at_cmd->expected; + at_cmd->expected = NULL; + g_message("Got a different result than expected, changing value..."); +-- +GitLab + + +From 828176baf168334744596e33b214551d8164346d Mon Sep 17 00:00:00 2001 +From: Dylan Van Assche +Date: Wed, 12 May 2021 18:21:29 +0200 +Subject: [PATCH 2/5] at: make next_at_command, send_at_command, + process_at_result, and append_at_command public methods + +Allows other modules to send AT commands as well +--- + src/at.c | 47 ++++++++++++++++++++++++----------------------- + src/at.h | 22 ++++++++++++++++------ + 2 files changed, 40 insertions(+), 29 deletions(-) + +diff --git a/src/at.c b/src/at.c +index c6a90d4..b08cf4f 100644 +--- a/src/at.c ++++ b/src/at.c +@@ -52,7 +52,7 @@ static int configure_serial(const char *tty) + return fd; + } + +-static gboolean send_at_command(struct EG25Manager *manager) ++gboolean at_send_command(struct EG25Manager *manager) + { + char command[256]; + struct AtCommand *at_cmd = manager->at_cmds ? g_list_nth_data(manager->at_cmds, 0) : NULL; +@@ -97,7 +97,7 @@ static gboolean send_at_command(struct EG25Manager *manager) + return FALSE; + } + +-static void next_at_command(struct EG25Manager *manager) ++void at_next_command(struct EG25Manager *manager) + { + struct AtCommand *at_cmd = manager->at_cmds ? g_list_nth_data(manager->at_cmds, 0) : NULL; + +@@ -111,7 +111,7 @@ static void next_at_command(struct EG25Manager *manager) + g_free(at_cmd); + manager->at_cmds = g_list_remove(manager->at_cmds, at_cmd); + +- send_at_command(manager); ++ at_send_command(manager); + } + + static void retry_at_command(struct EG25Manager *manager) +@@ -124,13 +124,14 @@ static void retry_at_command(struct EG25Manager *manager) + at_cmd->retries++; + if (at_cmd->retries > 3) { + g_critical("Command %s retried %d times, aborting...", at_cmd->cmd, at_cmd->retries); +- next_at_command(manager); ++ at_next_command(manager); + } else { +- g_timeout_add(500, G_SOURCE_FUNC(send_at_command), manager); ++ g_timeout_add(500, G_SOURCE_FUNC(at_send_command), manager); + } + } + +-static void process_at_result(struct EG25Manager *manager, char *response) ++void at_process_result(struct EG25Manager *manager, ++ const char *response) + { + struct AtCommand *at_cmd = manager->at_cmds ? g_list_nth_data(manager->at_cmds, 0) : NULL; + +@@ -143,17 +144,17 @@ static void process_at_result(struct EG25Manager *manager, char *response) + at_cmd->expected = NULL; + g_message("Got a different result than expected, changing value..."); + g_message("\t%s\n\t%s", at_cmd->expected, response); +- send_at_command(manager); ++ at_send_command(manager); + } else { +- next_at_command(manager); ++ at_next_command(manager); + } + } + +-static int append_at_command(struct EG25Manager *manager, +- const char *cmd, +- const char *subcmd, +- const char *value, +- const char *expected) ++int at_append_command(struct EG25Manager *manager, ++ const char *cmd, ++ const char *subcmd, ++ const char *value, ++ const char *expected) + { + struct AtCommand *at_cmd = calloc(1, sizeof(struct AtCommand)); + +@@ -213,10 +214,10 @@ static gboolean modem_response(gint fd, + else if (strstr(response, "ERROR")) + retry_at_command(manager); + else if (strstr(response, "OK")) +- process_at_result(manager, response); ++ at_process_result(manager, response); + else + // Not a recognized response, try running next command, just in case +- next_at_command(manager); ++ at_next_command(manager); + } + + return TRUE; +@@ -321,34 +322,34 @@ void at_sequence_configure(struct EG25Manager *manager) + { + for (guint i = 0; i < configure_commands->len; i++) { + struct AtCommand *cmd = &g_array_index(configure_commands, struct AtCommand, i); +- append_at_command(manager, cmd->cmd, cmd->subcmd, cmd->value, cmd->expected); ++ at_append_command(manager, cmd->cmd, cmd->subcmd, cmd->value, cmd->expected); + } +- send_at_command(manager); ++ at_send_command(manager); + } + + void at_sequence_suspend(struct EG25Manager *manager) + { + for (guint i = 0; i < suspend_commands->len; i++) { + struct AtCommand *cmd = &g_array_index(suspend_commands, struct AtCommand, i); +- append_at_command(manager, cmd->cmd, cmd->subcmd, cmd->value, cmd->expected); ++ at_append_command(manager, cmd->cmd, cmd->subcmd, cmd->value, cmd->expected); + } +- send_at_command(manager); ++ at_send_command(manager); + } + + void at_sequence_resume(struct EG25Manager *manager) + { + for (guint i = 0; i < resume_commands->len; i++) { + struct AtCommand *cmd = &g_array_index(resume_commands, struct AtCommand, i); +- append_at_command(manager, cmd->cmd, cmd->subcmd, cmd->value, cmd->expected); ++ at_append_command(manager, cmd->cmd, cmd->subcmd, cmd->value, cmd->expected); + } +- send_at_command(manager); ++ at_send_command(manager); + } + + void at_sequence_reset(struct EG25Manager *manager) + { + for (guint i = 0; i < reset_commands->len; i++) { + struct AtCommand *cmd = &g_array_index(reset_commands, struct AtCommand, i); +- append_at_command(manager, cmd->cmd, cmd->subcmd, cmd->value, cmd->expected); ++ at_append_command(manager, cmd->cmd, cmd->subcmd, cmd->value, cmd->expected); + } +- send_at_command(manager); ++ at_send_command(manager); + } +diff --git a/src/at.h b/src/at.h +index ba294a4..e0445af 100644 +--- a/src/at.h ++++ b/src/at.h +@@ -8,10 +8,20 @@ + + #include "manager.h" + +-int at_init(struct EG25Manager *data, toml_table_t *config); +-void at_destroy(struct EG25Manager *data); ++int at_init(struct EG25Manager *manager, toml_table_t *config); ++void at_destroy(struct EG25Manager *manager); + +-void at_sequence_configure(struct EG25Manager *data); +-void at_sequence_suspend(struct EG25Manager *data); +-void at_sequence_resume(struct EG25Manager *data); +-void at_sequence_reset(struct EG25Manager *data); ++void at_process_result(struct EG25Manager *manager, ++ const char *response); ++void at_next_command(struct EG25Manager *manager); ++gboolean at_send_command(struct EG25Manager *manager); ++int at_append_command(struct EG25Manager *manager, ++ const char *cmd, ++ const char *subcmd, ++ const char *value, ++ const char *expected); ++ ++void at_sequence_configure(struct EG25Manager *manager); ++void at_sequence_suspend(struct EG25Manager *manager); ++void at_sequence_resume(struct EG25Manager *manager); ++void at_sequence_reset(struct EG25Manager *manager); +-- +GitLab + + +From 59c701c38ddf0c861a35277615f91eb126538c6f Mon Sep 17 00:00:00 2001 +From: Dylan Van Assche +Date: Wed, 12 May 2021 18:25:40 +0200 +Subject: [PATCH 3/5] at: log expected result before setting it to NULL + +Otherwise the log contains NULL instead of the expected value, making it hard to debug +--- + src/at.c | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +diff --git a/src/at.c b/src/at.c +index b08cf4f..ccc3a2c 100644 +--- a/src/at.c ++++ b/src/at.c +@@ -141,9 +141,9 @@ void at_process_result(struct EG25Manager *manager, + if (at_cmd->expected && !strstr(response, at_cmd->expected)) { + g_free(at_cmd->value); + at_cmd->value = at_cmd->expected; +- at_cmd->expected = NULL; + g_message("Got a different result than expected, changing value..."); +- g_message("\t%s\n\t%s", at_cmd->expected, response); ++ g_message("Expected: [%s]\nResponse: [%s]", at_cmd->expected, response); ++ at_cmd->expected = NULL; + at_send_command(manager); + } else { + at_next_command(manager); +-- +GitLab + + +From e691f830f9f2ccad9916bd1e1720b42ce6a57269 Mon Sep 17 00:00:00 2001 +From: Dylan Van Assche +Date: Wed, 12 May 2021 18:34:03 +0200 +Subject: [PATCH 4/5] at: allow custom callbacks for AT command response + processing + +--- + src/at.c | 32 +++++++++++++++++--------------- + src/at.h | 14 +++++++++++++- + src/manager.h | 1 + + 3 files changed, 31 insertions(+), 16 deletions(-) + +diff --git a/src/at.c b/src/at.c +index ccc3a2c..1f3ad8c 100644 +--- a/src/at.c ++++ b/src/at.c +@@ -16,14 +16,6 @@ + + #include + +-struct AtCommand { +- char *cmd; +- char *subcmd; +- char *value; +- char *expected; +- int retries; +-}; +- + static GArray *configure_commands = NULL; + static GArray *suspend_commands = NULL; + static GArray *resume_commands = NULL; +@@ -69,6 +61,7 @@ gboolean at_send_command(struct EG25Manager *manager) + len = sprintf(command, "AT+%s=\"%s\"\r\n", at_cmd->cmd, at_cmd->subcmd); + else if (at_cmd->subcmd && at_cmd->value) + len = sprintf(command, "AT+%s=\"%s\",%s\r\n", at_cmd->cmd, at_cmd->subcmd, at_cmd->value); ++ manager->at_callback = at_cmd->callback; + + ret = write(manager->at_fd, command, len); + if (ret < len) +@@ -154,7 +147,10 @@ int at_append_command(struct EG25Manager *manager, + const char *cmd, + const char *subcmd, + const char *value, +- const char *expected) ++ const char *expected, ++ void (*callback) ++ (struct EG25Manager *manager, ++ const char *response)) + { + struct AtCommand *at_cmd = calloc(1, sizeof(struct AtCommand)); + +@@ -168,6 +164,8 @@ int at_append_command(struct EG25Manager *manager, + at_cmd->value = g_strdup(value); + if (expected) + at_cmd->expected = g_strdup(expected); ++ if (callback) ++ at_cmd->callback = callback; + + manager->at_cmds = g_list_append(manager->at_cmds, at_cmd); + +@@ -213,8 +211,12 @@ static gboolean modem_response(gint fd, + } + else if (strstr(response, "ERROR")) + retry_at_command(manager); +- else if (strstr(response, "OK")) +- at_process_result(manager, response); ++ else if (strstr(response, "OK")) { ++ if (manager->at_callback != NULL) ++ manager->at_callback(manager, response); ++ else ++ g_warning("AT command succesfull but no callback registered"); ++ } + else + // Not a recognized response, try running next command, just in case + at_next_command(manager); +@@ -322,7 +324,7 @@ void at_sequence_configure(struct EG25Manager *manager) + { + for (guint i = 0; i < configure_commands->len; i++) { + struct AtCommand *cmd = &g_array_index(configure_commands, struct AtCommand, i); +- at_append_command(manager, cmd->cmd, cmd->subcmd, cmd->value, cmd->expected); ++ at_append_command(manager, cmd->cmd, cmd->subcmd, cmd->value, cmd->expected, at_process_result); + } + at_send_command(manager); + } +@@ -331,7 +333,7 @@ void at_sequence_suspend(struct EG25Manager *manager) + { + for (guint i = 0; i < suspend_commands->len; i++) { + struct AtCommand *cmd = &g_array_index(suspend_commands, struct AtCommand, i); +- at_append_command(manager, cmd->cmd, cmd->subcmd, cmd->value, cmd->expected); ++ at_append_command(manager, cmd->cmd, cmd->subcmd, cmd->value, cmd->expected, at_process_result); + } + at_send_command(manager); + } +@@ -340,7 +342,7 @@ void at_sequence_resume(struct EG25Manager *manager) + { + for (guint i = 0; i < resume_commands->len; i++) { + struct AtCommand *cmd = &g_array_index(resume_commands, struct AtCommand, i); +- at_append_command(manager, cmd->cmd, cmd->subcmd, cmd->value, cmd->expected); ++ at_append_command(manager, cmd->cmd, cmd->subcmd, cmd->value, cmd->expected, at_process_result); + } + at_send_command(manager); + } +@@ -349,7 +351,7 @@ void at_sequence_reset(struct EG25Manager *manager) + { + for (guint i = 0; i < reset_commands->len; i++) { + struct AtCommand *cmd = &g_array_index(reset_commands, struct AtCommand, i); +- at_append_command(manager, cmd->cmd, cmd->subcmd, cmd->value, cmd->expected); ++ at_append_command(manager, cmd->cmd, cmd->subcmd, cmd->value, cmd->expected, at_process_result); + } + at_send_command(manager); + } +diff --git a/src/at.h b/src/at.h +index e0445af..0364e2c 100644 +--- a/src/at.h ++++ b/src/at.h +@@ -8,6 +8,15 @@ + + #include "manager.h" + ++typedef struct AtCommand { ++ char *cmd; ++ char *subcmd; ++ char *value; ++ char *expected; ++ void (*callback)(struct EG25Manager *manager, const char *response); ++ int retries; ++} AtCommand; ++ + int at_init(struct EG25Manager *manager, toml_table_t *config); + void at_destroy(struct EG25Manager *manager); + +@@ -19,7 +28,10 @@ int at_append_command(struct EG25Manager *manager, + const char *cmd, + const char *subcmd, + const char *value, +- const char *expected); ++ const char *expected, ++ void (*callback) ++ (struct EG25Manager *manager, ++ const char *response)); + + void at_sequence_configure(struct EG25Manager *manager); + void at_sequence_suspend(struct EG25Manager *manager); +diff --git a/src/manager.h b/src/manager.h +index 30028e6..3cc0118 100644 +--- a/src/manager.h ++++ b/src/manager.h +@@ -47,6 +47,7 @@ struct EG25Manager { + int at_fd; + guint at_source; + GList *at_cmds; ++ void (*at_callback)(struct EG25Manager *manager, const char *response); + + enum EG25State modem_state; + gchar *modem_usb_id; +-- +GitLab + + +From 6ebf4b78c0cb026c4b3c8429c122425bfbc437c6 Mon Sep 17 00:00:00 2001 +From: Dylan Van Assche +Date: Wed, 12 May 2021 19:37:21 +0200 +Subject: [PATCH 5/5] gnss: add GNSS assistance support + +Automatically fetch the GNSS assistance data from the Web +to heavily reduce the time to acquire a GNSS lock by uploading +the assistance data to the modem. +This feature is optional and can be disabled in the configuration +as people may prefer to not download the assistance data from +the Quectel/Qualcomm servers. +Also configure the GNSS engine to optimize the performance +and power consumption. +--- + data/pine64,pinephone-1.0.toml | 26 +- + data/pine64,pinephone-1.1.toml | 26 +- + data/pine64,pinephone-1.2.toml | 26 +- + meson.build | 1 + + src/at.c | 22 +- + src/gnss.c | 478 +++++++++++++++++++++++++++++++++ + src/gnss.h | 16 ++ + src/manager.c | 2 + + src/manager.h | 34 +++ + src/meson.build | 1 + + src/mm-iface.c | 5 +- + 11 files changed, 625 insertions(+), 12 deletions(-) + create mode 100644 src/gnss.c + create mode 100644 src/gnss.h + +diff --git a/data/pine64,pinephone-1.0.toml b/data/pine64,pinephone-1.0.toml +index e1f5b24..e127f1a 100644 +--- a/data/pine64,pinephone-1.0.toml ++++ b/data/pine64,pinephone-1.0.toml +@@ -43,7 +43,6 @@ configure = [ + { cmd = "QCFG", subcmd = "urc/ri/other", expect = "\"off\",1,1" }, + { cmd = "QCFG", subcmd = "urc/delay", expect = "1" }, + { cmd = "QURCCFG", subcmd = "urcport", expect = "\"all\"" }, +- { cmd = "QGPS", value = "1" }, + { cmd = "QSCLK", value = "1" }, + # Reset modem-power configurations to what we expect + { cmd = "QCFG", subcmd = "urc/cache", expect = "0" }, +@@ -54,11 +53,32 @@ configure = [ + { cmd = "QCFG", subcmd = "ApRstLevel", expect = "0" }, + { cmd = "QCFG", subcmd = "ModemRstLevel", expect = "0" }, + { cmd = "QCFG", subcmd = "urc/ri/pin", expect = "uart_ri" }, ++# GNSS configuration: ++# * Enable A-GPS data upload support (XTRA) ++# * Disable On-Demand-Positioning (ODP) mode ++# to avoid running the GNSS system in the background, even when not enabled. ++# * Enable Dynamic Power Optimizations (DPO) mode to turn off GNSS RF radios ++# when they are not in use. ++# * Only enable GPS and GLONASS, disable other GNSS systems. ++# A-GPS data upload doesn't work for Galileo anyway. ++# * Avoid turning on GNSS support automatically when the modem boots. ++ { cmd = "QGPSXTRA", expect = "1" }, ++ { cmd = "QGPSCFG", subcmd = "gnssconfig", expect = "4" }, ++ { cmd = "QGPSCFG", subcmd = "odpcontrol", expect = "0" }, ++ { cmd = "QGPSCFG", subcmd = "dpoenable", expect = "1" }, ++ { cmd = "QGPSCFG", subcmd = "gpsnmeatype", expect = "31" }, ++ { cmd = "QGPSCFG", subcmd = "glonassnmeatype", expect = "7" }, ++ { cmd = "QGPSCFG", subcmd = "galileonmeatype", expect = "0" }, ++ { cmd = "QGPSCFG", subcmd = "beidounmeatype", expect = "0" }, ++ { cmd = "QGPSCFG", subcmd = "autogps", expect = "0" }, + ] + suspend = [ +- { cmd = "QGPSEND" }, + ] + resume = [ +- { cmd = "QGPS", value = "1" } + ] + reset = [ { cmd = "CFUN", value = "1,1" } ] ++ ++[gnss] ++enabled = true ++url = "https://xtrapath4.izatcloud.net" ++file = "xtra2.bin" +diff --git a/data/pine64,pinephone-1.1.toml b/data/pine64,pinephone-1.1.toml +index e1f5b24..e127f1a 100644 +--- a/data/pine64,pinephone-1.1.toml ++++ b/data/pine64,pinephone-1.1.toml +@@ -43,7 +43,6 @@ configure = [ + { cmd = "QCFG", subcmd = "urc/ri/other", expect = "\"off\",1,1" }, + { cmd = "QCFG", subcmd = "urc/delay", expect = "1" }, + { cmd = "QURCCFG", subcmd = "urcport", expect = "\"all\"" }, +- { cmd = "QGPS", value = "1" }, + { cmd = "QSCLK", value = "1" }, + # Reset modem-power configurations to what we expect + { cmd = "QCFG", subcmd = "urc/cache", expect = "0" }, +@@ -54,11 +53,32 @@ configure = [ + { cmd = "QCFG", subcmd = "ApRstLevel", expect = "0" }, + { cmd = "QCFG", subcmd = "ModemRstLevel", expect = "0" }, + { cmd = "QCFG", subcmd = "urc/ri/pin", expect = "uart_ri" }, ++# GNSS configuration: ++# * Enable A-GPS data upload support (XTRA) ++# * Disable On-Demand-Positioning (ODP) mode ++# to avoid running the GNSS system in the background, even when not enabled. ++# * Enable Dynamic Power Optimizations (DPO) mode to turn off GNSS RF radios ++# when they are not in use. ++# * Only enable GPS and GLONASS, disable other GNSS systems. ++# A-GPS data upload doesn't work for Galileo anyway. ++# * Avoid turning on GNSS support automatically when the modem boots. ++ { cmd = "QGPSXTRA", expect = "1" }, ++ { cmd = "QGPSCFG", subcmd = "gnssconfig", expect = "4" }, ++ { cmd = "QGPSCFG", subcmd = "odpcontrol", expect = "0" }, ++ { cmd = "QGPSCFG", subcmd = "dpoenable", expect = "1" }, ++ { cmd = "QGPSCFG", subcmd = "gpsnmeatype", expect = "31" }, ++ { cmd = "QGPSCFG", subcmd = "glonassnmeatype", expect = "7" }, ++ { cmd = "QGPSCFG", subcmd = "galileonmeatype", expect = "0" }, ++ { cmd = "QGPSCFG", subcmd = "beidounmeatype", expect = "0" }, ++ { cmd = "QGPSCFG", subcmd = "autogps", expect = "0" }, + ] + suspend = [ +- { cmd = "QGPSEND" }, + ] + resume = [ +- { cmd = "QGPS", value = "1" } + ] + reset = [ { cmd = "CFUN", value = "1,1" } ] ++ ++[gnss] ++enabled = true ++url = "https://xtrapath4.izatcloud.net" ++file = "xtra2.bin" +diff --git a/data/pine64,pinephone-1.2.toml b/data/pine64,pinephone-1.2.toml +index 4ca1274..e16f4f5 100644 +--- a/data/pine64,pinephone-1.2.toml ++++ b/data/pine64,pinephone-1.2.toml +@@ -41,7 +41,6 @@ configure = [ + { cmd = "QCFG", subcmd = "urc/ri/other", expect = "\"off\",1,1" }, + { cmd = "QCFG", subcmd = "urc/delay", expect = "0" }, + { cmd = "QURCCFG", subcmd = "urcport", expect = "\"all\"" }, +- { cmd = "QGPS", value = "1" }, + { cmd = "QSCLK", value = "1" }, + # Reset modem-power configurations to what we expect + { cmd = "QCFG", subcmd = "urc/cache", expect = "0" }, +@@ -51,11 +50,32 @@ configure = [ + { cmd = "QCFG", subcmd = "ApRstLevel", expect = "0" }, + { cmd = "QCFG", subcmd = "ModemRstLevel", expect = "0" }, + { cmd = "QCFG", subcmd = "urc/ri/pin", expect = "uart_ri" }, ++# GNSS configuration: ++# * Enable A-GPS data upload support (XTRA) ++# * Disable On-Demand-Positioning (ODP) mode ++# to avoid running the GNSS system in the background, even when not enabled. ++# * Enable Dynamic Power Optimizations (DPO) mode to turn off GNSS RF radios ++# when they are not in use. ++# * Only enable GPS and GLONASS, disable other GNSS systems. ++# A-GPS data upload doesn't work for Galileo anyway. ++# * Avoid turning on GNSS support automatically when the modem boots. ++ { cmd = "QGPSXTRA", expect = "1" }, ++ { cmd = "QGPSCFG", subcmd = "gnssconfig", expect = "4" }, ++ { cmd = "QGPSCFG", subcmd = "odpcontrol", expect = "0" }, ++ { cmd = "QGPSCFG", subcmd = "dpoenable", expect = "1" }, ++ { cmd = "QGPSCFG", subcmd = "gpsnmeatype", expect = "31" }, ++ { cmd = "QGPSCFG", subcmd = "glonassnmeatype", expect = "7" }, ++ { cmd = "QGPSCFG", subcmd = "galileonmeatype", expect = "0" }, ++ { cmd = "QGPSCFG", subcmd = "beidounmeatype", expect = "0" }, ++ { cmd = "QGPSCFG", subcmd = "autogps", expect = "0" }, + ] + suspend = [ +- { cmd = "QGPSEND" }, + ] + resume = [ +- { cmd = "QGPS", value = "1" } + ] + reset = [ { cmd = "CFUN", value = "1,1" } ] ++ ++[gnss] ++enabled = true ++url = "https://xtrapath4.izatcloud.net" ++file = "xtra2.bin" +index d57d2cb..2852778 100644 +--- a/meson.build ++++ b/meson.build +@@ -58,6 +58,7 @@ mgr_deps = [ + dependency('gudev-1.0'), + dependency('libgpiod'), + dependency('libusb-1.0'), ++ dependency('libcurl'), + mmglib_dep, + ] + +diff --git a/src/at.c b/src/at.c +index 1f3ad8c..49cf213 100644 +--- a/src/at.c ++++ b/src/at.c +@@ -7,6 +7,7 @@ + #include "at.h" + #include "suspend.h" + #include "gpio.h" ++#include "gnss.h" + + #include + #include +@@ -216,24 +216,40 @@ static gboolean modem_response(gint fd, + + g_message("Response: [%s]", response); + ++ /* ++ * When the modem is started, it outputs 'RDY' to indicate that ++ * it is ready to receive AT commands. ++ */ + if (strcmp(response, "RDY") == 0) { + suspend_inhibit(manager, TRUE, TRUE); + manager->modem_state = EG25_STATE_STARTED; + } + /* ++ * Search for 'QGPSURC: "xtradataexpire"' in response to detect ++ * if GNSS assistance data must be re-uploaded. ++ * If that's the case, check if no AT commands are being processed ++ * to avoid deadlocks and start upload. ++ */ ++ else if (strstr(response, "QGPSURC: \"xtradataexpire\"") && manager->at_cmds == NULL) ++ gnss_upload_assistance_data(manager); ++ /* + * QCFG="fast/poweroff" configuration is only available in + * newer firmware versions + */ + else if (strstr(response, "ERROR") && !strstr(response, "fast/poweroff")) + retry_at_command(manager); ++ /* ++ * Successfull AT responses contain 'OK', except for AT+QFUPL which also ++ * returns 'CONNECT' when the modem is ready to receive data over serial ++ */ + else if (strstr(response, "OK")) { + if (manager->at_callback != NULL) + manager->at_callback(manager, response); + else + g_warning("AT command succesfull but no callback registered"); + } ++ /* Not a recognized response, try running next command, just in case */ + else +- // Not a recognized response, try running next command, just in case + at_next_command(manager); + } + +diff --git a/src/gnss.c b/src/gnss.c +new file mode 100644 +index 0000000..d986be0 +--- /dev/null ++++ b/src/gnss.c +@@ -0,0 +1,478 @@ ++/* ++ * Copyright (C) 2021 Dylan Van Assche ++ * ++ * SPDX-License-Identifier: GPL-3.0-or-later ++ */ ++ ++#include "gnss.h" ++#include "manager.h" ++#include "at.h" ++ ++#define BUFFER_SIZE 256 ++#define UPLOAD_DELAY 100000 ++#define RESCHEDULE_IN_SECS 30 ++ ++static void gnss_step(struct EG25Manager *manager); ++ ++void gnss_upload_assistance_data(struct EG25Manager *manager) ++{ ++ if (!manager->gnss_assistance_enabled) { ++ g_message("GNSS assistance is disabled!"); ++ return; ++ } ++ ++ if (manager->gnss_assistance_step < EG25_GNSS_STEP_LAST) { ++ g_warning("GNSS assistance data upload already in process (%d/%d)", ++ manager->gnss_assistance_step, EG25_GNSS_STEP_LAST); ++ return; ++ } ++ manager->gnss_assistance_step = EG25_GNSS_STEP_FIRST; ++ gnss_step(manager); ++} ++ ++void gnss_init(struct EG25Manager *manager, toml_table_t *config) ++{ ++ toml_datum_t enabled; ++ toml_datum_t url; ++ toml_datum_t file; ++ g_autoptr (GError) error = NULL; ++ ++ /* ++ * GNSS assistance is an optional feature, you can disable it ++ * if you want in the configuration file. ++ * In case the configuration is missing, we assume GNSS assistance ++ * to be disabled. ++ */ ++ enabled = toml_bool_in(config, "enabled"); ++ manager->gnss_assistance_enabled = FALSE; ++ if (enabled.ok) ++ manager->gnss_assistance_enabled = enabled.u.b; ++ ++ if (!manager->gnss_assistance_enabled) { ++ g_message("GNSS assistance is disabled!"); ++ return; ++ } ++ ++ url = toml_string_in(config, "url"); ++ if (url.ok) ++ manager->gnss_assistance_url = url.u.s; ++ else ++ g_error("GNSS assistance server URL is missing from config file"); ++ file = toml_string_in(config, "file"); ++ if (file.ok) ++ manager->gnss_assistance_file = file.u.s; ++ else ++ g_error("GNSS assistance file name is missing from config file"); ++ ++ /* Create temporary file to store assistance data */ ++ manager->gnss_assistance_fd = g_file_open_tmp(NULL, NULL, &error); ++ if (error != NULL) ++ g_error ("Unable to create temporary file: %s", error->message); ++ ++ /* Initialize state and schedule upload */ ++ manager->gnss_assistance_step = EG25_GNSS_STEP_LAST; ++ g_timeout_add_seconds(RESCHEDULE_IN_SECS, ++ G_SOURCE_FUNC(gnss_upload_assistance_data), manager); ++} ++ ++void gnss_destroy(struct EG25Manager *manager) ++{ ++ close(manager->gnss_assistance_fd); ++} ++ ++/******************************************************************************/ ++ ++#ifdef HAVE_MMGLIB ++static void disable_mm_gnss(struct EG25Manager *manager) ++{ ++ MMModemLocationSource sources; ++ gboolean signals_location; ++ g_autoptr (GError) error = NULL; ++ ++ sources = mm_modem_location_get_enabled(manager->mm_location); ++ signals_location = mm_modem_location_signals_location(manager->mm_location); ++ manager->gnss_sources = EG25_GNSS_SOURCE_NONE; ++ ++ /* Save GNSS engine state */ ++ if (sources & MM_MODEM_LOCATION_SOURCE_GPS_NMEA) ++ manager->gnss_sources |= EG25_GNSS_SOURCE_NMEA; ++ else ++ manager->gnss_sources &= ~EG25_GNSS_SOURCE_NMEA; ++ ++ if (sources & MM_MODEM_LOCATION_SOURCE_GPS_RAW) ++ manager->gnss_sources |= EG25_GNSS_SOURCE_RAW; ++ else ++ manager->gnss_sources &= ~EG25_GNSS_SOURCE_RAW; ++ ++ if (sources & MM_MODEM_LOCATION_SOURCE_GPS_UNMANAGED) ++ manager->gnss_sources |= EG25_GNSS_SOURCE_UNMANAGED; ++ else ++ manager->gnss_sources &= ~EG25_GNSS_SOURCE_UNMANAGED; ++ ++ /* Disable GNSS engine */ ++ sources &= ~MM_MODEM_LOCATION_SOURCE_GPS_RAW; ++ sources &= ~MM_MODEM_LOCATION_SOURCE_GPS_NMEA; ++ sources &= ~MM_MODEM_LOCATION_SOURCE_GPS_UNMANAGED; ++ mm_modem_location_setup_sync(manager->mm_location, sources, ++ signals_location, NULL, &error); ++ if (error != NULL) { ++ g_warning("Unable to disable GNSS engine through ModemManager: %s", ++ error->message); ++ } ++} ++#endif ++ ++static void disable_at_gnss_cb(struct EG25Manager *manager, ++ const char *response) ++{ ++ /* Clear QGPSEND AT command and process next */ ++ at_next_command(manager); ++ ++ /* Go to the next step */ ++ manager->gnss_assistance_step++; ++ gnss_step(manager); ++} ++ ++static void state_at_gnss_cb(struct EG25Manager *manager, const char *response) ++{ ++ struct AtCommand *at_cmd = manager->at_cmds ? g_list_nth_data(manager->at_cmds, 0) : NULL; ++ ++ if (!at_cmd) ++ return; ++ ++ /* Parse GNSS engine status and disable it if needed */ ++ if (strstr(response, "QGPS: 1")) { ++ manager->gnss_sources |= EG25_GNSS_SOURCE_QGPS; ++ g_free(at_cmd->value); ++ g_free(at_cmd->cmd); ++ at_cmd->expected = NULL; ++ at_cmd->subcmd = NULL; ++ at_cmd->value = NULL; ++ at_cmd->cmd = g_strdup("QGPSEND"); ++ at_cmd->callback = disable_at_gnss_cb; ++ at_send_command(manager); ++ } ++ /* QGPS is already disabled, move on to next step */ ++ else { ++ at_next_command(manager); ++ manager->gnss_sources &= ~EG25_GNSS_SOURCE_QGPS; ++ manager->gnss_assistance_step++; ++ gnss_step(manager); ++ } ++} ++ ++static void state_at_gnss(struct EG25Manager *manager) ++{ ++ /* Asynchronously send AT command to query status of GNSS engine */ ++ at_append_command(manager, "QGPS?", NULL, NULL, NULL, state_at_gnss_cb); ++ at_send_command(manager); ++} ++ ++/******************************************************************************/ ++ ++static void fetch_assistance_data(struct EG25Manager *manager) ++{ ++ CURL *curl; ++ CURLcode response; ++ long status_code; ++ gchar *url = NULL; ++ FILE *tmp_file = NULL; ++ long int size; ++ ++ /* Fetch assistance data with curl */ ++ tmp_file = fdopen(manager->gnss_assistance_fd, "wb"); ++ url = g_strconcat(manager->gnss_assistance_url, "/", ++ manager->gnss_assistance_file, NULL); ++ curl = curl_easy_init(); ++ if (!curl) ++ g_error ("Unable to initialize curl"); ++ curl_easy_setopt(curl, CURLOPT_URL, url); ++ curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, NULL); ++ curl_easy_setopt(curl, CURLOPT_WRITEDATA, tmp_file); ++ curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L); ++ curl_easy_setopt(curl, CURLOPT_FAILONERROR, 1L); ++ response = curl_easy_perform(curl); ++ if (response == CURLE_HTTP_RETURNED_ERROR) { ++ curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &status_code); ++ curl_easy_cleanup(curl); ++ g_warning ("Unable to fetch GNSS assistance data from %s (HTTP %ld)", ++ url, status_code); ++ ++ /* Restart upload on HTTP error status code */ ++ g_message ("Rescheduling upload because of failure in %ds", ++ RESCHEDULE_IN_SECS); ++ manager->gnss_assistance_step = EG25_GNSS_STEP_LAST; ++ g_timeout_add_seconds(RESCHEDULE_IN_SECS, ++ G_SOURCE_FUNC(gnss_upload_assistance_data), ++ manager); ++ return; ++ } ++ ++ /* Get file size in bytes */ ++ size = (long int)lseek(manager->gnss_assistance_fd, 0, SEEK_END); ++ lseek(manager->gnss_assistance_fd, 0, SEEK_SET); ++ ++ if (size <= 0) { ++ g_warning ("GNSS assistance data contains 0 bytes," ++ "check network connection."); ++ /* ++ * Restart upload when file does not contain any data, ++ * mostly because of no network connection. ++ */ ++ g_message ("Rescheduling upload because of failure in %ds", ++ RESCHEDULE_IN_SECS); ++ manager->gnss_assistance_step = EG25_GNSS_STEP_LAST; ++ g_timeout_add_seconds(RESCHEDULE_IN_SECS, ++ G_SOURCE_FUNC(gnss_upload_assistance_data), ++ manager); ++ return; ++ } ++ ++ g_message("Fetching GNSS assistance data from %s was successfull", url); ++ curl_easy_cleanup(curl); ++ g_free(url); ++ ++ /* Go to the next step */ ++ manager->gnss_assistance_step++; ++ gnss_step(manager); ++} ++ ++/******************************************************************************/ ++ ++static void init_assistance_data_upload_ready(struct EG25Manager *manager, ++ const char *response) ++{ ++ /* Search for 'CONNECT' in response to start upload */ ++ if (strstr(response, "CONNECT")) { ++ g_message("Modem ready for GNSS assistance data upload"); ++ manager->gnss_assistance_step++; ++ gnss_step(manager); ++ } ++} ++ ++static void init_assistance_data_upload_start(struct EG25Manager *manager, ++ const char *response) ++{ ++ gchar value[BUFFER_SIZE]; ++ long int size; ++ ++ /* Process AT response */ ++ at_process_result(manager, response); ++ ++ /* Get file size in bytes */ ++ size = (long int)lseek(manager->gnss_assistance_fd, 0, SEEK_END); ++ lseek(manager->gnss_assistance_fd, 0, SEEK_SET); ++ ++ /* Start upload */ ++ g_snprintf(value, BUFFER_SIZE, "\"RAM:%s\",%ld\r\n", ++ manager->gnss_assistance_file, size); ++ g_message("Initiate GNSS assistance data upload: %s", value); ++ at_append_command(manager, "QFUPL", NULL, value, NULL, ++ init_assistance_data_upload_ready); ++ at_send_command(manager); ++} ++ ++static void init_assistance_data_upload(struct EG25Manager *manager) ++{ ++ /* ++ * Delete all previous GNSS assistance data files in RAM ++ * and start uploading the latest one to RAM. ++ */ ++ at_append_command(manager, "QFDEL", NULL, "\"RAM:*\"\r\n", ++ NULL, init_assistance_data_upload_start); ++ at_send_command(manager); ++} ++ ++static void upload_assistance_data(struct EG25Manager *manager) ++{ ++ char buffer[2*BUFFER_SIZE]; ++ gint len; ++ gboolean success = TRUE; ++ ++ /* Copy downloaded XTRA assistance data to the modem over serial */ ++ while((len = read(manager->gnss_assistance_fd, buffer, 2*BUFFER_SIZE)) > 0) ++ { ++ len = write(manager->at_fd, buffer, len); ++ if (len < 0) { ++ success = FALSE; ++ g_error("Writing GNSS assistance data failed: %d", len); ++ break; ++ } ++ usleep(UPLOAD_DELAY); ++ g_message("Uploaded %d bytes", len); ++ } ++ ++ /* Clear QFUPL AT command and process next */ ++ at_next_command(manager); ++ ++ /* Go to the next step if successful */ ++ if (success) { ++ manager->gnss_assistance_step++; ++ gnss_step(manager); ++ } ++ /* Restart upload */ ++ else { ++ g_message ("Rescheduling upload because of failure in %ds", ++ RESCHEDULE_IN_SECS); ++ manager->gnss_assistance_step = EG25_GNSS_STEP_LAST; ++ g_timeout_add_seconds(RESCHEDULE_IN_SECS, ++ G_SOURCE_FUNC(gnss_upload_assistance_data), ++ manager); ++ } ++} ++ ++static void finish_assistance_data_upload_cb(struct EG25Manager *manager, ++ const char *response) ++{ ++ /* Process response */ ++ at_process_result(manager, response); ++ g_message("GNSS assistance data upload finished"); ++ ++ /* Go to the next step */ ++ manager->gnss_assistance_step++; ++ gnss_step(manager); ++} ++ ++static void finish_assistance_data_upload(struct EG25Manager *manager) ++{ ++ gchar value[BUFFER_SIZE]; ++ GDateTime *datetime; ++ gchar *timestring; ++ ++ /* Configure GNSS assistance clock to current system time (UTC) */ ++ datetime = g_date_time_new_now_utc(); ++ timestring = g_date_time_format(datetime, "0,\"%Y/%m/%d,%H:%M:%S\"\r\n"); ++ g_message("Setting GNSS assistance UTC clock to: %s", timestring); ++ at_append_command(manager, "QGPSXTRATIME", NULL, timestring, NULL, ++ at_process_result); ++ ++ /* Configure GNSS engine to use uploaded GNSS assistance data */ ++ g_snprintf(value, BUFFER_SIZE, "\"RAM:%s\"\r\n", ++ manager->gnss_assistance_file); ++ g_message("Setting GNSS assistance file to: %s", value); ++ at_append_command(manager, "QGPSXTRADATA", NULL, value, NULL, ++ finish_assistance_data_upload_cb); ++ at_send_command(manager); ++} ++ ++/******************************************************************************/ ++ ++#ifdef HAVE_MMGLIB ++static void enable_mm_gnss(struct EG25Manager *manager) ++{ ++ MMModemLocationSource sources; ++ gboolean signal_location; ++ g_autoptr (GError) error = NULL; ++ ++ if (manager->gnss_sources & EG25_GNSS_SOURCE_UNMANAGED) ++ sources |= MM_MODEM_LOCATION_SOURCE_GPS_UNMANAGED; ++ if (manager->gnss_sources & EG25_GNSS_SOURCE_NMEA) ++ sources |= MM_MODEM_LOCATION_SOURCE_GPS_NMEA; ++ if (manager->gnss_sources & EG25_GNSS_SOURCE_RAW) ++ sources |= MM_MODEM_LOCATION_SOURCE_GPS_RAW; ++ ++ sources = mm_modem_location_get_enabled(manager->mm_location); ++ signal_location = mm_modem_location_signals_location(manager->mm_location); ++ mm_modem_location_setup_sync(manager->mm_location, sources, ++ signal_location, NULL, &error); ++ if (error != NULL) ++ g_warning("Unable to enable GNSS engine through ModemManager: %s", ++ error->message); ++} ++#endif ++ ++static void enable_at_gnss_cb(struct EG25Manager *manager, const char *response) ++{ ++ manager->gnss_assistance_step++; ++ gnss_step(manager); ++} ++ ++static void enable_at_gnss(struct EG25Manager *manager) ++{ ++ if (manager->gnss_sources & EG25_GNSS_SOURCE_QGPS) { ++ at_append_command(manager, "QGPS", NULL, "1", NULL, ++ enable_at_gnss_cb); ++ at_send_command(manager); ++ return; ++ } ++ manager->gnss_assistance_step++; ++ gnss_step(manager); ++} ++ ++/******************************************************************************/ ++ ++void gnss_step(struct EG25Manager *manager) ++{ ++ switch(manager->gnss_assistance_step) { ++ case EG25_GNSS_STEP_FIRST: ++ manager->gnss_assistance_step++; ++ g_message("GNSS assistance upload started..."); ++ /* fall-through */ ++ ++#ifdef HAVE_MMGLIB ++ case EG25_GNSS_STEP_MM_GNSS_DISABLE: ++ g_message("GNSS assistance upload step (%d/%d): " ++ "disabling GNSS engine through ModemManager", ++ manager->gnss_assistance_step, EG25_GNSS_STEP_LAST); ++ disable_mm_gnss(manager); ++ manager->gnss_assistance_step++; ++ /* fall-through */ ++#endif ++ ++ case EG25_GNSS_STEP_AT_GNSS_DISABLE: ++ g_message("GNSS assistance upload step (%d/%d): " ++ "disabling GNSS engine through AT+QGPS", ++ manager->gnss_assistance_step, EG25_GNSS_STEP_LAST); ++ state_at_gnss(manager); ++ break; ++ ++ case EG25_GNSS_STEP_FETCH_ASSISTANCE_DATA: ++ g_message("GNSS assistance upload step (%d/%d): " ++ "fetching assistance data", ++ manager->gnss_assistance_step, EG25_GNSS_STEP_LAST); ++ fetch_assistance_data(manager); ++ break; ++ ++ case EG25_GNSS_STEP_INIT_UPLOAD: ++ g_message("GNSS assistance upload step (%d/%d): initiating upload", ++ manager->gnss_assistance_step, EG25_GNSS_STEP_LAST); ++ init_assistance_data_upload(manager); ++ break; ++ ++ case EG25_GNSS_STEP_UPLOAD: ++ g_message("GNSS assistance upload step (%d/%d): " ++ "uploading assistance data", ++ manager->gnss_assistance_step, EG25_GNSS_STEP_LAST); ++ upload_assistance_data(manager); ++ break; ++ ++ case EG25_GNSS_STEP_FINISH_UPLOAD: ++ g_message("GNSS assistance upload step (%d/%d): finishing upload", ++ manager->gnss_assistance_step, EG25_GNSS_STEP_LAST); ++ finish_assistance_data_upload(manager); ++ break; ++ ++#ifdef HAVE_MMGLIB ++ case EG25_GNSS_STEP_MM_GNSS_ENABLE: ++ g_message("GNSS assistance upload step (%d/%d): " ++ "re-enabling GNSS through ModemManager", ++ manager->gnss_assistance_step, EG25_GNSS_STEP_LAST); ++ enable_mm_gnss(manager); ++ manager->gnss_assistance_step++; ++ /* fall-through */ ++#endif ++ ++ case EG25_GNSS_STEP_AT_QGPS_ENABLE: ++ g_message("GNSS assistance upload step (%d/%d): " ++ "re-enabling GNSS through AT+QGPS", ++ manager->gnss_assistance_step, EG25_GNSS_STEP_LAST); ++ enable_at_gnss(manager); ++ break; ++ ++ case EG25_GNSS_STEP_LAST: ++ g_message("GNSS assistance upload step (%d/%d): finished", ++ manager->gnss_assistance_step, EG25_GNSS_STEP_LAST); ++ break; ++ } ++} ++ +diff --git a/src/gnss.h b/src/gnss.h +new file mode 100644 +index 0000000..931ab8b +--- /dev/null ++++ b/src/gnss.h +@@ -0,0 +1,16 @@ ++/* ++ * Copyright (C) 2021 Dylan Van Assche ++ * ++ * SPDX-License-Identifier: GPL-3.0-or-later ++ */ ++ ++#pragma once ++ ++#include ++#include ++ ++#include "manager.h" ++ ++void gnss_init(struct EG25Manager *manager, toml_table_t *config); ++void gnss_destroy(struct EG25Manager *manager); ++void gnss_upload_assistance_data(struct EG25Manager *manager); +diff --git a/src/manager.c b/src/manager.c +index c0b8a43..d13a2a0 100644 +--- a/src/manager.c ++++ b/src/manager.c +@@ -15,6 +15,7 @@ + #include "ofono-iface.h" + #include "suspend.h" + #include "udev.h" ++#include "gnss.h" + + #include + #include +@@ -342,6 +343,7 @@ int main(int argc, char *argv[]) + ofono_iface_init(&manager); + suspend_init(&manager, toml_table_in(toml_config, "suspend")); + udev_init(&manager, toml_table_in(toml_config, "udev")); ++ gnss_init(&manager, toml_table_in(toml_config, "gnss")); + + g_idle_add(G_SOURCE_FUNC(modem_start), &manager); + +diff --git a/src/manager.h b/src/manager.h +index 3cc0118..3e097b3 100644 +--- a/src/manager.h ++++ b/src/manager.h +@@ -16,6 +16,32 @@ + + #include "toml.h" + ++typedef enum { ++ EG25_GNSS_STEP_FIRST = 0, ++#ifdef HAVE_MMGLIB ++ EG25_GNSS_STEP_MM_GNSS_DISABLE, ++#endif ++ EG25_GNSS_STEP_AT_GNSS_DISABLE, ++ EG25_GNSS_STEP_FETCH_ASSISTANCE_DATA, ++ EG25_GNSS_STEP_INIT_UPLOAD, ++ EG25_GNSS_STEP_UPLOAD, ++ EG25_GNSS_STEP_FINISH_UPLOAD, ++#ifdef HAVE_MMGLIB ++ EG25_GNSS_STEP_MM_GNSS_ENABLE, ++#endif ++ EG25_GNSS_STEP_AT_QGPS_ENABLE, ++ EG25_GNSS_STEP_LAST, ++} EG25GNSSStep; ++ ++typedef enum { ++ EG25_GNSS_SOURCE_NONE = 0, ++ EG25_GNSS_SOURCE_NMEA = 1 << 0, ++ EG25_GNSS_SOURCE_RAW = 1 << 1, ++ EG25_GNSS_SOURCE_UNMANAGED = 1 << 2, ++ EG25_GNSS_SOURCE_QGPS = 1 << 3, ++} EG25GNSSSource; ++ ++ + enum EG25State { + EG25_STATE_INIT = 0, + EG25_STATE_POWERED, // Power-on sequence has been executed, but the modem isn't on yet +@@ -52,11 +78,19 @@ struct EG25Manager { + enum EG25State modem_state; + gchar *modem_usb_id; + ++ gboolean gnss_assistance_enabled; ++ EG25GNSSSource gnss_sources; ++ EG25GNSSStep gnss_assistance_step; ++ gint gnss_assistance_fd; ++ gchar *gnss_assistance_url; ++ gchar *gnss_assistance_file; ++ + enum ModemIface modem_iface; + guint mm_watch; + #ifdef HAVE_MMGLIB + MMManager *mm_manager; + MMModem *mm_modem; ++ MMModemLocation *mm_location; + #endif + guint ofono_watch; + GDBOManager *ofono_manager; +diff --git a/src/meson.build b/src/meson.build +index f9eb27f..d1127dc 100644 +--- a/src/meson.build ++++ b/src/meson.build +@@ -15,6 +15,7 @@ src = [ + 'suspend.c', 'suspend.h', + 'toml.c', 'toml.h', + 'udev.c', 'udev.h', ++ 'gnss.c', 'gnss.h', + gdbofono_headers, + ] + +diff --git a/src/mm-iface.c b/src/mm-iface.c +index 0409236..2bbdfe5 100644 +--- a/src/mm-iface.c ++++ b/src/mm-iface.c +@@ -32,7 +32,10 @@ static void add_modem(struct EG25Manager *manager, GDBusObject *object) + + g_assert(MM_IS_OBJECT (object)); + manager->mm_modem = mm_object_get_modem(MM_OBJECT(object)); +- g_assert(manager->mm_modem != NULL); ++ manager->mm_location = mm_object_get_modem_location(MM_OBJECT(object)); ++ g_assert_nonnull(manager->mm_modem); ++ g_assert_nonnull(manager->mm_location); ++ + + if (manager->modem_state == EG25_STATE_RESUMING) { + if (manager->modem_recovery_timer) { +-- +GitLab + diff --git a/net-misc/eg25-manager/files/17.patch b/net-misc/eg25-manager/files/17.patch new file mode 100644 index 0000000..5c5d7e9 --- /dev/null +++ b/net-misc/eg25-manager/files/17.patch @@ -0,0 +1,28 @@ +From 705950bb399781a291a3998014f916fc1effce16 Mon Sep 17 00:00:00 2001 +From: Bhushan Shah +Date: Thu, 15 Apr 2021 09:29:36 +0530 +Subject: [PATCH] mm-iface: clean out modem_iface if mm disappears + +otherwise we will be stuck in state where restarting of mm will not +reset the modem_iface and it will loop through hard resetting modem +--- + src/mm-iface.c | 3 +++ + 1 file changed, 3 insertions(+) + +diff --git a/src/mm-iface.c b/src/mm-iface.c +index d6a74f8..0409236 100644 +--- a/src/mm-iface.c ++++ b/src/mm-iface.c +@@ -183,6 +183,9 @@ static void mm_iface_clean(struct EG25Manager *manager) + g_free(manager->modem_usb_id); + manager->modem_usb_id = NULL; + } ++ if (manager->modem_iface == MODEM_IFACE_MODEMMANAGER) { ++ manager->modem_iface = MODEM_IFACE_NONE; ++ } + } + + static void mm_vanished_cb(GDBusConnection *connection, +-- +GitLab + diff --git a/net-misc/eg25-manager/files/18-fix.patch b/net-misc/eg25-manager/files/18-fix.patch new file mode 100644 index 0000000..6725df2 --- /dev/null +++ b/net-misc/eg25-manager/files/18-fix.patch @@ -0,0 +1,101 @@ +diff --git a/data/pine64,pinephone-1.0.toml b/data/pine64,pinephone-1.0.toml +index e127f1a..5269fc2 100644 +--- a/data/pine64,pinephone-1.0.toml ++++ b/data/pine64,pinephone-1.0.toml +@@ -39,8 +39,8 @@ configure = [ + { cmd = "QCFG", subcmd = "risignaltype", expect = "\"physical\"" }, + { cmd = "QCFG", subcmd = "ims", expect = "1" }, + { cmd = "QCFG", subcmd = "urc/ri/ring", expect = "\"pulse\",2000,1000,5000,\"off\",1" }, +- { cmd = "QCFG", subcmd = "urc/ri/smsincoming", expect = "\"pulse\",2000" }, +- { cmd = "QCFG", subcmd = "urc/ri/other", expect = "\"off\",1" }, ++ { cmd = "QCFG", subcmd = "urc/ri/smsincoming", expect = "\"pulse\",2000,1" }, ++ { cmd = "QCFG", subcmd = "urc/ri/other", expect = "\"off\",1,1" }, + { cmd = "QCFG", subcmd = "urc/delay", expect = "1" }, + { cmd = "QURCCFG", subcmd = "urcport", expect = "\"all\"" }, + { cmd = "QSCLK", value = "1" }, +@@ -62,6 +62,15 @@ configure = [ + { cmd = "QGPSCFG", subcmd = "galileonmeatype", expect = "0" }, + { cmd = "QGPSCFG", subcmd = "beidounmeatype", expect = "0" }, + { cmd = "QGPSCFG", subcmd = "autogps", expect = "0" }, ++# Reset modem-power configurations to what we expect ++ { cmd = "QCFG", subcmd = "urc/cache", expect = "0" }, ++ { cmd = "QCFG", subcmd = "fast/poweroff", expect = "1" }, ++ { cmd = "QCFG", subcmd = "apready", expect = "0,0,500" }, ++ { cmd = "QCFG", subcmd = "sleepind/level", expect = "0" }, ++ { cmd = "QCFG", subcmd = "wakeupin/level", expect = "0,0" }, ++ { cmd = "QCFG", subcmd = "ApRstLevel", expect = "0" }, ++ { cmd = "QCFG", subcmd = "ModemRstLevel", expect = "0" }, ++ { cmd = "QCFG", subcmd = "urc/ri/pin", expect = "uart_ri" }, + ] + suspend = [ + ] +diff --git a/data/pine64,pinephone-1.1.toml b/data/pine64,pinephone-1.1.toml +index e127f1a..c2ee8bd 100644 +--- a/data/pine64,pinephone-1.1.toml ++++ b/data/pine64,pinephone-1.1.toml +@@ -39,11 +39,20 @@ configure = [ + { cmd = "QCFG", subcmd = "risignaltype", expect = "\"physical\"" }, + { cmd = "QCFG", subcmd = "ims", expect = "1" }, + { cmd = "QCFG", subcmd = "urc/ri/ring", expect = "\"pulse\",2000,1000,5000,\"off\",1" }, +- { cmd = "QCFG", subcmd = "urc/ri/smsincoming", expect = "\"pulse\",2000" }, +- { cmd = "QCFG", subcmd = "urc/ri/other", expect = "\"off\",1" }, ++ { cmd = "QCFG", subcmd = "urc/ri/smsincoming", expect = "\"pulse\",2000,1" }, ++ { cmd = "QCFG", subcmd = "urc/ri/other", expect = "\"off\",1,1" }, + { cmd = "QCFG", subcmd = "urc/delay", expect = "1" }, + { cmd = "QURCCFG", subcmd = "urcport", expect = "\"all\"" }, + { cmd = "QSCLK", value = "1" }, ++# Reset modem-power configurations to what we expect ++ { cmd = "QCFG", subcmd = "urc/cache", expect = "0" }, ++ { cmd = "QCFG", subcmd = "fast/poweroff", expect = "1" }, ++ { cmd = "QCFG", subcmd = "apready", expect = "0,0,500" }, ++ { cmd = "QCFG", subcmd = "sleepind/level", expect = "0" }, ++ { cmd = "QCFG", subcmd = "wakeupin/level", expect = "0,0" }, ++ { cmd = "QCFG", subcmd = "ApRstLevel", expect = "0" }, ++ { cmd = "QCFG", subcmd = "ModemRstLevel", expect = "0" }, ++ { cmd = "QCFG", subcmd = "urc/ri/pin", expect = "uart_ri" }, + # GNSS configuration: + # * Enable A-GPS data upload support (XTRA) + # * Disable On-Demand-Positioning (ODP) mode +diff --git a/data/pine64,pinephone-1.2.toml b/data/pine64,pinephone-1.2.toml +index e16f4f5..a45c7ec 100644 +--- a/data/pine64,pinephone-1.2.toml ++++ b/data/pine64,pinephone-1.2.toml +@@ -36,8 +36,20 @@ configure = [ + { cmd = "QCFG", subcmd = "risignaltype", expect = "\"physical\"" }, + { cmd = "QCFG", subcmd = "ims", expect = "1" }, + { cmd = "QCFG", subcmd = "apready", expect = "1,0,500" }, ++ { cmd = "QCFG", subcmd = "urc/ri/ring", expect = "\"pulse\",120,1000,5000,\"off\",1" }, ++ { cmd = "QCFG", subcmd = "urc/ri/smsincoming", expect = "\"pulse\",120,1" }, ++ { cmd = "QCFG", subcmd = "urc/ri/other", expect = "\"off\",1,1" }, ++ { cmd = "QCFG", subcmd = "urc/delay", expect = "0" }, + { cmd = "QURCCFG", subcmd = "urcport", expect = "\"all\"" }, + { cmd = "QSCLK", value = "1" }, ++# Reset modem-power configurations to what we expect ++ { cmd = "QCFG", subcmd = "urc/cache", expect = "0" }, ++ { cmd = "QCFG", subcmd = "fast/poweroff", expect = "1" }, ++ { cmd = "QCFG", subcmd = "sleepind/level", expect = "0" }, ++ { cmd = "QCFG", subcmd = "wakeupin/level", expect = "0,0" }, ++ { cmd = "QCFG", subcmd = "ApRstLevel", expect = "0" }, ++ { cmd = "QCFG", subcmd = "ModemRstLevel", expect = "0" }, ++ { cmd = "QCFG", subcmd = "urc/ri/pin", expect = "uart_ri" }, + # GNSS configuration: + # * Enable A-GPS data upload support (XTRA) + # * Disable On-Demand-Positioning (ODP) mode +diff --git a/src/at.c b/src/at.c +index f694507..7c5f092 100644 +--- a/src/at.c ++++ b/src/at.c +@@ -222,7 +222,12 @@ static gboolean modem_response(gint fd, + else if (strstr(response, "QGPSURC: \"xtradataexpire\"")) + gnss_upload_assistance_data(manager); + /* AT command failed, retry */ +- else if (strstr(response, "ERROR")) ++ /* ++ * QCFG="fast/poweroff" configuration is only available in ++ * newer firmware versions ++ */ ++ else if (strstr(response, "ERROR") && !strstr(response, "fast/poweroff")) ++ + retry_at_command(manager); + /* + * Successfull AT responses contain 'OK', except for AT+QFUPL which also diff --git a/net-misc/eg25-manager/files/18.patch b/net-misc/eg25-manager/files/18.patch new file mode 100644 index 0000000..9f446f5 --- /dev/null +++ b/net-misc/eg25-manager/files/18.patch @@ -0,0 +1,129 @@ +From a046b539418958eeb2ee26ec5ef273835c98d0d2 Mon Sep 17 00:00:00 2001 +From: Dylan Van Assche +Date: Thu, 15 Apr 2021 20:11:03 +0200 +Subject: [PATCH 1/2] config: synchronize with modem-power + +We cannot assume default factory values if other drivers change them +--- + data/pine64,pinephone-1.0.toml | 13 +++++++++++-- + data/pine64,pinephone-1.1.toml | 13 +++++++++++-- + data/pine64,pinephone-1.2.toml | 12 ++++++++++++ + 3 files changed, 34 insertions(+), 4 deletions(-) + +diff --git a/data/pine64,pinephone-1.0.toml b/data/pine64,pinephone-1.0.toml +index e1f5b24..6e21bda 100644 +--- a/data/pine64,pinephone-1.0.toml ++++ b/data/pine64,pinephone-1.0.toml +@@ -39,12 +39,21 @@ configure = [ + { cmd = "QCFG", subcmd = "risignaltype", expect = "\"physical\"" }, + { cmd = "QCFG", subcmd = "ims", expect = "1" }, + { cmd = "QCFG", subcmd = "urc/ri/ring", expect = "\"pulse\",2000,1000,5000,\"off\",1" }, +- { cmd = "QCFG", subcmd = "urc/ri/smsincoming", expect = "\"pulse\",2000" }, +- { cmd = "QCFG", subcmd = "urc/ri/other", expect = "\"off\",1" }, ++ { cmd = "QCFG", subcmd = "urc/ri/smsincoming", expect = "\"pulse\",2000,1" }, ++ { cmd = "QCFG", subcmd = "urc/ri/other", expect = "\"off\",1,1" }, + { cmd = "QCFG", subcmd = "urc/delay", expect = "1" }, + { cmd = "QURCCFG", subcmd = "urcport", expect = "\"all\"" }, + { cmd = "QGPS", value = "1" }, + { cmd = "QSCLK", value = "1" }, ++# Reset modem-power configurations to what we expect ++ { cmd = "QCFG", subcmd = "urc/cache", expect = "0" }, ++ { cmd = "QCFG", subcmd = "fast/poweroff", expect = "1" }, ++ { cmd = "QCFG", subcmd = "apready", expect = "0,0,500" }, ++ { cmd = "QCFG", subcmd = "sleepind/level", expect = "0" }, ++ { cmd = "QCFG", subcmd = "wakeupin/level", expect = "0,0" }, ++ { cmd = "QCFG", subcmd = "ApRstLevel", expect = "0" }, ++ { cmd = "QCFG", subcmd = "ModemRstLevel", expect = "0" }, ++ { cmd = "QCFG", subcmd = "urc/ri/pin", expect = "uart_ri" }, + ] + suspend = [ + { cmd = "QGPSEND" }, +diff --git a/data/pine64,pinephone-1.1.toml b/data/pine64,pinephone-1.1.toml +index e1f5b24..6e21bda 100644 +--- a/data/pine64,pinephone-1.1.toml ++++ b/data/pine64,pinephone-1.1.toml +@@ -39,12 +39,21 @@ configure = [ + { cmd = "QCFG", subcmd = "risignaltype", expect = "\"physical\"" }, + { cmd = "QCFG", subcmd = "ims", expect = "1" }, + { cmd = "QCFG", subcmd = "urc/ri/ring", expect = "\"pulse\",2000,1000,5000,\"off\",1" }, +- { cmd = "QCFG", subcmd = "urc/ri/smsincoming", expect = "\"pulse\",2000" }, +- { cmd = "QCFG", subcmd = "urc/ri/other", expect = "\"off\",1" }, ++ { cmd = "QCFG", subcmd = "urc/ri/smsincoming", expect = "\"pulse\",2000,1" }, ++ { cmd = "QCFG", subcmd = "urc/ri/other", expect = "\"off\",1,1" }, + { cmd = "QCFG", subcmd = "urc/delay", expect = "1" }, + { cmd = "QURCCFG", subcmd = "urcport", expect = "\"all\"" }, + { cmd = "QGPS", value = "1" }, + { cmd = "QSCLK", value = "1" }, ++# Reset modem-power configurations to what we expect ++ { cmd = "QCFG", subcmd = "urc/cache", expect = "0" }, ++ { cmd = "QCFG", subcmd = "fast/poweroff", expect = "1" }, ++ { cmd = "QCFG", subcmd = "apready", expect = "0,0,500" }, ++ { cmd = "QCFG", subcmd = "sleepind/level", expect = "0" }, ++ { cmd = "QCFG", subcmd = "wakeupin/level", expect = "0,0" }, ++ { cmd = "QCFG", subcmd = "ApRstLevel", expect = "0" }, ++ { cmd = "QCFG", subcmd = "ModemRstLevel", expect = "0" }, ++ { cmd = "QCFG", subcmd = "urc/ri/pin", expect = "uart_ri" }, + ] + suspend = [ + { cmd = "QGPSEND" }, +diff --git a/data/pine64,pinephone-1.2.toml b/data/pine64,pinephone-1.2.toml +index 4ca1274..598ebaf 100644 +--- a/data/pine64,pinephone-1.2.toml ++++ b/data/pine64,pinephone-1.2.toml +@@ -36,9 +36,21 @@ configure = [ + { cmd = "QCFG", subcmd = "risignaltype", expect = "\"physical\"" }, + { cmd = "QCFG", subcmd = "ims", expect = "1" }, + { cmd = "QCFG", subcmd = "apready", expect = "1,0,500" }, ++ { cmd = "QCFG", subcmd = "urc/ri/ring", expect = "\"pulse\",120,1000,5000,\"off\",1" }, ++ { cmd = "QCFG", subcmd = "urc/ri/smsincoming", expect = "\"pulse\",120,1" }, ++ { cmd = "QCFG", subcmd = "urc/ri/other", expect = "\"off\",1,1" }, ++ { cmd = "QCFG", subcmd = "urc/delay", expect = "0" }, + { cmd = "QURCCFG", subcmd = "urcport", expect = "\"all\"" }, + { cmd = "QGPS", value = "1" }, + { cmd = "QSCLK", value = "1" }, ++# Reset modem-power configurations to what we expect ++ { cmd = "QCFG", subcmd = "urc/cache", expect = "0" }, ++ { cmd = "QCFG", subcmd = "fast/poweroff", expect = "1" }, ++ { cmd = "QCFG", subcmd = "sleepind/level", expect = "0" }, ++ { cmd = "QCFG", subcmd = "wakeupin/level", expect = "0,0" }, ++ { cmd = "QCFG", subcmd = "ApRstLevel", expect = "0" }, ++ { cmd = "QCFG", subcmd = "ModemRstLevel", expect = "0" }, ++ { cmd = "QCFG", subcmd = "urc/ri/pin", expect = "uart_ri" }, + ] + suspend = [ + { cmd = "QGPSEND" }, +-- +GitLab + + +From 0094dea49d8bc5a60c044da1e01417c29397c7b0 Mon Sep 17 00:00:00 2001 +From: Dylan Van Assche +Date: Sat, 17 Apr 2021 15:50:07 +0200 +Subject: [PATCH 2/2] at: fast/poweroff is only available in newer firmware + versions + +Do not retry the AT command of the fast/poweroff setting as it may not be supported by the firmware +--- + src/at.c | 6 +++++- + 1 file changed, 5 insertions(+), 1 deletion(-) + +diff --git a/src/at.c b/src/at.c +index a34025f..4469d26 100644 +--- a/src/at.c ++++ b/src/at.c +@@ -215,7 +215,11 @@ static gboolean modem_response(gint fd, + suspend_inhibit(manager, TRUE, TRUE); + manager->modem_state = EG25_STATE_STARTED; + } +- else if (strstr(response, "ERROR")) ++ /* ++ * QCFG="fast/poweroff" configuration is only available in ++ * newer firmware versions ++ */ ++ else if (strstr(response, "ERROR") && !strstr(response, "fast/poweroff")) + retry_at_command(manager); + else if (strstr(response, "OK")) + process_at_result(manager, response); +-- +GitLab + diff --git a/net-misc/eg25-manager/files/21.patch b/net-misc/eg25-manager/files/21.patch new file mode 100644 index 0000000..739149c --- /dev/null +++ b/net-misc/eg25-manager/files/21.patch @@ -0,0 +1,152 @@ +From e690e2a17d9798ad22d961bd4ee2613593d68e44 Mon Sep 17 00:00:00 2001 +From: Dylan Van Assche +Date: Sun, 23 May 2021 20:00:42 +0200 +Subject: [PATCH] at: wake only when sending AT commands + +Allow the modem to enter soft sleep when +we don't talk to the modem using AT commands. +This was already the case in suspend, but +not during runtime. By only waking the modem +from soft sleep when we need to send +an AT command, we can save some power. +--- + src/at.c | 36 +++++++++++++++++++++++------------- + src/gpio.c | 24 ++++++++++++++++++++++-- + src/gpio.h | 2 ++ + 3 files changed, 47 insertions(+), 15 deletions(-) + +diff --git a/src/at.c b/src/at.c +index a34025f..358d95a 100644 +--- a/src/at.c ++++ b/src/at.c +@@ -6,6 +6,7 @@ + + #include "at.h" + #include "suspend.h" ++#include "gpio.h" + #include "gnss.h" + + #include +@@ -52,6 +53,10 @@ gboolean at_send_command(struct EG25Mana + int ret, len = 0; + + if (at_cmd) { ++ /* Wake up the modem from soft sleep before sending an AT command */ ++ gpio_sequence_wake(manager); ++ ++ /* Send AT command */ + if (at_cmd->subcmd == NULL && at_cmd->value == NULL && at_cmd->expected == NULL) + len = sprintf(command, "AT+%s\r\n", at_cmd->cmd); + else if (at_cmd->subcmd == NULL && at_cmd->value == NULL) +@@ -69,23 +74,28 @@ gboolean at_send_command(struct EG25Mana + g_warning("Couldn't write full AT command: wrote %d/%d bytes", ret, len); + + g_message("Sending command: %s", g_strstrip(command)); +- } else if (manager->modem_state < EG25_STATE_CONFIGURED) { +- if (manager->modem_iface == MODEM_IFACE_MODEMMANAGER) { ++ } else { ++ /* Allow the modem to enter soft sleep again when we sent the AT command*/ ++ gpio_sequence_sleep(manager); ++ ++ if (manager->modem_state < EG25_STATE_CONFIGURED) { ++ if (manager->modem_iface == MODEM_IFACE_MODEMMANAGER) { + #ifdef HAVE_MMGLIB +- MMModemState modem_state = mm_modem_get_state(manager->mm_modem); ++ MMModemState modem_state = mm_modem_get_state(manager->mm_modem); + +- if (manager->mm_modem && modem_state >= MM_MODEM_STATE_REGISTERED) +- modem_update_state(manager, modem_state); +- else +- manager->modem_state = EG25_STATE_CONFIGURED; ++ if (manager->mm_modem && modem_state >= MM_MODEM_STATE_REGISTERED) ++ modem_update_state(manager, modem_state); ++ else ++ manager->modem_state = EG25_STATE_CONFIGURED; + #endif +- } else { +- manager->modem_state = EG25_STATE_CONFIGURED; ++ } else { ++ manager->modem_state = EG25_STATE_CONFIGURED; ++ } ++ } else if (manager->modem_state == EG25_STATE_SUSPENDING) { ++ modem_suspend_post(manager); ++ } else if (manager->modem_state == EG25_STATE_RESETTING) { ++ manager->modem_state = EG25_STATE_POWERED; + } +- } else if (manager->modem_state == EG25_STATE_SUSPENDING) { +- modem_suspend_post(manager); +- } else if (manager->modem_state == EG25_STATE_RESETTING) { +- manager->modem_state = EG25_STATE_POWERED; + } + + return FALSE; +diff --git a/src/gpio.c b/src/gpio.c +index a5d3bf0..617b69e 100644 +--- a/src/gpio.c ++++ b/src/gpio.c +@@ -6,6 +6,8 @@ + + #include "gpio.h" + ++#include ++ + #define GPIO_CHIP1_LABEL "1c20800.pinctrl" + #define GPIO_CHIP2_LABEL "1f02c00.pinctrl" + +@@ -52,7 +54,6 @@ int gpio_sequence_shutdown(struct EG25Manager *manager) + int gpio_sequence_suspend(struct EG25Manager *manager) + { + gpiod_line_set_value(manager->gpio_out[GPIO_OUT_APREADY], 1); +- gpiod_line_set_value(manager->gpio_out[GPIO_OUT_DTR], 1); + + g_message("Executed suspend sequence"); + +@@ -62,13 +63,32 @@ int gpio_sequence_suspend(struct EG25Manager *manager) + int gpio_sequence_resume(struct EG25Manager *manager) + { + gpiod_line_set_value(manager->gpio_out[GPIO_OUT_APREADY], 0); +- gpiod_line_set_value(manager->gpio_out[GPIO_OUT_DTR], 0); + + g_message("Executed resume sequence"); + + return 0; + } + ++int gpio_sequence_wake(struct EG25Manager *manager) ++{ ++ gpiod_line_set_value(manager->gpio_out[GPIO_OUT_DTR], 0); ++ ++ /* Give the modem 5ms to wake from soft sleep */ ++ usleep(5000); ++ ++ g_message("Executed soft wake sequence"); ++ ++ return 0; ++} ++ ++int gpio_sequence_sleep(struct EG25Manager *manager) ++{ ++ gpiod_line_set_value(manager->gpio_out[GPIO_OUT_DTR], 1); ++ g_message("Executed soft sleep sequence"); ++ ++ return 0; ++} ++ + static guint get_config_gpio(toml_table_t *config, const char *id) + { + toml_datum_t value = toml_int_in(config, id); +diff --git a/src/gpio.h b/src/gpio.h +index 8d94013..a041bdc 100644 +--- a/src/gpio.h ++++ b/src/gpio.h +@@ -15,5 +15,7 @@ int gpio_sequence_poweron(struct EG25Manager *state); + int gpio_sequence_shutdown(struct EG25Manager *state); + int gpio_sequence_suspend(struct EG25Manager *state); + int gpio_sequence_resume(struct EG25Manager *state); ++int gpio_sequence_wake(struct EG25Manager *state); ++int gpio_sequence_sleep(struct EG25Manager *state); + + gboolean gpio_check_poweroff(struct EG25Manager *manager, gboolean keep_down); +-- +GitLab + diff --git a/net-misc/eg25-manager/files/6.patch b/net-misc/eg25-manager/files/6.patch new file mode 100644 index 0000000..77c9a51 --- /dev/null +++ b/net-misc/eg25-manager/files/6.patch @@ -0,0 +1,895 @@ +From 09c0ddfbb81f15bf732b9a5af15eee9eb1be848b Mon Sep 17 00:00:00 2001 +From: Oliver Smith +Date: Mon, 8 Feb 2021 10:29:56 +0530 +Subject: [PATCH 1/5] src: add ofono-iface + +Start work on new ofono interface. So far, this detects ofono on dbus +and complains if both mm and ofono are running. +--- + src/manager.c | 10 ++++++++ + src/manager.h | 8 ++++++ + src/meson.build | 1 + + src/mm-iface.c | 7 ++++++ + src/ofono-iface.c | 62 +++++++++++++++++++++++++++++++++++++++++++++++ + src/ofono-iface.h | 12 +++++++++ + 6 files changed, 100 insertions(+) + create mode 100644 src/ofono-iface.c + create mode 100644 src/ofono-iface.h + +diff --git a/src/manager.c b/src/manager.c +index aaa3905..7e43e62 100644 +--- a/src/manager.c ++++ b/src/manager.c +@@ -8,6 +8,7 @@ + #include "gpio.h" + #include "manager.h" + #include "mm-iface.h" ++#include "ofono-iface.h" + #include "suspend.h" + #include "udev.h" + +@@ -35,6 +36,7 @@ static gboolean quit_app(struct EG25Manager *manager) + + at_destroy(manager); + mm_iface_destroy(manager); ++ ofono_iface_destroy(manager); + suspend_destroy(manager); + udev_destroy(manager); + +@@ -131,6 +133,13 @@ void modem_reset(struct EG25Manager *manager) + if (manager->reset_timer) + return; + ++ /* If we are managing the modem through lets say ofono, we should not ++ * reset the modem based on the availability of USB ID ++ * TODO: Improve ofono plugin and add support for fetching USB ID ++ */ ++ if (manager->modem_iface != MODEM_IFACE_MODEMMANAGER) ++ return; ++ + if (manager->modem_recovery_timer) { + g_source_remove(manager->modem_recovery_timer); + manager->modem_recovery_timer = 0; +@@ -305,6 +314,7 @@ int main(int argc, char *argv[]) + at_init(&manager, toml_table_in(toml_config, "at")); + gpio_init(&manager, toml_table_in(toml_config, "gpio")); + mm_iface_init(&manager, toml_table_in(toml_config, "mm-iface")); ++ ofono_iface_init(&manager); + suspend_init(&manager, toml_table_in(toml_config, "suspend")); + udev_init(&manager, toml_table_in(toml_config, "udev")); + +diff --git a/src/manager.h b/src/manager.h +index 48ff237..5ed9e75 100644 +--- a/src/manager.h ++++ b/src/manager.h +@@ -27,6 +27,12 @@ enum EG25State { + EG25_STATE_FINISHING + }; + ++enum ModemIface { ++ MODEM_IFACE_NONE = 0, ++ MODEM_IFACE_MODEMMANAGER, ++ MODEM_IFACE_OFONO ++}; ++ + struct EG25Manager { + GMainLoop *loop; + guint reset_timer; +@@ -41,9 +47,11 @@ struct EG25Manager { + enum EG25State modem_state; + gchar *modem_usb_id; + ++ enum ModemIface modem_iface; + guint mm_watch; + MMManager *mm_manager; + MMModem *mm_modem; ++ guint ofono_watch; + + GDBusProxy *suspend_proxy; + int suspend_delay_fd; +diff --git a/src/meson.build b/src/meson.build +index 0d10362..7429950 100644 +--- a/src/meson.build ++++ b/src/meson.build +@@ -11,6 +11,7 @@ executable ( + 'gpio.c', 'gpio.h', + 'manager.c', 'manager.h', + 'mm-iface.c', 'mm-iface.h', ++ 'ofono-iface.c', 'ofono-iface.h', + 'suspend.c', 'suspend.h', + 'toml.c', 'toml.h', + 'udev.c', 'udev.h', +diff --git a/src/mm-iface.c b/src/mm-iface.c +index 8afe2ea..e1e3726 100644 +--- a/src/mm-iface.c ++++ b/src/mm-iface.c +@@ -163,6 +163,13 @@ static void mm_appeared_cb(GDBusConnection *connection, + { + g_message("ModemManager appeared on D-Bus"); + ++ if (manager->modem_iface != MODEM_IFACE_NONE) { ++ g_critical("Modem interface already found!"); ++ g_critical("Make sure to only run either of ModemManager or oFono."); ++ return; ++ } ++ manager->modem_iface = MODEM_IFACE_MODEMMANAGER; ++ + mm_manager_new(connection, G_DBUS_OBJECT_MANAGER_CLIENT_FLAGS_NONE, + NULL, (GAsyncReadyCallback)mm_manager_new_cb, manager); + } +diff --git a/src/ofono-iface.c b/src/ofono-iface.c +new file mode 100644 +index 0000000..d9051a3 +--- /dev/null ++++ b/src/ofono-iface.c +@@ -0,0 +1,62 @@ ++/* ++ * Copyright (C) 2020 Oliver Smith ++ * ++ * SPDX-License-Identifier: GPL-3.0-or-later ++ */ ++ ++#include "ofono-iface.h" ++ ++#include ++ ++// #include ++#define OFONO_SERVICE "org.ofono" ++ ++static void ofono_appeared_cb(GDBusConnection *connection, ++ const gchar *name, ++ const gchar *name_owner, ++ struct EG25Manager *manager) ++{ ++ g_message("oFono appeared on D-Bus"); ++ ++ if (manager->modem_iface != MODEM_IFACE_NONE) { ++ g_critical("Modem interface already found!"); ++ g_critical("Make sure to only run either of ModemManager or oFono."); ++ return; ++ } ++ manager->modem_iface = MODEM_IFACE_OFONO; ++ ++ /* now connect to oFono! */ ++} ++ ++static void ofono_vanished_cb(GDBusConnection *connection, ++ const gchar *name, ++ struct EG25Manager *manager) ++{ ++ g_message("oFono vanished from D-Bus"); ++ ++ if (manager->modem_iface == MODEM_IFACE_OFONO) { ++ manager->modem_iface = MODEM_IFACE_NONE; ++ ofono_iface_destroy(manager); ++ } ++} ++ ++void ofono_iface_init(struct EG25Manager *manager) ++{ ++ manager->ofono_watch = g_bus_watch_name(G_BUS_TYPE_SYSTEM, OFONO_SERVICE, ++ G_BUS_NAME_WATCHER_FLAGS_AUTO_START, ++ (GBusNameAppearedCallback)ofono_appeared_cb, ++ (GBusNameVanishedCallback)ofono_vanished_cb, ++ manager, NULL); ++} ++ ++void ofono_iface_destroy(struct EG25Manager *manager) ++{ ++ if (manager->modem_usb_id) { ++ g_free(manager->modem_usb_id); ++ manager->modem_usb_id = NULL; ++ } ++ if (manager->ofono_watch != 0) { ++ g_bus_unwatch_name(manager->ofono_watch); ++ manager->ofono_watch = 0; ++ } ++} +diff --git a/src/ofono-iface.h b/src/ofono-iface.h +new file mode 100644 +index 0000000..fd3766e +--- /dev/null ++++ b/src/ofono-iface.h +@@ -0,0 +1,12 @@ ++/* ++ * Copyright (C) 2020 Oliver Smith ++ * ++ * SPDX-License-Identifier: GPL-3.0-or-later ++ */ ++ ++#pragma once ++ ++#include "manager.h" ++ ++void ofono_iface_init(struct EG25Manager *data); ++void ofono_iface_destroy(struct EG25Manager *data); +-- +GitLab + + +From a3952107f5950535dd10d8c48ae807f3f4ec207e Mon Sep 17 00:00:00 2001 +From: Bhushan Shah +Date: Thu, 11 Mar 2021 11:58:46 +0530 +Subject: [PATCH 2/5] src: watch ofono service for new modem + +If system is using ofono, use ofono dbus service to figure out the +modem's USB id. +--- + src/libgdbofono/dbus-introspect.sh | 11 ++ + src/libgdbofono/manager.xml | 13 ++ + src/libgdbofono/meson.build | 50 ++++++ + src/libgdbofono/modem-full.xml | 249 +++++++++++++++++++++++++++++ + src/libgdbofono/modem.xml | 37 +++++ + src/manager.h | 4 + + src/meson.build | 4 + + src/ofono-iface.c | 100 +++++++++++- + 8 files changed, 467 insertions(+), 1 deletion(-) + create mode 100755 src/libgdbofono/dbus-introspect.sh + create mode 100644 src/libgdbofono/manager.xml + create mode 100644 src/libgdbofono/meson.build + create mode 100644 src/libgdbofono/modem-full.xml + create mode 100644 src/libgdbofono/modem.xml + +diff --git a/src/libgdbofono/dbus-introspect.sh b/src/libgdbofono/dbus-introspect.sh +new file mode 100755 +index 0000000..5a851ad +--- /dev/null ++++ b/src/libgdbofono/dbus-introspect.sh +@@ -0,0 +1,11 @@ ++#!/bin/bash ++ ++DEST="$1" ++OBJ_PATH="$2" ++METHOD="$3" ++shift 3 ++ ++dbus-send "$@" --print-reply --dest="$DEST" "$OBJ_PATH" "$METHOD" | \ ++ grep -v '^method return' | \ ++ sed -e 's/^[[:space:]]\+string ""__' +diff --git a/src/libgdbofono/manager.xml b/src/libgdbofono/manager.xml +new file mode 100644 +index 0000000..13e9d56 +--- /dev/null ++++ b/src/libgdbofono/manager.xml +@@ -0,0 +1,13 @@ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ +diff --git a/src/libgdbofono/meson.build b/src/libgdbofono/meson.build +new file mode 100644 +index 0000000..98eb9a3 +--- /dev/null ++++ b/src/libgdbofono/meson.build +@@ -0,0 +1,50 @@ ++# ++# Copyright (C) 2018 Purism SPC ++# ++# This file is part of Calls. ++# ++# Calls is free software: you can redistribute it and/or modify it ++# under the terms of the GNU General Public License as published by ++# the Free Software Foundation, either version 3 of the License, or ++# (at your option) any later version. ++# ++# Calls 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 Calls. If not, see . ++# ++# Author: Bob Ham ++# ++# SPDX-License-Identifier: GPL-3.0-or-later ++# ++ ++ ++gnome = import('gnome') ++dbus_interfaces = ['manager', 'modem'] ++ ++gdbofono_src = [] ++gdbofono_headers = [] ++foreach iface: dbus_interfaces ++ src = gnome.gdbus_codegen( ++ 'gdbo-' + iface, ++ iface + '.xml', ++ interface_prefix: 'org.ofono.', ++ namespace: 'GDBO' ++ ) ++ gdbofono_src += src ++ gdbofono_headers += src[1] ++endforeach ++ ++gdbofono_deps = [ ++ dependency('gio-2.0'), ++ dependency('gio-unix-2.0'), ++] ++ ++gdbofono_lib = static_library( ++ 'gdbofono', ++ gdbofono_src, ++ dependencies: gdbofono_deps ++) +diff --git a/src/libgdbofono/modem-full.xml b/src/libgdbofono/modem-full.xml +new file mode 100644 +index 0000000..5319672 +--- /dev/null ++++ b/src/libgdbofono/modem-full.xml +@@ -0,0 +1,249 @@ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ +diff --git a/src/libgdbofono/modem.xml b/src/libgdbofono/modem.xml +new file mode 100644 +index 0000000..c02d250 +--- /dev/null ++++ b/src/libgdbofono/modem.xml +@@ -0,0 +1,37 @@ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ +diff --git a/src/manager.h b/src/manager.h +index 5ed9e75..ec5550b 100644 +--- a/src/manager.h ++++ b/src/manager.h +@@ -10,6 +10,7 @@ + #include + #include + #include ++#include + + #include "toml.h" + +@@ -51,7 +52,10 @@ struct EG25Manager { + guint mm_watch; + MMManager *mm_manager; + MMModem *mm_modem; ++ + guint ofono_watch; ++ GDBOManager *ofono_manager; ++ GDBusConnection *ofono_connection; + + GDBusProxy *suspend_proxy; + int suspend_delay_fd; +diff --git a/src/meson.build b/src/meson.build +index 7429950..6a9f52d 100644 +--- a/src/meson.build ++++ b/src/meson.build +@@ -4,6 +4,9 @@ + # SPDX-License-Identifier: GPL-3.0-or-later + # + ++ ++subdir('libgdbofono') ++ + executable ( + 'eg25manager', + [ +@@ -17,5 +20,6 @@ executable ( + 'udev.c', 'udev.h', + ], + dependencies : mgr_deps, ++ link_with: gdbofono_lib, + install : true + ) +diff --git a/src/ofono-iface.c b/src/ofono-iface.c +index d9051a3..00bf665 100644 +--- a/src/ofono-iface.c ++++ b/src/ofono-iface.c +@@ -8,14 +8,91 @@ + + #include + ++#include ++#include ++ + // #include + #define OFONO_SERVICE "org.ofono" + ++static void modem_added_cb(GDBOManager *manager_proxy, ++ const gchar *path, ++ GVariant *properties, ++ struct EG25Manager *manager) ++{ ++ GVariant *modem_path; ++ /* ++ gdbo_modem_proxy_new(manager->ofono_connection, ++ G_DBUS_PROXY_FLAGS_NONE, ++ g_dbus_proxy_get_name(G_DBUS_PROXY(manager_proxy)), ++ path, ++ NULL, ++ (GAsyncReadyCallback) modem_proxy_new_cb, ++ manager); ++ */ ++ g_debug("Adding ofono modem '%s'", path); ++ ++ if (manager->modem_state == EG25_STATE_RESUMING) { ++ if (manager->modem_recovery_timer) { ++ g_source_remove(manager->modem_recovery_timer); ++ manager->modem_recovery_timer = 0; ++ } ++ modem_resume_post(manager); ++ manager->modem_state = EG25_STATE_CONFIGURED; ++ } ++ ++ if (manager->modem_state < EG25_STATE_ACQUIRED) ++ manager->modem_state = EG25_STATE_ACQUIRED; ++ ++ if (manager->modem_state < EG25_STATE_CONFIGURED) ++ modem_configure(manager); ++ ++ modem_path = g_variant_lookup_value(properties, "SystemPath", G_VARIANT_TYPE_STRING); ++ if (manager->modem_usb_id) ++ g_free(manager->modem_usb_id); ++ manager->modem_usb_id = g_strdup(strrchr(g_variant_dup_string(modem_path, NULL), '/') + 1); ++} ++ ++static void modem_removed_cb(GDBOManager *manager_proxy, ++ const gchar *path, ++ struct EG25Manager *manager) ++{ ++} ++ ++static void get_modems_cb(GDBOManager *manager_proxy, ++ GAsyncResult *res, ++ struct EG25Manager *manager) ++{ ++ gboolean ok; ++ GVariant *modems; ++ GVariantIter *modems_iter = NULL; ++ g_autoptr(GError) error = NULL; ++ ++ const gchar *path; ++ GVariant *properties; ++ ++ ok = gdbo_manager_call_get_modems_finish(manager_proxy, &modems, ++ res, &error); ++ if (!ok) { ++ g_warning("Error getting modems from ofono manager: %s", error->message); ++ return; ++ } ++ ++ g_variant_get(modems, "a(oa{sv})", &modems_iter); ++ while(g_variant_iter_loop(modems_iter, "(&o@a{sv})", &path, &properties)) { ++ g_debug("Got modem object path '%s'", path); ++ modem_added_cb(manager_proxy, path, properties, manager); ++ } ++ g_variant_iter_free(modems_iter); ++ g_variant_unref(modems); ++} ++ + static void ofono_appeared_cb(GDBusConnection *connection, + const gchar *name, + const gchar *name_owner, + struct EG25Manager *manager) + { ++ GError *error = NULL; ++ + g_message("oFono appeared on D-Bus"); + + if (manager->modem_iface != MODEM_IFACE_NONE) { +@@ -23,9 +100,30 @@ static void ofono_appeared_cb(GDBusConnection *connection, + g_critical("Make sure to only run either of ModemManager or oFono."); + return; + } ++ /* now connect to oFono! */ ++ manager->ofono_connection = connection; ++ manager->ofono_manager = gdbo_manager_proxy_new_sync(connection, ++ G_DBUS_PROXY_FLAGS_NONE, ++ OFONO_SERVICE, ++ "/", ++ NULL, ++ &error); ++ if (!manager->ofono_manager) { ++ g_critical("Error creating ofono object manager proxy: %s", error->message); ++ return; ++ } ++ + manager->modem_iface = MODEM_IFACE_OFONO; + +- /* now connect to oFono! */ ++ g_signal_connect(manager->ofono_manager, "modem-added", ++ G_CALLBACK(modem_added_cb), manager); ++ g_signal_connect(manager->ofono_manager, "modem-removed", ++ G_CALLBACK(modem_removed_cb), manager); ++ ++ gdbo_manager_call_get_modems(manager->ofono_manager, ++ NULL, ++ (GAsyncReadyCallback) get_modems_cb, ++ manager); + } + + static void ofono_vanished_cb(GDBusConnection *connection, +-- +GitLab + + +From 9c4f5669fc6b76eac280f165aab972552f2df824 Mon Sep 17 00:00:00 2001 +From: Bhushan Shah +Date: Thu, 11 Mar 2021 12:13:04 +0530 +Subject: [PATCH 3/5] at: if we are using ofono, don't query modem manager for + state + +--- + src/at.c | 14 +++++++++----- + 1 file changed, 9 insertions(+), 5 deletions(-) + +diff --git a/src/at.c b/src/at.c +index 4b5cd62..32138d0 100644 +--- a/src/at.c ++++ b/src/at.c +@@ -76,12 +76,16 @@ static gboolean send_at_command(struct EG25Manager *manager) + + g_message("Sending command: %s", g_strstrip(command)); + } else if (manager->modem_state < EG25_STATE_CONFIGURED) { +- MMModemState modem_state = mm_modem_get_state(manager->mm_modem); +- +- if (manager->mm_modem && modem_state >= MM_MODEM_STATE_REGISTERED) +- modem_update_state(manager, modem_state); +- else ++ if (manager->modem_iface == MODEM_IFACE_MODEMMANAGER) { ++ MMModemState modem_state = mm_modem_get_state(manager->mm_modem); ++ ++ if (manager->mm_modem && modem_state >= MM_MODEM_STATE_REGISTERED) ++ modem_update_state(manager, modem_state); ++ else ++ manager->modem_state = EG25_STATE_CONFIGURED; ++ } else { + manager->modem_state = EG25_STATE_CONFIGURED; ++ } + } else if (manager->modem_state == EG25_STATE_SUSPENDING) { + modem_suspend_post(manager); + } else if (manager->modem_state == EG25_STATE_RESETTING) { +-- +GitLab + + +From 742172a9129a3c11c2825d323f8f98730f7533db Mon Sep 17 00:00:00 2001 +From: Bhushan Shah +Date: Thu, 11 Mar 2021 13:08:21 +0530 +Subject: [PATCH 4/5] suspend: if we are using ofono, mark modem as resumed + immediately + +--- + src/suspend.c | 5 ++++- + 1 file changed, 4 insertions(+), 1 deletion(-) + +diff --git a/src/suspend.c b/src/suspend.c +index 256636e..b82ca7e 100644 +--- a/src/suspend.c ++++ b/src/suspend.c +@@ -170,11 +170,14 @@ static void signal_cb(GDBusProxy *proxy, + g_message("system is resuming"); + take_inhibitor(manager, FALSE); + modem_resume_pre(manager); +- if (manager->mm_modem) { ++ if (manager->mm_modem || manager->modem_iface == MODEM_IFACE_OFONO) { + /* + * On some systems ModemManager doesn't handle suspend/resume, so + * we still have a valid/managed modem when resuming. In this case, + * do the whole resume sequence immediately. ++ * ++ * If modem is managed by ofono, we also do resume sequence immediately ++ * as ofono handles resuming from sleep itself. + */ + manager->modem_state = EG25_STATE_CONFIGURED; + modem_resume_post(manager); +-- +GitLab + + +From 87ff1e0a5a6f379528e1e581372e9d60d35e044f Mon Sep 17 00:00:00 2001 +From: Bhushan Shah +Date: Thu, 11 Mar 2021 14:22:50 +0530 +Subject: [PATCH 5/5] ofono-iface: add spdx copyright info + +--- + src/ofono-iface.c | 1 + + 1 file changed, 1 insertion(+) + +diff --git a/src/ofono-iface.c b/src/ofono-iface.c +index 00bf665..caf32dd 100644 +--- a/src/ofono-iface.c ++++ b/src/ofono-iface.c +@@ -1,5 +1,6 @@ + /* + * Copyright (C) 2020 Oliver Smith ++ * Copyright (C) 2021 Bhushan Shah + * + * SPDX-License-Identifier: GPL-3.0-or-later + */ +-- +GitLab + diff --git a/net-misc/eg25-manager/files/eg25-manager.service b/net-misc/eg25-manager/files/eg25-manager.service new file mode 100644 index 0000000..f791027 --- /dev/null +++ b/net-misc/eg25-manager/files/eg25-manager.service @@ -0,0 +1,29 @@ +[Unit] +Description=Quectel EG25 modem +Before=ModemManager.service + +[Service] +Type=simple +ExecStart=/usr/bin/eg25manager +Restart=on-failure +ProtectControlGroups=true +ProtectHome=true +ProtectKernelTunables=true +ProtectSystem=strict +RestrictSUIDSGID=true +PrivateTmp=true +ProtectedKernelModules=true +MemoryDenyWriteExecute=true +PrivateMounts=true +NoNewPrivileges=true +CapabilityBoundingSet= +ProtectProc=true +ProtectDevices=true +DeviceAllow=/dev/ttyS2 +LockPersonality=true +PrivateNetwork=true +ProtectClock=true +ProtectKernelLog=true + +[Install] +WantedBy=multi-user.target