46 Commits

Author SHA1 Message Date
Egor Shestakov
0d5117d4e3 set proper version 2025-01-14 18:11:11 +07:00
Arnaud Ferraris
9f8ae70cba gitlab-ci: add static analysis check
Similar to the formatting check job, take advantage of the `clang-tidy`
configuration we just created to add a new job running this tool.

Running `clang-tidy` requires the following:
* build dependencies must be present on the system so it can resolve the
  includes
* the build folder must be configured as it relies on the
  `compile_commands.json` to get build flags (such as the include paths)

This could be run in the `build` job, but it would make it harder to
understand the actual underlying issue, so this is executed as a new,
separate job, re-using the artifacts from the `build` job.
2024-11-12 13:10:43 +01:00
Arnaud Ferraris
f978b775e0 src: fix issues reported by clang-tidy
Some of those are simply ignored as they're false positives.
2024-11-12 13:10:43 +01:00
Arnaud Ferraris
2c311b9022 add clang-tidy configuration
This will allow us to run a static analysis tool, catching the most
obvious issues (use-after-free, potential NULL dereference...) and
(hopefully) increasing this software's overall robustness.
2024-11-12 13:10:43 +01:00
Arnaud Ferraris
c785f7cb8d add gitlab CI pipeline
The current pipeline (from Mobian) just doesn't work, we should get to
a more efficient, simpler one.

With the `clang-format` config now in place, this pipeline has 2 jobs:
* ensure the code is properly formatted
* run a test build to make sure it won't break
2024-11-12 13:10:43 +01:00
Arnaud Ferraris
9f6178dafd src: format using clang-format 2024-10-30 12:01:46 +01:00
Arnaud Ferraris
57142c2c11 add clang-format configuration
This will ensure our coding style is more clearly defined and allow
contibutors to auto-format their code, bringing some more consistency.
2024-10-30 11:59:45 +01:00
Aren Moynihan
99aa7e2bf7 gpio: remove handling of W_DISABLE pin
The PinePhone has circuitry that makes this pin active-high, but the
PinePhone Pro doesn't appear to, which means we would need read active
high / active low from the config file. Since the modem works fine
without setting this pin, remove the logic to handle the W_DISBALE pin.
2024-10-28 16:08:37 -04:00
Aren Moynihan
ce0fc25892 at: don't clear the resetting state on sequence end
If the reset sequence fails, we don't want to transition to the powered
state, instead we should stay in the resetting state so we eventually
timeout and try a gpio reset. If the reset command succeeds the modem
will eventually send RDY and transition the state to STARTED.

Since this might take longer to change the modem state, add 15 seconds
(for a total of 45) to the at reset timeout just in case. It looks like
the modem typically takes about 20 between sending "AT+CFUN=1,1" and
receiving "RDY", so this should be plenty.
2024-10-28 12:48:08 -04:00
Aren Moynihan
ec96153b0f at: fix indentation
A few tabs slipped in here, convert them to spaces.
2024-10-28 12:47:24 -04:00
Arnaud Ferraris
b000a75238 meson.build: release version 0.5.1 2024-10-28 09:37:49 +01:00
Aren Moynihan
541aa00198 gpio: disable airplane mode when starting the modem
I'm not sure why this was being done when disabling the modem, move it
to the powerup sequence for more consistency.
2024-10-21 16:54:58 -04:00
Aren Moynihan
b53702ad0b manager: modem_reset: fix file descriptor leek
fd wasn't being closed if the write failed. Theoretically if a system
with an unstable modem ran for long enough, this could bring down
eg25-manager.
2024-10-21 16:54:58 -04:00
Aren Moynihan
1482899888 manager: reset using gpio pins if the mode is unresponsive
If the modem doesn't respond to the at reboot sequence, this will
attempt reboot the modem using both the power pin and then the reset pin
in case that doesn't work.
2024-10-21 16:54:58 -04:00
Aren Moynihan
c4b3b75047 manager: always set the reset line when exiting
This splits the logic from gpio_check_poweroff into two separate
functions, allowing the caller more control over when the reset line is
asserted to force the modem to turn off.

Previously if the modem didn't respond to the power key sequence in
quit_app, gpio_check_poweroff wouldn't set the reset pin, and
eg25-manager could leave the modem running after exiting.
2024-10-04 17:11:26 -04:00
Aren Moynihan
3c5d455f20 gpio: toggle the RESET_N pin before booting the modem to ensure it is actually off 2024-10-04 17:08:45 -04:00
Arnaud Ferraris
1acd15ca2f src: gnss: fix build on 32-bit architectures
It seems `off_t` is now 64-bits wide on 32-bit architectures, so using
`glong` causes a build failure in this case. Switch to using the correct
type from the `sendfile()` prototype to fix this.
2024-08-27 11:01:08 +02:00
Arnaud Ferraris
012c09e630 meson.build: release version 0.5.0 2024-08-27 09:45:45 +02:00
Arnaud Ferraris
40623d1572 src: gpio: small cleanups following the port to libgpiod 2.x 2024-08-27 09:44:52 +02:00
marcin
93bdfbdfa2 libgpiod 2.0: specify that we need at least libgpiod 2.0 2024-05-13 23:19:28 +02:00
marcin
bc5a25b17e gpio: libgpiod 2.0: port gpiod_line_{get,set}_value 2024-05-13 23:16:35 +02:00
marcin
b1bb871eb7 gpio: libgpiod 2.0: port gpiod_chip_num_lines 2024-05-13 23:16:35 +02:00
marcin
88e85ae7c2 gpio: libgpiod 2.0: port gpio_get_{input,output}_line 2024-05-13 23:16:27 +02:00
marcin
b8ff1ef3d4 gpio: libgpiod 2.0: port gpiod_chip_open_by_label 2024-05-13 23:02:58 +02:00
Arnaud Ferraris
e7790f941c meson.build: release version 0.4.6 2022-11-01 15:26:17 +01:00
ArenM
6b41ae6b3b udev rules: fix detection of community firmware
`!=` doesn't seem to be the exact inverse of `==`. To prove this I added
the following to the udev rules. On a system with the community firmware
both COMM_FW and NOT_COMM_FW were set to true, with the quectel firmware
only NOT_COMM_FW was true.

ATTRS{serial}=="community_fw", ENV{COMM_FW}="true"
ATTRS{serial}!="community_fw", ENV{NOT_COMM_FW}="true"
2022-10-24 19:07:00 -04:00
Arnaud Ferraris
5b4f9bcc12 meson.build: release version 0.4.5 2022-09-19 02:09:51 +02:00
joerg
2218a908ab replace DRIVER w/ DRIVERS in 80-modem-eg25.rules 2022-09-19 02:08:13 +02:00
Arnaud Ferraris
be6a924f8d meson.build: release version 0.4.4 2022-07-26 12:27:59 +02:00
Arnaud Ferraris
34472a5cff udev: allow different attribute values for the community firmware
When using the community-maintained firmware, some attributes need to be
set with different values than what works best with the stock firmware:
this is the case of the `power/control` attribute which should be set to
"on". Similarly, on the PPP `power/persist` should be set to "1".

This commit reworks the udev rules so we can differentiate between
firmwares and host devices.
2022-06-30 02:02:50 +02:00
Arnaud Ferraris
ee70cf7d2f meson.build: release version 0.4.3 2022-02-19 15:17:40 +01:00
Arnaud Ferraris
0e2311fb36 Rename executable to eg25-manager
This brings more consistency with how other components are named
(repo, package, service file, manpages...).
2022-02-19 15:17:27 +01:00
Arnaud Ferraris
5e4c797695 meson: bump minimum version
`doc/meson.build` used currently-deprecated functions. Replace those
with the proper alternatives, which were introduced in a newer meson
version than our requirement. As a consequence, bump minimal version.
2022-02-19 15:05:31 +01:00
Arnaud Ferraris
be1ae18592 Move manpages to doc/ subfolder 2022-02-19 15:02:43 +01:00
Arnaud Ferraris
19eb488e29 gpio: fix init for BH edition PinePhone
BH PinePhone don't have the modem's `STATUS` pin connected to the SoC,
and as such require using `libusb` for checking the modem power state.
We didn't handle this case previously due to lack of on-device testing,
causing BH phones to fail with newer versions.

