Initial release

This commit is contained in:
Arnaud Ferraris
2020-12-10 14:41:36 +01:00
commit 87ade8dd27
15 changed files with 1839 additions and 0 deletions

267
src/at.c Normal file
View File

@@ -0,0 +1,267 @@
/*
* Copyright (C) 2020 Arnaud Ferraris <arnaud.ferraris@gmail.com>
*
* SPDX-License-Identifier: GPL-3.0-or-later
*/
#include "at.h"
#include "suspend.h"
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <termios.h>
#include <unistd.h>
#include <glib-unix.h>
#define MODEM_UART "/dev/ttyS2"
struct AtCommand {
char *cmd;
char *subcmd;
char *value;
char *expected;
int retries;
};
static int configure_serial(const char *tty)
{
struct termios ttycfg;
int fd;
fd = open(tty, O_RDWR | O_NOCTTY);
if (fd > 0) {
tcgetattr(fd, &ttycfg);
ttycfg.c_iflag &= ~(IGNBRK | BRKINT | ICRNL | INLCR | PARMRK | INPCK | ISTRIP | IXON);
ttycfg.c_oflag = 0;
ttycfg.c_lflag &= ~(ECHO | ECHONL | ICANON | IEXTEN | ISIG);
ttycfg.c_cflag &= ~(CSIZE | PARENB);
ttycfg.c_cflag |= CS8;
ttycfg.c_cc[VMIN] = 1;
ttycfg.c_cc[VTIME] = 0;
cfsetspeed(&ttycfg, B115200);
tcsetattr(fd, TCSANOW, &ttycfg);
}
return fd;
}
static gboolean send_at_command(struct EG25Manager *manager)
{
char command[256];
struct AtCommand *at_cmd = g_list_nth_data(manager->at_cmds, 0);
if (at_cmd) {
if (at_cmd->subcmd == NULL && at_cmd->value == NULL)
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);
else if (at_cmd->subcmd && at_cmd->value == NULL)
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);
write(manager->at_fd, command, strlen(command));
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
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);
} else if (manager->modem_state == EG25_STATE_RESETTING) {
manager->modem_state = EG25_STATE_POWERED;
}
return FALSE;
}
static void next_at_command(struct EG25Manager *manager)
{
struct AtCommand *at_cmd = g_list_nth_data(manager->at_cmds, 0);
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);
manager->at_cmds = g_list_remove(manager->at_cmds, at_cmd);
send_at_command(manager);
}
static void retry_at_command(struct EG25Manager *manager)
{
struct AtCommand *at_cmd = g_list_nth_data(manager->at_cmds, 0);
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);
} else {
g_timeout_add_seconds(3, G_SOURCE_FUNC(send_at_command), manager);
}
}
static void process_at_result(struct EG25Manager *manager, char *response)
{
struct AtCommand *at_cmd = g_list_nth_data(manager->at_cmds, 0);
if (at_cmd->expected && !strstr(response, at_cmd->expected)) {
if (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...");
g_message("\t%s\n\t%s", at_cmd->expected, response);
send_at_command(manager);
} else {
next_at_command(manager);
}
}
static int append_at_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));
if (!at_cmd)
return -1;
at_cmd->retries = 0;
at_cmd->cmd = g_strdup(cmd);
if (subcmd)
at_cmd->subcmd = g_strdup(subcmd);
if (value)
at_cmd->value = g_strdup(value);
if (expected)
at_cmd->expected = g_strdup(expected);
manager->at_cmds = g_list_append(manager->at_cmds, at_cmd);
return 0;
}
static gboolean modem_response(gint fd,
GIOCondition event,
gpointer data)
{
struct EG25Manager *manager = data;
char response[256];
int ret;
/*
* TODO: several reads can be necessary to get the full response, we could
* loop until we read 0 chars with a reasonable delay between attempts
* (remember the transfer rate is 115200 here)
*/
ret = read(fd, response, sizeof(response));
if (ret > 0) {
response[ret] = 0;
g_strstrip(response);
if (strlen(response) == 0)
return TRUE;
g_message("Response: [%s]", response);
if (strcmp(response, "RDY") == 0)
manager->modem_state = EG25_STATE_STARTED;
else if (strcmp(response, "ERROR") == 0)
retry_at_command(manager);
else if (strstr(response, "OK"))
process_at_result(manager, response);
}
return TRUE;
}
int at_init(struct EG25Manager *manager)
{
manager->at_fd = configure_serial(MODEM_UART);
if (manager->at_fd < 0) {
g_critical("Unable to configure %s", MODEM_UART);
return 1;
}
manager->at_source = g_unix_fd_add(manager->at_fd, G_IO_IN, modem_response, manager);
return 0;
}
void at_destroy(struct EG25Manager *manager)
{
g_source_remove(manager->at_source);
close(manager->at_fd);
}
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)
* - apready is disabled
*
* Parameters set in megi's kernel but not here:
* - sleepind/level = 0
* - wakeupin/level = 0
* - ApRstLevel = 0
* - ModemRstLevel = 0
* - airplanecontrol = 1
* - fast/poweroff = 1
* (those would need to be researched to check their usefulness for our
* use-case)
*/
append_at_command(manager, "QDAI", NULL, NULL, "1,1,0,1,0,0,1,1");
append_at_command(manager, "QCFG", "risignaltype", NULL, "\"physical\"");
append_at_command(manager, "QCFG", "ims", NULL, "1");
if (manager->braveheart) {
append_at_command(manager, "QCFG", "urc/ri/ring", NULL, "\"pulse\",2000,1000,5000,\"off\",1");
append_at_command(manager, "QCFG", "urc/ri/smsincoming", NULL, "\"pulse\",2000");
append_at_command(manager, "QCFG", "urc/ri/other", NULL, "\"off\",1");
append_at_command(manager, "QCFG", "urc/delay", NULL, "1");
} else {
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");
append_at_command(manager, "QSCLK", NULL, "1", NULL);
send_at_command(manager);
}
void at_sequence_suspend(struct EG25Manager *manager)
{
append_at_command(manager, "QGPS", NULL, NULL, "0");
append_at_command(manager, "QCFG", "urc/cache", "1", NULL);
send_at_command(manager);
}
void at_sequence_resume(struct EG25Manager *manager)
{
append_at_command(manager, "QCFG", "urc/cache", "0", NULL);
append_at_command(manager, "QGPS", NULL, NULL, "1");
send_at_command(manager);
}
void at_sequence_reset(struct EG25Manager *manager)
{
append_at_command(manager, "CFUN", NULL, "1,1", NULL);
send_at_command(manager);
}

