mirror of
https://gitlab.com/mobian1/eg25-manager.git
synced 2025-08-30 15:52:11 +02:00
Compare commits
29 Commits
mobian/0.4
...
0.5.0
Author | SHA1 | Date | |
---|---|---|---|
|
012c09e630 | ||
|
40623d1572 | ||
|
93bdfbdfa2 | ||
|
bc5a25b17e | ||
|
b1bb871eb7 | ||
|
88e85ae7c2 | ||
|
b8ff1ef3d4 | ||
|
e7790f941c | ||
|
6b41ae6b3b | ||
|
5b4f9bcc12 | ||
|
2218a908ab | ||
|
be6a924f8d | ||
|
34472a5cff | ||
|
ee70cf7d2f | ||
|
0e2311fb36 | ||
|
5e4c797695 | ||
|
be1ae18592 | ||
|
19eb488e29 | ||
|
a3c51fc513 | ||
|
75400fb9c0 | ||
|
97b1878ebc | ||
|
9e6bccdf37 | ||
|
df79247821 | ||
|
61c89a003a | ||
|
9cf51b9529 | ||
|
50b4c00c16 | ||
|
fedce7298b | ||
|
8665f8a4a6 | ||
|
88c68b9933 |
@@ -15,7 +15,7 @@ It implements the following features:
|
|||||||
|
|
||||||
`eg25-manager` requires the following development libraries:
|
`eg25-manager` requires the following development libraries:
|
||||||
- libglib2.0-dev
|
- libglib2.0-dev
|
||||||
- libgpiod-dev
|
- libgpiod-dev (>= 2.0)
|
||||||
- libmm-glib-dev
|
- libmm-glib-dev
|
||||||
|
|
||||||
## Building
|
## Building
|
||||||
|
@@ -4,7 +4,7 @@ Before=ModemManager.service
|
|||||||
|
|
||||||
[Service]
|
[Service]
|
||||||
Type=simple
|
Type=simple
|
||||||
ExecStart=@bindir@/eg25manager
|
ExecStart=@bindir@/eg25-manager
|
||||||
Restart=on-failure
|
Restart=on-failure
|
||||||
ProtectControlGroups=true
|
ProtectControlGroups=true
|
||||||
ProtectHome=true
|
ProtectHome=true
|
||||||
|
@@ -1,4 +1,5 @@
|
|||||||
[manager]
|
[manager]
|
||||||
|
monitor_udev = true
|
||||||
need_libusb = true
|
need_libusb = true
|
||||||
usb_vid = 0x2c7c
|
usb_vid = 0x2c7c
|
||||||
usb_pid = 0x0125
|
usb_pid = 0x0125
|
||||||
|
@@ -1,4 +1,5 @@
|
|||||||
[manager]
|
[manager]
|
||||||
|
monitor_udev = true
|
||||||
need_libusb = true
|
need_libusb = true
|
||||||
usb_vid = 0x2c7c
|
usb_vid = 0x2c7c
|
||||||
usb_pid = 0x0125
|
usb_pid = 0x0125
|
||||||
|
@@ -1,4 +1,7 @@
|
|||||||
[manager]
|
[manager]
|
||||||
|
monitor_udev = true
|
||||||
|
usb_vid = 0x2c7c
|
||||||
|
usb_pid = 0x0125
|
||||||
# Delay between setting GPIO and PWRKEY sequence, set in microseconds
|
# Delay between setting GPIO and PWRKEY sequence, set in microseconds
|
||||||
poweron_delay = 100000
|
poweron_delay = 100000
|
||||||
|
|
||||||
|
@@ -1,4 +1,7 @@
|
|||||||
[manager]
|
[manager]
|
||||||
|
monitor_udev = false
|
||||||
|
usb_vid = 0x2c7c
|
||||||
|
usb_pid = 0x0125
|
||||||
# Delay between setting GPIO and PWRKEY sequence, set in microseconds
|
# Delay between setting GPIO and PWRKEY sequence, set in microseconds
|
||||||
poweron_delay = 100000
|
poweron_delay = 100000
|
||||||
|
|
||||||
@@ -35,7 +38,7 @@ configure = [
|
|||||||
# Print software version
|
# Print software version
|
||||||
{ cmd = "QGMR" },
|
{ cmd = "QGMR" },
|
||||||
# Configure audio
|
# 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
|
# RI signaling using physical RI pin
|
||||||
{ cmd = "QCFG", subcmd = "risignaltype", expect = "\"physical\"" },
|
{ cmd = "QCFG", subcmd = "risignaltype", expect = "\"physical\"" },
|
||||||
# Enable VoLTE support
|
# 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 (
|
project (
|
||||||
'eg25-manager',
|
'eg25-manager',
|
||||||
'c',
|
'c',
|
||||||
version : '0.4.2',
|
version : '0.5.0',
|
||||||
license : 'GPLv3+',
|
license : 'GPLv3+',
|
||||||
meson_version : '>= 0.50.0',
|
meson_version : '>= 0.58.0',
|
||||||
default_options :
|
default_options :
|
||||||
[
|
[
|
||||||
'warning_level=1',
|
'warning_level=1',
|
||||||
@@ -59,12 +59,13 @@ mgr_deps = [
|
|||||||
dependency('glib-2.0'),
|
dependency('glib-2.0'),
|
||||||
dependency('gio-unix-2.0'),
|
dependency('gio-unix-2.0'),
|
||||||
dependency('gudev-1.0'),
|
dependency('gudev-1.0'),
|
||||||
dependency('libgpiod'),
|
dependency('libgpiod', version: '>= 2.0'),
|
||||||
dependency('libusb-1.0'),
|
dependency('libusb-1.0'),
|
||||||
dependency('libcurl'),
|
dependency('libcurl'),
|
||||||
mmglib_dep,
|
mmglib_dep,
|
||||||
]
|
]
|
||||||
|
|
||||||
subdir('data')
|
subdir('data')
|
||||||
|
subdir('doc')
|
||||||
subdir('src')
|
subdir('src')
|
||||||
subdir('udev')
|
subdir('udev')
|
||||||
|
227
src/gpio.c
227
src/gpio.c
@@ -8,6 +8,8 @@
|
|||||||
#include "gpio.h"
|
#include "gpio.h"
|
||||||
|
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
|
#include <dirent.h>
|
||||||
|
#include <sys/stat.h>
|
||||||
|
|
||||||
/* Those defines are used for legacy config files only */
|
/* Those defines are used for legacy config files only */
|
||||||
#define GPIO_CHIP1_LABEL "1c20800.pinctrl"
|
#define GPIO_CHIP1_LABEL "1c20800.pinctrl"
|
||||||
@@ -40,11 +42,41 @@ static char *gpio_in_names[] = {
|
|||||||
"status",
|
"status",
|
||||||
};
|
};
|
||||||
|
|
||||||
|
enum gpiod_line_value gpio_line_get_value(struct EG25Manager *manager, int line) {
|
||||||
|
enum gpiod_line_value value;
|
||||||
|
unsigned int offset;
|
||||||
|
|
||||||
|
gpiod_line_request_get_requested_offsets(manager->gpio_in[line], &offset, 1);
|
||||||
|
value = gpiod_line_request_get_value(manager->gpio_in[line], offset);
|
||||||
|
|
||||||
|
if (value == GPIOD_LINE_VALUE_ERROR) {
|
||||||
|
g_warning("gpio: couldn't get value on line %d", line);
|
||||||
|
}
|
||||||
|
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
int gpio_line_set_value(struct EG25Manager *manager, int line, enum gpiod_line_value value) {
|
||||||
|
unsigned int offset;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
gpiod_line_request_get_requested_offsets(manager->gpio_out[line], &offset, 1);
|
||||||
|
ret = gpiod_line_request_set_value(manager->gpio_out[line], offset, value);
|
||||||
|
if (ret) {
|
||||||
|
g_warning("gpio: couldn't set value %d on line %d", value, line);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
manager->gpio_out_value[line] = value;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
int gpio_sequence_poweron(struct EG25Manager *manager)
|
int gpio_sequence_poweron(struct EG25Manager *manager)
|
||||||
{
|
{
|
||||||
gpiod_line_set_value(manager->gpio_out[GPIO_OUT_PWRKEY], 1);
|
gpio_line_set_value(manager, GPIO_OUT_PWRKEY, GPIOD_LINE_VALUE_ACTIVE);
|
||||||
sleep(1);
|
sleep(1);
|
||||||
gpiod_line_set_value(manager->gpio_out[GPIO_OUT_PWRKEY], 0);
|
gpio_line_set_value(manager, GPIO_OUT_PWRKEY, GPIOD_LINE_VALUE_INACTIVE);
|
||||||
|
|
||||||
g_message("Executed power-on/off sequence");
|
g_message("Executed power-on/off sequence");
|
||||||
|
|
||||||
@@ -53,7 +85,7 @@ int gpio_sequence_poweron(struct EG25Manager *manager)
|
|||||||
|
|
||||||
int gpio_sequence_shutdown(struct EG25Manager *manager)
|
int gpio_sequence_shutdown(struct EG25Manager *manager)
|
||||||
{
|
{
|
||||||
gpiod_line_set_value(manager->gpio_out[GPIO_OUT_DISABLE], 1);
|
gpio_line_set_value(manager, GPIO_OUT_DISABLE, GPIOD_LINE_VALUE_ACTIVE);
|
||||||
gpio_sequence_poweron(manager);
|
gpio_sequence_poweron(manager);
|
||||||
|
|
||||||
g_message("Executed power-off sequence");
|
g_message("Executed power-off sequence");
|
||||||
@@ -63,7 +95,7 @@ int gpio_sequence_shutdown(struct EG25Manager *manager)
|
|||||||
|
|
||||||
int gpio_sequence_suspend(struct EG25Manager *manager)
|
int gpio_sequence_suspend(struct EG25Manager *manager)
|
||||||
{
|
{
|
||||||
gpiod_line_set_value(manager->gpio_out[GPIO_OUT_APREADY], 1);
|
gpio_line_set_value(manager, GPIO_OUT_APREADY, GPIOD_LINE_VALUE_ACTIVE);
|
||||||
|
|
||||||
g_message("Executed suspend sequence");
|
g_message("Executed suspend sequence");
|
||||||
|
|
||||||
@@ -72,7 +104,7 @@ int gpio_sequence_suspend(struct EG25Manager *manager)
|
|||||||
|
|
||||||
int gpio_sequence_resume(struct EG25Manager *manager)
|
int gpio_sequence_resume(struct EG25Manager *manager)
|
||||||
{
|
{
|
||||||
gpiod_line_set_value(manager->gpio_out[GPIO_OUT_APREADY], 0);
|
gpio_line_set_value(manager, GPIO_OUT_APREADY, GPIOD_LINE_VALUE_INACTIVE);
|
||||||
|
|
||||||
g_message("Executed resume sequence");
|
g_message("Executed resume sequence");
|
||||||
|
|
||||||
@@ -81,8 +113,8 @@ int gpio_sequence_resume(struct EG25Manager *manager)
|
|||||||
|
|
||||||
int gpio_sequence_wake(struct EG25Manager *manager)
|
int gpio_sequence_wake(struct EG25Manager *manager)
|
||||||
{
|
{
|
||||||
if (gpiod_line_get_value(manager->gpio_out[GPIO_OUT_DTR])) {
|
if (manager->gpio_out_value[GPIO_OUT_DTR]) {
|
||||||
gpiod_line_set_value(manager->gpio_out[GPIO_OUT_DTR], 0);
|
gpio_line_set_value(manager, GPIO_OUT_DTR, GPIOD_LINE_VALUE_INACTIVE);
|
||||||
|
|
||||||
/* Give the modem 200ms to wake from soft sleep */
|
/* Give the modem 200ms to wake from soft sleep */
|
||||||
usleep(200000);
|
usleep(200000);
|
||||||
@@ -95,42 +127,150 @@ int gpio_sequence_wake(struct EG25Manager *manager)
|
|||||||
|
|
||||||
int gpio_sequence_sleep(struct EG25Manager *manager)
|
int gpio_sequence_sleep(struct EG25Manager *manager)
|
||||||
{
|
{
|
||||||
gpiod_line_set_value(manager->gpio_out[GPIO_OUT_DTR], 1);
|
gpio_line_set_value(manager, GPIO_OUT_DTR, GPIOD_LINE_VALUE_ACTIVE);
|
||||||
g_message("Executed soft sleep sequence");
|
g_message("Executed soft sleep sequence");
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
struct gpiod_line *gpio_get_output_line(struct EG25Manager *manager, int chip, int line)
|
struct gpiod_line_request *gpio_request_line(struct EG25Manager *manager, int chip, unsigned int line, enum gpiod_line_direction direction) {
|
||||||
{
|
struct gpiod_line_request *request = NULL;
|
||||||
struct gpiod_line *gpio_line;
|
struct gpiod_line_settings *settings;
|
||||||
|
struct gpiod_line_config *line_cfg;
|
||||||
|
struct gpiod_request_config *req_cfg;
|
||||||
|
int ret;
|
||||||
|
|
||||||
gpio_line = gpiod_chip_get_line(manager->gpiochip[chip], line);
|
settings = gpiod_line_settings_new();
|
||||||
if (!gpio_line)
|
if (!settings)
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
if (gpiod_line_request_output(gpio_line, "eg25manager", 0) < 0) {
|
gpiod_line_settings_set_direction(settings, direction);
|
||||||
gpiod_line_release(gpio_line);
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
return gpio_line;
|
line_cfg = gpiod_line_config_new();
|
||||||
|
if (!line_cfg)
|
||||||
|
goto free_settings;
|
||||||
|
|
||||||
|
ret = gpiod_line_config_add_line_settings(line_cfg, &line, 1, settings);
|
||||||
|
if (ret)
|
||||||
|
goto free_line_config;
|
||||||
|
|
||||||
|
req_cfg = gpiod_request_config_new();
|
||||||
|
if (!req_cfg)
|
||||||
|
goto free_line_config;
|
||||||
|
|
||||||
|
gpiod_request_config_set_consumer(req_cfg, "eg25-manager");
|
||||||
|
|
||||||
|
request = gpiod_chip_request_lines(manager->gpiochip[chip], req_cfg, line_cfg);
|
||||||
|
|
||||||
|
gpiod_request_config_free(req_cfg);
|
||||||
|
|
||||||
|
free_line_config:
|
||||||
|
gpiod_line_config_free(line_cfg);
|
||||||
|
|
||||||
|
free_settings:
|
||||||
|
gpiod_line_settings_free(settings);
|
||||||
|
|
||||||
|
return request;
|
||||||
}
|
}
|
||||||
|
|
||||||
struct gpiod_line *gpio_get_input_line(struct EG25Manager *manager, int chip, int line)
|
static int gpio_chip_dir_filter(const struct dirent *entry)
|
||||||
{
|
{
|
||||||
struct gpiod_line *gpio_line;
|
struct stat sb;
|
||||||
|
int ret = 0;
|
||||||
|
char *path;
|
||||||
|
|
||||||
gpio_line = gpiod_chip_get_line(manager->gpiochip[chip], line);
|
if (asprintf(&path, "/dev/%s", entry->d_name) < 0)
|
||||||
if (!gpio_line)
|
return 0;
|
||||||
return NULL;
|
|
||||||
|
|
||||||
if (gpiod_line_request_input(gpio_line, "eg25manager") < 0) {
|
if ((lstat(path, &sb) == 0) && (!S_ISLNK(sb.st_mode)) &&
|
||||||
gpiod_line_release(gpio_line);
|
gpiod_is_gpiochip_device(path))
|
||||||
return NULL;
|
ret = 1;
|
||||||
|
|
||||||
|
free(path);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
int gpio_all_chip_paths(char ***paths_ptr)
|
||||||
|
{
|
||||||
|
int i, j, num_chips, ret = 0;
|
||||||
|
struct dirent **entries;
|
||||||
|
char **paths;
|
||||||
|
|
||||||
|
num_chips = scandir("/dev/", &entries, gpio_chip_dir_filter, alphasort);
|
||||||
|
if (num_chips < 0)
|
||||||
|
g_error("gpio: unable to scan /dev: %s", g_strerror(errno));
|
||||||
|
|
||||||
|
paths = calloc(num_chips, sizeof(*paths));
|
||||||
|
if (paths == NULL)
|
||||||
|
g_error("gpio: out of memory");
|
||||||
|
|
||||||
|
for (i = 0; i < num_chips; i++) {
|
||||||
|
if (asprintf(&paths[i], "/dev/%s", entries[i]->d_name) < 0) {
|
||||||
|
for (j = 0; j < i; j++)
|
||||||
|
free(paths[j]);
|
||||||
|
|
||||||
|
free(paths);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return gpio_line;
|
*paths_ptr = paths;
|
||||||
|
ret = num_chips;
|
||||||
|
|
||||||
|
for (i = 0; i < num_chips; i++)
|
||||||
|
free(entries[i]);
|
||||||
|
|
||||||
|
free(entries);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct gpiod_chip *gpio_chip_open_by_label(const char *label)
|
||||||
|
{
|
||||||
|
int num_chips, i;
|
||||||
|
char **paths;
|
||||||
|
const char *clabel;
|
||||||
|
struct gpiod_chip *chip;
|
||||||
|
struct gpiod_chip_info *cinfo;
|
||||||
|
|
||||||
|
num_chips = gpio_all_chip_paths(&paths);
|
||||||
|
|
||||||
|
for (i = 0; i < num_chips; i++) {
|
||||||
|
chip = gpiod_chip_open(paths[i]);
|
||||||
|
if (!chip)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
cinfo = gpiod_chip_get_info(chip);
|
||||||
|
if (!cinfo)
|
||||||
|
goto clean_chip_open;
|
||||||
|
|
||||||
|
clabel = gpiod_chip_info_get_label(cinfo);
|
||||||
|
|
||||||
|
if (strcmp(label, clabel) == 0) {
|
||||||
|
return chip;
|
||||||
|
}
|
||||||
|
|
||||||
|
clean_chip_open:
|
||||||
|
gpiod_chip_close(chip);
|
||||||
|
}
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned int gpio_chip_num_lines(struct EG25Manager *manager, unsigned int chip_num) {
|
||||||
|
struct gpiod_chip *chip = manager->gpiochip[chip_num];
|
||||||
|
struct gpiod_chip_info *info;
|
||||||
|
unsigned int num_lines;
|
||||||
|
|
||||||
|
info = gpiod_chip_get_info(chip);
|
||||||
|
if (!info)
|
||||||
|
g_error("gpio: failed to read info: %s", strerror(errno));
|
||||||
|
|
||||||
|
num_lines = gpiod_chip_info_get_num_lines(info);
|
||||||
|
|
||||||
|
gpiod_chip_info_free(info);
|
||||||
|
|
||||||
|
return num_lines;
|
||||||
}
|
}
|
||||||
|
|
||||||
int gpio_init(struct EG25Manager *manager, toml_table_t *config[])
|
int gpio_init(struct EG25Manager *manager, toml_table_t *config[])
|
||||||
@@ -169,7 +309,7 @@ int gpio_init(struct EG25Manager *manager, toml_table_t *config[])
|
|||||||
toml_datum_t data = toml_string_at(chipslist, i);
|
toml_datum_t data = toml_string_at(chipslist, i);
|
||||||
if (!data.ok)
|
if (!data.ok)
|
||||||
continue;
|
continue;
|
||||||
manager->gpiochip[i] = gpiod_chip_open_by_label(data.u.s);
|
manager->gpiochip[i] = gpio_chip_open_by_label(data.u.s);
|
||||||
if (!manager->gpiochip[i])
|
if (!manager->gpiochip[i])
|
||||||
g_error("Unable to find GPIO chip '%s'", data.u.s);
|
g_error("Unable to find GPIO chip '%s'", data.u.s);
|
||||||
}
|
}
|
||||||
@@ -185,10 +325,10 @@ int gpio_init(struct EG25Manager *manager, toml_table_t *config[])
|
|||||||
g_error("Wrong chip ID for output GPIO '%s'", gpio_out_names[i]);
|
g_error("Wrong chip ID for output GPIO '%s'", gpio_out_names[i]);
|
||||||
|
|
||||||
line = toml_int_in(table, "line");
|
line = toml_int_in(table, "line");
|
||||||
if (!line.ok || line.u.i < 0 || line.u.i > gpiod_chip_num_lines(manager->gpiochip[chip.u.i]))
|
if (!line.ok || line.u.i < 0 || line.u.i > gpio_chip_num_lines(manager, chip.u.i))
|
||||||
g_error("Wrong line ID for output GPIO '%s'", gpio_out_names[i]);
|
g_error("Wrong line ID for output GPIO '%s'", gpio_out_names[i]);
|
||||||
|
|
||||||
manager->gpio_out[i] = gpio_get_output_line(manager, chip.u.i, line.u.i);
|
manager->gpio_out[i] = gpio_request_line(manager, chip.u.i, line.u.i, GPIOD_LINE_DIRECTION_OUTPUT);
|
||||||
if (!manager->gpio_out[i])
|
if (!manager->gpio_out[i])
|
||||||
g_error("Unable to get output GPIO %d", i);
|
g_error("Unable to get output GPIO %d", i);
|
||||||
}
|
}
|
||||||
@@ -196,18 +336,23 @@ int gpio_init(struct EG25Manager *manager, toml_table_t *config[])
|
|||||||
for (i = 0; i < GPIO_IN_COUNT; i++) {
|
for (i = 0; i < GPIO_IN_COUNT; i++) {
|
||||||
toml_table_t *table;
|
toml_table_t *table;
|
||||||
toml_datum_t chip, line;
|
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]);
|
g_error("Unable to get config for input GPIO '%s'", gpio_in_names[i]);
|
||||||
|
}
|
||||||
|
|
||||||
chip = toml_int_in(table, "chip");
|
chip = toml_int_in(table, "chip");
|
||||||
if (!chip.ok || chip.u.i < 0 || chip.u.i > 2)
|
if (!chip.ok || chip.u.i < 0 || chip.u.i > 2)
|
||||||
g_error("Wrong chip ID for input GPIO '%s'", gpio_in_names[i]);
|
g_error("Wrong chip ID for input GPIO '%s'", gpio_in_names[i]);
|
||||||
|
|
||||||
line = toml_int_in(table, "line");
|
line = toml_int_in(table, "line");
|
||||||
if (!line.ok || line.u.i < 0 || line.u.i > gpiod_chip_num_lines(manager->gpiochip[chip.u.i]))
|
if (!line.ok || line.u.i < 0 || line.u.i > gpio_chip_num_lines(manager, chip.u.i))
|
||||||
g_error("Wrong line ID for input GPIO '%s'", gpio_in_names[i]);
|
g_error("Wrong line ID for input GPIO '%s'", gpio_in_names[i]);
|
||||||
|
|
||||||
manager->gpio_in[i] = gpio_get_input_line(manager, chip.u.i, line.u.i);
|
manager->gpio_in[i] = gpio_request_line(manager, chip.u.i, line.u.i, GPIOD_LINE_DIRECTION_INPUT);
|
||||||
if (!manager->gpio_in[i])
|
if (!manager->gpio_in[i])
|
||||||
g_error("Unable to get input GPIO %d", i);
|
g_error("Unable to get input GPIO %d", i);
|
||||||
}
|
}
|
||||||
@@ -215,11 +360,11 @@ int gpio_init(struct EG25Manager *manager, toml_table_t *config[])
|
|||||||
guint offset, chipidx, gpio_idx;
|
guint offset, chipidx, gpio_idx;
|
||||||
|
|
||||||
/* Legacy config file, only used on the OG PinePhone */
|
/* Legacy config file, only used on the OG PinePhone */
|
||||||
manager->gpiochip[0] = gpiod_chip_open_by_label(GPIO_CHIP1_LABEL);
|
manager->gpiochip[0] = gpio_chip_open_by_label(GPIO_CHIP1_LABEL);
|
||||||
if (!manager->gpiochip[0])
|
if (!manager->gpiochip[0])
|
||||||
g_error("Unable to open GPIO chip " GPIO_CHIP1_LABEL);
|
g_error("Unable to open GPIO chip " GPIO_CHIP1_LABEL);
|
||||||
|
|
||||||
manager->gpiochip[1] = gpiod_chip_open_by_label(GPIO_CHIP2_LABEL);
|
manager->gpiochip[1] = gpio_chip_open_by_label(GPIO_CHIP2_LABEL);
|
||||||
if (!manager->gpiochip[1])
|
if (!manager->gpiochip[1])
|
||||||
g_error("Unable to open GPIO chip " GPIO_CHIP2_LABEL);
|
g_error("Unable to open GPIO chip " GPIO_CHIP2_LABEL);
|
||||||
|
|
||||||
@@ -235,7 +380,7 @@ int gpio_init(struct EG25Manager *manager, toml_table_t *config[])
|
|||||||
chipidx = 1;
|
chipidx = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
manager->gpio_out[i] = gpio_get_input_line(manager, chipidx, offset);
|
manager->gpio_out[i] = gpio_request_line(manager, chipidx, offset, GPIOD_LINE_DIRECTION_OUTPUT);
|
||||||
if (!manager->gpio_out[i])
|
if (!manager->gpio_out[i])
|
||||||
g_error("Unable to get output GPIO %d", i);
|
g_error("Unable to get output GPIO %d", i);
|
||||||
}
|
}
|
||||||
@@ -252,7 +397,7 @@ int gpio_init(struct EG25Manager *manager, toml_table_t *config[])
|
|||||||
chipidx = 1;
|
chipidx = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
manager->gpio_in[i] = gpio_get_input_line(manager, chipidx, offset);
|
manager->gpio_in[i] = gpio_request_line(manager, chipidx, offset, GPIOD_LINE_DIRECTION_INPUT);
|
||||||
if (!manager->gpio_in[i])
|
if (!manager->gpio_in[i])
|
||||||
g_error("Unable to get input GPIO %d", i);
|
g_error("Unable to get input GPIO %d", i);
|
||||||
}
|
}
|
||||||
@@ -264,11 +409,11 @@ int gpio_init(struct EG25Manager *manager, toml_table_t *config[])
|
|||||||
gboolean gpio_check_poweroff(struct EG25Manager *manager, gboolean keep_down)
|
gboolean gpio_check_poweroff(struct EG25Manager *manager, gboolean keep_down)
|
||||||
{
|
{
|
||||||
if (manager->gpio_in[GPIO_IN_STATUS] &&
|
if (manager->gpio_in[GPIO_IN_STATUS] &&
|
||||||
gpiod_line_get_value(manager->gpio_in[GPIO_IN_STATUS]) == 1) {
|
gpio_line_get_value(manager, GPIO_IN_STATUS) == GPIOD_LINE_VALUE_ACTIVE) {
|
||||||
|
|
||||||
if (keep_down && manager->gpio_out[GPIO_OUT_RESET]) {
|
if (keep_down && manager->gpio_out[GPIO_OUT_RESET]) {
|
||||||
// Asserting RESET line to prevent modem from rebooting
|
// Asserting RESET line to prevent modem from rebooting
|
||||||
gpiod_line_set_value(manager->gpio_out[GPIO_OUT_RESET], 1);
|
gpio_line_set_value(manager, GPIO_OUT_RESET, GPIOD_LINE_VALUE_ACTIVE);
|
||||||
}
|
}
|
||||||
|
|
||||||
return TRUE;
|
return TRUE;
|
||||||
@@ -283,12 +428,12 @@ void gpio_destroy(struct EG25Manager *manager)
|
|||||||
|
|
||||||
for (i = 0; i < GPIO_OUT_COUNT; i++) {
|
for (i = 0; i < GPIO_OUT_COUNT; i++) {
|
||||||
if (manager->gpio_out[i])
|
if (manager->gpio_out[i])
|
||||||
gpiod_line_release(manager->gpio_out[i]);
|
gpiod_line_request_release(manager->gpio_out[i]);
|
||||||
}
|
}
|
||||||
|
|
||||||
for (i = 0; i < GPIO_IN_COUNT; i++) {
|
for (i = 0; i < GPIO_IN_COUNT; i++) {
|
||||||
if (manager->gpio_in[i])
|
if (manager->gpio_in[i])
|
||||||
gpiod_line_release(manager->gpio_in[i]);
|
gpiod_line_request_release(manager->gpio_in[i]);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (manager->gpiochip[0])
|
if (manager->gpiochip[0])
|
||||||
|
@@ -38,6 +38,9 @@
|
|||||||
#define EG25_VERSION "0.0.0"
|
#define EG25_VERSION "0.0.0"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#define EG25_DEFAULT_VENDOR_ID 0x2c7c
|
||||||
|
#define EG25_DEFAULT_PRODUCT_ID 0x0125
|
||||||
|
|
||||||
#define POWERON_DELAY_US 100000UL
|
#define POWERON_DELAY_US 100000UL
|
||||||
|
|
||||||
static gboolean quit_app(struct EG25Manager *manager)
|
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)
|
static gboolean modem_reset_done(struct EG25Manager* manager)
|
||||||
{
|
{
|
||||||
manager->modem_state = EG25_STATE_RESUMING;
|
manager->modem_state = EG25_STATE_RESUMING;
|
||||||
manager->reset_timer = 0;
|
manager->complete_reset_timer = 0;
|
||||||
return FALSE;
|
return FALSE;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -149,7 +152,18 @@ gboolean modem_reset(struct EG25Manager *manager)
|
|||||||
{
|
{
|
||||||
int fd, ret, len;
|
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...");
|
g_message("modem_reset: timer already setup, skipping...");
|
||||||
return G_SOURCE_REMOVE;
|
return G_SOURCE_REMOVE;
|
||||||
}
|
}
|
||||||
@@ -164,11 +178,6 @@ gboolean modem_reset(struct EG25Manager *manager)
|
|||||||
return G_SOURCE_REMOVE;
|
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) {
|
if (!manager->modem_usb_id) {
|
||||||
g_warning("Empty modem USB ID");
|
g_warning("Empty modem USB ID");
|
||||||
goto error;
|
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
|
* 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
|
* 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;
|
return G_SOURCE_REMOVE;
|
||||||
|
|
||||||
@@ -228,7 +237,7 @@ error:
|
|||||||
at_sequence_reset(manager);
|
at_sequence_reset(manager);
|
||||||
|
|
||||||
// Setup timer for making sure we don't queue other reset commands
|
// 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;
|
return G_SOURCE_REMOVE;
|
||||||
}
|
}
|
||||||
@@ -314,6 +323,7 @@ int main(int argc, char *argv[])
|
|||||||
struct EG25Manager manager;
|
struct EG25Manager manager;
|
||||||
gchar *config_file = NULL;
|
gchar *config_file = NULL;
|
||||||
gboolean show_version = FALSE;
|
gboolean show_version = FALSE;
|
||||||
|
gboolean monitor_udev = TRUE;
|
||||||
toml_table_t *toml_config[EG25_CONFIG_COUNT];
|
toml_table_t *toml_config[EG25_CONFIG_COUNT];
|
||||||
toml_table_t *manager_config[EG25_CONFIG_COUNT];
|
toml_table_t *manager_config[EG25_CONFIG_COUNT];
|
||||||
const GOptionEntry options[] = {
|
const GOptionEntry options[] = {
|
||||||
@@ -363,10 +373,13 @@ int main(int argc, char *argv[])
|
|||||||
if (!manager_config[EG25_CONFIG_SYS])
|
if (!manager_config[EG25_CONFIG_SYS])
|
||||||
g_error("Default config file lacks the 'manager' section!");
|
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_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);
|
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);
|
at_init(&manager, toml_config);
|
||||||
gpio_init(&manager, toml_config);
|
gpio_init(&manager, toml_config);
|
||||||
@@ -375,6 +388,7 @@ int main(int argc, char *argv[])
|
|||||||
#endif
|
#endif
|
||||||
ofono_iface_init(&manager, toml_config);
|
ofono_iface_init(&manager, toml_config);
|
||||||
suspend_init(&manager, toml_config);
|
suspend_init(&manager, toml_config);
|
||||||
|
if (monitor_udev)
|
||||||
udev_init(&manager, toml_config);
|
udev_init(&manager, toml_config);
|
||||||
gnss_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_SUSPENDING, // System is going into suspend
|
||||||
EG25_STATE_RESUMING, // System is being resumed, waiting for modem to come back
|
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_RESETTING, // Something went wrong, we're restarting the modem
|
||||||
|
EG25_STATE_UPDATING, // Modem is present but being updated
|
||||||
EG25_STATE_FINISHING
|
EG25_STATE_FINISHING
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -70,7 +71,8 @@ enum EG25Config {
|
|||||||
|
|
||||||
struct EG25Manager {
|
struct EG25Manager {
|
||||||
GMainLoop *loop;
|
GMainLoop *loop;
|
||||||
guint reset_timer;
|
guint complete_reset_timer;
|
||||||
|
guint schedule_reset_timer;
|
||||||
gboolean use_libusb;
|
gboolean use_libusb;
|
||||||
guint usb_vid;
|
guint usb_vid;
|
||||||
guint usb_pid;
|
guint usb_pid;
|
||||||
@@ -114,8 +116,9 @@ struct EG25Manager {
|
|||||||
GUdevClient *udev;
|
GUdevClient *udev;
|
||||||
|
|
||||||
struct gpiod_chip *gpiochip[2];
|
struct gpiod_chip *gpiochip[2];
|
||||||
struct gpiod_line *gpio_out[5];
|
struct gpiod_line_request *gpio_out[5];
|
||||||
struct gpiod_line *gpio_in[2];
|
guint gpio_out_value[5];
|
||||||
|
struct gpiod_line_request *gpio_in[2];
|
||||||
};
|
};
|
||||||
|
|
||||||
void modem_configure(struct EG25Manager *data);
|
void modem_configure(struct EG25Manager *data);
|
||||||
|
@@ -25,7 +25,7 @@ if mmglib_dep.found()
|
|||||||
endif
|
endif
|
||||||
|
|
||||||
executable (
|
executable (
|
||||||
'eg25manager',
|
'eg25-manager',
|
||||||
src,
|
src,
|
||||||
dependencies : mgr_deps,
|
dependencies : mgr_deps,
|
||||||
link_with: gdbofono_lib,
|
link_with: gdbofono_lib,
|
||||||
|
@@ -18,10 +18,24 @@
|
|||||||
#define SD_PATH "/org/freedesktop/login1"
|
#define SD_PATH "/org/freedesktop/login1"
|
||||||
#define SD_INTERFACE "org.freedesktop.login1.Manager"
|
#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)
|
static gboolean check_modem_resume(struct EG25Manager *manager)
|
||||||
{
|
{
|
||||||
g_message("Modem wasn't probed in time, restart it!");
|
|
||||||
manager->modem_recovery_timer = 0;
|
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);
|
modem_reset(manager);
|
||||||
|
|
||||||
return FALSE;
|
return FALSE;
|
||||||
@@ -184,8 +198,7 @@ static void signal_cb(GDBusProxy *proxy,
|
|||||||
* If modem is managed by ofono, we also do resume sequence immediately
|
* If modem is managed by ofono, we also do resume sequence immediately
|
||||||
* as ofono handles resuming from sleep itself.
|
* as ofono handles resuming from sleep itself.
|
||||||
*/
|
*/
|
||||||
manager->modem_state = EG25_STATE_CONFIGURED;
|
resume_ok(manager);
|
||||||
modem_resume_post(manager);
|
|
||||||
} else {
|
} else {
|
||||||
manager->modem_state = EG25_STATE_RESUMING;
|
manager->modem_state = EG25_STATE_RESUMING;
|
||||||
manager->modem_recovery_timer = g_timeout_add_seconds(manager->modem_recovery_timeout,
|
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)
|
static void udev_event_cb(GUdevClient *client, gchar *action, GUdevDevice *device, gpointer data)
|
||||||
{
|
{
|
||||||
struct EG25Manager *manager = data;
|
struct EG25Manager *manager = data;
|
||||||
|
const gchar *prop;
|
||||||
|
long vid = 0, pid = 0;
|
||||||
|
|
||||||
if (strcmp(action, "unbind") != 0 ||
|
/*
|
||||||
manager->modem_state == EG25_STATE_RESETTING ||
|
* Act only if the device is the one identified as a modem by MM/ofono
|
||||||
!manager->modem_usb_id) {
|
*/
|
||||||
|
if (!manager->modem_usb_id ||
|
||||||
|
strcmp(g_udev_device_get_name(device), manager->modem_usb_id) != 0) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (strcmp(g_udev_device_get_name(device), manager->modem_usb_id) == 0 &&
|
prop = g_udev_device_get_property(device, "ID_VENDOR_ID");
|
||||||
manager->reset_timer == 0) {
|
if (prop)
|
||||||
g_message("Lost modem, resetting...");
|
vid = strtol(prop, NULL, 16);
|
||||||
g_timeout_add_seconds(2, G_SOURCE_FUNC(modem_reset), manager);
|
|
||||||
|
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[])
|
void udev_init (struct EG25Manager *manager, toml_table_t *config[])
|
||||||
|
@@ -1,4 +1,18 @@
|
|||||||
ACTION=="add", SUBSYSTEM=="usb", DRIVERS=="usb", ENV{DEVTYPE}=="usb_device", ATTRS{idVendor}=="2c7c", ATTRS{idProduct}=="0125", ATTR{power/control}="auto"
|
ACTION=="add", SUBSYSTEM=="usb", DRIVERS=="usb", ENV{DEVTYPE}=="usb_device", GOTO="eg25_start"
|
||||||
ACTION=="add", SUBSYSTEM=="usb", DRIVERS=="usb", ENV{DEVTYPE}=="usb_device", ATTRS{idVendor}=="2c7c", ATTRS{idProduct}=="0125", ATTR{power/autosuspend_delay_ms}="3000"
|
GOTO="eg25_end"
|
||||||
ACTION=="add", SUBSYSTEM=="usb", DRIVERS=="usb", ENV{DEVTYPE}=="usb_device", ATTRS{idVendor}=="2c7c", ATTRS{idProduct}=="0125", ATTR{power/wakeup}="enabled"
|
|
||||||
ACTION=="add", SUBSYSTEM=="usb", DRIVERS=="usb", ENV{DEVTYPE}=="usb_device", ATTRS{idVendor}=="2c7c", ATTRS{idProduct}=="0125", ATTR{power/persist}="0"
|
# Default attributes values
|
||||||
|
LABEL="eg25_start"
|
||||||
|
ATTRS{idVendor}=="2c7c", ATTRS{idProduct}=="0125", ATTR{power/control}="auto"
|
||||||
|
ATTRS{idVendor}=="2c7c", ATTRS{idProduct}=="0125", ATTR{power/autosuspend_delay_ms}="3000"
|
||||||
|
ATTRS{idVendor}=="2c7c", ATTRS{idProduct}=="0125", ATTR{power/wakeup}="enabled"
|
||||||
|
ATTRS{idVendor}=="2c7c", ATTRS{idProduct}=="0125", ATTR{power/persist}="0"
|
||||||
|
|
||||||
|
# power/control needs to be "on" for the community-maintained firmware
|
||||||
|
ATTRS{serial}=="community_fw", ATTRS{idVendor}=="2c7c", ATTRS{idProduct}=="0125", ATTR{power/control}="on"
|
||||||
|
|
||||||
|
# Special trick for the PinePhone Pro: set power/persist to 1 *only* with the community FW
|
||||||
|
# We can identify the PPP by looking for the string "pinephone-pro" in the device tree "compatible" property
|
||||||
|
ATTRS{serial}=="community_fw", ATTRS{idVendor}=="2c7c", ATTRS{idProduct}=="0125", PROGRAM=="/bin/grep pine64,pinephone-pro /proc/device-tree/compatible", ATTR{power/persist}="1"
|
||||||
|
|
||||||
|
LABEL="eg25_end"
|
||||||
|
Reference in New Issue
Block a user