This patch ignores the `status` GPIO for devices relying on `libusb`,
which are only pre-CE PinePhones.
2022-02-19 13:50:41 +00:00
ArenM
a3c51fc513 Add manpages for eg25-manager and it's config file 2022-02-18 18:54:47 -05:00
Arnaud Ferraris
75400fb9c0 data: add USB vendor/product IDs to all configs
PP 1.2 and PPP configs were lacking this parameter. Although it's not a
problem as `eg25-manager` will then use a default value, adding them
won't harm and make them more consistent with other config files.
2022-02-07 12:43:49 +01:00
Arnaud Ferraris
97b1878ebc manager: disable reset timers once reset sequence starts
`modem_reset()` can be called from multiple places, both tied to a
specific timer. In order to prevent calling this function multiple times
in a row, disable all related timers as soon as we enter this function.
2022-02-07 12:43:49 +01:00
Arnaud Ferraris
9e6bccdf37 udev: use USB vendor/product ID to determine behavior
When going into firmware upgrade mode, the modem comes back with
different IDs than in normal use. We should make sure we cancel the
reset sequence when that happens, and not start a new one.
2022-02-07 12:43:49 +01:00
Arnaud Ferraris
df79247821 manager: populate USB vendor/product ID's with default values
We'll need those in the `udev` module, but never made it a mandatory
config option. This commit makes sure those values are properly filled
in.
2022-01-04 15:24:01 +01:00
Dylan Van Assche
61c89a003a udev: cancel reset if modem is already back
Cancel reset if modem is back before reset sequence is started.
When upgrading the modem through fwupd, it will enter fastboot.
Therefore, it disappears from the USB bus for a few ms.
However, the eg25-manager considers this as an issue and
resets the modem a bit later during the upgrade process.
To avoid this, cancel the reset sequence if the modem is already
back before the reset sequence is started. This will take less than 3
seconds.
2022-01-04 14:41:54 +01:00
Arnaud Ferraris
9cf51b9529 pinephone-pro: switch to 16K audio
As the modem is connected to a dedicated audio codec, we can select any
sample rate. Let's go for 16K as it should improve call audio quality.

Note: the bitclock's frequency is left untouched due to a bug in the
EG25 kernel, so we can't take advantage of the recommended higher
frequency.
2021-12-24 15:14:43 +00:00
Arnaud Ferraris
50b4c00c16 data: add 'monitor_udev' parameter to existing configs
We disable dev monitoring only for the PinePhone Pro, it remains enabled
for all variants of the OG PinePhone.
2021-12-24 15:14:43 +00:00
Arnaud Ferraris
fedce7298b manager: start udev monitoring module conditionally
The problem addressed by monitoring the modem status through udev seems
tied to the specific USB controller used by the A64. On RK3399 devices,
this quirk is apparently unneeded, and actually harmful as it resets the
modem USB connection while ModemManager is already configuring it.

This commit adds an optional config parameter for disabling this module
(enabled by default).
2021-12-24 15:14:43 +00:00
Rafael Diniz
8665f8a4a6 Fix compile error when not using Modem Manager. 2021-12-23 21:12:24 +03:00
Kai Lüke
88c68b9933 suspend: actually check if the modem is found before resetting
The timeout was meant to give more time to find the modem, yet the
callback didn't actually check if it came back but blindly issued a
reset.
Add a check to the callback to see if the modem was found and only
reset if not.

Fixes https://gitlab.com/mobian1/devices/eg25-manager/-/issues/28
2021-12-15 22:47:40 +00:00
32 changed files with 3001 additions and 2298 deletions

48
.clang-format Normal file
View File

@@ -0,0 +1,48 @@
BasedOnStyle: LLVM
ColumnLimit: 100
IndentWidth: 4
IndentGotoLabels: false
AlignArrayOfStructures: Left
AlignConsecutiveAssignments: None
AlignConsecutiveMacros: AcrossEmptyLines
AlignConsecutiveDeclarations:
Enabled: true
# Available only with clang-format 20+
# AlignFunctionDeclarations: true
AlignFunctionPointers: true
# Ensure we either have all args/params on a single line,
# or only one per line
BinPackArguments: false
BinPackParameters: false
# To be changed for clang-format 20+
# BinPackParameters: OnePerLine
# Avoid single-line enums/functions/ifs/loops/etc
AllowShortBlocksOnASingleLine: Never
AllowShortEnumsOnASingleLine: false
AllowShortFunctionsOnASingleLine: None
AllowShortCaseExpressionOnASingleLine: false
AllowShortCaseLabelsOnASingleLine: false
AllowShortIfStatementsOnASingleLine: Never
AllowShortLoopsOnASingleLine: false
BreakBeforeBinaryOperators: None
# Ensure function opening brackets are on their own line
BreakBeforeBraces: Linux
# Available only with clang-format 20+
# BreakBinaryOperations: OnePerLine
InsertNewlineAtEOF: true
# Don't be too strict on line lengths
PenaltyExcessCharacter: 10
# Prefer breaking arguments list over putting a function call
# on its own line after an assignment
PenaltyBreakAssignment: 200
PenaltyBreakBeforeFirstCallParameter: 200
# Ensure we never ever have the return type on a single line
PenaltyReturnTypeOnItsOwnLine: 1000

4
.clang-tidy Normal file
View File

@@ -0,0 +1,4 @@
# DeprecatedOrUnsafeBufferHandling requests that we use safe versions
# of mem{cpy,set} for example, but this would affect portability
Checks: 'clang-diagnostic-*,clang-analyzer-*,-clang-analyzer-security.insecureAPI.DeprecatedOrUnsafeBufferHandling'
WarningsAsErrors: 'clang-diagnostic-*,clang-analyzer-*'

49
.gitlab-ci.yml Normal file
View File

@@ -0,0 +1,49 @@
stages:
- build
- check
variables:
CLANG_VERSION: 19
BUILD_DEPS_NO_MM: "build-essential libcurl4-openssl-dev libgpiod-dev libgudev-1.0-dev libusb-1.0-0-dev meson scdoc"
BUILD_DEPS: "${BUILD_DEPS_NO_MM} libmm-glib-dev"
image: debian:unstable-slim
build:
stage: build
script:
- apt-get update
- apt-get -y install ${BUILD_DEPS}
- meson build
- meson compile -C build
artifacts:
paths:
- build
# ModemManager is an optional dependency, let's ensure eg25-manager still builds fine without it
build-no-mm:
stage: build
script:
- apt-get update
- apt-get -y install ${BUILD_DEPS_NO_MM}
- meson build
- meson compile -C build
format:
stage: check
dependencies:
- build
script:
- apt-get update
- apt-get -y install ${BUILD_DEPS} clang-format-${CLANG_VERSION}
- ninja -C build clang-format-check
check:
stage: check
dependencies:
- build
script:
- apt-get update
# clang-tidy needs all dependencies to be installed so it can inspect their headers
- apt-get -y install ${BUILD_DEPS} clang-tidy-${CLANG_VERSION}
- ninja -C build clang-tidy

View File

@@ -15,7 +15,7 @@ It implements the following features:
`eg25-manager` requires the following development libraries:
- libglib2.0-dev
- libgpiod-dev
- libgpiod-dev (>= 2.0)
- libmm-glib-dev
## Building

View File

@@ -4,7 +4,7 @@ Before=ModemManager.service
[Service]
Type=simple
ExecStart=@bindir@/eg25manager
ExecStart=@bindir@/eg25-manager
Restart=on-failure
ProtectControlGroups=true
ProtectHome=true

View File

@@ -1,4 +1,5 @@
[manager]
monitor_udev = true
need_libusb = true
usb_vid = 0x2c7c
usb_pid = 0x0125

View File