17
src/at.h Normal file
View File

@@ -0,0 +1,17 @@
/*
* Copyright (C) 2020 Arnaud Ferraris <arnaud.ferraris@gmail.com>
*
* SPDX-License-Identifier: GPL-3.0-or-later
*/
#pragma once
#include "manager.h"
int at_init(struct EG25Manager *data);
void at_destroy(struct EG25Manager *data);
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);

143
src/gpio.c Normal file
View File

@@ -0,0 +1,143 @@
/*
* Copyright (C) 2020 Arnaud Ferraris <arnaud.ferraris@gmail.com>
*
* SPDX-License-Identifier: GPL-3.0-or-later
*/
#include "gpio.h"
#define GPIO_CHIP1_LABEL "1c20800.pinctrl"
#define GPIO_CHIP2_LABEL "1f02c00.pinctrl"
#define MAX_GPIOCHIP_LINES 352
enum {
GPIO_OUT_DTR = 0,
GPIO_OUT_PWRKEY,
GPIO_OUT_RESET,
GPIO_OUT_APREADY,
GPIO_OUT_DISABLE,
GPIO_OUT_COUNT
};
static unsigned gpio_idx_bh[] = {
358,
35,
68,
231,
232
};
static unsigned gpio_idx_ce[] = {
34,
35,
68,
231,
232
};
int gpio_sequence_poweron(struct EG25Manager *manager)
{
gpiod_line_set_value(manager->gpio_out[GPIO_OUT_PWRKEY], 1);
sleep(1);
gpiod_line_set_value(manager->gpio_out[GPIO_OUT_PWRKEY], 0);
g_message("Executed power-on sequence");
return 0;
}
int gpio_sequence_shutdown(struct EG25Manager *manager)
{
gpiod_line_set_value(manager->gpio_out[GPIO_OUT_RESET], 1);
gpiod_line_set_value(manager->gpio_out[GPIO_OUT_DISABLE], 1);
gpiod_line_set_value(manager->gpio_out[GPIO_OUT_PWRKEY], 1);
sleep(1);
gpiod_line_set_value(manager->gpio_out[GPIO_OUT_PWRKEY], 0);
g_message("Executed power-off sequence");
return 0;
}
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");
return 0;
}
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_init(struct EG25Manager *manager)
{
int i, ret;
unsigned *gpio_idx;
manager->gpiochip[0] = gpiod_chip_open_by_label(GPIO_CHIP1_LABEL);
if (!manager->gpiochip[0]) {
g_critical("Unable to open GPIO chip " GPIO_CHIP1_LABEL);
return 1;
}
if (manager->braveheart) {
// BH have DTR on the 2nd gpiochip
manager->gpiochip[1] = gpiod_chip_open_by_label(GPIO_CHIP2_LABEL);
if (!manager->gpiochip[1]) {
g_critical("Unable to open GPIO chip " GPIO_CHIP2_LABEL);
return 1;
}
gpio_idx = gpio_idx_bh;
} else {
gpio_idx = gpio_idx_ce;
}
for (i = 0; i < GPIO_OUT_COUNT; i++) {
unsigned int offset, chipidx;
if (gpio_idx[i] < MAX_GPIOCHIP_LINES) {
offset = gpio_idx[i];
chipidx = 0;
} else {
offset = gpio_idx[i] - MAX_GPIOCHIP_LINES;
chipidx = 1;
}
manager->gpio_out[i] = gpiod_chip_get_line(manager->gpiochip[chipidx], offset);
if (!manager->gpio_out[i]) {
g_critical("Unable to get line %d", i);
return 1;
}
ret = gpiod_line_request_output(manager->gpio_out[i], "eg25manager", 0);
if (ret < 0) {
g_critical("Unable to request line %d", i);
return 1;
}
}
return 0;
}
void gpio_destroy(struct EG25Manager *manager)
{
int i;
for (i = 0; i < GPIO_OUT_COUNT; i++)
gpiod_line_release(manager->gpio_out[i]);
gpiod_chip_close(manager->gpiochip[0]);
if (manager->gpiochip[1])
gpiod_chip_close(manager->gpiochip[1]);
}

