22 Commits
0.0.4 ... 0.1.1

Author SHA1 Message Date
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
Arnaud Ferraris
fdbc2cfa69 Merge branch 'fix_gerror_pointer' into 'master'
mm-iface: fix GError pointer

See merge request mobian1/devices/eg25-manager!2
2020-12-14 00:29:58 +00:00
fortysixandtwo
aa85cd873c mm-iface: fix GError pointer 2020-12-13 16:54:10 +01:00
Arnaud Ferraris
a8e6da534c Merge branch 'autofree_gerror' into 'master'
use g_autoptr for GError

See merge request mobian1/devices/eg25-manager!1
2020-12-13 14:33:52 +00:00
fortysixandtwo
1bb2f80fef use g_autoptr for GError 2020-12-13 15:14:11 +01:00
Arnaud Ferraris
9c9169a972 manager: get rid of compiler warnings 2020-12-13 00:55:22 +01:00
Arnaud Ferraris
b495d6c747 gpio: get rid of compiler warnings 2020-12-13 00:52:38 +01:00
Arnaud Ferraris
dd904bc8c1 at: get rid of compiler warnings 2020-12-13 00:52:35 +01:00
Arnaud Ferraris
8c9a2b21f9 gpio: exit if we can't request output GPIOs
This means the system is in a very bad shape as no other software should 
make use of those, so exit the daemon (will be restarted by systemd).
2020-12-13 00:35:17 +01:00
Arnaud Ferraris
90a016a8f6 src: be more careful before dereferencing potentially NULL pointers 2020-12-12 23:59:53 +01:00
Arnaud Ferraris
ff60016e5d release version 0.0.6 2020-12-11 15:10:13 +01:00
Arnaud Ferraris
5715138a96 suspend: reset sleep inhibitor if already present upon resume 2020-12-11 15:09:43 +01:00
Arnaud Ferraris
fd6a292a8f manager: don't change modem state after reset
This makes sure the resume commands (disable URC cache and enable GPS)
are executed.
2020-12-11 15:09:38 +01:00
Arnaud Ferraris
5fa345ec92 mm-iface: don't reset USB ID unless quitting
As ModemManager releases the modem while going to sleep, we clear the
USB ID too, which causes a segfault when trying to recover the modem.
2020-12-11 14:34:34 +01:00
Arnaud Ferraris
a9725243ec meson.build: bump version 2020-12-11 13:37:40 +01:00
Arnaud Ferraris
75b0920e9d Revert "manager: split modem_suspend() into _pre() and _post() functions"
This reverts commit ff9b26b831.
2020-12-11 13:36:28 +01:00
Arnaud Ferraris
9713af7ca8 at: fix GPS disabling on suspend 2020-12-11 13:31:11 +01:00
Arnaud Ferraris
664f82d570 at: add default handling for unrecognized responses 2020-12-11 13:31:11 +01:00
10 changed files with 159 additions and 62 deletions

View File

