mirror of
https://gitlab.com/mobian1/eg25-manager.git
synced 2025-08-29 23:32:14 +02:00
New upstream version 0.4.3
eg25-manager v0.4.3 IMPORTANT: this release renames the executable to `eg25-manager` (added dash). Make sure existing scripts are updated to reflect this change. Changes: * improve udev monitoring: - filter based on vendor/product ID - disable udev monitoring for the PinePhone Pro * add manpages for `eg25-manager` and its configuration files - this adds an optional build dependency on `scdoc` * Fix build when not using ModemManager * Fix GPIO module initialization for BraveHeart Edition PinePhones * Various build system improvements (now requires meson >= 0.58.0)
This commit is contained in:
@@ -4,7 +4,7 @@ Before=ModemManager.service
|
||||
|
||||
[Service]
|
||||
Type=simple
|
||||
ExecStart=@bindir@/eg25manager
|
||||
ExecStart=@bindir@/eg25-manager
|
||||
Restart=on-failure
|
||||
ProtectControlGroups=true
|
||||
ProtectHome=true
|
||||
|
@@ -1,4 +1,5 @@
|
||||
[manager]
|
||||
monitor_udev = true
|
||||
need_libusb = true
|
||||
usb_vid = 0x2c7c
|
||||
usb_pid = 0x0125
|
||||
|
@@ -1,4 +1,5 @@
|
||||
[manager]
|
||||
monitor_udev = true
|
||||
need_libusb = true
|
||||
usb_vid = 0x2c7c
|
||||
usb_pid = 0x0125
|
||||
|
@@ -1,4 +1,7 @@
|
||||
[manager]
|
||||
monitor_udev = true
|
||||
usb_vid = 0x2c7c
|
||||
usb_pid = 0x0125
|
||||
# Delay between setting GPIO and PWRKEY sequence, set in microseconds
|
||||
poweron_delay = 100000
|
||||
|
||||
|
@@ -1,4 +1,7 @@
|
||||
[manager]
|
||||
monitor_udev = false
|
||||
usb_vid = 0x2c7c
|
||||
usb_pid = 0x0125
|
||||
# Delay between setting GPIO and PWRKEY sequence, set in microseconds
|
||||
poweron_delay = 100000
|
||||
|
||||
@@ -35,7 +38,7 @@ configure = [
|
||||
# Print software version
|
||||
{ cmd = "QGMR" },
|
||||
# Configure audio
|
||||
{ cmd = "QDAI", expect = "3,0,0,4,0,0,1,1" },
|
||||
{ cmd = "QDAI", expect = "3,0,0,4,0,1,1,1" },
|
||||
# RI signaling using physical RI pin
|
||||
{ cmd = "QCFG", subcmd = "risignaltype", expect = "\"physical\"" },
|
||||
# Enable VoLTE support
|
||||
|
106
doc/eg25-manager.5.scd
Normal file
106
doc/eg25-manager.5.scd
Normal file
@@ -0,0 +1,106 @@
|
||||
eg25-manager(5)
|
||||
|
||||
# NAME
|
||||
eg25-manager configuration file format
|
||||
|
||||
# SYNOPSIS
|
||||
eg25-manager uses toml formatted files for configuration.
|
||||
|
||||
Configurations are loaded from:
|
||||
- *@eg25_confdir@/<compatible>.toml*: User-provided overrides (optional)
|
||||
- *@eg25_datadir@/<compatible>.toml*: Default configuration (required)
|
||||
|
||||
# SECTION: manager
|
||||
General settings for eg25-manager.
|
||||
|
||||
*poweron_delay* int (microseconds)
|
||||
Delay between de-asserting RESET and starting the PWRKEY sequence.
|
||||
|
||||
# SECTION: suspend
|
||||
Settings for how to handle suspend on the system where eg25-manager is running.
|
||||
|
||||
*boot_timeout* int (seconds)
|
||||
Prevent the system from suspending for boot_timeout seconds to allow the
|
||||
modem to fully boot.
|
||||
|
||||
Default: 120 if unset or zero.
|
||||
|
||||
*recovery_timeout* int (seconds)
|
||||
Amount of time to wait for the modem to reappear after suspend. If the
|
||||
timeout is reached the modem's USB connection will be reset.
|
||||
|
||||
Default: 9 if unset or zero.
|
||||
|
||||
# SECTION: at
|
||||
AT commands to send when different events happen, and where to send them to.
|
||||
|
||||
Each command has 4 possible elements:
|
||||
- *cmd*: the AT command itself, which will be translated to "AT+`cmd`"
|
||||
- *subcmd*: the subcommand in case a single AT command can be used to
|
||||
change multiple parameters, such as QCFG
|
||||
- *value*: the command's argument(s), usually used to set the value of a
|
||||
specific parameter
|
||||
- *expect*: the expected return value; the command is first executed
|
||||
without any value in order to query the current state. This state is
|
||||
then compared to the *expect* string; if they don't match, the command
|
||||
is then executed with value *expect* in order to set the parameter to
|
||||
the expected value
|
||||
A command can have *expect* OR *value* configured, but it shouldn't have both
|
||||
|
||||
*NOTE:* If a command sequence is configured in an override file, the default
|
||||
commands won't be loaded from the system configuration. The default commands
|
||||
should be copied into the override file when changing them.
|
||||
|
||||
*uart* string
|
||||
The serial port to use for sending AT commands to the modem.
|
||||
|
||||
*configure* List of commands
|
||||
AT commands to send to the modem when it is first started.
|
||||
|
||||
*suspend* List of commands
|
||||
AT commands to send to the modem before the system suspends.
|
||||
|
||||
*resume* List of commands
|
||||
AT commands to send to the modem after the system resumes from suspend.
|
||||
|
||||
*reset* List of commands
|
||||
AT commands to send to the modem if resetting the usb port fails.
|
||||
|
||||
# SECTION: gnss
|
||||
Settings for uploading AGPS assistance data to the modem.
|
||||
|
||||
*enabled* boolean
|
||||
Enable or disable uploading AGPS data to the modem
|
||||
|
||||
*url* string
|
||||
The directory on the server that contains the assistance files
|
||||
|
||||
Example: https://xtrapath4.izatcloud.net
|
||||
|
||||
*file* string
|
||||
The name of the assistance file on the server.
|
||||
|
||||
Example: xtra2.bin
|
||||
|
||||
# SECTION: gpio
|
||||
The *gpio* section defines the GPIO pins to use for different modem functions.
|
||||
These settings should only be changed when porting eg25-manager to a new device;
|
||||
for this reason they aren't documented here.
|
||||
|
||||
# EXAMPLES
|
||||
Print the firmware version every time the phone wakes from suspend:
|
||||
```
|
||||
[at]
|
||||
resume = [
|
||||
{ cmd = "QGMR" },
|
||||
]
|
||||
```
|
||||
|
||||
Disable uploading AGPS data to the modem:
|
||||
```
|
||||
[gnss]
|
||||
enabled = false
|
||||
```
|
||||
|
||||
# SEE AlSO
|
||||
*eg25-manager*(8)
|
37
doc/eg25-manager.8.scd
Normal file
37
doc/eg25-manager.8.scd
Normal file
@@ -0,0 +1,37 @@
|
||||
eg25-manager(8)
|
||||
|
||||
# NAME
|
||||
eg25-manager - a daemon for managing the Quectel EG25 modem found on the
|
||||
Pine64 PinePhone.
|
||||
|
||||
# SYNOPSIS
|
||||
*eg25-manager* [-v] [-c config_file]
|
||||
|
||||
# OPTIONS
|
||||
*-v*
|
||||
Show the version number and quit.
|
||||
*-c*
|
||||
User configuration file, defaults to the device configuration file in
|
||||
/etc/eg25-manager.
|
||||
|
||||
# FILES
|
||||
Configurations are loaded from:
|
||||
- *@eg25_confdir@/<compatible>.toml*: User-provided overrides (optional)
|
||||
- *@eg25_datadir@/<compatible>.toml*: Default configuration (required)
|
||||
|
||||
eg25-manager will search these folders for files named after the value of the
|
||||
compatible device-tree property (with the .toml file extension) and use the
|
||||
first matching file in each directory. If no matching default configuration is
|
||||
found, eg25-manager will exit with an error message.
|
||||
|
||||
Values from the user-provided overrides will take priority over values stored in
|
||||
the default configuration. Only changed values must be stored as user overrides,
|
||||
so eg25-manager can fall back to the default configuration as often as possible.
|
||||
|
||||
The file names eg25-manager will check can be listed using:
|
||||
```
|
||||
xargs -0 printf '%s.toml\\n' < /proc/device-tree/compatible
|
||||
```
|
||||
|
||||
# SEE ALSO
|
||||
*eg25-manager*(5) *ModemManager*(8) *ofono*(8)
|
33
doc/meson.build
Normal file
33
doc/meson.build
Normal file
@@ -0,0 +1,33 @@
|
||||
#
|
||||
# Copyright (C) 2020 Arnaud Ferraris <arnaud.ferraris@gmail.com>
|
||||
#
|
||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
#
|
||||
|
||||
scdoc = dependency('scdoc', native: true, required: false)
|
||||
if scdoc.found()
|
||||
scdoc_prog = find_program(scdoc.get_variable('scdoc'), native: true)
|
||||
|
||||
foreach section: [5, 8]
|
||||
name = 'eg25-manager'
|
||||
out = '@0@.@1@'.format(name, section)
|
||||
|
||||
preprocessed = configure_file(
|
||||
input: '@0@.scd'.format(out),
|
||||
output: '@BASENAME@.preprocessed',
|
||||
configuration: {
|
||||
'eg25_confdir': eg25_confdir,
|
||||
'eg25_datadir': eg25_datadir,
|
||||
}
|
||||
)
|
||||
|
||||
custom_target(
|
||||
out,
|
||||
output: out,
|
||||
input: preprocessed,
|
||||
command: ['sh', '-c', '@0@ < @INPUT@'.format(scdoc_prog.full_path())],
|
||||
capture: true,
|
||||
install: true,
|
||||
install_dir: join_paths(get_option('mandir'), 'man@0@'.format(section)))
|
||||
endforeach
|
||||
endif
|
@@ -8,9 +8,9 @@
|
||||
project (
|
||||
'eg25-manager',
|
||||
'c',
|
||||
version : '0.4.2',
|
||||
version : '0.4.3',
|
||||
license : 'GPLv3+',
|
||||
meson_version : '>= 0.50.0',
|
||||
meson_version : '>= 0.58.0',
|
||||
default_options :
|
||||
[
|
||||
'warning_level=1',
|
||||
@@ -66,5 +66,6 @@ mgr_deps = [
|
||||
]
|
||||
|
||||
subdir('data')
|
||||
subdir('doc')
|
||||
subdir('src')
|
||||
subdir('udev')
|
||||
|
@@ -196,8 +196,13 @@ int gpio_init(struct EG25Manager *manager, toml_table_t *config[])
|
||||
for (i = 0; i < GPIO_IN_COUNT; i++) {
|
||||
toml_table_t *table;
|
||||
toml_datum_t chip, line;
|
||||
if (!config_get_table(gpio_config, gpio_in_names[i], &table))
|
||||
|
||||
if (!config_get_table(gpio_config, gpio_in_names[i], &table)) {
|
||||
// BH edition don't have the STATUS line connected, ignore it
|
||||
if (manager->use_libusb && g_strcmp0(gpio_in_names[i], "status") == 0)
|
||||
continue;
|
||||
g_error("Unable to get config for input GPIO '%s'", gpio_in_names[i]);
|
||||
}
|
||||
|
||||
chip = toml_int_in(table, "chip");
|
||||
if (!chip.ok || chip.u.i < 0 || chip.u.i > 2)
|
||||
|
@@ -38,6 +38,9 @@
|
||||
#define EG25_VERSION "0.0.0"
|
||||
#endif
|
||||
|
||||
#define EG25_DEFAULT_VENDOR_ID 0x2c7c
|
||||
#define EG25_DEFAULT_PRODUCT_ID 0x0125
|
||||
|
||||
#define POWERON_DELAY_US 100000UL
|
||||
|
||||
static gboolean quit_app(struct EG25Manager *manager)
|
||||
@@ -141,7 +144,7 @@ void modem_configure(struct EG25Manager *manager)
|
||||
static gboolean modem_reset_done(struct EG25Manager* manager)
|
||||
{
|
||||
manager->modem_state = EG25_STATE_RESUMING;
|
||||
manager->reset_timer = 0;
|
||||
manager->complete_reset_timer = 0;
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
@@ -149,7 +152,18 @@ gboolean modem_reset(struct EG25Manager *manager)
|
||||
{
|
||||
int fd, ret, len;
|
||||
|
||||
if (manager->reset_timer) {
|
||||
/* reset sequence started, cannot be canceled anymore */
|
||||
if (manager->schedule_reset_timer) {
|
||||
g_source_remove(manager->schedule_reset_timer);
|
||||
manager->schedule_reset_timer = 0;
|
||||
}
|
||||
|
||||
if (manager->modem_recovery_timer) {
|
||||
g_source_remove(manager->modem_recovery_timer);
|
||||
manager->modem_recovery_timer = 0;
|
||||
}
|
||||
|
||||
if (manager->complete_reset_timer) {
|
||||
g_message("modem_reset: timer already setup, skipping...");
|
||||
return G_SOURCE_REMOVE;
|
||||
}
|
||||
@@ -164,11 +178,6 @@ gboolean modem_reset(struct EG25Manager *manager)
|
||||
return G_SOURCE_REMOVE;
|
||||
}
|
||||
|
||||
if (manager->modem_recovery_timer) {
|
||||
g_source_remove(manager->modem_recovery_timer);
|
||||
manager->modem_recovery_timer = 0;
|
||||
}
|
||||
|
||||
if (!manager->modem_usb_id) {
|
||||
g_warning("Empty modem USB ID");
|
||||
goto error;
|
||||
@@ -210,7 +219,7 @@ gboolean modem_reset(struct EG25Manager *manager)
|
||||
* 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);
|
||||
manager->complete_reset_timer = g_timeout_add_seconds(3, G_SOURCE_FUNC(modem_reset_done), manager);
|
||||
|
||||
return G_SOURCE_REMOVE;
|
||||
|
||||
@@ -228,7 +237,7 @@ error:
|
||||
at_sequence_reset(manager);
|
||||
|
||||
// Setup timer for making sure we don't queue other reset commands
|
||||
manager->reset_timer = g_timeout_add_seconds(30, G_SOURCE_FUNC(modem_reset_done), manager);
|
||||
manager->complete_reset_timer = g_timeout_add_seconds(30, G_SOURCE_FUNC(modem_reset_done), manager);
|
||||
|
||||
return G_SOURCE_REMOVE;
|
||||
}
|
||||
@@ -314,6 +323,7 @@ int main(int argc, char *argv[])
|
||||
struct EG25Manager manager;
|
||||
gchar *config_file = NULL;
|
||||
gboolean show_version = FALSE;
|
||||
gboolean monitor_udev = TRUE;
|
||||
toml_table_t *toml_config[EG25_CONFIG_COUNT];
|
||||
toml_table_t *manager_config[EG25_CONFIG_COUNT];
|
||||
const GOptionEntry options[] = {
|
||||
@@ -363,10 +373,13 @@ int main(int argc, char *argv[])
|
||||
if (!manager_config[EG25_CONFIG_SYS])
|
||||
g_error("Default config file lacks the 'manager' section!");
|
||||
|
||||
config_get_bool(manager_config, "monitor_udev", &monitor_udev);
|
||||
config_get_bool(manager_config, "need_libusb", &manager.use_libusb);
|
||||
config_get_uint(manager_config, "usb_vid", &manager.usb_vid);
|
||||
config_get_uint(manager_config, "usb_pid", &manager.usb_pid);
|
||||
config_get_uint(manager_config, "poweron_delay", &manager.poweron_delay);
|
||||
if (!config_get_uint(manager_config, "usb_vid", &manager.usb_vid))
|
||||
manager.usb_vid = EG25_DEFAULT_VENDOR_ID;
|
||||
if (!config_get_uint(manager_config, "usb_pid", &manager.usb_pid))
|
||||
manager.usb_pid = EG25_DEFAULT_PRODUCT_ID;
|
||||
|
||||
at_init(&manager, toml_config);
|
||||
gpio_init(&manager, toml_config);
|
||||
@@ -375,6 +388,7 @@ int main(int argc, char *argv[])
|
||||
#endif
|
||||
ofono_iface_init(&manager, toml_config);
|
||||
suspend_init(&manager, toml_config);
|
||||
if (monitor_udev)
|
||||
udev_init(&manager, toml_config);
|
||||
gnss_init(&manager, toml_config);
|
||||
|
||||
|
@@ -53,6 +53,7 @@ enum EG25State {
|
||||
EG25_STATE_SUSPENDING, // System is going into suspend
|
||||
EG25_STATE_RESUMING, // System is being resumed, waiting for modem to come back
|
||||
EG25_STATE_RESETTING, // Something went wrong, we're restarting the modem
|
||||
EG25_STATE_UPDATING, // Modem is present but being updated
|
||||
EG25_STATE_FINISHING
|
||||
};
|
||||
|
||||
@@ -70,7 +71,8 @@ enum EG25Config {
|
||||
|
||||
struct EG25Manager {
|
||||
GMainLoop *loop;
|
||||
guint reset_timer;
|
||||
guint complete_reset_timer;
|
||||
guint schedule_reset_timer;
|
||||
gboolean use_libusb;
|
||||
guint usb_vid;
|
||||
guint usb_pid;
|
||||
|
@@ -25,7 +25,7 @@ if mmglib_dep.found()
|
||||
endif
|
||||
|
||||
executable (
|
||||
'eg25manager',
|
||||
'eg25-manager',
|
||||
src,
|
||||
dependencies : mgr_deps,
|
||||
link_with: gdbofono_lib,
|
||||
|
@@ -18,10 +18,24 @@
|
||||
#define SD_PATH "/org/freedesktop/login1"
|
||||
#define SD_INTERFACE "org.freedesktop.login1.Manager"
|
||||
|
||||
static void resume_ok(struct EG25Manager *manager)
|
||||
{
|
||||
manager->modem_state = EG25_STATE_CONFIGURED;
|
||||
modem_resume_post(manager);
|
||||
}
|
||||
|
||||
static gboolean check_modem_resume(struct EG25Manager *manager)
|
||||
{
|
||||
g_message("Modem wasn't probed in time, restart it!");
|
||||
manager->modem_recovery_timer = 0;
|
||||
|
||||
#ifdef HAVE_MMGLIB
|
||||
if (manager->mm_modem) {
|
||||
resume_ok(manager);
|
||||
return FALSE;
|
||||
}
|
||||
#endif
|
||||
|
||||
g_message("Modem wasn't probed in time, restart it!");
|
||||
modem_reset(manager);
|
||||
|
||||
return FALSE;
|
||||
@@ -184,8 +198,7 @@ static void signal_cb(GDBusProxy *proxy,
|
||||
* 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);
|
||||
resume_ok(manager);
|
||||
} else {
|
||||
manager->modem_state = EG25_STATE_RESUMING;
|
||||
manager->modem_recovery_timer = g_timeout_add_seconds(manager->modem_recovery_timeout,
|
||||
|
43
src/udev.c
43
src/udev.c
@@ -11,18 +11,47 @@
|
||||
static void udev_event_cb(GUdevClient *client, gchar *action, GUdevDevice *device, gpointer data)
|
||||
{
|
||||
struct EG25Manager *manager = data;
|
||||
const gchar *prop;
|
||||
long vid = 0, pid = 0;
|
||||
|
||||
if (strcmp(action, "unbind") != 0 ||
|
||||
manager->modem_state == EG25_STATE_RESETTING ||
|
||||
!manager->modem_usb_id) {
|
||||
/*
|
||||
* Act only if the device is the one identified as a modem by MM/ofono
|
||||
*/
|
||||
if (!manager->modem_usb_id ||
|
||||
strcmp(g_udev_device_get_name(device), manager->modem_usb_id) != 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (strcmp(g_udev_device_get_name(device), manager->modem_usb_id) == 0 &&
|
||||
manager->reset_timer == 0) {
|
||||
g_message("Lost modem, resetting...");
|
||||
g_timeout_add_seconds(2, G_SOURCE_FUNC(modem_reset), manager);
|
||||
prop = g_udev_device_get_property(device, "ID_VENDOR_ID");
|
||||
if (prop)
|
||||
vid = strtol(prop, NULL, 16);
|
||||
|
||||
prop = g_udev_device_get_property(device, "ID_MODEL_ID");
|
||||
if (prop)
|
||||
pid = strtol(prop, NULL, 16);
|
||||
|
||||
if (strcmp(action, "bind") == 0 && vid != manager->usb_vid && pid != manager->usb_pid) {
|
||||
/*
|
||||
* Modem is probably executing a FW upgrade, make sure we don't interrupt it
|
||||
*/
|
||||
if (manager->schedule_reset_timer != 0) {
|
||||
g_message("Modem re-appeared with different VID/PID, cancel reset.");
|
||||
g_source_remove(manager->schedule_reset_timer);
|
||||
manager->schedule_reset_timer = 0;
|
||||
}
|
||||
manager->modem_state = EG25_STATE_UPDATING;
|
||||
}
|
||||
|
||||
if (strcmp(action, "unbind") != 0 ||
|
||||
manager->modem_state == EG25_STATE_UPDATING ||
|
||||
manager->modem_state == EG25_STATE_RESETTING ||
|
||||
manager->complete_reset_timer != 0 ||
|
||||
manager->schedule_reset_timer != 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
g_message("Lost modem, resetting...");
|
||||
manager->schedule_reset_timer = g_timeout_add_seconds(3, G_SOURCE_FUNC(modem_reset), manager);
|
||||
}
|
||||
|
||||
void udev_init (struct EG25Manager *manager, toml_table_t *config[])
|
||||
|
Reference in New Issue
Block a user