17
src/gpio.h Normal file
View File

@@ -0,0 +1,17 @@
/*
* Copyright (C) 2020 Arnaud Ferraris <arnaud.ferraris@gmail.com>
*
* SPDX-License-Identifier: GPL-3.0-or-later
*/
#pragma once
#include "manager.h"
int gpio_init(struct EG25Manager *state);
void gpio_destroy(struct EG25Manager *state);
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);

160
src/manager.c Normal file
View File

@@ -0,0 +1,160 @@
/*
* Copyright (C) 2020 Arnaud Ferraris <arnaud.ferraris@gmail.com>
*
* SPDX-License-Identifier: GPL-3.0-or-later
*/
#include "at.h"
#include "gpio.h"
#include "manager.h"
#include "mm-iface.h"
#include "suspend.h"
#include <fcntl.h>
#include <signal.h>
#include <string.h>
#include <unistd.h>
#include <glib-unix.h>
static gboolean quit_timeout_cb(struct EG25Manager *manager)
{
g_message("Modem down, quitting...");
g_main_loop_quit(manager->loop);
at_destroy(manager);
gpio_destroy(manager);
return FALSE;
}
static gboolean quit_app(struct EG25Manager *manager)
{
g_message("Request to quit...");
if (manager->modem_state >= EG25_STATE_STARTED) {
g_message("Powering down the modem...");
gpio_sequence_shutdown(manager);
manager->modem_state = EG25_STATE_FINISHING;
/*
* TODO: add a polling function to check STATUS and RI pins state
* (that way we could reduce the poweroff delay)
*/
g_timeout_add_seconds(30, G_SOURCE_FUNC(quit_timeout_cb), manager);
}
mm_iface_destroy(manager);
suspend_destroy(manager);
g_bus_unwatch_name(manager->mm_watch);
return FALSE;
}
static gboolean modem_start(struct EG25Manager *manager)
{
g_message("Starting modem...");
gpio_sequence_poweron(manager);
manager->modem_state = EG25_STATE_POWERED;
return FALSE;
}
void modem_update_state(struct EG25Manager *manager, MMModemState state)
{
switch (state) {
case MM_MODEM_STATE_REGISTERED:
case MM_MODEM_STATE_DISCONNECTING:
case MM_MODEM_STATE_CONNECTING:
manager->modem_state = EG25_STATE_REGISTERED;
break;
case MM_MODEM_STATE_CONNECTED:
manager->modem_state = EG25_STATE_CONNECTED;
break;
default:
manager->modem_state = EG25_STATE_CONFIGURED;
break;
}
}
void modem_configure(struct EG25Manager *manager)
{
at_sequence_configure(manager);
}
void modem_reset(struct EG25Manager *manager)
{
int fd;
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));
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));
close(fd);
manager->modem_state = EG25_STATE_CONFIGURED;
return;
error:
// Everything else failed, reset the modem
at_sequence_reset(manager);
manager->modem_state = EG25_STATE_RESETTING;
}
void modem_suspend(struct EG25Manager *manager)
{
gpio_sequence_suspend(manager);
at_sequence_suspend(manager);
}
void modem_resume_pre(struct EG25Manager *manager)
{
gpio_sequence_resume(manager);
}
void modem_resume_post(struct EG25Manager *manager)
{
at_sequence_resume(manager);
}
int main(int argc, char *argv[])
{
struct EG25Manager manager;
char compatible[32];
int fd;
memset(&manager, 0, sizeof(manager));
manager.at_fd = -1;
manager.suspend_inhibit_fd = -1;
manager.loop = g_main_loop_new(NULL, FALSE);
fd = open("/proc/device-tree/compatible", O_RDONLY);
if (fd < 0) {
g_critical("Unable to read 'compatible' string from device tree");
return 1;
}
read(fd, compatible, sizeof(compatible));
if (strstr(compatible, "pine64,pinephone-1.1"))
manager.braveheart = TRUE;
close(fd);
at_init(&manager);
gpio_init(&manager);
mm_iface_init(&manager);
suspend_init(&manager);
g_idle_add(G_SOURCE_FUNC(modem_start), &manager);
g_unix_signal_add(SIGINT, G_SOURCE_FUNC(quit_app), &manager);
g_unix_signal_add(SIGTERM, G_SOURCE_FUNC(quit_app), &manager);
g_main_loop_run(manager.loop);
return 0;
}