@@ -8,7 +8,7 @@
project (
'eg25manager',
'c',
version : '0.0.1',
version : '0.1.1',
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

@@ -52,19 +52,24 @@ static int configure_serial(const char *tty)
static gboolean send_at_command(struct EG25Manager *manager)
{
char command[256];
struct AtCommand *at_cmd = g_list_nth_data(manager->at_cmds, 0);
struct AtCommand *at_cmd = manager->at_cmds ? g_list_nth_data(manager->at_cmds, 0) : NULL;
int ret, len = 0;
if (at_cmd) {
if (at_cmd->subcmd == NULL && at_cmd->value == NULL)
sprintf(command, "AT+%s?\r\n", at_cmd->cmd);
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)
len = sprintf(command, "AT+%s?\r\n", at_cmd->cmd);
else if (at_cmd->subcmd == NULL && at_cmd->value)
sprintf(command, "AT+%s=%s\r\n", at_cmd->cmd, at_cmd->value);
len = sprintf(command, "AT+%s=%s\r\n", at_cmd->cmd, at_cmd->value);
else if (at_cmd->subcmd && at_cmd->value == NULL)
sprintf(command, "AT+%s=\"%s\"\r\n", at_cmd->cmd, at_cmd->subcmd);
len = sprintf(command, "AT+%s=\"%s\"\r\n", at_cmd->cmd, at_cmd->subcmd);
else if (at_cmd->subcmd && at_cmd->value)
sprintf(command, "AT+%s=\"%s\",%s\r\n", at_cmd->cmd, at_cmd->subcmd, at_cmd->value);
len = sprintf(command, "AT+%s=\"%s\",%s\r\n", at_cmd->cmd, at_cmd->subcmd, at_cmd->value);
write(manager->at_fd, command, strlen(command));
ret = write(manager->at_fd, command, len);
if (ret < len)
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) {
@@ -86,7 +91,10 @@ static gboolean send_at_command(struct EG25Manager *manager)
static void next_at_command(struct EG25Manager *manager)
{
struct AtCommand *at_cmd = g_list_nth_data(manager->at_cmds, 0);
struct AtCommand *at_cmd = manager->at_cmds ? g_list_nth_data(manager->at_cmds, 0) : NULL;
if (!at_cmd)
return;
if (at_cmd->cmd)
g_free(at_cmd->cmd);
@@ -104,7 +112,10 @@ static void next_at_command(struct EG25Manager *manager)
static void retry_at_command(struct EG25Manager *manager)
{
struct AtCommand *at_cmd = g_list_nth_data(manager->at_cmds, 0);
struct AtCommand *at_cmd = manager->at_cmds ? g_list_nth_data(manager->at_cmds, 0) : NULL;
if (!at_cmd)
return;
at_cmd->retries++;
if (at_cmd->retries > 3) {
@@ -117,7 +128,10 @@ static void retry_at_command(struct EG25Manager *manager)
static void process_at_result(struct EG25Manager *manager, char *response)
{
struct AtCommand *at_cmd = g_list_nth_data(manager->at_cmds, 0);
struct AtCommand *at_cmd = manager->at_cmds ? g_list_nth_data(manager->at_cmds, 0) : NULL;
if (!at_cmd)
return;
if (at_cmd->expected && !strstr(response, at_cmd->expected)) {
if (at_cmd->value)
@@ -195,6 +209,9 @@ static gboolean modem_response(gint fd,
retry_at_command(manager);
else if (strstr(response, "OK"))
process_at_result(manager, response);
else
// Not a recognized response, try running next command, just in case
next_at_command(manager);
}
return TRUE;
@@ -224,9 +241,9 @@ void at_sequence_configure(struct EG25Manager *manager)
{
/*
* Default parameters in megi's driver which differ with our own:
* - urc/ri/* are always set the same way on both BH and CE
* - urc/ri/* pulse duration is 1 ms and urc/delay is 0 (no need to delay
* URCs if the pulse is that short)
* - urc/ri/x are always set the same way on both BH and CE
* - urc/ri/x pulse duration is 1 ms and urc/delay is 0 (no need to delay
* URCs if the pulse is that short, so this is expected)
* - apready is disabled
*
* Parameters set in megi's kernel but not here:
@@ -259,7 +276,7 @@ void at_sequence_configure(struct EG25Manager *manager)
void at_sequence_suspend(struct EG25Manager *manager)
{
append_at_command(manager, "QGPS", NULL, "0", NULL);
append_at_command(manager, "QGPSEND", NULL, NULL, NULL);
append_at_command(manager, "QCFG", "urc/cache", "1", NULL);
send_at_command(manager);
}

View File

@@ -131,13 +131,13 @@ int gpio_init(struct EG25Manager *manager)
manager->gpio_out[i] = gpiod_chip_get_line(manager->gpiochip[chipidx], offset);
if (!manager->gpio_out[i]) {
g_critical("Unable to get output GPIO %d", i);
g_error("Unable to get output GPIO %d", i);
return 1;
}
ret = gpiod_line_request_output(manager->gpio_out[i], "eg25manager", 0);
if (ret < 0) {
g_critical("Unable to request output GPIO %d", i);
g_error("Unable to request output GPIO %d", i);
return 1;
}
}
@@ -177,7 +177,7 @@ gboolean gpio_check_poweroff(struct EG25Manager *manager, gboolean keep_down)
if (manager->gpio_in[GPIO_IN_STATUS] &&
gpiod_line_get_value(manager->gpio_in[GPIO_IN_STATUS]) == 1) {
if (keep_down) {
if (keep_down && manager->gpio_out[GPIO_OUT_RESET]) {
// Asserting RESET line to prevent modem from rebooting
gpiod_line_set_value(manager->gpio_out[GPIO_OUT_RESET], 1);
}

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,45 +112,64 @@ 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;
int fd, ret, len = strlen(manager->modem_usb_id);
if (manager->reset_timer)
return;
if (manager->suspend_timer) {
g_source_remove(manager->suspend_timer);
manager->suspend_timer = 0;
}
manager->modem_state = EG25_STATE_RESETTING;
fd = open("/sys/bus/usb/drivers/usb/unbind", O_WRONLY);
if (fd < 0)
goto error;
write(fd, manager->modem_usb_id, strlen(manager->modem_usb_id));
ret = write(fd, manager->modem_usb_id, len);
if (ret < len)
g_warning("Couldn't unbind modem: wrote %d/%d bytes", ret, len);
close(fd);
fd = open("/sys/bus/usb/drivers/usb/bind", O_WRONLY);
if (fd < 0)
goto error;
write(fd, manager->modem_usb_id, strlen(manager->modem_usb_id));
ret = write(fd, manager->modem_usb_id, len);
if (ret < len)
g_warning("Couldn't bind modem: wrote %d/%d bytes", ret, len);
close(fd);
manager->modem_state = EG25_STATE_CONFIGURED;
/*
* 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_pre(struct EG25Manager *manager)
void modem_suspend(struct EG25Manager *manager)
{
manager->modem_state = EG25_STATE_SUSPENDING;
gpio_sequence_suspend(manager);
}
void modem_suspend_post(struct EG25Manager *manager)
{
at_sequence_suspend(manager);
}
void modem_resume_pre(struct EG25Manager *manager)
{
manager->modem_state = EG25_STATE_RESUMING;
gpio_sequence_resume(manager);
}
@@ -161,7 +182,7 @@ int main(int argc, char *argv[])
{
struct EG25Manager manager;
char compatible[32];
int fd;
int fd, ret;
memset(&manager, 0, sizeof(manager));
manager.at_fd = -1;
@@ -174,8 +195,8 @@ int main(int argc, char *argv[])
g_critical("Unable to read 'compatible' string from device tree");
return 1;
}
read(fd, compatible, sizeof(compatible));
if (!strstr(compatible, "pine64,pinephone-1.2"))
ret = read(fd, compatible, sizeof(compatible));
if (ret > 0 && !strstr(compatible, "pine64,pinephone-1.2"))
manager.braveheart = TRUE;
close(fd);
@@ -183,6 +204,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,11 @@ enum EG25State {
struct EG25Manager {
GMainLoop *loop;
guint reset_timer;
int at_fd;
guint at_source;
GList *at_cmds;
enum EG25State modem_state;
gchar *modem_usb_id;
@@ -37,12 +43,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,8 +54,7 @@ struct EG25Manager {
void modem_configure(struct EG25Manager *data);
void modem_reset(struct EG25Manager *data);
void modem_suspend_pre(struct EG25Manager *data);
void modem_suspend_post(struct EG25Manager *data);
void modem_suspend(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;
}
@@ -84,13 +87,8 @@ static void interface_removed_cb(struct EG25Manager *manager,
g_message("ModemManager interface `%s' removed on object `%s'", info->name, path);
if (g_strcmp0(info->name, MM_DBUS_INTERFACE_MODEM) == 0) {
if (g_strcmp0(info->name, MM_DBUS_INTERFACE_MODEM) == 0)
manager->mm_modem = NULL;
if (manager->modem_usb_id) {
g_free(manager->modem_usb_id);
manager->modem_usb_id = NULL;
}
}
}
@@ -132,14 +130,7 @@ static void object_removed_cb(struct EG25Manager *manager, GDBusObject *object)
path = g_dbus_object_get_object_path(object);
g_message("ModemManager object `%s' removed", path);
if (manager->modem_state == EG25_STATE_SUSPENDING)
modem_suspend_post(manager);
manager->mm_modem = NULL;
if (manager->modem_usb_id) {
g_free(manager->modem_usb_id);
manager->modem_usb_id = NULL;
}
}
@@ -147,7 +138,7 @@ static void mm_manager_new_cb(GDBusConnection *connection,
GAsyncResult *res,
struct EG25Manager *manager)
{
GError *error = NULL;
g_autoptr (GError) error = NULL;
manager->mm_manager = mm_manager_new_finish(res, &error);
if (!manager->mm_manager)
@@ -199,6 +190,10 @@ void mm_iface_destroy(struct EG25Manager *manager)
g_clear_object(&manager->mm_manager);
manager->mm_manager = NULL;
}
if (manager->modem_usb_id) {
g_free(manager->modem_usb_id);
manager->modem_usb_id = NULL;
}
if (manager->mm_watch != 0) {
g_bus_unwatch_name(manager->mm_watch);
manager->mm_watch = 0;

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;
@@ -42,14 +43,13 @@ static void inhibit_done(GObject *source,
{
GDBusProxy *suspend_proxy = G_DBUS_PROXY(source);
struct EG25Manager *manager = user_data;
GError *error = NULL;
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);
g_error_free(error);
} else {
if (!fd_list || g_unix_fd_list_get_length(fd_list) != 1)
g_warning("didn't get a single fd back");
@@ -66,7 +66,8 @@ static void take_inhibitor(struct EG25Manager *manager)
{
GVariant *variant_arg;
g_assert(manager->suspend_inhibit_fd == -1);
if(manager->suspend_inhibit_fd != -1)
drop_inhibitor(manager);
variant_arg = g_variant_new ("(ssss)", "sleep", "eg25manager",
"eg25manager needs to prepare modem for sleep", "delay");
@@ -92,12 +93,14 @@ static void signal_cb(GDBusProxy *proxy,
if (is_about_to_suspend) {
g_message("system is about to suspend");
modem_suspend_pre(manager);
manager->modem_state = EG25_STATE_SUSPENDING;
modem_suspend(manager);
} else {
g_message("system is resuming");
take_inhibitor(manager);
modem_resume_pre(manager);
manager->suspend_source = g_timeout_add_seconds(8, G_SOURCE_FUNC(check_modem_resume), manager);
manager->modem_state = EG25_STATE_RESUMING;
manager->suspend_timer = g_timeout_add_seconds(9, G_SOURCE_FUNC(check_modem_resume), manager);
}
}
@@ -124,13 +127,12 @@ static void on_proxy_acquired(GObject *object,
GAsyncResult *res,
struct EG25Manager *manager)
{
GError *error = NULL;
g_autoptr (GError) error = NULL;
char *owner;
manager->suspend_proxy = g_dbus_proxy_new_for_bus_finish(res, &error);
if (!manager->suspend_proxy) {
g_warning("failed to acquire logind proxy: %s", error->message);
g_clear_error(&error);
return;
}
@@ -156,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)
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);