12 Commits
0.1.0 ... 0.1.2

Author SHA1 Message Date
Arnaud Ferraris
31133affa0 meson.build: release version 0.1.2 2021-01-14 00:07:15 +01:00
Arnaud Ferraris
79974bc9ee manager: don't manage the GNSS by default 2021-01-14 00:06:55 +01:00
Arnaud Ferraris
150ff67e7b udev: only check if modem USB ID is set 2021-01-13 23:46:38 +01:00
Arnaud Ferraris
c2e83f15a6 manager: improve error checking in modem_reset() 2020-12-30 17:31:44 +01:00
Arnaud Ferraris
cb5220a1b8 at: reduce delay between retries
There's no reason we should wait 3s before retrying a failed AT command. 
This can even cause issues with suspend inhibition: 3 retries of a 
failed command would take 9s, while logind ignores the inhibitor after 
only 5s.

Reducing the delay to 500ms should be enough and wouldn't interfere with 
suspend inhibition.

Fixes #3
2020-12-30 16:28:39 +01:00
Arnaud Ferraris
abc05e86e5 manager: only toggle GPIOs for suspend after executing all AT commands 2020-12-30 16:20:32 +01:00
Arnaud Ferraris
d990ab667e at: make sure URC cache is disabled on startup 2020-12-30 16:10:41 +01:00
Arnaud Ferraris
2a18b1cb0c meson.build: release version 0.1.1 2020-12-18 01:38:58 +01:00
Arnaud Ferraris
067c01b685 manager: rename suspend_source to suspend_timer
This makes its role more explicit.
2020-12-18 01:38:27 +01:00
Arnaud Ferraris
62a07f9c51 src: add udev watcher to improve modem recovery
Most of the modem issues follow a (incomplete) USB device reset. Instead 
of relying solely on the existing timer, this patch adds a udev monitor 
which resets the modem as soon as its associated USB device is reset, 
which greatly improves recovery time.
2020-12-18 01:37:06 +01:00
Arnaud Ferraris
74b91c7d58 manager: make sure we don't reset the modem twice in a row
This patch adds a 3s delay when resetting the modem during which we 
avoid triggering a new reset. This makes sure we don't trigger a reset 
twice in a row.

It also disables any related running timer to avoid being re-triggered 
unnecessarily.
2020-12-18 00:45:58 +01:00
Arnaud Ferraris
c39000bf93 suspend: increase modem detection delay by 1s
Sometimes it takes just a little bit longer than usual, so this avoids 
unnecessary modem recovery.
2020-12-18 00:35:32 +01:00
9 changed files with 144 additions and 22 deletions

View File

@@ -8,7 +8,7 @@
project (
'eg25manager',
'c',
version : '0.0.6',
version : '0.1.2',
license : 'GPLv3+',
meson_version : '>= 0.50.0',
default_options :
@@ -43,6 +43,7 @@ endif
mgr_deps = [
dependency('glib-2.0'),
dependency('gio-unix-2.0'),
dependency('gudev-1.0'),
dependency('libgpiod'),
dependency('libusb-1.0'),
dependency('mm-glib'),

View File

@@ -80,8 +80,7 @@ static gboolean send_at_command(struct EG25Manager *manager)
else
manager->modem_state = EG25_STATE_CONFIGURED;
} else if (manager->modem_state == EG25_STATE_SUSPENDING) {
g_message("suspend sequence is over, drop inhibitor");
suspend_inhibit(manager, FALSE);
modem_suspend_post(manager);
} else if (manager->modem_state == EG25_STATE_RESETTING) {
manager->modem_state = EG25_STATE_POWERED;
}
@@ -122,7 +121,7 @@ static void retry_at_command(struct EG25Manager *manager)
g_critical("Command %s retried %d times, aborting...", at_cmd->cmd, at_cmd->retries);
next_at_command(manager);
} else {
g_timeout_add_seconds(3, G_SOURCE_FUNC(send_at_command), manager);
g_timeout_add(500, G_SOURCE_FUNC(send_at_command), manager);
}
}
@@ -269,14 +268,18 @@ void at_sequence_configure(struct EG25Manager *manager)
append_at_command(manager, "QCFG", "apready", NULL, "1,0,500");
}
append_at_command(manager, "QURCCFG", "urcport", NULL, "\"usbat\"");
append_at_command(manager, "QGPS", NULL, NULL, "1");
if (manager->manage_gnss)
append_at_command(manager, "QGPS", NULL, NULL, "1");
append_at_command(manager, "QSCLK", NULL, "1", NULL);
// Make sure URC cache is disabled
append_at_command(manager, "QCFG", "urc/cache", "0", NULL);
send_at_command(manager);
}
void at_sequence_suspend(struct EG25Manager *manager)
{
append_at_command(manager, "QGPSEND", NULL, NULL, NULL);
if (manager->manage_gnss)
append_at_command(manager, "QGPSEND", NULL, NULL, NULL);
append_at_command(manager, "QCFG", "urc/cache", "1", NULL);
send_at_command(manager);
}
@@ -284,7 +287,8 @@ void at_sequence_suspend(struct EG25Manager *manager)
void at_sequence_resume(struct EG25Manager *manager)
{
append_at_command(manager, "QCFG", "urc/cache", "0", NULL);
append_at_command(manager, "QGPS", NULL, "1", NULL);
if (manager->manage_gnss)
append_at_command(manager, "QGPS", NULL, "1", NULL);
send_at_command(manager);
}