56
src/manager.h Normal file
View File

@@ -0,0 +1,56 @@
/*
* Copyright (C) 2020 Arnaud Ferraris <arnaud.ferraris@gmail.com>
*
* SPDX-License-Identifier: GPL-3.0-or-later
*/
#pragma once
#include <glib.h>
#include <gpiod.h>
#include <libmm-glib.h>
enum EG25State {
EG25_STATE_INIT = 0,
EG25_STATE_POWERED, // Power-on sequence has been executed, but the modem isn't on yet
EG25_STATE_STARTED, // Modem has been started and declared itdata ready
EG25_STATE_ACQUIRED, // Modem has been probed by ModemManager
EG25_STATE_CONFIGURED, // Modem has been configured through AT commands
EG25_STATE_SUSPENDING, // System is going into suspend
EG25_STATE_RESUMING, // System is being resumed, waiting for modem to come back
EG25_STATE_REGISTERED, // Modem is unlocked and registered to a network provider
EG25_STATE_CONNECTED, // Modem is connected (data connection active)
EG25_STATE_RESETTING, // Something went wrong, we're restarting the modem
EG25_STATE_FINISHING
};
struct EG25Manager {
GMainLoop *loop;
enum EG25State modem_state;
gchar *modem_usb_id;
gboolean braveheart;
guint mm_watch;
MMManager *mm_manager;
MMModem *mm_modem;
GDBusProxy *suspend_proxy;
int suspend_inhibit_fd;
guint suspend_source;
int at_fd;
guint at_source;
GList *at_cmds;
struct gpiod_chip *gpiochip[2];
struct gpiod_line *gpio_out[5];
};
void modem_configure(struct EG25Manager *data);
void modem_reset(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);