@@ -1,4 +1,5 @@
[manager]
monitor_udev = true
need_libusb = true
usb_vid = 0x2c7c
usb_pid = 0x0125

View File

@@ -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

View File

@@ -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
View 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
View 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
View 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

View File

@@ -8,9 +8,9 @@
project (
'eg25-manager',
'c',
version : '0.4.2',
version : '0.5.2',
license : 'GPLv3+',
meson_version : '>= 0.50.0',
meson_version : '>= 0.58.0',
default_options :
[
'warning_level=1',
@@ -59,12 +59,13 @@ mgr_deps = [
dependency('glib-2.0'),
dependency('gio-unix-2.0'),
dependency('gudev-1.0'),
dependency('libgpiod'),
dependency('libgpiod', version: '>= 2.0'),
dependency('libusb-1.0'),
dependency('libcurl'),
mmglib_dep,
]
subdir('data')
subdir('doc')
subdir('src')
subdir('udev')

View File

@@ -6,9 +6,9 @@
#include "at.h"
#include "config.h"
#include "suspend.h"
#include "gpio.h"
#include "gnss.h"
#include "gpio.h"
#include "suspend.h"
#include <errno.h>
#include <fcntl.h>
@@ -56,14 +56,14 @@ static void at_free_command(gpointer cmd, gpointer data)
if (!at_cmd)
return;
if (manager && manager->at_cmds)
manager->at_cmds = g_list_remove(manager->at_cmds, at_cmd);
g_free(at_cmd->cmd);
g_free(at_cmd->subcmd);
g_free(at_cmd->value);
g_free(at_cmd->expected);
g_free(at_cmd);
if (manager && manager->at_cmds)
manager->at_cmds = g_list_remove(manager->at_cmds, at_cmd);
}
gboolean at_send_command(struct EG25Manager *manager)
@@ -86,16 +86,22 @@ gboolean at_send_command(struct EG25Manager *manager)
else if (at_cmd->subcmd && at_cmd->value == NULL)
len = snprintf(command, sizeof(command), "AT+%s=\"%s\"\r\n", at_cmd->cmd, at_cmd->subcmd);
else if (at_cmd->subcmd && at_cmd->value)
len = snprintf(command, sizeof(command), "AT+%s=\"%s\",%s\r\n", at_cmd->cmd, at_cmd->subcmd, at_cmd->value);
len = snprintf(command,
sizeof(command),
"AT+%s=\"%s\",%s\r\n",
at_cmd->cmd,
at_cmd->subcmd,
at_cmd->value);
if (len < 0) {
g_warning("snprintf(3) failed");
at_next_command(manager);
return FALSE;
}
else if (len >= sizeof(command)) {
} else if (len >= sizeof(command)) {
g_warning("AT command does not fit into buffer "
"(%d bytes required, %zu available)", len, sizeof(command));
"(%d bytes required, %zu available)",
len,
sizeof(command));
at_next_command(manager);
return FALSE;
}
@@ -117,8 +123,7 @@ gboolean at_send_command(struct EG25Manager *manager)
at_next_command(manager);
return FALSE;
}
}
else {
} else {
len -= ret;
pos += ret;
}
@@ -146,8 +151,6 @@ gboolean at_send_command(struct EG25Manager *manager)
}
} else if (manager->modem_state == EG25_STATE_SUSPENDING) {
modem_suspend_post(manager);
} else if (manager->modem_state == EG25_STATE_RESETTING) {
manager->modem_state = EG25_STATE_POWERED;
}
}
@@ -178,8 +181,7 @@ static void retry_at_command(struct EG25Manager *manager)
}
}
void at_process_result(struct EG25Manager *manager,
const char *response)
void at_process_result(struct EG25Manager *manager, const char *response)
{
struct AtCommand *at_cmd = manager->at_cmds ? g_list_nth_data(manager->at_cmds, 0) : NULL;
@@ -203,9 +205,7 @@ int at_append_command(struct EG25Manager *manager,
const char *subcmd,
const char *value,
const char *expected,
void (*callback)
(struct EG25Manager *manager,
const char *response))
void (*callback)(struct EG25Manager *manager, const char *response))
{
struct AtCommand *at_cmd = calloc(1, sizeof(struct AtCommand));
@@ -229,9 +229,7 @@ int at_append_command(struct EG25Manager *manager,
#define READ_BUFFER_SIZE 256
static gboolean modem_response(gint fd,
GIOCondition event,
gpointer data)
static gboolean modem_response(gint fd, GIOCondition event, gpointer data)
{
struct EG25Manager *manager = data;
char response[READ_BUFFER_SIZE * 4 + 1];

View File

@@ -20,8 +20,7 @@ typedef struct AtCommand {
int at_init(struct EG25Manager *manager, toml_table_t *config[]);
void at_destroy(struct EG25Manager *manager);
void at_process_result(struct EG25Manager *manager,
const char *response);
void at_process_result(struct EG25Manager *manager, const char *response);
void at_next_command(struct EG25Manager *manager);
gboolean at_send_command(struct EG25Manager *manager);
int at_append_command(struct EG25Manager *manager,
@@ -29,9 +28,7 @@ int at_append_command(struct EG25Manager *manager,
const char *subcmd,
const char *value,
const char *expected,
void (*callback)
(struct EG25Manager *manager,
const char *response));
void (*callback)(struct EG25Manager *manager, const char *response));
void at_sequence_configure(struct EG25Manager *manager);
void at_sequence_suspend(struct EG25Manager *manager);

View File

@@ -4,14 +4,14 @@
* SPDX-License-Identifier: GPL-3.0-or-later
*/
#include "config.h"
#include "gnss.h"
#include "manager.h"
#include "at.h"
#include "config.h"
#include "manager.h"
#include <errno.h>
#include <sys/sendfile.h>
#include <sys/stat.h>
#include <errno.h>
#define BUFFER_SIZE 256
#define UPLOAD_DELAY_US 25000
@@ -29,17 +29,16 @@ gboolean gnss_upload_assistance_data(struct EG25Manager *manager)
if (manager->gnss_assistance_step < EG25_GNSS_STEP_LAST) {
g_warning("GNSS assistance data upload already in process (%d/%d)",
manager->gnss_assistance_step, EG25_GNSS_STEP_LAST);
manager->gnss_assistance_step,
EG25_GNSS_STEP_LAST);
return FALSE;
}
/* data upload isn't necessary to bring the modem onine, so we should wait
* until we've finished the rest of our configuration */
if (!manager->modem_iface ||
manager->modem_state < EG25_STATE_CONFIGURED ||
if (!manager->modem_iface || manager->modem_state < EG25_STATE_CONFIGURED ||
manager->modem_state > EG25_STATE_CONNECTED) {
g_message ("Rescheduling upload since modem isn't online yet, in %ds",
RESCHEDULE_IN_SECS);
g_message("Rescheduling upload since modem isn't online yet, in %ds", RESCHEDULE_IN_SECS);
manager->gnss_assistance_step = EG25_GNSS_STEP_LAST;
return TRUE;
}
@@ -96,8 +95,7 @@ void gnss_init(struct EG25Manager *manager, toml_table_t *config[])
/* Initialize state and schedule upload */
manager->gnss_assistance_step = EG25_GNSS_STEP_LAST;
g_timeout_add_seconds(RESCHEDULE_IN_SECS,
G_SOURCE_FUNC(gnss_upload_assistance_data), manager);
g_timeout_add_seconds(RESCHEDULE_IN_SECS, G_SOURCE_FUNC(gnss_upload_assistance_data), manager);
}
void gnss_destroy(struct EG25Manager *manager)
@@ -140,17 +138,14 @@ static void disable_mm_gnss(struct EG25Manager *manager)
sources &= ~MM_MODEM_LOCATION_SOURCE_GPS_RAW;
sources &= ~MM_MODEM_LOCATION_SOURCE_GPS_NMEA;
sources &= ~MM_MODEM_LOCATION_SOURCE_GPS_UNMANAGED;
mm_modem_location_setup_sync(manager->mm_location, sources,
signals_location, NULL, &error);
mm_modem_location_setup_sync(manager->mm_location, sources, signals_location, NULL, &error);
if (error != NULL) {
g_warning("Unable to disable GNSS engine through ModemManager: %s",
error->message);
g_warning("Unable to disable GNSS engine through ModemManager: %s", error->message);
}
}
#endif
static void disable_at_gnss_cb(struct EG25Manager *manager,
const char *response)
static void disable_at_gnss_cb(struct EG25Manager *manager, const char *response)
{
/* Clear QGPSEND AT command and process next */
at_next_command(manager);
@@ -210,16 +205,14 @@ static void fetch_assistance_data(struct EG25Manager *manager)
/* Fetch assistance data with curl */
tmp_file = fdopen(manager->gnss_assistance_fd, "wb+");
if (tmp_file == NULL) {
g_critical("Unable to open file to save assistance data: %s",
g_strerror(errno));
g_critical("Unable to open file to save assistance data: %s", g_strerror(errno));
goto bail;
}
lseek(manager->gnss_assistance_fd, 0, SEEK_SET);
if (ftruncate(manager->gnss_assistance_fd, 0) < 0)
g_warning("Unable to truncate file, assistance data might be invalid!");
url = g_strconcat(manager->gnss_assistance_url, "/",
manager->gnss_assistance_file, NULL);
url = g_strconcat(manager->gnss_assistance_url, "/", manager->gnss_assistance_file, NULL);
curl = curl_easy_init();
if (!curl) {
@@ -237,7 +230,8 @@ static void fetch_assistance_data(struct EG25Manager *manager)
response = curl_easy_perform(curl);
if (response != CURLE_OK) {
g_warning("Unable to fetch GNSS assistance data from %s: %s",
url, strlen(errbuf) ? errbuf : curl_easy_strerror(response));
url,
strlen(errbuf) ? errbuf : curl_easy_strerror(response));
goto bail;
}
@@ -252,24 +246,24 @@ static void fetch_assistance_data(struct EG25Manager *manager)
g_message("Fetching GNSS assistance data from %s was successful", url);
fflush(tmp_file);
curl_easy_cleanup(curl);
/* Go to the next step */
manager->gnss_assistance_step++;
gnss_step(manager);
return;
goto cleanup;
bail:
manager->gnss_assistance_step = EG25_GNSS_STEP_LAST;
cleanup:
fflush(tmp_file);
fclose(tmp_file);
if (curl != NULL)
curl_easy_cleanup(curl);
manager->gnss_assistance_step = EG25_GNSS_STEP_LAST;
}
/******************************************************************************/
static void init_assistance_data_upload_ready(struct EG25Manager *manager,
const char *response)
static void init_assistance_data_upload_ready(struct EG25Manager *manager, const char *response)
{
/* Search for 'CONNECT' in response to start upload */
if (strstr(response, "CONNECT")) {
@@ -285,8 +279,7 @@ static void init_assistance_data_upload_ready(struct EG25Manager *manager,
}
}
static void init_assistance_data_upload_start(struct EG25Manager *manager,
const char *response)
static void init_assistance_data_upload_start(struct EG25Manager *manager, const char *response)
{
gchar value[BUFFER_SIZE];
off_t size;
@@ -305,11 +298,14 @@ static void init_assistance_data_upload_start(struct EG25Manager *manager,
lseek(manager->gnss_assistance_fd, 0, SEEK_SET);
/* Start upload */
g_snprintf(value, BUFFER_SIZE, "\"RAM:%s\",%ld,%d",
manager->gnss_assistance_file, size, UPLOAD_TIMEOUT_S);
g_snprintf(value,
BUFFER_SIZE,
"\"RAM:%s\",%ld,%d",
manager->gnss_assistance_file,
size,
UPLOAD_TIMEOUT_S);
g_message("Initiate GNSS assistance data upload: %s", value);
at_append_command(manager, "QFUPL", NULL, value, NULL,
init_assistance_data_upload_ready);
at_append_command(manager, "QFUPL", NULL, value, NULL, init_assistance_data_upload_ready);
at_send_command(manager);
}
@@ -319,15 +315,14 @@ static void init_assistance_data_upload(struct EG25Manager *manager)
* Delete all previous GNSS assistance data files in RAM
* and start uploading the latest one to RAM.
*/
at_append_command(manager, "QFDEL", NULL, "\"RAM:*\"\r\n",
NULL, init_assistance_data_upload_start);
at_append_command(manager, "QFDEL", NULL, "\"RAM:*\"\r\n", NULL, init_assistance_data_upload_start);
at_send_command(manager);
}
static void upload_assistance_data(struct EG25Manager *manager)
{
gint error;
glong written_total = 0;
off_t written_total = 0;
gint ret;
struct stat sb;
@@ -357,8 +352,7 @@ static void upload_assistance_data(struct EG25Manager *manager)
}
}
static void finish_assistance_data_upload_cb(struct EG25Manager *manager,
const char *response)
static void finish_assistance_data_upload_cb(struct EG25Manager *manager, const char *response)
{
/* Process response */
at_process_result(manager, response);
@@ -379,15 +373,12 @@ static void finish_assistance_data_upload(struct EG25Manager *manager)
datetime = g_date_time_new_now_utc();
timestring = g_date_time_format(datetime, "0,\"%Y/%m/%d,%H:%M:%S\"");
g_message("Setting GNSS assistance UTC clock to: %s", timestring);
at_append_command(manager, "QGPSXTRATIME", NULL, timestring, NULL,
at_process_result);
at_append_command(manager, "QGPSXTRATIME", NULL, timestring, NULL, at_process_result);
/* Configure GNSS engine to use uploaded GNSS assistance data */
g_snprintf(value, BUFFER_SIZE, "\"RAM:%s\"",
manager->gnss_assistance_file);
g_snprintf(value, BUFFER_SIZE, "\"RAM:%s\"", manager->gnss_assistance_file);
g_message("Setting GNSS assistance file to: %s", value);
at_append_command(manager, "QGPSXTRADATA", NULL, value, NULL,
finish_assistance_data_upload_cb);
at_append_command(manager, "QGPSXTRADATA", NULL, value, NULL, finish_assistance_data_upload_cb);
at_send_command(manager);
}
@@ -407,11 +398,9 @@ static void enable_mm_gnss(struct EG25Manager *manager)
if (manager->gnss_sources & EG25_GNSS_SOURCE_RAW)
sources |= MM_MODEM_LOCATION_SOURCE_GPS_RAW;
mm_modem_location_setup_sync(manager->mm_location, sources,
signal_location, NULL, &error);
mm_modem_location_setup_sync(manager->mm_location, sources, signal_location, NULL, &error);
if (error != NULL)
g_warning("Unable to enable GNSS engine through ModemManager: %s",
error->message);
g_warning("Unable to enable GNSS engine through ModemManager: %s", error->message);
}
#endif
@@ -424,8 +413,7 @@ static void enable_at_gnss_cb(struct EG25Manager *manager, const char *response)
static void enable_at_gnss(struct EG25Manager *manager)
{
if (manager->gnss_sources & EG25_GNSS_SOURCE_QGPS) {
at_append_command(manager, "QGPS", NULL, "1", NULL,
enable_at_gnss_cb);
at_append_command(manager, "QGPS", NULL, "1", NULL, enable_at_gnss_cb);
at_send_command(manager);
return;
}
@@ -446,7 +434,8 @@ void gnss_step(struct EG25Manager *manager)
case EG25_GNSS_STEP_FETCH_ASSISTANCE_DATA:
g_message("GNSS assistance upload step (%d/%d): "
"fetching assistance data",
manager->gnss_assistance_step, EG25_GNSS_STEP_LAST);
manager->gnss_assistance_step,
EG25_GNSS_STEP_LAST);
fetch_assistance_data(manager);
break;
@@ -455,7 +444,8 @@ void gnss_step(struct EG25Manager *manager)
if (manager->modem_iface == MODEM_IFACE_MODEMMANAGER) {
g_message("GNSS assistance upload step (%d/%d): "
"disabling GNSS engine through ModemManager",
manager->gnss_assistance_step, EG25_GNSS_STEP_LAST);
manager->gnss_assistance_step,
EG25_GNSS_STEP_LAST);
disable_mm_gnss(manager);
}
manager->gnss_assistance_step++;
@@ -465,26 +455,30 @@ void gnss_step(struct EG25Manager *manager)
case EG25_GNSS_STEP_AT_GNSS_DISABLE:
g_message("GNSS assistance upload step (%d/%d): "
"disabling GNSS engine through AT+QGPS",
manager->gnss_assistance_step, EG25_GNSS_STEP_LAST);
manager->gnss_assistance_step,
EG25_GNSS_STEP_LAST);
state_at_gnss(manager);
break;
case EG25_GNSS_STEP_INIT_UPLOAD:
g_message("GNSS assistance upload step (%d/%d): initiating upload",
manager->gnss_assistance_step, EG25_GNSS_STEP_LAST);
manager->gnss_assistance_step,
EG25_GNSS_STEP_LAST);
init_assistance_data_upload(manager);
break;
case EG25_GNSS_STEP_UPLOAD:
g_message("GNSS assistance upload step (%d/%d): "
"uploading assistance data",
manager->gnss_assistance_step, EG25_GNSS_STEP_LAST);
manager->gnss_assistance_step,
EG25_GNSS_STEP_LAST);
upload_assistance_data(manager);
break;
case EG25_GNSS_STEP_FINISH_UPLOAD:
g_message("GNSS assistance upload step (%d/%d): finishing upload",
manager->gnss_assistance_step, EG25_GNSS_STEP_LAST);
manager->gnss_assistance_step,
EG25_GNSS_STEP_LAST);
finish_assistance_data_upload(manager);
break;
@@ -493,7 +487,8 @@ void gnss_step(struct EG25Manager *manager)
if (manager->modem_iface == MODEM_IFACE_MODEMMANAGER) {
g_message("GNSS assistance upload step (%d/%d): "
"re-enabling GNSS through ModemManager",
manager->gnss_assistance_step, EG25_GNSS_STEP_LAST);
manager->gnss_assistance_step,
EG25_GNSS_STEP_LAST);
enable_mm_gnss(manager);
}
manager->gnss_assistance_step++;
@@ -503,13 +498,15 @@ void gnss_step(struct EG25Manager *manager)
case EG25_GNSS_STEP_AT_QGPS_ENABLE:
g_message("GNSS assistance upload step (%d/%d): "
"re-enabling GNSS through AT+QGPS",
manager->gnss_assistance_step, EG25_GNSS_STEP_LAST);
manager->gnss_assistance_step,
EG25_GNSS_STEP_LAST);
enable_at_gnss(manager);
break;
case EG25_GNSS_STEP_LAST:
g_message("GNSS assistance upload step (%d/%d): finished",
manager->gnss_assistance_step, EG25_GNSS_STEP_LAST);
manager->gnss_assistance_step,
EG25_GNSS_STEP_LAST);
break;
}
}