View File

@@ -9,6 +9,7 @@
#include "manager.h"
#include "mm-iface.h"
#include "suspend.h"
#include "udev.h"
#include <fcntl.h>
#include <signal.h>
@@ -30,6 +31,7 @@ static gboolean quit_app(struct EG25Manager *manager)
at_destroy(manager);
mm_iface_destroy(manager);
suspend_destroy(manager);
udev_destroy(manager);
if (manager->modem_state >= EG25_STATE_STARTED) {
g_message("Powering down the modem...");
@@ -110,9 +112,33 @@ void modem_configure(struct EG25Manager *manager)
at_sequence_configure(manager);
}
static gboolean modem_reset_done(struct EG25Manager* manager)
{
manager->modem_state = EG25_STATE_RESUMING;
manager->reset_timer = 0;
return FALSE;
}
void modem_reset(struct EG25Manager *manager)
{
int fd, ret, len = strlen(manager->modem_usb_id);
int fd, ret, len;
if (manager->reset_timer)
return;
if (manager->suspend_timer) {
g_source_remove(manager->suspend_timer);
manager->suspend_timer = 0;
}
if (!manager->modem_usb_id) {
g_warning("Unknown modem USB ID");
goto error;
}
len = strlen(manager->modem_usb_id);
manager->modem_state = EG25_STATE_RESETTING;
fd = open("/sys/bus/usb/drivers/usb/unbind", O_WRONLY);
if (fd < 0)
@@ -127,22 +153,32 @@ void modem_reset(struct EG25Manager *manager)
goto error;
ret = write(fd, manager->modem_usb_id, len);
if (ret < len)
g_warning("Couldn't unbind modem: wrote %d/%d bytes", ret, len);
g_warning("Couldn't bind modem: wrote %d/%d bytes", ret, len);
close(fd);
/*
* 3s is long enough to make sure the modem has been bound back and
* short enough to ensure it hasn't been acquired by ModemManager
*/
manager->reset_timer = g_timeout_add_seconds(3, G_SOURCE_FUNC(modem_reset_done), manager);
return;
error:
// Everything else failed, reset the modem
at_sequence_reset(manager);
manager->modem_state = EG25_STATE_RESETTING;
}
void modem_suspend(struct EG25Manager *manager)
void modem_suspend_pre(struct EG25Manager *manager)
{
at_sequence_suspend(manager);
}
void modem_suspend_post(struct EG25Manager *manager)
{
gpio_sequence_suspend(manager);
at_sequence_suspend(manager);
g_message("suspend sequence is over, drop inhibitor");
suspend_inhibit(manager, FALSE);
}
void modem_resume_pre(struct EG25Manager *manager)
@@ -157,14 +193,27 @@ void modem_resume_post(struct EG25Manager *manager)
int main(int argc, char *argv[])
{
g_autoptr(GOptionContext) opt_context = NULL;
g_autoptr(GError) err = NULL;
struct EG25Manager manager;
char compatible[32];
int fd, ret;
const GOptionEntry options[] = {
{ "gnss", 'g', 0, G_OPTION_ARG_NONE, &manager.manage_gnss, "Manage the GNSS feature.", NULL },
{ NULL, 0, 0, G_OPTION_ARG_NONE, NULL, NULL, NULL }
};
memset(&manager, 0, sizeof(manager));
manager.at_fd = -1;
manager.suspend_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);
if (!g_option_context_parse (opt_context, &argc, &argv, &err)) {
g_warning ("%s", err->message);
return 1;
}
manager.loop = g_main_loop_new(NULL, FALSE);
fd = open("/proc/device-tree/compatible", O_RDONLY);
@@ -181,6 +230,7 @@ int main(int argc, char *argv[])
gpio_init(&manager);
mm_iface_init(&manager);
suspend_init(&manager);
udev_init(&manager);
g_idle_add(G_SOURCE_FUNC(modem_start), &manager);

View File