18
src/meson.build Normal file
View File

@@ -0,0 +1,18 @@
#
# Copyright (C) 2020 Arnaud Ferraris <arnaud.ferraris@gmail.com>
#
# SPDX-License-Identifier: GPL-3.0-or-later
#
executable (
'eg25manager',
[
'at.c', 'at.h',
'gpio.c', 'gpio.h',
'manager.c', 'manager.h',
'mm-iface.c', 'mm-iface.h',
'suspend.c', 'suspend.h',
],
dependencies : mgr_deps,
install : true
)

193
src/mm-iface.c Normal file
View File

@@ -0,0 +1,193 @@
/*
* Copyright (C) 2019 Purism SPC
* Copyright (C) 2020 Arnaud Ferraris <arnaud.ferraris@gmail.com>
*
* Copied and adapted from Wys' `main.c`: https://source.puri.sm/Librem5/wys/
* Author: Bob Ham <bob.ham@puri.sm>
*
* SPDX-License-Identifier: GPL-3.0-or-later
*/
#include "mm-iface.h"
#include <string.h>
static void state_changed_cb(MMModem *modem,
MMModemState old,
MMModemState new,
MMModemStateChangeReason reason,
struct EG25Manager *manager)
{
if (manager->modem_state >= EG25_STATE_CONFIGURED)
modem_update_state(manager, new);
}
static void add_modem(struct EG25Manager *manager, GDBusObject *object)
{
const gchar *path;
MmGdbusModem *gdbus_modem;
path = g_dbus_object_get_object_path(object);
g_message("Adding new modem `%s'", path);
g_assert(MM_IS_OBJECT (object));
manager->mm_modem = mm_object_get_modem(MM_OBJECT(object));
g_assert(manager->mm_modem != NULL);
if (manager->modem_state == EG25_STATE_RESUMING) {
g_source_remove(manager->suspend_source);
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);
path = mm_modem_get_device(manager->mm_modem);
manager->modem_usb_id = g_strdup(strrchr(path, '/') + 1);
gdbus_modem = MM_GDBUS_MODEM(manager->mm_modem);
g_signal_connect(gdbus_modem, "state-changed", G_CALLBACK(state_changed_cb), manager);
}
static void interface_added_cb (struct EG25Manager *manager,
GDBusObject *object,
GDBusInterface *interface)
{
GDBusInterfaceInfo *info;
info = g_dbus_interface_get_info(interface);
g_message("ModemManager interface `%s' found on object `%s'",
info->name, g_dbus_object_get_object_path(object));
if (g_strcmp0(info->name, MM_DBUS_INTERFACE_MODEM) == 0)
add_modem(manager, object);
}
static void interface_removed_cb(struct EG25Manager *manager,
GDBusObject *object,
GDBusInterface *interface)
{
const gchar *path;
GDBusInterfaceInfo *info;
path = g_dbus_object_get_object_path(object);
info = g_dbus_interface_get_info(interface);
g_message("ModemManager interface `%s' removed on object `%s'", info->name, path);
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;
}
}
}
static void add_mm_object(struct EG25Manager *manager, GDBusObject *object)
{
GList *ifaces, *node;
ifaces = g_dbus_object_get_interfaces(object);
for (node = ifaces; node; node = node->next)
interface_added_cb(manager, object, G_DBUS_INTERFACE(node->data));
g_list_free_full(ifaces, g_object_unref);
}
static void add_mm_objects(struct EG25Manager *manager)
{
GList *objects, *node;
objects = g_dbus_object_manager_get_objects(G_DBUS_OBJECT_MANAGER(manager->mm_manager));
for (node = objects; node; node = node->next)
add_mm_object(manager, G_DBUS_OBJECT(node->data));
g_list_free_full(objects, g_object_unref);
}
static void object_added_cb(struct EG25Manager *manager, GDBusObject *object)
{
g_message("ModemManager object `%s' added", g_dbus_object_get_object_path(object));
add_mm_object(manager, object);
}
static void object_removed_cb(struct EG25Manager *manager, GDBusObject *object)
{
const gchar *path;
path = g_dbus_object_get_object_path(object);
g_message("ModemManager object `%s' removed", path);
manager->mm_modem = NULL;
if (manager->modem_usb_id) {
g_free(manager->modem_usb_id);
manager->modem_usb_id = NULL;
}
}
static void mm_manager_new_cb(GDBusConnection *connection,
GAsyncResult *res,
struct EG25Manager *manager)
{
GError *error = NULL;
manager->mm_manager = mm_manager_new_finish(res, &error);
if (!manager->mm_manager)
g_critical("Error creating ModemManager Manager: %s", error->message);
g_signal_connect_swapped(G_DBUS_OBJECT_MANAGER(manager->mm_manager),
"interface-added", G_CALLBACK(interface_added_cb), manager);
g_signal_connect_swapped(G_DBUS_OBJECT_MANAGER(manager->mm_manager),
"interface-removed", G_CALLBACK(interface_removed_cb), manager);
g_signal_connect_swapped(G_DBUS_OBJECT_MANAGER(manager->mm_manager),
"object-added", G_CALLBACK(object_added_cb), manager);
g_signal_connect_swapped(G_DBUS_OBJECT_MANAGER(manager->mm_manager),
"object-removed", G_CALLBACK(object_removed_cb), manager);
add_mm_objects(manager);
}
static void mm_appeared_cb(GDBusConnection *connection,
const gchar *name,
const gchar *name_owner,
struct EG25Manager *manager)
{
g_message("ModemManager appeared on D-Bus");
mm_manager_new(connection, G_DBUS_OBJECT_MANAGER_CLIENT_FLAGS_NONE,
NULL, (GAsyncReadyCallback)mm_manager_new_cb, manager);
}
static void mm_vanished_cb(GDBusConnection *connection,
const gchar *name,
struct EG25Manager *manager)
{
g_message("ModemManager vanished from D-Bus");
mm_iface_destroy(manager);
}
void mm_iface_init(struct EG25Manager *manager)
{
manager->mm_watch = g_bus_watch_name(G_BUS_TYPE_SYSTEM, MM_DBUS_SERVICE,
G_BUS_NAME_WATCHER_FLAGS_AUTO_START,
(GBusNameAppearedCallback)mm_appeared_cb,
(GBusNameVanishedCallback)mm_vanished_cb,
manager, NULL);
}
void mm_iface_destroy(struct EG25Manager *manager)
{
g_clear_object(&manager->mm_manager);
}