View File

@@ -6,9 +6,9 @@
#pragma once
#include <curl/curl.h>
#include <time.h>
#include <unistd.h>
#include <curl/curl.h>
#include "manager.h"

View File

@@ -4,9 +4,11 @@
* SPDX-License-Identifier: GPL-3.0-or-later
*/
#include "config.h"
#include "gpio.h"
#include "config.h"
#include <dirent.h>
#include <sys/stat.h>
#include <unistd.h>
/* Those defines are used for legacy config files only */
@@ -40,21 +42,78 @@ static char *gpio_in_names[] = {
"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)
{
gpiod_line_set_value(manager->gpio_out[GPIO_OUT_PWRKEY], 1);
sleep(1);
gpiod_line_set_value(manager->gpio_out[GPIO_OUT_PWRKEY], 0);
/*
* Force the modem to poweroff using the RESET_N pin before attempting to
* boot in case the it got into a bad state.
*
* If the modem was on, this will cause it to start booting, so press the
* power button while in reset to avoid a (probably only theoretical) race
* condition where it starts booting after reset, and then powers off from
* the power key.
*/
gpio_line_set_value(manager, GPIO_OUT_RESET, GPIOD_LINE_VALUE_ACTIVE);
gpio_line_set_value(manager, GPIO_OUT_PWRKEY, GPIOD_LINE_VALUE_ACTIVE);
g_message("Executed power-on/off sequence");
/*
* The datasheet says to pull the pin low for between 150 and 460 ms. usleep
* should always sleep for at least the specified amount of time, so use
* 200ms because it's closer to the bottom of that range.
*/
usleep(200000);
gpio_line_set_value(manager, GPIO_OUT_RESET, GPIOD_LINE_VALUE_INACTIVE);
/*
* The modem has finished it's reset, now we wait to allow it a chance to
* react to the power key
*/
sleep(1);
gpio_line_set_value(manager, GPIO_OUT_PWRKEY, GPIOD_LINE_VALUE_INACTIVE);
g_message("Executed power-on sequence");
return 0;
}
int gpio_sequence_shutdown(struct EG25Manager *manager)
{
gpiod_line_set_value(manager->gpio_out[GPIO_OUT_DISABLE], 1);
gpio_sequence_poweron(manager);
gpio_line_set_value(manager, GPIO_OUT_PWRKEY, GPIOD_LINE_VALUE_ACTIVE);
sleep(1);
gpio_line_set_value(manager, GPIO_OUT_PWRKEY, GPIOD_LINE_VALUE_INACTIVE);
g_message("Executed power-off sequence");
@@ -63,7 +122,7 @@ int gpio_sequence_shutdown(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");
@@ -72,7 +131,7 @@ int gpio_sequence_suspend(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");
@@ -81,8 +140,8 @@ int gpio_sequence_resume(struct EG25Manager *manager)
int gpio_sequence_wake(struct EG25Manager *manager)
{
if (gpiod_line_get_value(manager->gpio_out[GPIO_OUT_DTR])) {
gpiod_line_set_value(manager->gpio_out[GPIO_OUT_DTR], 0);
if (manager->gpio_out_value[GPIO_OUT_DTR]) {
gpio_line_set_value(manager, GPIO_OUT_DTR, GPIOD_LINE_VALUE_INACTIVE);
/* Give the modem 200ms to wake from soft sleep */
usleep(200000);
@@ -95,42 +154,158 @@ int gpio_sequence_wake(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");
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 *gpio_line;
struct gpiod_line_request *request = NULL;
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);
if (!gpio_line)
settings = gpiod_line_settings_new();
if (!settings)
return NULL;
if (gpiod_line_request_output(gpio_line, "eg25manager", 0) < 0) {
gpiod_line_release(gpio_line);
return NULL;
gpiod_line_settings_set_direction(settings, direction);
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;
}
return gpio_line;
}
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 (!gpio_line)
return NULL;
if (asprintf(&path, "/dev/%s", entry->d_name) < 0)
return 0;
if ((lstat(path, &sb) == 0) && (!S_ISLNK(sb.st_mode)) && gpiod_is_gpiochip_device(path))
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;
}
}
*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 = NULL;
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) {
free(paths);
return chip;
}
clean_chip_open:
gpiod_chip_close(chip);
}
if (paths)
free(paths);
if (gpiod_line_request_input(gpio_line, "eg25manager") < 0) {
gpiod_line_release(gpio_line);
return NULL;
}
return gpio_line;
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[])
@@ -155,8 +330,7 @@ int gpio_init(struct EG25Manager *manager, toml_table_t *config[])
* format, but error out if user config overrides gpios using the
* old format
*/
if (!gpio_config[EG25_CONFIG_USER] || toml_array_in(gpio_config[EG25_CONFIG_USER], "chips"))
{
if (!gpio_config[EG25_CONFIG_USER] || toml_array_in(gpio_config[EG25_CONFIG_USER], "chips")) {
int numchips;
toml_array_t *chipslist = NULL;
@@ -169,7 +343,7 @@ int gpio_init(struct EG25Manager *manager, toml_table_t *config[])
toml_datum_t data = toml_string_at(chipslist, i);
if (!data.ok)
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])
g_error("Unable to find GPIO chip '%s'", data.u.s);
}
@@ -185,10 +359,13 @@ int gpio_init(struct EG25Manager *manager, toml_table_t *config[])
g_error("Wrong chip ID for output GPIO '%s'", gpio_out_names[i]);
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]);
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])
g_error("Unable to get output GPIO %d", i);
}
@@ -196,18 +373,26 @@ 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)
g_error("Wrong chip ID for input GPIO '%s'", gpio_in_names[i]);
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]);
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])
g_error("Unable to get input GPIO %d", i);
}
@@ -215,11 +400,11 @@ int gpio_init(struct EG25Manager *manager, toml_table_t *config[])
guint offset, chipidx, gpio_idx;
/* 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])
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])
g_error("Unable to open GPIO chip " GPIO_CHIP2_LABEL);
@@ -235,7 +420,10 @@ int gpio_init(struct EG25Manager *manager, toml_table_t *config[])
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])
g_error("Unable to get output GPIO %d", i);
}
@@ -252,7 +440,10 @@ int gpio_init(struct EG25Manager *manager, toml_table_t *config[])
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])
g_error("Unable to get input GPIO %d", i);
}
@@ -261,16 +452,18 @@ int gpio_init(struct EG25Manager *manager, toml_table_t *config[])
return 0;
}
gboolean gpio_check_poweroff(struct EG25Manager *manager, gboolean keep_down)
void gpio_force_off(struct EG25Manager *manager)
{
if (manager->gpio_in[GPIO_IN_STATUS] &&
gpiod_line_get_value(manager->gpio_in[GPIO_IN_STATUS]) == 1) {
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);
if (manager->gpio_out[GPIO_OUT_RESET]) {
g_message("Setting the reset pin to ensure the modem stays off");
gpio_line_set_value(manager, GPIO_OUT_RESET, GPIOD_LINE_VALUE_ACTIVE);
}
}
gboolean gpio_check_poweroff(struct EG25Manager *manager)
{
if (manager->gpio_in[GPIO_IN_STATUS] &&
gpio_line_get_value(manager, GPIO_IN_STATUS) == GPIOD_LINE_VALUE_ACTIVE) {
return TRUE;
}
@@ -283,12 +476,12 @@ void gpio_destroy(struct EG25Manager *manager)
for (i = 0; i < GPIO_OUT_COUNT; 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++) {
if (manager->gpio_in[i])
gpiod_line_release(manager->gpio_in[i]);
gpiod_line_request_release(manager->gpio_in[i]);
}
if (manager->gpiochip[0])

View File

@@ -18,4 +18,5 @@ int gpio_sequence_resume(struct EG25Manager *state);
int gpio_sequence_wake(struct EG25Manager *state);
int gpio_sequence_sleep(struct EG25Manager *state);
gboolean gpio_check_poweroff(struct EG25Manager *manager, gboolean keep_down);
void gpio_force_off(struct EG25Manager *manager);
gboolean gpio_check_poweroff(struct EG25Manager *manager);

View File

@@ -4,19 +4,19 @@
* SPDX-License-Identifier: GPL-3.0-or-later
*/
#include "manager.h"
#include "at.h"
#include "config.h"
#include "gpio.h"
#include "manager.h"
#ifdef HAVE_MMGLIB
#include "mm-iface.h"
#endif
#include "gnss.h"
#include "ofono-iface.h"
#include "suspend.h"
#include "udev.h"
#include "gnss.h"
#include <fcntl.h>
#include <signal.h>
@@ -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)
@@ -59,13 +62,17 @@ static gboolean quit_app(struct EG25Manager *manager)
gpio_sequence_shutdown(manager);
manager->modem_state = EG25_STATE_FINISHING;
for (i = 0; i < 30; i++) {
if (gpio_check_poweroff(manager, TRUE))
if (gpio_check_poweroff(manager)) {
g_message("Modem successfully powered down");
break;
}
sleep(1);
}
}
g_message("Modem down, quitting...");
gpio_force_off(manager);
g_message("Modem down, quitting...");
g_main_loop_quit(manager->loop);
return FALSE;
@@ -95,7 +102,7 @@ static gboolean modem_start(struct EG25Manager *manager)
libusb_free_device_list(devices, 1);
libusb_exit(ctx);
} else if (!gpio_check_poweroff(manager, FALSE)) {
} else if (!gpio_check_poweroff(manager)) {
g_message("STATUS is low, modem already powered");
should_boot = FALSE;
}
@@ -138,10 +145,40 @@ void modem_configure(struct EG25Manager *manager)
at_sequence_configure(manager);
}
static gboolean modem_reset_done(struct EG25Manager* manager)
static gboolean modem_gpio_reset_done(struct EG25Manager *manager)
{
gpio_sequence_poweron(manager);
manager->modem_state = EG25_STATE_POWERED;
manager->complete_reset_timer = 0;
return G_SOURCE_REMOVE;
}
static gboolean modem_at_reset_done(struct EG25Manager *manager)
{
/*
* If the modem was successfully rebooted, then we should have received
* "RDY" by now which will transition the state to started.
*/
if (manager->modem_state != EG25_STATE_RESETTING) {
manager->complete_reset_timer = 0;
return G_SOURCE_REMOVE;
}
g_message("AT reset failed, falling back to GPIO reset");
gpio_sequence_shutdown(manager);
manager->complete_reset_timer = g_timeout_add_seconds(30,
G_SOURCE_FUNC(modem_gpio_reset_done),
manager);
return G_SOURCE_REMOVE;
}
static gboolean modem_rebind_done(struct EG25Manager *manager)
{
manager->modem_state = EG25_STATE_RESUMING;
manager->reset_timer = 0;
manager->complete_reset_timer = 0;
return FALSE;
}
@@ -149,7 +186,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 +212,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;
@@ -185,24 +228,26 @@ gboolean modem_reset(struct EG25Manager *manager)
g_warning("Unable to open /sys/bus/usb/drivers/usb/unbind");
goto error;
}
ret = write(fd, manager->modem_usb_id, len);
close(fd);
if (ret < len) {
g_warning("Couldn't unbind modem: wrote %d/%d bytes", ret, len);
goto error;
}
close(fd);
fd = open("/sys/bus/usb/drivers/usb/bind", O_WRONLY);
if (fd < 0) {
g_warning("Unable to open /sys/bus/usb/drivers/usb/unbind");
g_warning("Unable to open /sys/bus/usb/drivers/usb/bind");
goto error;
}
ret = write(fd, manager->modem_usb_id, len);
close(fd);
if (ret < len) {
g_warning("Couldn't bind modem: wrote %d/%d bytes", ret, len);
goto error;
}
close(fd);
g_message("Successfully reset modem's USB connection");
@@ -210,7 +255,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_rebind_done), manager);
return G_SOURCE_REMOVE;
@@ -228,7 +273,9 @@ 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(45,
G_SOURCE_FUNC(modem_at_reset_done),
manager);
return G_SOURCE_REMOVE;
}
@@ -281,9 +328,11 @@ static toml_table_t *parse_config_file(char *config_file, gboolean force_default
for (pos = 0; pos < compat->len; pos++) {
g_autofree gchar *filename = NULL;
if (force_default)
filename = g_strdup_printf(EG25_DATADIR "/%s.toml", (gchar *)g_ptr_array_index(compat, pos));
filename = g_strdup_printf(EG25_DATADIR "/%s.toml",
(gchar *)g_ptr_array_index(compat, pos));
else
filename = g_strdup_printf(EG25_CONFDIR "/%s.toml", (gchar *)g_ptr_array_index(compat, pos));
filename = g_strdup_printf(EG25_CONFDIR "/%s.toml",
(gchar *)g_ptr_array_index(compat, pos));
if (access(filename, F_OK) == 0) {
g_message("Opening config file: %s", filename);
@@ -314,6 +363,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 +413,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 +428,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);

View File

@@ -41,7 +41,6 @@ typedef enum {
EG25_GNSS_SOURCE_QGPS = 1 << 3,
} EG25GNSSSource;
enum EG25State {
EG25_STATE_INIT = 0,
EG25_STATE_POWERED, // Power-on sequence has been executed, but the modem isn't on yet
@@ -53,6 +52,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 +70,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;
@@ -114,8 +115,9 @@ struct EG25Manager {
GUdevClient *udev;
struct gpiod_chip *gpiochip[2];
struct gpiod_line *gpio_out[5];
struct gpiod_line *gpio_in[2];
struct gpiod_line_request *gpio_out[5];
guint gpio_out_value[5];
struct gpiod_line_request *gpio_in[2];
};
void modem_configure(struct EG25Manager *data);

View File

@@ -25,7 +25,7 @@ if mmglib_dep.found()
endif
executable (
'eg25manager',
'eg25-manager',
src,
dependencies : mgr_deps,
link_with: gdbofono_lib,

View File

@@ -78,7 +78,8 @@ static void interface_added_cb (struct EG25Manager *manager,
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));
info->name,
g_dbus_object_get_object_path(object));
if (g_strcmp0(info->name, MM_DBUS_INTERFACE_MODEM) == 0)
add_modem(manager, object);
@@ -87,7 +88,6 @@ static void interface_added_cb (struct EG25Manager *manager,
add_modem_location(manager, object);
}
static void interface_removed_cb(struct EG25Manager *manager,
GDBusObject *object,
GDBusInterface *interface)
@@ -104,7 +104,6 @@ static void interface_removed_cb(struct EG25Manager *manager,
manager->mm_modem = NULL;
}
static void add_mm_object(struct EG25Manager *manager, GDBusObject *object)
{
GList *ifaces, *node;
@@ -116,7 +115,6 @@ static void add_mm_object(struct EG25Manager *manager, GDBusObject *object)
g_list_free_full(ifaces, g_object_unref);
}
static void add_mm_objects(struct EG25Manager *manager)
{
GList *objects, *node;
@@ -128,14 +126,12 @@ static void add_mm_objects(struct EG25Manager *manager)
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;
@@ -146,7 +142,6 @@ static void object_removed_cb(struct EG25Manager *manager, GDBusObject *object)
manager->mm_modem = NULL;
}
static void mm_manager_new_cb(GDBusConnection *connection,
GAsyncResult *res,
struct EG25Manager *manager)
@@ -158,13 +153,21 @@ static void mm_manager_new_cb(GDBusConnection *connection,
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);
"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);
"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);
"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);
"object-removed",
G_CALLBACK(object_removed_cb),
manager);
add_mm_objects(manager);
}
@@ -177,13 +180,18 @@ static void mm_appeared_cb(GDBusConnection *connection,
g_message("ModemManager appeared on D-Bus");
if (manager->modem_iface != MODEM_IFACE_NONE) {
g_critical("Modem interface already found! Make sure to only run either of ModemManager or oFono.");
g_critical("Modem interface already found! Make sure to only run either of "
"ModemManager or "
"oFono.");
return;
}
manager->modem_iface = MODEM_IFACE_MODEMMANAGER;
mm_manager_new(connection, G_DBUS_OBJECT_MANAGER_CLIENT_FLAGS_NONE,
NULL, (GAsyncReadyCallback)mm_manager_new_cb, manager);
mm_manager_new(connection,
G_DBUS_OBJECT_MANAGER_CLIENT_FLAGS_NONE,
NULL,
(GAsyncReadyCallback)mm_manager_new_cb,
manager);
}
static void mm_iface_clean(struct EG25Manager *manager)
@@ -201,9 +209,7 @@ static void mm_iface_clean(struct EG25Manager *manager)
}
}
static void mm_vanished_cb(GDBusConnection *connection,
const gchar *name,
struct EG25Manager *manager)
static void mm_vanished_cb(GDBusConnection *connection, const gchar *name, struct EG25Manager *manager)
{
g_message("ModemManager vanished from D-Bus");
mm_iface_clean(manager);
@@ -211,11 +217,13 @@ static void mm_vanished_cb(GDBusConnection *connection,
void mm_iface_init(struct EG25Manager *manager, toml_table_t *config[])
{
manager->mm_watch = g_bus_watch_name(G_BUS_TYPE_SYSTEM, MM_DBUS_SERVICE,
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);
manager,
NULL);
}
void mm_iface_destroy(struct EG25Manager *manager)

View File

@@ -43,15 +43,11 @@ static void modem_added_cb(GDBOManager *manager_proxy,
manager->modem_usb_id = g_strdup(strrchr(g_variant_dup_string(modem_path, NULL), '/') + 1);
}
static void modem_removed_cb(GDBOManager *manager_proxy,
const gchar *path,
struct EG25Manager *manager)
static void modem_removed_cb(GDBOManager *manager_proxy, const gchar *path, struct EG25Manager *manager)
{
}
static void get_modems_cb(GDBOManager *manager_proxy,
GAsyncResult *res,
struct EG25Manager *manager)
static void get_modems_cb(GDBOManager *manager_proxy, GAsyncResult *res, struct EG25Manager *manager)
{
gboolean ok;
GVariant *modems;
@@ -61,8 +57,7 @@ static void get_modems_cb(GDBOManager *manager_proxy,
const gchar *path;
GVariant *properties;
ok = gdbo_manager_call_get_modems_finish(manager_proxy, &modems,
res, &error);
ok = gdbo_manager_call_get_modems_finish(manager_proxy, &modems, res, &error);
if (!ok) {
g_warning("Error getting modems from ofono manager: %s", error->message);
return;
@@ -87,7 +82,9 @@ static void ofono_appeared_cb(GDBusConnection *connection,
g_message("oFono appeared on D-Bus");
if (manager->modem_iface != MODEM_IFACE_NONE) {
g_critical("Modem interface already found! Make sure to only run either of ModemManager or oFono.");
g_critical("Modem interface already found! Make sure to only run either of "
"ModemManager or "
"oFono.");
return;
}
/* now connect to oFono! */
@@ -105,10 +102,8 @@ static void ofono_appeared_cb(GDBusConnection *connection,
manager->modem_iface = MODEM_IFACE_OFONO;
g_signal_connect(manager->ofono_manager, "modem-added",
G_CALLBACK(modem_added_cb), manager);
g_signal_connect(manager->ofono_manager, "modem-removed",
G_CALLBACK(modem_removed_cb), manager);
g_signal_connect(manager->ofono_manager, "modem-added", G_CALLBACK(modem_added_cb), manager);
g_signal_connect(manager->ofono_manager, "modem-removed", G_CALLBACK(modem_removed_cb), manager);
gdbo_manager_call_get_modems(manager->ofono_manager,
NULL,
@@ -130,11 +125,13 @@ static void ofono_vanished_cb(GDBusConnection *connection,
void ofono_iface_init(struct EG25Manager *manager, toml_table_t *config[])
{
manager->ofono_watch = g_bus_watch_name(G_BUS_TYPE_SYSTEM, OFONO_SERVICE,
manager->ofono_watch = g_bus_watch_name(G_BUS_TYPE_SYSTEM,
OFONO_SERVICE,
G_BUS_NAME_WATCHER_FLAGS_AUTO_START,
(GBusNameAppearedCallback)ofono_appeared_cb,
(GBusNameVanishedCallback)ofono_vanished_cb,
manager, NULL);
manager,
NULL);
}
void ofono_iface_destroy(struct EG25Manager *manager)

View File

@@ -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;
@@ -36,8 +50,7 @@ static gboolean drop_inhibitor(struct EG25Manager *manager, gboolean block)
manager->suspend_block_fd = -1;
return TRUE;
}
}
else {
} else {
if (manager->suspend_delay_fd >= 0) {
g_message("dropping systemd sleep delay inhibitor");
close(manager->suspend_delay_fd);
@@ -48,9 +61,7 @@ static gboolean drop_inhibitor(struct EG25Manager *manager, gboolean block)
return FALSE;
}
static void inhibit_done_delay(GObject *source,
GAsyncResult *result,
gpointer user_data)
static void inhibit_done_delay(GObject *source, GAsyncResult *result, gpointer user_data)
{
GDBusProxy *suspend_proxy = G_DBUS_PROXY(source);
struct EG25Manager *manager = user_data;
@@ -58,8 +69,7 @@ static void inhibit_done_delay(GObject *source,
GVariant *res;
GUnixFDList *fd_list;
res = g_dbus_proxy_call_with_unix_fd_list_finish(suspend_proxy, &fd_list,
result, &error);
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);
} else {
@@ -74,9 +84,7 @@ static void inhibit_done_delay(GObject *source,
}
}
static void inhibit_done_block(GObject *source,
GAsyncResult *result,
gpointer user_data)
static void inhibit_done_block(GObject *source, GAsyncResult *result, gpointer user_data)
{
GDBusProxy *suspend_proxy = G_DBUS_PROXY(source);
struct EG25Manager *manager = user_data;
@@ -84,8 +92,7 @@ static void inhibit_done_block(GObject *source,
GVariant *res;
GUnixFDList *fd_list;
res = g_dbus_proxy_call_with_unix_fd_list_finish(suspend_proxy, &fd_list,
result, &error);
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);
} else {
@@ -122,30 +129,45 @@ static void take_inhibitor(struct EG25Manager *manager, gboolean block)
if (manager->suspend_block_fd != -1)
drop_inhibitor(manager, TRUE);
variant_arg = g_variant_new ("(ssss)", "sleep", "eg25manager",
variant_arg = g_variant_new("(ssss)",
"sleep",
"eg25manager",
"eg25manager needs to wait for modem to be fully booted",
"block");
g_message("taking systemd sleep inhibitor (blocking)");
g_dbus_proxy_call_with_unix_fd_list(manager->suspend_proxy, "Inhibit",
variant_arg, 0, G_MAXINT, NULL, NULL,
inhibit_done_block, manager);
g_dbus_proxy_call_with_unix_fd_list(manager->suspend_proxy,
"Inhibit",
variant_arg,
0,
G_MAXINT,
NULL,
NULL,
inhibit_done_block,
manager);
manager->modem_boot_timer = g_timeout_add_seconds(manager->modem_boot_timeout,
G_SOURCE_FUNC(modem_fully_booted),
manager);
}
else {
} else {
if (manager->suspend_delay_fd != -1)
drop_inhibitor(manager, FALSE);
variant_arg = g_variant_new ("(ssss)", "sleep", "eg25manager",
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_delay, manager);
g_dbus_proxy_call_with_unix_fd_list(manager->suspend_proxy,
"Inhibit",
variant_arg,
0,
G_MAXINT,
NULL,
NULL,
inhibit_done_delay,
manager);
}
}
@@ -184,8 +206,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,
@@ -195,9 +216,7 @@ static void signal_cb(GDBusProxy *proxy,
}
}
static void name_owner_cb(GObject *object,
GParamSpec *pspec,
gpointer user_data)
static void name_owner_cb(GObject *object, GParamSpec *pspec, gpointer user_data)
{
GDBusProxy *proxy = G_DBUS_PROXY(object);
struct EG25Manager *manager = user_data;
@@ -214,9 +233,7 @@ static void name_owner_cb(GObject *object,
}
}
static void on_proxy_acquired(GObject *object,
GAsyncResult *res,
struct EG25Manager *manager)
static void on_proxy_acquired(GObject *object, GAsyncResult *res, struct EG25Manager *manager)
{
g_autoptr(GError) error = NULL;
char *owner;
@@ -227,10 +244,8 @@ static void on_proxy_acquired(GObject *object,
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);
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) {
@@ -267,10 +282,16 @@ void suspend_init(struct EG25Manager *manager, toml_table_t *config[])
manager->modem_recovery_timeout = 9;
g_dbus_proxy_new_for_bus(G_BUS_TYPE_SYSTEM,
// NOLINTNEXTLINE Flags combination is valid even if not matching an enum entry
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);
NULL,
SD_NAME,
SD_PATH,
SD_INTERFACE,
NULL,
(GAsyncReadyCallback)on_proxy_acquired,
manager);
}
void suspend_destroy(struct EG25Manager *manager)

File diff suppressed because it is too large Load Diff

View File

@@ -25,10 +25,8 @@
#ifndef TOML_H
#define TOML_H
#include <stdio.h>
#include <stdint.h>
#include <stdio.h>
#ifdef __cplusplus
#define TOML_EXTERN extern "C"
@@ -44,9 +42,7 @@ typedef struct toml_datum_t toml_datum_t;
/* Parse a file. Return a table on success, or 0 otherwise.
* Caller must toml_free(the-return-value) after use.
*/
TOML_EXTERN toml_table_t* toml_parse_file(FILE* fp,
char* errbuf,
int errbufsz);
TOML_EXTERN toml_table_t *toml_parse_file(FILE *fp, char *errbuf, int errbufsz);
/* Parse a string containing the full config.
* Return a table on success, or 0 otherwise.
@@ -62,7 +58,6 @@ TOML_EXTERN toml_table_t* toml_parse(char* conf, /* NUL terminated, please. */
*/
TOML_EXTERN void toml_free(toml_table_t *tab);
/* Timestamp types. The year, month, day, hour, minute, second, z
* fields may be NULL if they are not relevant. e.g. In a DATE
* type, the hour, minute, second and z fields will be NULLs.
@@ -78,7 +73,6 @@ struct toml_timestamp_t {
char *z;
};
/*-----------------------------------------------------------------
* Enhanced access methods
*/
@@ -116,10 +110,8 @@ TOML_EXTERN toml_datum_t toml_int_in(const toml_table_t* arr, const char* key);
TOML_EXTERN toml_datum_t toml_double_in(const toml_table_t *arr, const char *key);
TOML_EXTERN toml_datum_t toml_timestamp_in(const toml_table_t *arr, const char *key);
/* .. retrieve array or table using key. */
TOML_EXTERN toml_array_t* toml_array_in(const toml_table_t* tab,
const char* key);
TOML_EXTERN toml_table_t* toml_table_in(const toml_table_t* tab,
const char* key);
TOML_EXTERN toml_array_t *toml_array_in(const toml_table_t *tab, const char *key);
TOML_EXTERN toml_table_t *toml_table_in(const toml_table_t *tab, const char *key);
/*-----------------------------------------------------------------
* lesser used
@@ -153,9 +145,7 @@ TOML_EXTERN const char* toml_table_key(const toml_table_t* tab);
*/
TOML_EXTERN int toml_utf8_to_ucs(const char *orig, int len, int64_t *ret);
TOML_EXTERN int toml_ucs_to_utf8(int64_t code, char buf[6]);
TOML_EXTERN void toml_set_memutil(void* (*xxmalloc)(size_t),
void (*xxfree)(void*));
TOML_EXTERN void toml_set_memutil(void *(*xxmalloc)(size_t), void (*xxfree)(void *));
/*--------------------------------------------------------------
* deprecated
@@ -171,5 +161,4 @@ TOML_EXTERN int toml_rtod(toml_raw_t s, double* ret);
TOML_EXTERN int toml_rtod_ex(toml_raw_t s, double *ret, char *buf, int buflen);
TOML_EXTERN int toml_rtots(toml_raw_t s, toml_timestamp_t *ret);
#endif /* TOML_H */

View File

@@ -11,18 +11,44 @@
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[])

View File

@@ -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", ATTRS{idVendor}=="2c7c", ATTRS{idProduct}=="0125", ATTR{power/autosuspend_delay_ms}="3000"
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"
ACTION=="add", SUBSYSTEM=="usb", DRIVERS=="usb", ENV{DEVTYPE}=="usb_device", GOTO="eg25_start"
GOTO="eg25_end"
# 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"