@@ -8,6 +8,7 @@
#include <glib.h>
#include <gpiod.h>
#include <gudev/gudev.h>
#include <libmm-glib.h>
enum EG25State {
@@ -26,6 +27,12 @@ enum EG25State {
struct EG25Manager {
GMainLoop *loop;
guint reset_timer;
gboolean manage_gnss;
int at_fd;
guint at_source;
GList *at_cmds;
enum EG25State modem_state;
gchar *modem_usb_id;
@@ -37,12 +44,9 @@ struct EG25Manager {
GDBusProxy *suspend_proxy;
int suspend_inhibit_fd;
guint suspend_source;
guint suspend_timer;
int at_fd;
guint at_source;
GList *at_cmds;
GUdevClient *udev;
struct gpiod_chip *gpiochip[2];
struct gpiod_line *gpio_out[5];
@@ -51,7 +55,8 @@ struct EG25Manager {
void modem_configure(struct EG25Manager *data);
void modem_reset(struct EG25Manager *data);
void modem_suspend(struct EG25Manager *data);
void modem_suspend_pre(struct EG25Manager *data);
void modem_suspend_post(struct EG25Manager *data);
void modem_resume_pre(struct EG25Manager *data);
void modem_resume_post(struct EG25Manager *data);
void modem_update_state(struct EG25Manager *data, MMModemState state);

View File

@@ -12,6 +12,7 @@ executable (
'manager.c', 'manager.h',
'mm-iface.c', 'mm-iface.h',
'suspend.c', 'suspend.h',
'udev.c', 'udev.h',
],
dependencies : mgr_deps,
install : true

View File

@@ -35,7 +35,10 @@ static void add_modem(struct EG25Manager *manager, GDBusObject *object)
g_assert(manager->mm_modem != NULL);
if (manager->modem_state == EG25_STATE_RESUMING) {
g_source_remove(manager->suspend_source);
if (manager->suspend_timer) {
g_source_remove(manager->suspend_timer);
manager->suspend_timer = 0;
}
modem_resume_post(manager);
manager->modem_state = EG25_STATE_CONFIGURED;
}

View File

@@ -20,6 +20,7 @@
static gboolean check_modem_resume(struct EG25Manager *manager)
{
g_message("Modem wasn't probed in time, restart it!");
manager->suspend_timer = 0;
modem_reset(manager);
return FALSE;
@@ -93,13 +94,13 @@ static void signal_cb(GDBusProxy *proxy,
if (is_about_to_suspend) {
g_message("system is about to suspend");
manager->modem_state = EG25_STATE_SUSPENDING;
modem_suspend(manager);
modem_suspend_pre(manager);
} else {
g_message("system is resuming");
take_inhibitor(manager);
modem_resume_pre(manager);
manager->modem_state = EG25_STATE_RESUMING;
manager->suspend_source = g_timeout_add_seconds(8, G_SOURCE_FUNC(check_modem_resume), manager);
manager->suspend_timer = g_timeout_add_seconds(9, G_SOURCE_FUNC(check_modem_resume), manager);
}
}
@@ -157,6 +158,10 @@ void suspend_init(struct EG25Manager *manager)
void suspend_destroy(struct EG25Manager *manager)
{
drop_inhibitor(manager);
if (manager->suspend_timer) {
g_source_remove(manager->suspend_timer);
manager->suspend_timer = 0;
}
if (manager->suspend_proxy) {
g_object_unref(manager->suspend_proxy);
manager->suspend_proxy = NULL;

41
src/udev.c Normal file
View File

@@ -0,0 +1,41 @@
/*
* Copyright (C) 2020 Arnaud Ferraris <arnaud.ferraris@gmail.com>
*
* SPDX-License-Identifier: GPL-3.0-or-later
*/
#include "udev.h"
#include <string.h>
static void udev_event_cb(GUdevClient *client, gchar *action, GUdevDevice *device, gpointer data)
{
struct EG25Manager *manager = data;
if (strcmp(action, "unbind") != 0 || manager->modem_state == EG25_STATE_RESETTING || !manager->modem_usb_id)
return;
if (strncmp(g_udev_device_get_name(device), manager->modem_usb_id, strlen(manager->modem_usb_id)) == 0 &&
manager->reset_timer == 0) {
g_message("Lost modem, resetting...");
modem_reset(manager);
}
}
void udev_init (struct EG25Manager *manager)
{
const char * const subsystems[] = { "usb", NULL };
manager->udev = g_udev_client_new(subsystems);
g_signal_connect(manager->udev, "uevent", G_CALLBACK(udev_event_cb), manager);
return;
}
void udev_destroy (struct EG25Manager *manager)
{
if (manager->udev) {
g_object_unref(manager->udev);
manager->udev = NULL;
}
}

12
src/udev.h Normal file
View File

@@ -0,0 +1,12 @@
/*
* Copyright (C) 2020 Arnaud Ferraris <arnaud.ferraris@gmail.com>
*
* SPDX-License-Identifier: GPL-3.0-or-later
*/
#pragma once
#include "manager.h"
void udev_init (struct EG25Manager *data);
void udev_destroy (struct EG25Manager *data);