12
src/mm-iface.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 mm_iface_init(struct EG25Manager *data);
void mm_iface_destroy(struct EG25Manager *data);

168
src/suspend.c Normal file
View File

@@ -0,0 +1,168 @@
/*
* Copyright (C) 2012 Red Hat, Inc.
* Copyright (C) 2020 Arnaud Ferraris <arnaud.ferraris@gmail.com>
*
* Copied and adapted from ModemManager's `mm-sleep-monitor.c`:
* https://gitlab.freedesktop.org/mobile-broadband/ModemManager/
* Author: Matthias Clasen <mclasen@redhat.com>
*
* SPDX-License-Identifier: GPL-3.0-or-later
*/
#include "manager.h"
#include <gio/gunixfdlist.h>
#define SD_NAME "org.freedesktop.login1"
#define SD_PATH "/org/freedesktop/login1"
#define SD_INTERFACE "org.freedesktop.login1.Manager"
static gboolean check_modem_resume(struct EG25Manager *manager)
{
g_message("Modem wasn't probed in time, restart it!");
modem_reset(manager);
return FALSE;
}
static gboolean drop_inhibitor(struct EG25Manager *manager)
{
if (manager->suspend_inhibit_fd >= 0) {
g_message("dropping systemd sleep inhibitor");
close(manager->suspend_inhibit_fd);
manager->suspend_inhibit_fd = -1;
return TRUE;
}
return FALSE;
}
static void inhibit_done(GObject *source,
GAsyncResult *result,
gpointer user_data)
{
GDBusProxy *suspend_proxy = G_DBUS_PROXY(source);
struct EG25Manager *manager = user_data;
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");
manager->suspend_inhibit_fd = g_unix_fd_list_get(fd_list, 0, NULL);
g_message("inhibitor fd is %d", manager->suspend_inhibit_fd);
g_object_unref(fd_list);
g_variant_unref(res);
}
}
static void take_inhibitor(struct EG25Manager *manager)
{
GVariant *variant_arg;
g_assert(manager->suspend_inhibit_fd == -1);
variant_arg = g_variant_new ("(ssss)", "sleep", "eg25manager",
"eg25manager needs to prepare modem for sleep", "delay");
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);
}
static void signal_cb(GDBusProxy *proxy,
const gchar *sendername,
const gchar *signalname,
GVariant *args,
gpointer user_data)
{
struct EG25Manager *manager = user_data;
gboolean is_about_to_suspend;
if (strcmp(signalname, "PrepareForSleep") != 0)
return;
g_variant_get(args, "(b)", &is_about_to_suspend);
if (is_about_to_suspend) {
g_message("system is about to suspend");
manager->modem_state = EG25_STATE_SUSPENDING;
modem_suspend(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);
}
}
static void name_owner_cb(GObject *object,
GParamSpec *pspec,
gpointer user_data)
{
GDBusProxy *proxy = G_DBUS_PROXY(object);
struct EG25Manager *manager = user_data;
char *owner;
g_assert(proxy == manager->suspend_proxy);
owner = g_dbus_proxy_get_name_owner(proxy);
if (owner)
take_inhibitor(manager);
else
drop_inhibitor(manager);
g_free(owner);
}
static void on_proxy_acquired(GObject *object,
GAsyncResult *res,
struct EG25Manager *manager)
{
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;
}
g_signal_connect(manager->suspend_proxy, "notify::g-name-owner", G_CALLBACK(name_owner_cb), manager);
g_signal_connect(manager->suspend_proxy, "g-signal", G_CALLBACK(signal_cb), manager);
owner = g_dbus_proxy_get_name_owner(manager->suspend_proxy);
if (owner)
take_inhibitor(manager);
g_free(owner);
}
void suspend_init(struct EG25Manager *manager)
{
g_dbus_proxy_new_for_bus(G_BUS_TYPE_SYSTEM,
G_DBUS_PROXY_FLAGS_DO_NOT_AUTO_START |
G_DBUS_PROXY_FLAGS_DO_NOT_LOAD_PROPERTIES,
NULL, SD_NAME, SD_PATH, SD_INTERFACE, NULL,
(GAsyncReadyCallback)on_proxy_acquired, manager);
}
void suspend_destroy(struct EG25Manager *manager)
{
drop_inhibitor(manager);
g_object_unref(manager->suspend_proxy);
}
void suspend_inhibit(struct EG25Manager *manager, gboolean inhibit)
{
if (inhibit)
take_inhibitor(manager);
else
drop_inhibitor(manager);
}

14
src/suspend.h Normal file
View File

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