mirror of
https://gitlab.com/mobian1/eg25-manager.git
synced 2025-08-29 23:32:14 +02:00
Compare commits
46 Commits
Author | SHA1 | Date | |
---|---|---|---|
|
0d5117d4e3 | ||
|
9f8ae70cba | ||
|
f978b775e0 | ||
|
2c311b9022 | ||
|
c785f7cb8d | ||
|
9f6178dafd | ||
|
57142c2c11 | ||
|
99aa7e2bf7 | ||
|
ce0fc25892 | ||
|
ec96153b0f | ||
|
b000a75238 | ||
|
541aa00198 | ||
|
b53702ad0b | ||
|
1482899888 | ||
|
c4b3b75047 | ||
|
3c5d455f20 | ||
|
1acd15ca2f | ||
|
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 |
48
.clang-format
Normal file
48
.clang-format
Normal 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
4
.clang-tidy
Normal 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
49
.gitlab-ci.yml
Normal 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
|
@@ -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
|
||||||
|
|
||||||
@@ -60,9 +63,9 @@ configure = [
|
|||||||
{ cmd = "QSCLK", value = "1" },
|
{ cmd = "QSCLK", value = "1" },
|
||||||
# GNSS configuration:
|
# GNSS configuration:
|
||||||
# * Enable A-GPS data upload support (XTRA)
|
# * Enable A-GPS data upload support (XTRA)
|
||||||
# * Disable On-Demand-Positioning (ODP) mode
|
# * Disable On-Demand-Positioning (ODP) mode
|
||||||
# to avoid running the GNSS system in the background, even when not enabled.
|
# to avoid running the GNSS system in the background, even when not enabled.
|
||||||
# * Enable Dynamic Power Optimizations (DPO) mode to turn off GNSS RF radios
|
# * Enable Dynamic Power Optimizations (DPO) mode to turn off GNSS RF radios
|
||||||
# when they are not in use.
|
# when they are not in use.
|
||||||
# * Only enable GPS and GLONASS, disable other GNSS systems.
|
# * Only enable GPS and GLONASS, disable other GNSS systems.
|
||||||
# A-GPS data upload doesn't work for Galileo anyway.
|
# A-GPS data upload doesn't work for Galileo anyway.
|
||||||
|
@@ -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
|
||||||
@@ -60,9 +63,9 @@ configure = [
|
|||||||
{ cmd = "QSCLK", value = "1" },
|
{ cmd = "QSCLK", value = "1" },
|
||||||
# GNSS configuration:
|
# GNSS configuration:
|
||||||
# * Enable A-GPS data upload support (XTRA)
|
# * Enable A-GPS data upload support (XTRA)
|
||||||
# * Disable On-Demand-Positioning (ODP) mode
|
# * Disable On-Demand-Positioning (ODP) mode
|
||||||
# to avoid running the GNSS system in the background, even when not enabled.
|
# to avoid running the GNSS system in the background, even when not enabled.
|
||||||
# * Enable Dynamic Power Optimizations (DPO) mode to turn off GNSS RF radios
|
# * Enable Dynamic Power Optimizations (DPO) mode to turn off GNSS RF radios
|
||||||
# when they are not in use.
|
# when they are not in use.
|
||||||
# * Only enable GPS and GLONASS, disable other GNSS systems.
|
# * Only enable GPS and GLONASS, disable other GNSS systems.
|
||||||
# A-GPS data upload doesn't work for Galileo anyway.
|
# A-GPS data upload doesn't work for Galileo anyway.
|
||||||
|
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.2',
|
||||||
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')
|
||||||
|
72
src/at.c
72
src/at.c
@@ -6,9 +6,9 @@
|
|||||||
|
|
||||||
#include "at.h"
|
#include "at.h"
|
||||||
#include "config.h"
|
#include "config.h"
|
||||||
#include "suspend.h"
|
|
||||||
#include "gpio.h"
|
|
||||||
#include "gnss.h"
|
#include "gnss.h"
|
||||||
|
#include "gpio.h"
|
||||||
|
#include "suspend.h"
|
||||||
|
|
||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
#include <fcntl.h>
|
#include <fcntl.h>
|
||||||
@@ -28,7 +28,7 @@ static GArray *reset_commands = NULL;
|
|||||||
static int configure_serial(const char *tty)
|
static int configure_serial(const char *tty)
|
||||||
{
|
{
|
||||||
struct termios ttycfg;
|
struct termios ttycfg;
|
||||||
int fd;
|
int fd;
|
||||||
|
|
||||||
fd = open(tty, O_RDWR | O_NOCTTY | O_NONBLOCK);
|
fd = open(tty, O_RDWR | O_NOCTTY | O_NONBLOCK);
|
||||||
if (fd > 0) {
|
if (fd > 0) {
|
||||||
@@ -50,52 +50,58 @@ static int configure_serial(const char *tty)
|
|||||||
|
|
||||||
static void at_free_command(gpointer cmd, gpointer data)
|
static void at_free_command(gpointer cmd, gpointer data)
|
||||||
{
|
{
|
||||||
struct AtCommand *at_cmd = cmd;
|
struct AtCommand *at_cmd = cmd;
|
||||||
struct EG25Manager *manager = data;
|
struct EG25Manager *manager = data;
|
||||||
|
|
||||||
if (!at_cmd)
|
if (!at_cmd)
|
||||||
return;
|
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->cmd);
|
||||||
g_free(at_cmd->subcmd);
|
g_free(at_cmd->subcmd);
|
||||||
g_free(at_cmd->value);
|
g_free(at_cmd->value);
|
||||||
g_free(at_cmd->expected);
|
g_free(at_cmd->expected);
|
||||||
g_free(at_cmd);
|
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)
|
gboolean at_send_command(struct EG25Manager *manager)
|
||||||
{
|
{
|
||||||
char command[256];
|
char command[256];
|
||||||
struct AtCommand *at_cmd = manager->at_cmds ? g_list_nth_data(manager->at_cmds, 0) : NULL;
|
struct AtCommand *at_cmd = manager->at_cmds ? g_list_nth_data(manager->at_cmds, 0) : NULL;
|
||||||
int ret, len = 0, pos = 0;
|
int ret, len = 0, pos = 0;
|
||||||
|
|
||||||
if (at_cmd) {
|
if (at_cmd) {
|
||||||
/* Wake up the modem from soft sleep before sending an AT command */
|
/* Wake up the modem from soft sleep before sending an AT command */
|
||||||
gpio_sequence_wake(manager);
|
gpio_sequence_wake(manager);
|
||||||
|
|
||||||
/* Send AT command */
|
/* Send AT command */
|
||||||
if (at_cmd->subcmd == NULL && at_cmd->value == NULL && at_cmd->expected == NULL)
|
if (at_cmd->subcmd == NULL && at_cmd->value == NULL && at_cmd->expected == NULL)
|
||||||
len = snprintf(command, sizeof(command), "AT+%s\r\n", at_cmd->cmd);
|
len = snprintf(command, sizeof(command), "AT+%s\r\n", at_cmd->cmd);
|
||||||
else if (at_cmd->subcmd == NULL && at_cmd->value == NULL)
|
else if (at_cmd->subcmd == NULL && at_cmd->value == NULL)
|
||||||
len = snprintf(command, sizeof(command), "AT+%s?\r\n", at_cmd->cmd);
|
len = snprintf(command, sizeof(command), "AT+%s?\r\n", at_cmd->cmd);
|
||||||
else if (at_cmd->subcmd == NULL && at_cmd->value)
|
else if (at_cmd->subcmd == NULL && at_cmd->value)
|
||||||
len = snprintf(command, sizeof(command),"AT+%s=%s\r\n", at_cmd->cmd, at_cmd->value);
|
len = snprintf(command, sizeof(command), "AT+%s=%s\r\n", at_cmd->cmd, at_cmd->value);
|
||||||
else if (at_cmd->subcmd && at_cmd->value == NULL)
|
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);
|
len = snprintf(command, sizeof(command), "AT+%s=\"%s\"\r\n", at_cmd->cmd, at_cmd->subcmd);
|
||||||
else if (at_cmd->subcmd && at_cmd->value)
|
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) {
|
if (len < 0) {
|
||||||
g_warning("snprintf(3) failed");
|
g_warning("snprintf(3) failed");
|
||||||
at_next_command(manager);
|
at_next_command(manager);
|
||||||
return FALSE;
|
return FALSE;
|
||||||
}
|
} else if (len >= sizeof(command)) {
|
||||||
else if (len >= sizeof(command)) {
|
|
||||||
g_warning("AT command does not fit into buffer "
|
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);
|
at_next_command(manager);
|
||||||
return FALSE;
|
return FALSE;
|
||||||
}
|
}
|
||||||
@@ -117,8 +123,7 @@ gboolean at_send_command(struct EG25Manager *manager)
|
|||||||
at_next_command(manager);
|
at_next_command(manager);
|
||||||
return FALSE;
|
return FALSE;
|
||||||
}
|
}
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
len -= ret;
|
len -= ret;
|
||||||
pos += ret;
|
pos += ret;
|
||||||
}
|
}
|
||||||
@@ -126,8 +131,8 @@ gboolean at_send_command(struct EG25Manager *manager)
|
|||||||
|
|
||||||
g_message("Successfully sent command: %s", g_strstrip(command));
|
g_message("Successfully sent command: %s", g_strstrip(command));
|
||||||
} else {
|
} else {
|
||||||
/* Allow the modem to enter soft sleep again when we sent the AT command*/
|
/* Allow the modem to enter soft sleep again when we sent the AT command*/
|
||||||
gpio_sequence_sleep(manager);
|
gpio_sequence_sleep(manager);
|
||||||
|
|
||||||
if (manager->modem_state < EG25_STATE_CONFIGURED) {
|
if (manager->modem_state < EG25_STATE_CONFIGURED) {
|
||||||
if (manager->modem_iface == MODEM_IFACE_MODEMMANAGER) {
|
if (manager->modem_iface == MODEM_IFACE_MODEMMANAGER) {
|
||||||
@@ -146,8 +151,6 @@ gboolean at_send_command(struct EG25Manager *manager)
|
|||||||
}
|
}
|
||||||
} else if (manager->modem_state == EG25_STATE_SUSPENDING) {
|
} else if (manager->modem_state == EG25_STATE_SUSPENDING) {
|
||||||
modem_suspend_post(manager);
|
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,
|
void at_process_result(struct EG25Manager *manager, const char *response)
|
||||||
const char *response)
|
|
||||||
{
|
{
|
||||||
struct AtCommand *at_cmd = manager->at_cmds ? g_list_nth_data(manager->at_cmds, 0) : NULL;
|
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 *subcmd,
|
||||||
const char *value,
|
const char *value,
|
||||||
const char *expected,
|
const char *expected,
|
||||||
void (*callback)
|
void (*callback)(struct EG25Manager *manager, const char *response))
|
||||||
(struct EG25Manager *manager,
|
|
||||||
const char *response))
|
|
||||||
{
|
{
|
||||||
struct AtCommand *at_cmd = calloc(1, sizeof(struct AtCommand));
|
struct AtCommand *at_cmd = calloc(1, sizeof(struct AtCommand));
|
||||||
|
|
||||||
@@ -229,14 +229,12 @@ int at_append_command(struct EG25Manager *manager,
|
|||||||
|
|
||||||
#define READ_BUFFER_SIZE 256
|
#define READ_BUFFER_SIZE 256
|
||||||
|
|
||||||
static gboolean modem_response(gint fd,
|
static gboolean modem_response(gint fd, GIOCondition event, gpointer data)
|
||||||
GIOCondition event,
|
|
||||||
gpointer data)
|
|
||||||
{
|
{
|
||||||
struct EG25Manager *manager = data;
|
struct EG25Manager *manager = data;
|
||||||
char response[READ_BUFFER_SIZE*4+1];
|
char response[READ_BUFFER_SIZE * 4 + 1];
|
||||||
char tmp[READ_BUFFER_SIZE];
|
char tmp[READ_BUFFER_SIZE];
|
||||||
ssize_t ret, pos = 0;
|
ssize_t ret, pos = 0;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Several reads can be necessary to get the full response, so we loop
|
* Several reads can be necessary to get the full response, so we loop
|
||||||
@@ -321,8 +319,8 @@ static void parse_commands_list(toml_array_t *array, GArray **cmds)
|
|||||||
g_array_set_size(*cmds, (guint)len);
|
g_array_set_size(*cmds, (guint)len);
|
||||||
for (int i = 0; i < len; i++) {
|
for (int i = 0; i < len; i++) {
|
||||||
struct AtCommand *cmd = &g_array_index(*cmds, struct AtCommand, i);
|
struct AtCommand *cmd = &g_array_index(*cmds, struct AtCommand, i);
|
||||||
toml_table_t *table = toml_table_at(array, i);
|
toml_table_t *table = toml_table_at(array, i);
|
||||||
toml_datum_t value;
|
toml_datum_t value;
|
||||||
|
|
||||||
if (!table)
|
if (!table)
|
||||||
continue;
|
continue;
|
||||||
@@ -348,7 +346,7 @@ static void parse_commands_list(toml_array_t *array, GArray **cmds)
|
|||||||
int at_init(struct EG25Manager *manager, toml_table_t *config[])
|
int at_init(struct EG25Manager *manager, toml_table_t *config[])
|
||||||
{
|
{
|
||||||
toml_array_t *commands = NULL;
|
toml_array_t *commands = NULL;
|
||||||
gchar *uart_port = NULL;
|
gchar *uart_port = NULL;
|
||||||
toml_table_t *at_config[EG25_CONFIG_COUNT];
|
toml_table_t *at_config[EG25_CONFIG_COUNT];
|
||||||
|
|
||||||
for (int i = 0; i < EG25_CONFIG_COUNT; i++)
|
for (int i = 0; i < EG25_CONFIG_COUNT; i++)
|
||||||
|
25
src/at.h
25
src/at.h
@@ -13,25 +13,22 @@ typedef struct AtCommand {
|
|||||||
char *subcmd;
|
char *subcmd;
|
||||||
char *value;
|
char *value;
|
||||||
char *expected;
|
char *expected;
|
||||||
void (*callback)(struct EG25Manager *manager, const char *response);
|
void (*callback)(struct EG25Manager *manager, const char *response);
|
||||||
int retries;
|
int retries;
|
||||||
} AtCommand;
|
} AtCommand;
|
||||||
|
|
||||||
int at_init(struct EG25Manager *manager, toml_table_t *config[]);
|
int at_init(struct EG25Manager *manager, toml_table_t *config[]);
|
||||||
void at_destroy(struct EG25Manager *manager);
|
void at_destroy(struct EG25Manager *manager);
|
||||||
|
|
||||||
void at_process_result(struct EG25Manager *manager,
|
void at_process_result(struct EG25Manager *manager, const char *response);
|
||||||
const char *response);
|
void at_next_command(struct EG25Manager *manager);
|
||||||
void at_next_command(struct EG25Manager *manager);
|
|
||||||
gboolean at_send_command(struct EG25Manager *manager);
|
gboolean at_send_command(struct EG25Manager *manager);
|
||||||
int at_append_command(struct EG25Manager *manager,
|
int at_append_command(struct EG25Manager *manager,
|
||||||
const char *cmd,
|
const char *cmd,
|
||||||
const char *subcmd,
|
const char *subcmd,
|
||||||
const char *value,
|
const char *value,
|
||||||
const char *expected,
|
const char *expected,
|
||||||
void (*callback)
|
void (*callback)(struct EG25Manager *manager, const char *response));
|
||||||
(struct EG25Manager *manager,
|
|
||||||
const char *response));
|
|
||||||
|
|
||||||
void at_sequence_configure(struct EG25Manager *manager);
|
void at_sequence_configure(struct EG25Manager *manager);
|
||||||
void at_sequence_suspend(struct EG25Manager *manager);
|
void at_sequence_suspend(struct EG25Manager *manager);
|
||||||
|
10
src/config.c
10
src/config.c
@@ -9,7 +9,7 @@
|
|||||||
|
|
||||||
gboolean config_get_bool(toml_table_t **config, const gchar *key, gboolean *result)
|
gboolean config_get_bool(toml_table_t **config, const gchar *key, gboolean *result)
|
||||||
{
|
{
|
||||||
toml_datum_t value = { .ok = 0 };
|
toml_datum_t value = {.ok = 0};
|
||||||
|
|
||||||
if (config[EG25_CONFIG_USER])
|
if (config[EG25_CONFIG_USER])
|
||||||
value = toml_bool_in(config[EG25_CONFIG_USER], key);
|
value = toml_bool_in(config[EG25_CONFIG_USER], key);
|
||||||
@@ -23,7 +23,7 @@ gboolean config_get_bool(toml_table_t **config, const gchar *key, gboolean *resu
|
|||||||
|
|
||||||
gboolean config_get_int(toml_table_t **config, const gchar *key, gint *result)
|
gboolean config_get_int(toml_table_t **config, const gchar *key, gint *result)
|
||||||
{
|
{
|
||||||
toml_datum_t value = { .ok = 0 };
|
toml_datum_t value = {.ok = 0};
|
||||||
|
|
||||||
if (config[EG25_CONFIG_USER])
|
if (config[EG25_CONFIG_USER])
|
||||||
value = toml_int_in(config[EG25_CONFIG_USER], key);
|
value = toml_int_in(config[EG25_CONFIG_USER], key);
|
||||||
@@ -37,7 +37,7 @@ gboolean config_get_int(toml_table_t **config, const gchar *key, gint *result)
|
|||||||
|
|
||||||
gboolean config_get_uint(toml_table_t **config, const gchar *key, guint *result)
|
gboolean config_get_uint(toml_table_t **config, const gchar *key, guint *result)
|
||||||
{
|
{
|
||||||
gint value;
|
gint value;
|
||||||
gboolean found;
|
gboolean found;
|
||||||
|
|
||||||
found = config_get_int(config, key, &value);
|
found = config_get_int(config, key, &value);
|
||||||
@@ -49,14 +49,14 @@ gboolean config_get_uint(toml_table_t **config, const gchar *key, guint *result)
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (found && result)
|
if (found && result)
|
||||||
*result = (guint) value;
|
*result = (guint)value;
|
||||||
|
|
||||||
return found;
|
return found;
|
||||||
}
|
}
|
||||||
|
|
||||||
gboolean config_get_string(toml_table_t **config, const gchar *key, gchar **result)
|
gboolean config_get_string(toml_table_t **config, const gchar *key, gchar **result)
|
||||||
{
|
{
|
||||||
toml_datum_t value = { .ok = 0 };
|
toml_datum_t value = {.ok = 0};
|
||||||
|
|
||||||
if (config[EG25_CONFIG_USER])
|
if (config[EG25_CONFIG_USER])
|
||||||
value = toml_string_in(config[EG25_CONFIG_USER], key);
|
value = toml_string_in(config[EG25_CONFIG_USER], key);
|
||||||
|
161
src/gnss.c
161
src/gnss.c
@@ -4,18 +4,18 @@
|
|||||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "config.h"
|
|
||||||
#include "gnss.h"
|
#include "gnss.h"
|
||||||
#include "manager.h"
|
|
||||||
#include "at.h"
|
#include "at.h"
|
||||||
|
#include "config.h"
|
||||||
|
#include "manager.h"
|
||||||
|
|
||||||
|
#include <errno.h>
|
||||||
#include <sys/sendfile.h>
|
#include <sys/sendfile.h>
|
||||||
#include <sys/stat.h>
|
#include <sys/stat.h>
|
||||||
#include <errno.h>
|
|
||||||
|
|
||||||
#define BUFFER_SIZE 256
|
#define BUFFER_SIZE 256
|
||||||
#define UPLOAD_DELAY_US 25000
|
#define UPLOAD_DELAY_US 25000
|
||||||
#define UPLOAD_TIMEOUT_S 10
|
#define UPLOAD_TIMEOUT_S 10
|
||||||
#define RESCHEDULE_IN_SECS 30
|
#define RESCHEDULE_IN_SECS 30
|
||||||
|
|
||||||
static void gnss_step(struct EG25Manager *manager);
|
static void gnss_step(struct EG25Manager *manager);
|
||||||
@@ -29,26 +29,25 @@ gboolean gnss_upload_assistance_data(struct EG25Manager *manager)
|
|||||||
|
|
||||||
if (manager->gnss_assistance_step < EG25_GNSS_STEP_LAST) {
|
if (manager->gnss_assistance_step < EG25_GNSS_STEP_LAST) {
|
||||||
g_warning("GNSS assistance data upload already in process (%d/%d)",
|
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;
|
return FALSE;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* data upload isn't necessary to bring the modem onine, so we should wait
|
/* data upload isn't necessary to bring the modem onine, so we should wait
|
||||||
* until we've finished the rest of our configuration */
|
* until we've finished the rest of our configuration */
|
||||||
if (!manager->modem_iface ||
|
if (!manager->modem_iface || manager->modem_state < EG25_STATE_CONFIGURED ||
|
||||||
manager->modem_state < EG25_STATE_CONFIGURED ||
|
manager->modem_state > EG25_STATE_CONNECTED) {
|
||||||
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;
|
manager->gnss_assistance_step = EG25_GNSS_STEP_LAST;
|
||||||
return TRUE;
|
return TRUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef HAVE_MMGLIB
|
#ifdef HAVE_MMGLIB
|
||||||
/* ModemManager's Location is only available after unlocking */
|
/* ModemManager's Location is only available after unlocking */
|
||||||
if(manager->modem_iface == MODEM_IFACE_MODEMMANAGER && !manager->mm_location) {
|
if (manager->modem_iface == MODEM_IFACE_MODEMMANAGER && !manager->mm_location) {
|
||||||
g_message ("Rescheduling upload since Location interface is not available, in %ds",
|
g_message("Rescheduling upload since Location interface is not available, in %ds",
|
||||||
RESCHEDULE_IN_SECS);
|
RESCHEDULE_IN_SECS);
|
||||||
manager->gnss_assistance_step = EG25_GNSS_STEP_LAST;
|
manager->gnss_assistance_step = EG25_GNSS_STEP_LAST;
|
||||||
return TRUE;
|
return TRUE;
|
||||||
}
|
}
|
||||||
@@ -62,7 +61,7 @@ gboolean gnss_upload_assistance_data(struct EG25Manager *manager)
|
|||||||
void gnss_init(struct EG25Manager *manager, toml_table_t *config[])
|
void gnss_init(struct EG25Manager *manager, toml_table_t *config[])
|
||||||
{
|
{
|
||||||
toml_table_t *gnss_config[EG25_CONFIG_COUNT];
|
toml_table_t *gnss_config[EG25_CONFIG_COUNT];
|
||||||
g_autoptr (GError) error = NULL;
|
g_autoptr(GError) error = NULL;
|
||||||
|
|
||||||
for (int i = 0; i < EG25_CONFIG_COUNT; i++)
|
for (int i = 0; i < EG25_CONFIG_COUNT; i++)
|
||||||
gnss_config[i] = config[i] ? toml_table_in(config[i], "gnss") : NULL;
|
gnss_config[i] = config[i] ? toml_table_in(config[i], "gnss") : NULL;
|
||||||
@@ -92,12 +91,11 @@ void gnss_init(struct EG25Manager *manager, toml_table_t *config[])
|
|||||||
/* Create temporary file to store assistance data */
|
/* Create temporary file to store assistance data */
|
||||||
manager->gnss_assistance_fd = g_file_open_tmp(NULL, NULL, &error);
|
manager->gnss_assistance_fd = g_file_open_tmp(NULL, NULL, &error);
|
||||||
if (error != NULL)
|
if (error != NULL)
|
||||||
g_error ("Unable to create temporary file: %s", error->message);
|
g_error("Unable to create temporary file: %s", error->message);
|
||||||
|
|
||||||
/* Initialize state and schedule upload */
|
/* Initialize state and schedule upload */
|
||||||
manager->gnss_assistance_step = EG25_GNSS_STEP_LAST;
|
manager->gnss_assistance_step = EG25_GNSS_STEP_LAST;
|
||||||
g_timeout_add_seconds(RESCHEDULE_IN_SECS,
|
g_timeout_add_seconds(RESCHEDULE_IN_SECS, G_SOURCE_FUNC(gnss_upload_assistance_data), manager);
|
||||||
G_SOURCE_FUNC(gnss_upload_assistance_data), manager);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void gnss_destroy(struct EG25Manager *manager)
|
void gnss_destroy(struct EG25Manager *manager)
|
||||||
@@ -113,8 +111,8 @@ void gnss_destroy(struct EG25Manager *manager)
|
|||||||
static void disable_mm_gnss(struct EG25Manager *manager)
|
static void disable_mm_gnss(struct EG25Manager *manager)
|
||||||
{
|
{
|
||||||
MMModemLocationSource sources;
|
MMModemLocationSource sources;
|
||||||
gboolean signals_location;
|
gboolean signals_location;
|
||||||
g_autoptr (GError) error = NULL;
|
g_autoptr(GError) error = NULL;
|
||||||
|
|
||||||
sources = mm_modem_location_get_enabled(manager->mm_location);
|
sources = mm_modem_location_get_enabled(manager->mm_location);
|
||||||
signals_location = mm_modem_location_signals_location(manager->mm_location);
|
signals_location = mm_modem_location_signals_location(manager->mm_location);
|
||||||
@@ -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_RAW;
|
||||||
sources &= ~MM_MODEM_LOCATION_SOURCE_GPS_NMEA;
|
sources &= ~MM_MODEM_LOCATION_SOURCE_GPS_NMEA;
|
||||||
sources &= ~MM_MODEM_LOCATION_SOURCE_GPS_UNMANAGED;
|
sources &= ~MM_MODEM_LOCATION_SOURCE_GPS_UNMANAGED;
|
||||||
mm_modem_location_setup_sync(manager->mm_location, sources,
|
mm_modem_location_setup_sync(manager->mm_location, sources, signals_location, NULL, &error);
|
||||||
signals_location, NULL, &error);
|
|
||||||
if (error != NULL) {
|
if (error != NULL) {
|
||||||
g_warning("Unable to disable GNSS engine through ModemManager: %s",
|
g_warning("Unable to disable GNSS engine through ModemManager: %s", error->message);
|
||||||
error->message);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
static void disable_at_gnss_cb(struct EG25Manager *manager,
|
static void disable_at_gnss_cb(struct EG25Manager *manager, const char *response)
|
||||||
const char *response)
|
|
||||||
{
|
{
|
||||||
/* Clear QGPSEND AT command and process next */
|
/* Clear QGPSEND AT command and process next */
|
||||||
at_next_command(manager);
|
at_next_command(manager);
|
||||||
@@ -199,27 +194,25 @@ static void state_at_gnss(struct EG25Manager *manager)
|
|||||||
|
|
||||||
static void fetch_assistance_data(struct EG25Manager *manager)
|
static void fetch_assistance_data(struct EG25Manager *manager)
|
||||||
{
|
{
|
||||||
CURLcode response;
|
CURLcode response;
|
||||||
curl_off_t downloaded;
|
curl_off_t downloaded;
|
||||||
CURL *curl = NULL;
|
CURL *curl = NULL;
|
||||||
g_autofree gchar *url = NULL;
|
g_autofree gchar *url = NULL;
|
||||||
FILE *tmp_file = NULL;
|
FILE *tmp_file = NULL;
|
||||||
gchar errbuf[CURL_ERROR_SIZE];
|
gchar errbuf[CURL_ERROR_SIZE];
|
||||||
errbuf[0] = 0;
|
errbuf[0] = 0;
|
||||||
|
|
||||||
/* Fetch assistance data with curl */
|
/* Fetch assistance data with curl */
|
||||||
tmp_file = fdopen(manager->gnss_assistance_fd, "wb+");
|
tmp_file = fdopen(manager->gnss_assistance_fd, "wb+");
|
||||||
if (tmp_file == NULL) {
|
if (tmp_file == NULL) {
|
||||||
g_critical("Unable to open file to save assistance data: %s",
|
g_critical("Unable to open file to save assistance data: %s", g_strerror(errno));
|
||||||
g_strerror(errno));
|
|
||||||
goto bail;
|
goto bail;
|
||||||
}
|
}
|
||||||
|
|
||||||
lseek(manager->gnss_assistance_fd, 0, SEEK_SET);
|
lseek(manager->gnss_assistance_fd, 0, SEEK_SET);
|
||||||
if (ftruncate(manager->gnss_assistance_fd, 0) < 0)
|
if (ftruncate(manager->gnss_assistance_fd, 0) < 0)
|
||||||
g_warning("Unable to truncate file, assistance data might be invalid!");
|
g_warning("Unable to truncate file, assistance data might be invalid!");
|
||||||
url = g_strconcat(manager->gnss_assistance_url, "/",
|
url = g_strconcat(manager->gnss_assistance_url, "/", manager->gnss_assistance_file, NULL);
|
||||||
manager->gnss_assistance_file, NULL);
|
|
||||||
|
|
||||||
curl = curl_easy_init();
|
curl = curl_easy_init();
|
||||||
if (!curl) {
|
if (!curl) {
|
||||||
@@ -237,7 +230,8 @@ static void fetch_assistance_data(struct EG25Manager *manager)
|
|||||||
response = curl_easy_perform(curl);
|
response = curl_easy_perform(curl);
|
||||||
if (response != CURLE_OK) {
|
if (response != CURLE_OK) {
|
||||||
g_warning("Unable to fetch GNSS assistance data from %s: %s",
|
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;
|
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);
|
g_message("Fetching GNSS assistance data from %s was successful", url);
|
||||||
|
|
||||||
fflush(tmp_file);
|
|
||||||
curl_easy_cleanup(curl);
|
|
||||||
|
|
||||||
/* Go to the next step */
|
/* Go to the next step */
|
||||||
manager->gnss_assistance_step++;
|
manager->gnss_assistance_step++;
|
||||||
gnss_step(manager);
|
gnss_step(manager);
|
||||||
return;
|
|
||||||
|
goto cleanup;
|
||||||
|
|
||||||
bail:
|
bail:
|
||||||
|
manager->gnss_assistance_step = EG25_GNSS_STEP_LAST;
|
||||||
|
cleanup:
|
||||||
|
fflush(tmp_file);
|
||||||
|
fclose(tmp_file);
|
||||||
if (curl != NULL)
|
if (curl != NULL)
|
||||||
curl_easy_cleanup(curl);
|
curl_easy_cleanup(curl);
|
||||||
manager->gnss_assistance_step = EG25_GNSS_STEP_LAST;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/******************************************************************************/
|
/******************************************************************************/
|
||||||
|
|
||||||
static void init_assistance_data_upload_ready(struct EG25Manager *manager,
|
static void init_assistance_data_upload_ready(struct EG25Manager *manager, const char *response)
|
||||||
const char *response)
|
|
||||||
{
|
{
|
||||||
/* Search for 'CONNECT' in response to start upload */
|
/* Search for 'CONNECT' in response to start upload */
|
||||||
if (strstr(response, "CONNECT")) {
|
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,
|
static void init_assistance_data_upload_start(struct EG25Manager *manager, const char *response)
|
||||||
const char *response)
|
|
||||||
{
|
{
|
||||||
gchar value[BUFFER_SIZE];
|
gchar value[BUFFER_SIZE];
|
||||||
off_t 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);
|
lseek(manager->gnss_assistance_fd, 0, SEEK_SET);
|
||||||
|
|
||||||
/* Start upload */
|
/* Start upload */
|
||||||
g_snprintf(value, BUFFER_SIZE, "\"RAM:%s\",%ld,%d",
|
g_snprintf(value,
|
||||||
manager->gnss_assistance_file, size, UPLOAD_TIMEOUT_S);
|
BUFFER_SIZE,
|
||||||
|
"\"RAM:%s\",%ld,%d",
|
||||||
|
manager->gnss_assistance_file,
|
||||||
|
size,
|
||||||
|
UPLOAD_TIMEOUT_S);
|
||||||
g_message("Initiate GNSS assistance data upload: %s", value);
|
g_message("Initiate GNSS assistance data upload: %s", value);
|
||||||
at_append_command(manager, "QFUPL", NULL, value, NULL,
|
at_append_command(manager, "QFUPL", NULL, value, NULL, init_assistance_data_upload_ready);
|
||||||
init_assistance_data_upload_ready);
|
|
||||||
at_send_command(manager);
|
at_send_command(manager);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -319,16 +315,15 @@ static void init_assistance_data_upload(struct EG25Manager *manager)
|
|||||||
* Delete all previous GNSS assistance data files in RAM
|
* Delete all previous GNSS assistance data files in RAM
|
||||||
* and start uploading the latest one to RAM.
|
* and start uploading the latest one to RAM.
|
||||||
*/
|
*/
|
||||||
at_append_command(manager, "QFDEL", NULL, "\"RAM:*\"\r\n",
|
at_append_command(manager, "QFDEL", NULL, "\"RAM:*\"\r\n", NULL, init_assistance_data_upload_start);
|
||||||
NULL, init_assistance_data_upload_start);
|
|
||||||
at_send_command(manager);
|
at_send_command(manager);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void upload_assistance_data(struct EG25Manager *manager)
|
static void upload_assistance_data(struct EG25Manager *manager)
|
||||||
{
|
{
|
||||||
gint error;
|
gint error;
|
||||||
glong written_total = 0;
|
off_t written_total = 0;
|
||||||
gint ret;
|
gint ret;
|
||||||
struct stat sb;
|
struct stat sb;
|
||||||
|
|
||||||
if (fstat(manager->gnss_assistance_fd, &sb) != 0) {
|
if (fstat(manager->gnss_assistance_fd, &sb) != 0) {
|
||||||
@@ -357,8 +352,7 @@ static void upload_assistance_data(struct EG25Manager *manager)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void finish_assistance_data_upload_cb(struct EG25Manager *manager,
|
static void finish_assistance_data_upload_cb(struct EG25Manager *manager, const char *response)
|
||||||
const char *response)
|
|
||||||
{
|
{
|
||||||
/* Process response */
|
/* Process response */
|
||||||
at_process_result(manager, response);
|
at_process_result(manager, response);
|
||||||
@@ -371,23 +365,20 @@ static void finish_assistance_data_upload_cb(struct EG25Manager *manager,
|
|||||||
|
|
||||||
static void finish_assistance_data_upload(struct EG25Manager *manager)
|
static void finish_assistance_data_upload(struct EG25Manager *manager)
|
||||||
{
|
{
|
||||||
gchar value[BUFFER_SIZE];
|
gchar value[BUFFER_SIZE];
|
||||||
GDateTime *datetime;
|
GDateTime *datetime;
|
||||||
gchar *timestring;
|
gchar *timestring;
|
||||||
|
|
||||||
/* Configure GNSS assistance clock to current system time (UTC) */
|
/* Configure GNSS assistance clock to current system time (UTC) */
|
||||||
datetime = g_date_time_new_now_utc();
|
datetime = g_date_time_new_now_utc();
|
||||||
timestring = g_date_time_format(datetime, "0,\"%Y/%m/%d,%H:%M:%S\"");
|
timestring = g_date_time_format(datetime, "0,\"%Y/%m/%d,%H:%M:%S\"");
|
||||||
g_message("Setting GNSS assistance UTC clock to: %s", timestring);
|
g_message("Setting GNSS assistance UTC clock to: %s", timestring);
|
||||||
at_append_command(manager, "QGPSXTRATIME", NULL, timestring, NULL,
|
at_append_command(manager, "QGPSXTRATIME", NULL, timestring, NULL, at_process_result);
|
||||||
at_process_result);
|
|
||||||
|
|
||||||
/* Configure GNSS engine to use uploaded GNSS assistance data */
|
/* Configure GNSS engine to use uploaded GNSS assistance data */
|
||||||
g_snprintf(value, BUFFER_SIZE, "\"RAM:%s\"",
|
g_snprintf(value, BUFFER_SIZE, "\"RAM:%s\"", manager->gnss_assistance_file);
|
||||||
manager->gnss_assistance_file);
|
|
||||||
g_message("Setting GNSS assistance file to: %s", value);
|
g_message("Setting GNSS assistance file to: %s", value);
|
||||||
at_append_command(manager, "QGPSXTRADATA", NULL, value, NULL,
|
at_append_command(manager, "QGPSXTRADATA", NULL, value, NULL, finish_assistance_data_upload_cb);
|
||||||
finish_assistance_data_upload_cb);
|
|
||||||
at_send_command(manager);
|
at_send_command(manager);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -396,7 +387,7 @@ static void finish_assistance_data_upload(struct EG25Manager *manager)
|
|||||||
#ifdef HAVE_MMGLIB
|
#ifdef HAVE_MMGLIB
|
||||||
static void enable_mm_gnss(struct EG25Manager *manager)
|
static void enable_mm_gnss(struct EG25Manager *manager)
|
||||||
{
|
{
|
||||||
g_autoptr (GError) error = NULL;
|
g_autoptr(GError) error = NULL;
|
||||||
MMModemLocationSource sources = mm_modem_location_get_enabled(manager->mm_location);
|
MMModemLocationSource sources = mm_modem_location_get_enabled(manager->mm_location);
|
||||||
gboolean signal_location = mm_modem_location_signals_location(manager->mm_location);
|
gboolean signal_location = mm_modem_location_signals_location(manager->mm_location);
|
||||||
|
|
||||||
@@ -407,11 +398,9 @@ static void enable_mm_gnss(struct EG25Manager *manager)
|
|||||||
if (manager->gnss_sources & EG25_GNSS_SOURCE_RAW)
|
if (manager->gnss_sources & EG25_GNSS_SOURCE_RAW)
|
||||||
sources |= MM_MODEM_LOCATION_SOURCE_GPS_RAW;
|
sources |= MM_MODEM_LOCATION_SOURCE_GPS_RAW;
|
||||||
|
|
||||||
mm_modem_location_setup_sync(manager->mm_location, sources,
|
mm_modem_location_setup_sync(manager->mm_location, sources, signal_location, NULL, &error);
|
||||||
signal_location, NULL, &error);
|
|
||||||
if (error != NULL)
|
if (error != NULL)
|
||||||
g_warning("Unable to enable GNSS engine through ModemManager: %s",
|
g_warning("Unable to enable GNSS engine through ModemManager: %s", error->message);
|
||||||
error->message);
|
|
||||||
}
|
}
|
||||||
#endif
|
#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)
|
static void enable_at_gnss(struct EG25Manager *manager)
|
||||||
{
|
{
|
||||||
if (manager->gnss_sources & EG25_GNSS_SOURCE_QGPS) {
|
if (manager->gnss_sources & EG25_GNSS_SOURCE_QGPS) {
|
||||||
at_append_command(manager, "QGPS", NULL, "1", NULL,
|
at_append_command(manager, "QGPS", NULL, "1", NULL, enable_at_gnss_cb);
|
||||||
enable_at_gnss_cb);
|
|
||||||
at_send_command(manager);
|
at_send_command(manager);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -437,7 +425,7 @@ static void enable_at_gnss(struct EG25Manager *manager)
|
|||||||
|
|
||||||
void gnss_step(struct EG25Manager *manager)
|
void gnss_step(struct EG25Manager *manager)
|
||||||
{
|
{
|
||||||
switch(manager->gnss_assistance_step) {
|
switch (manager->gnss_assistance_step) {
|
||||||
case EG25_GNSS_STEP_FIRST:
|
case EG25_GNSS_STEP_FIRST:
|
||||||
manager->gnss_assistance_step++;
|
manager->gnss_assistance_step++;
|
||||||
g_message("GNSS assistance upload started...");
|
g_message("GNSS assistance upload started...");
|
||||||
@@ -446,7 +434,8 @@ void gnss_step(struct EG25Manager *manager)
|
|||||||
case EG25_GNSS_STEP_FETCH_ASSISTANCE_DATA:
|
case EG25_GNSS_STEP_FETCH_ASSISTANCE_DATA:
|
||||||
g_message("GNSS assistance upload step (%d/%d): "
|
g_message("GNSS assistance upload step (%d/%d): "
|
||||||
"fetching assistance data",
|
"fetching assistance data",
|
||||||
manager->gnss_assistance_step, EG25_GNSS_STEP_LAST);
|
manager->gnss_assistance_step,
|
||||||
|
EG25_GNSS_STEP_LAST);
|
||||||
fetch_assistance_data(manager);
|
fetch_assistance_data(manager);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
@@ -455,7 +444,8 @@ void gnss_step(struct EG25Manager *manager)
|
|||||||
if (manager->modem_iface == MODEM_IFACE_MODEMMANAGER) {
|
if (manager->modem_iface == MODEM_IFACE_MODEMMANAGER) {
|
||||||
g_message("GNSS assistance upload step (%d/%d): "
|
g_message("GNSS assistance upload step (%d/%d): "
|
||||||
"disabling GNSS engine through ModemManager",
|
"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);
|
disable_mm_gnss(manager);
|
||||||
}
|
}
|
||||||
manager->gnss_assistance_step++;
|
manager->gnss_assistance_step++;
|
||||||
@@ -465,26 +455,30 @@ void gnss_step(struct EG25Manager *manager)
|
|||||||
case EG25_GNSS_STEP_AT_GNSS_DISABLE:
|
case EG25_GNSS_STEP_AT_GNSS_DISABLE:
|
||||||
g_message("GNSS assistance upload step (%d/%d): "
|
g_message("GNSS assistance upload step (%d/%d): "
|
||||||
"disabling GNSS engine through AT+QGPS",
|
"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);
|
state_at_gnss(manager);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case EG25_GNSS_STEP_INIT_UPLOAD:
|
case EG25_GNSS_STEP_INIT_UPLOAD:
|
||||||
g_message("GNSS assistance upload step (%d/%d): initiating 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);
|
init_assistance_data_upload(manager);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case EG25_GNSS_STEP_UPLOAD:
|
case EG25_GNSS_STEP_UPLOAD:
|
||||||
g_message("GNSS assistance upload step (%d/%d): "
|
g_message("GNSS assistance upload step (%d/%d): "
|
||||||
"uploading assistance data",
|
"uploading assistance data",
|
||||||
manager->gnss_assistance_step, EG25_GNSS_STEP_LAST);
|
manager->gnss_assistance_step,
|
||||||
|
EG25_GNSS_STEP_LAST);
|
||||||
upload_assistance_data(manager);
|
upload_assistance_data(manager);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case EG25_GNSS_STEP_FINISH_UPLOAD:
|
case EG25_GNSS_STEP_FINISH_UPLOAD:
|
||||||
g_message("GNSS assistance upload step (%d/%d): finishing 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);
|
finish_assistance_data_upload(manager);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
@@ -493,7 +487,8 @@ void gnss_step(struct EG25Manager *manager)
|
|||||||
if (manager->modem_iface == MODEM_IFACE_MODEMMANAGER) {
|
if (manager->modem_iface == MODEM_IFACE_MODEMMANAGER) {
|
||||||
g_message("GNSS assistance upload step (%d/%d): "
|
g_message("GNSS assistance upload step (%d/%d): "
|
||||||
"re-enabling GNSS through ModemManager",
|
"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);
|
enable_mm_gnss(manager);
|
||||||
}
|
}
|
||||||
manager->gnss_assistance_step++;
|
manager->gnss_assistance_step++;
|
||||||
@@ -503,13 +498,15 @@ void gnss_step(struct EG25Manager *manager)
|
|||||||
case EG25_GNSS_STEP_AT_QGPS_ENABLE:
|
case EG25_GNSS_STEP_AT_QGPS_ENABLE:
|
||||||
g_message("GNSS assistance upload step (%d/%d): "
|
g_message("GNSS assistance upload step (%d/%d): "
|
||||||
"re-enabling GNSS through AT+QGPS",
|
"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);
|
enable_at_gnss(manager);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case EG25_GNSS_STEP_LAST:
|
case EG25_GNSS_STEP_LAST:
|
||||||
g_message("GNSS assistance upload step (%d/%d): finished",
|
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;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -6,12 +6,12 @@
|
|||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include <curl/curl.h>
|
||||||
#include <time.h>
|
#include <time.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
#include <curl/curl.h>
|
|
||||||
|
|
||||||
#include "manager.h"
|
#include "manager.h"
|
||||||
|
|
||||||
void gnss_init(struct EG25Manager *manager, toml_table_t *config[]);
|
void gnss_init(struct EG25Manager *manager, toml_table_t *config[]);
|
||||||
void gnss_destroy(struct EG25Manager *manager);
|
void gnss_destroy(struct EG25Manager *manager);
|
||||||
gboolean gnss_upload_assistance_data(struct EG25Manager *manager);
|
gboolean gnss_upload_assistance_data(struct EG25Manager *manager);
|
||||||
|
311
src/gpio.c
311
src/gpio.c
@@ -4,14 +4,16 @@
|
|||||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "config.h"
|
|
||||||
#include "gpio.h"
|
#include "gpio.h"
|
||||||
|
#include "config.h"
|
||||||
|
|
||||||
|
#include <dirent.h>
|
||||||
|
#include <sys/stat.h>
|
||||||
#include <unistd.h>
|
#include <unistd.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"
|
||||||
#define GPIO_CHIP2_LABEL "1f02c00.pinctrl"
|
#define GPIO_CHIP2_LABEL "1f02c00.pinctrl"
|
||||||
#define MAX_GPIOCHIP_LINES 352
|
#define MAX_GPIOCHIP_LINES 352
|
||||||
|
|
||||||
enum {
|
enum {
|
||||||
@@ -40,21 +42,78 @@ 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);
|
/*
|
||||||
sleep(1);
|
* Force the modem to poweroff using the RESET_N pin before attempting to
|
||||||
gpiod_line_set_value(manager->gpio_out[GPIO_OUT_PWRKEY], 0);
|
* 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;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
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_PWRKEY, GPIOD_LINE_VALUE_ACTIVE);
|
||||||
gpio_sequence_poweron(manager);
|
sleep(1);
|
||||||
|
gpio_line_set_value(manager, GPIO_OUT_PWRKEY, GPIOD_LINE_VALUE_INACTIVE);
|
||||||
|
|
||||||
g_message("Executed power-off sequence");
|
g_message("Executed power-off sequence");
|
||||||
|
|
||||||
@@ -63,7 +122,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 +131,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 +140,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,47 +154,163 @@ 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 *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);
|
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_is_gpiochip_device(path))
|
||||||
gpiod_line_release(gpio_line);
|
ret = 1;
|
||||||
return NULL;
|
|
||||||
|
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 = 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);
|
||||||
|
|
||||||
|
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[])
|
||||||
{
|
{
|
||||||
int i;
|
int i;
|
||||||
toml_table_t *gpio_config[EG25_CONFIG_COUNT];
|
toml_table_t *gpio_config[EG25_CONFIG_COUNT];
|
||||||
|
|
||||||
for (i = 0; i < EG25_CONFIG_COUNT; i++)
|
for (i = 0; i < EG25_CONFIG_COUNT; i++)
|
||||||
@@ -144,7 +319,7 @@ int gpio_init(struct EG25Manager *manager, toml_table_t *config[])
|
|||||||
if (!gpio_config[EG25_CONFIG_SYS])
|
if (!gpio_config[EG25_CONFIG_SYS])
|
||||||
g_error("Default config file lacks the 'gpio' section!");
|
g_error("Default config file lacks the 'gpio' section!");
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* The system config could have the `chips` key, but the user one
|
* The system config could have the `chips` key, but the user one
|
||||||
* could still use the old format! In order to avoid problems, we
|
* could still use the old format! In order to avoid problems, we
|
||||||
* should use the new format only if:
|
* should use the new format only if:
|
||||||
@@ -155,9 +330,8 @@ int gpio_init(struct EG25Manager *manager, toml_table_t *config[])
|
|||||||
* format, but error out if user config overrides gpios using the
|
* format, but error out if user config overrides gpios using the
|
||||||
* old format
|
* 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;
|
||||||
int numchips;
|
|
||||||
toml_array_t *chipslist = NULL;
|
toml_array_t *chipslist = NULL;
|
||||||
|
|
||||||
config_get_array(gpio_config, "chips", &chipslist);
|
config_get_array(gpio_config, "chips", &chipslist);
|
||||||
@@ -169,14 +343,14 @@ 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);
|
||||||
}
|
}
|
||||||
|
|
||||||
for (i = 0; i < GPIO_OUT_COUNT; i++) {
|
for (i = 0; i < GPIO_OUT_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_out_names[i], &table))
|
if (!config_get_table(gpio_config, gpio_out_names[i], &table))
|
||||||
g_error("Unable to get config for output GPIO '%s'", gpio_out_names[i]);
|
g_error("Unable to get config for output GPIO '%s'", gpio_out_names[i]);
|
||||||
|
|
||||||
@@ -185,29 +359,40 @@ 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);
|
||||||
}
|
}
|
||||||
|
|
||||||
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 +400,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 +420,10 @@ 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 +440,10 @@ 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);
|
||||||
}
|
}
|
||||||
@@ -261,16 +452,18 @@ int gpio_init(struct EG25Manager *manager, toml_table_t *config[])
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
gboolean gpio_check_poweroff(struct EG25Manager *manager, gboolean keep_down)
|
void gpio_force_off(struct EG25Manager *manager)
|
||||||
|
{
|
||||||
|
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] &&
|
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]) {
|
|
||||||
// Asserting RESET line to prevent modem from rebooting
|
|
||||||
gpiod_line_set_value(manager->gpio_out[GPIO_OUT_RESET], 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
return TRUE;
|
return TRUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -283,12 +476,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])
|
||||||
|
@@ -8,7 +8,7 @@
|
|||||||
|
|
||||||
#include "manager.h"
|
#include "manager.h"
|
||||||
|
|
||||||
int gpio_init(struct EG25Manager *state, toml_table_t *config[]);
|
int gpio_init(struct EG25Manager *state, toml_table_t *config[]);
|
||||||
void gpio_destroy(struct EG25Manager *state);
|
void gpio_destroy(struct EG25Manager *state);
|
||||||
|
|
||||||
int gpio_sequence_poweron(struct EG25Manager *state);
|
int gpio_sequence_poweron(struct EG25Manager *state);
|
||||||
@@ -18,4 +18,5 @@ int gpio_sequence_resume(struct EG25Manager *state);
|
|||||||
int gpio_sequence_wake(struct EG25Manager *state);
|
int gpio_sequence_wake(struct EG25Manager *state);
|
||||||
int gpio_sequence_sleep(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);
|
||||||
|
142
src/manager.c
142
src/manager.c
@@ -4,19 +4,19 @@
|
|||||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#include "manager.h"
|
||||||
#include "at.h"
|
#include "at.h"
|
||||||
#include "config.h"
|
#include "config.h"
|
||||||
#include "gpio.h"
|
#include "gpio.h"
|
||||||
#include "manager.h"
|
|
||||||
|
|
||||||
#ifdef HAVE_MMGLIB
|
#ifdef HAVE_MMGLIB
|
||||||
#include "mm-iface.h"
|
#include "mm-iface.h"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#include "gnss.h"
|
||||||
#include "ofono-iface.h"
|
#include "ofono-iface.h"
|
||||||
#include "suspend.h"
|
#include "suspend.h"
|
||||||
#include "udev.h"
|
#include "udev.h"
|
||||||
#include "gnss.h"
|
|
||||||
|
|
||||||
#include <fcntl.h>
|
#include <fcntl.h>
|
||||||
#include <signal.h>
|
#include <signal.h>
|
||||||
@@ -38,7 +38,10 @@
|
|||||||
#define EG25_VERSION "0.0.0"
|
#define EG25_VERSION "0.0.0"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#define POWERON_DELAY_US 100000UL
|
#define EG25_DEFAULT_VENDOR_ID 0x2c7c
|
||||||
|
#define EG25_DEFAULT_PRODUCT_ID 0x0125
|
||||||
|
|
||||||
|
#define POWERON_DELAY_US 100000UL
|
||||||
|
|
||||||
static gboolean quit_app(struct EG25Manager *manager)
|
static gboolean quit_app(struct EG25Manager *manager)
|
||||||
{
|
{
|
||||||
@@ -59,13 +62,17 @@ static gboolean quit_app(struct EG25Manager *manager)
|
|||||||
gpio_sequence_shutdown(manager);
|
gpio_sequence_shutdown(manager);
|
||||||
manager->modem_state = EG25_STATE_FINISHING;
|
manager->modem_state = EG25_STATE_FINISHING;
|
||||||
for (i = 0; i < 30; i++) {
|
for (i = 0; i < 30; i++) {
|
||||||
if (gpio_check_poweroff(manager, TRUE))
|
if (gpio_check_poweroff(manager)) {
|
||||||
|
g_message("Modem successfully powered down");
|
||||||
break;
|
break;
|
||||||
|
}
|
||||||
sleep(1);
|
sleep(1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
g_message("Modem down, quitting...");
|
|
||||||
|
|
||||||
|
gpio_force_off(manager);
|
||||||
|
|
||||||
|
g_message("Modem down, quitting...");
|
||||||
g_main_loop_quit(manager->loop);
|
g_main_loop_quit(manager->loop);
|
||||||
|
|
||||||
return FALSE;
|
return FALSE;
|
||||||
@@ -73,10 +80,10 @@ static gboolean quit_app(struct EG25Manager *manager)
|
|||||||
|
|
||||||
static gboolean modem_start(struct EG25Manager *manager)
|
static gboolean modem_start(struct EG25Manager *manager)
|
||||||
{
|
{
|
||||||
ssize_t i, count;
|
ssize_t i, count;
|
||||||
gboolean should_boot = TRUE;
|
gboolean should_boot = TRUE;
|
||||||
libusb_context *ctx = NULL;
|
libusb_context *ctx = NULL;
|
||||||
libusb_device **devices = NULL;
|
libusb_device **devices = NULL;
|
||||||
struct libusb_device_descriptor desc;
|
struct libusb_device_descriptor desc;
|
||||||
|
|
||||||
if (manager->use_libusb) {
|
if (manager->use_libusb) {
|
||||||
@@ -95,7 +102,7 @@ static gboolean modem_start(struct EG25Manager *manager)
|
|||||||
|
|
||||||
libusb_free_device_list(devices, 1);
|
libusb_free_device_list(devices, 1);
|
||||||
libusb_exit(ctx);
|
libusb_exit(ctx);
|
||||||
} else if (!gpio_check_poweroff(manager, FALSE)) {
|
} else if (!gpio_check_poweroff(manager)) {
|
||||||
g_message("STATUS is low, modem already powered");
|
g_message("STATUS is low, modem already powered");
|
||||||
should_boot = FALSE;
|
should_boot = FALSE;
|
||||||
}
|
}
|
||||||
@@ -138,10 +145,40 @@ void modem_configure(struct EG25Manager *manager)
|
|||||||
at_sequence_configure(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->modem_state = EG25_STATE_RESUMING;
|
||||||
manager->reset_timer = 0;
|
manager->complete_reset_timer = 0;
|
||||||
return FALSE;
|
return FALSE;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -149,7 +186,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 +212,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;
|
||||||
@@ -185,24 +228,26 @@ gboolean modem_reset(struct EG25Manager *manager)
|
|||||||
g_warning("Unable to open /sys/bus/usb/drivers/usb/unbind");
|
g_warning("Unable to open /sys/bus/usb/drivers/usb/unbind");
|
||||||
goto error;
|
goto error;
|
||||||
}
|
}
|
||||||
|
|
||||||
ret = write(fd, manager->modem_usb_id, len);
|
ret = write(fd, manager->modem_usb_id, len);
|
||||||
|
close(fd);
|
||||||
if (ret < len) {
|
if (ret < len) {
|
||||||
g_warning("Couldn't unbind modem: wrote %d/%d bytes", ret, len);
|
g_warning("Couldn't unbind modem: wrote %d/%d bytes", ret, len);
|
||||||
goto error;
|
goto error;
|
||||||
}
|
}
|
||||||
close(fd);
|
|
||||||
|
|
||||||
fd = open("/sys/bus/usb/drivers/usb/bind", O_WRONLY);
|
fd = open("/sys/bus/usb/drivers/usb/bind", O_WRONLY);
|
||||||
if (fd < 0) {
|
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;
|
goto error;
|
||||||
}
|
}
|
||||||
|
|
||||||
ret = write(fd, manager->modem_usb_id, len);
|
ret = write(fd, manager->modem_usb_id, len);
|
||||||
|
close(fd);
|
||||||
if (ret < len) {
|
if (ret < len) {
|
||||||
g_warning("Couldn't bind modem: wrote %d/%d bytes", ret, len);
|
g_warning("Couldn't bind modem: wrote %d/%d bytes", ret, len);
|
||||||
goto error;
|
goto error;
|
||||||
}
|
}
|
||||||
close(fd);
|
|
||||||
|
|
||||||
g_message("Successfully reset modem's USB connection");
|
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
|
* 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_rebind_done), manager);
|
||||||
|
|
||||||
return G_SOURCE_REMOVE;
|
return G_SOURCE_REMOVE;
|
||||||
|
|
||||||
@@ -228,7 +273,9 @@ 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(45,
|
||||||
|
G_SOURCE_FUNC(modem_at_reset_done),
|
||||||
|
manager);
|
||||||
|
|
||||||
return G_SOURCE_REMOVE;
|
return G_SOURCE_REMOVE;
|
||||||
}
|
}
|
||||||
@@ -258,15 +305,15 @@ void modem_resume_post(struct EG25Manager *manager)
|
|||||||
static toml_table_t *parse_config_file(char *config_file, gboolean force_default)
|
static toml_table_t *parse_config_file(char *config_file, gboolean force_default)
|
||||||
{
|
{
|
||||||
toml_table_t *toml_config;
|
toml_table_t *toml_config;
|
||||||
gchar *compatible;
|
gchar *compatible;
|
||||||
gchar error[256];
|
gchar error[256];
|
||||||
gsize len;
|
gsize len;
|
||||||
FILE *f = NULL;
|
FILE *f = NULL;
|
||||||
|
|
||||||
if (config_file) {
|
if (config_file) {
|
||||||
f = fopen(config_file, "r");
|
f = fopen(config_file, "r");
|
||||||
} else if (g_file_get_contents("/proc/device-tree/compatible", &compatible, &len, NULL)) {
|
} else if (g_file_get_contents("/proc/device-tree/compatible", &compatible, &len, NULL)) {
|
||||||
g_autoptr (GPtrArray) compat = g_ptr_array_new();
|
g_autoptr(GPtrArray) compat = g_ptr_array_new();
|
||||||
gsize pos = 0;
|
gsize pos = 0;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -281,9 +328,11 @@ static toml_table_t *parse_config_file(char *config_file, gboolean force_default
|
|||||||
for (pos = 0; pos < compat->len; pos++) {
|
for (pos = 0; pos < compat->len; pos++) {
|
||||||
g_autofree gchar *filename = NULL;
|
g_autofree gchar *filename = NULL;
|
||||||
if (force_default)
|
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
|
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) {
|
if (access(filename, F_OK) == 0) {
|
||||||
g_message("Opening config file: %s", filename);
|
g_message("Opening config file: %s", filename);
|
||||||
@@ -312,14 +361,15 @@ int main(int argc, char *argv[])
|
|||||||
g_autoptr(GOptionContext) opt_context = NULL;
|
g_autoptr(GOptionContext) opt_context = NULL;
|
||||||
g_autoptr(GError) err = NULL;
|
g_autoptr(GError) err = NULL;
|
||||||
struct EG25Manager manager;
|
struct EG25Manager manager;
|
||||||
gchar *config_file = NULL;
|
gchar *config_file = NULL;
|
||||||
gboolean show_version = FALSE;
|
gboolean show_version = FALSE;
|
||||||
toml_table_t *toml_config[EG25_CONFIG_COUNT];
|
gboolean monitor_udev = TRUE;
|
||||||
toml_table_t *manager_config[EG25_CONFIG_COUNT];
|
toml_table_t *toml_config[EG25_CONFIG_COUNT];
|
||||||
|
toml_table_t *manager_config[EG25_CONFIG_COUNT];
|
||||||
const GOptionEntry options[] = {
|
const GOptionEntry options[] = {
|
||||||
{ "config", 'c', 0, G_OPTION_ARG_STRING, &config_file, "Config file to use.", NULL },
|
{"config", 'c', 0, G_OPTION_ARG_STRING, &config_file, "Config file to use.", NULL},
|
||||||
{ "version", 'v', 0, G_OPTION_ARG_NONE, &show_version, "Display version information and exit.", NULL },
|
{"version", 'v', 0, G_OPTION_ARG_NONE, &show_version, "Display version information and exit.", NULL},
|
||||||
{ NULL, 0, 0, G_OPTION_ARG_NONE, NULL, NULL, NULL }
|
{NULL, 0, 0, G_OPTION_ARG_NONE, NULL, NULL, NULL}
|
||||||
};
|
};
|
||||||
|
|
||||||
memset(&manager, 0, sizeof(manager));
|
memset(&manager, 0, sizeof(manager));
|
||||||
@@ -328,10 +378,10 @@ int main(int argc, char *argv[])
|
|||||||
manager.suspend_delay_fd = -1;
|
manager.suspend_delay_fd = -1;
|
||||||
manager.suspend_block_fd = -1;
|
manager.suspend_block_fd = -1;
|
||||||
|
|
||||||
opt_context = g_option_context_new ("- Power management for the Quectel EG25 modem");
|
opt_context = g_option_context_new("- Power management for the Quectel EG25 modem");
|
||||||
g_option_context_add_main_entries (opt_context, options, NULL);
|
g_option_context_add_main_entries(opt_context, options, NULL);
|
||||||
if (!g_option_context_parse (opt_context, &argc, &argv, &err)) {
|
if (!g_option_context_parse(opt_context, &argc, &argv, &err)) {
|
||||||
g_warning ("%s", err->message);
|
g_warning("%s", err->message);
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -363,10 +413,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,7 +428,8 @@ 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);
|
||||||
udev_init(&manager, toml_config);
|
if (monitor_udev)
|
||||||
|
udev_init(&manager, toml_config);
|
||||||
gnss_init(&manager, toml_config);
|
gnss_init(&manager, toml_config);
|
||||||
|
|
||||||
for (int i = 0; i < EG25_CONFIG_COUNT; i++) {
|
for (int i = 0; i < EG25_CONFIG_COUNT; i++) {
|
||||||
|
@@ -41,18 +41,18 @@ typedef enum {
|
|||||||
EG25_GNSS_SOURCE_QGPS = 1 << 3,
|
EG25_GNSS_SOURCE_QGPS = 1 << 3,
|
||||||
} EG25GNSSSource;
|
} EG25GNSSSource;
|
||||||
|
|
||||||
|
|
||||||
enum EG25State {
|
enum EG25State {
|
||||||
EG25_STATE_INIT = 0,
|
EG25_STATE_INIT = 0,
|
||||||
EG25_STATE_POWERED, // Power-on sequence has been executed, but the modem isn't on yet
|
EG25_STATE_POWERED, // Power-on sequence has been executed, but the modem isn't on yet
|
||||||
EG25_STATE_STARTED, // Modem has been started and declared itdata ready
|
EG25_STATE_STARTED, // Modem has been started and declared itdata ready
|
||||||
EG25_STATE_ACQUIRED, // Modem has been probed by ModemManager
|
EG25_STATE_ACQUIRED, // Modem has been probed by ModemManager
|
||||||
EG25_STATE_CONFIGURED, // Modem has been configured through AT commands
|
EG25_STATE_CONFIGURED, // Modem has been configured through AT commands
|
||||||
EG25_STATE_REGISTERED, // Modem is unlocked and registered to a network provider
|
EG25_STATE_REGISTERED, // Modem is unlocked and registered to a network provider
|
||||||
EG25_STATE_CONNECTED, // Modem is connected (data connection active)
|
EG25_STATE_CONNECTED, // Modem is connected (data connection active)
|
||||||
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,41 +70,42 @@ enum EG25Config {
|
|||||||
|
|
||||||
struct EG25Manager {
|
struct EG25Manager {
|
||||||
GMainLoop *loop;
|
GMainLoop *loop;
|
||||||
guint reset_timer;
|
guint complete_reset_timer;
|
||||||
gboolean use_libusb;
|
guint schedule_reset_timer;
|
||||||
guint usb_vid;
|
gboolean use_libusb;
|
||||||
guint usb_pid;
|
guint usb_vid;
|
||||||
guint poweron_delay;
|
guint usb_pid;
|
||||||
|
guint poweron_delay;
|
||||||
|
|
||||||
int at_fd;
|
int at_fd;
|
||||||
guint at_source;
|
guint at_source;
|
||||||
GList *at_cmds;
|
GList *at_cmds;
|
||||||
void (*at_callback)(struct EG25Manager *manager, const char *response);
|
void (*at_callback)(struct EG25Manager *manager, const char *response);
|
||||||
|
|
||||||
enum EG25State modem_state;
|
enum EG25State modem_state;
|
||||||
gchar *modem_usb_id;
|
gchar *modem_usb_id;
|
||||||
|
|
||||||
gboolean gnss_assistance_enabled;
|
gboolean gnss_assistance_enabled;
|
||||||
EG25GNSSSource gnss_sources;
|
EG25GNSSSource gnss_sources;
|
||||||
EG25GNSSStep gnss_assistance_step;
|
EG25GNSSStep gnss_assistance_step;
|
||||||
gint gnss_assistance_fd;
|
gint gnss_assistance_fd;
|
||||||
gchar *gnss_assistance_url;
|
gchar *gnss_assistance_url;
|
||||||
gchar *gnss_assistance_file;
|
gchar *gnss_assistance_file;
|
||||||
|
|
||||||
enum ModemIface modem_iface;
|
enum ModemIface modem_iface;
|
||||||
guint mm_watch;
|
guint mm_watch;
|
||||||
#ifdef HAVE_MMGLIB
|
#ifdef HAVE_MMGLIB
|
||||||
MMManager *mm_manager;
|
MMManager *mm_manager;
|
||||||
MMModem *mm_modem;
|
MMModem *mm_modem;
|
||||||
MMModemLocation *mm_location;
|
MMModemLocation *mm_location;
|
||||||
#endif
|
#endif
|
||||||
guint ofono_watch;
|
guint ofono_watch;
|
||||||
GDBOManager *ofono_manager;
|
GDBOManager *ofono_manager;
|
||||||
GDBusConnection *ofono_connection;
|
GDBusConnection *ofono_connection;
|
||||||
|
|
||||||
GDBusProxy *suspend_proxy;
|
GDBusProxy *suspend_proxy;
|
||||||
int suspend_delay_fd;
|
int suspend_delay_fd;
|
||||||
int suspend_block_fd;
|
int suspend_block_fd;
|
||||||
|
|
||||||
guint modem_recovery_timer;
|
guint modem_recovery_timer;
|
||||||
guint modem_recovery_timeout;
|
guint modem_recovery_timeout;
|
||||||
@@ -113,17 +114,18 @@ 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);
|
||||||
gboolean modem_reset(struct EG25Manager *data);
|
gboolean modem_reset(struct EG25Manager *data);
|
||||||
void modem_suspend_pre(struct EG25Manager *data);
|
void modem_suspend_pre(struct EG25Manager *data);
|
||||||
void modem_suspend_post(struct EG25Manager *data);
|
void modem_suspend_post(struct EG25Manager *data);
|
||||||
void modem_resume_pre(struct EG25Manager *data);
|
void modem_resume_pre(struct EG25Manager *data);
|
||||||
void modem_resume_post(struct EG25Manager *data);
|
void modem_resume_post(struct EG25Manager *data);
|
||||||
#ifdef HAVE_MMGLIB
|
#ifdef HAVE_MMGLIB
|
||||||
void modem_update_state(struct EG25Manager *data, MMModemState state);
|
void modem_update_state(struct EG25Manager *data, MMModemState state);
|
||||||
#endif
|
#endif
|
||||||
|
@@ -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,
|
||||||
|
@@ -12,11 +12,11 @@
|
|||||||
|
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
|
||||||
static void state_changed_cb(MMModem *modem,
|
static void state_changed_cb(MMModem *modem,
|
||||||
MMModemState old,
|
MMModemState old,
|
||||||
MMModemState new,
|
MMModemState new,
|
||||||
MMModemStateChangeReason reason,
|
MMModemStateChangeReason reason,
|
||||||
struct EG25Manager *manager)
|
struct EG25Manager *manager)
|
||||||
{
|
{
|
||||||
if (manager->modem_state >= EG25_STATE_CONFIGURED)
|
if (manager->modem_state >= EG25_STATE_CONFIGURED)
|
||||||
modem_update_state(manager, new);
|
modem_update_state(manager, new);
|
||||||
@@ -24,13 +24,13 @@ static void state_changed_cb(MMModem *modem,
|
|||||||
|
|
||||||
static void add_modem(struct EG25Manager *manager, GDBusObject *object)
|
static void add_modem(struct EG25Manager *manager, GDBusObject *object)
|
||||||
{
|
{
|
||||||
const gchar *path;
|
const gchar *path;
|
||||||
MmGdbusModem *gdbus_modem;
|
MmGdbusModem *gdbus_modem;
|
||||||
|
|
||||||
path = g_dbus_object_get_object_path(object);
|
path = g_dbus_object_get_object_path(object);
|
||||||
g_message("Adding new modem `%s'", path);
|
g_message("Adding new modem `%s'", path);
|
||||||
|
|
||||||
g_assert(MM_IS_OBJECT (object));
|
g_assert(MM_IS_OBJECT(object));
|
||||||
manager->mm_modem = mm_object_get_modem(MM_OBJECT(object));
|
manager->mm_modem = mm_object_get_modem(MM_OBJECT(object));
|
||||||
g_assert_nonnull(manager->mm_modem);
|
g_assert_nonnull(manager->mm_modem);
|
||||||
|
|
||||||
@@ -70,15 +70,16 @@ static void add_modem_location(struct EG25Manager *manager, GDBusObject *object)
|
|||||||
g_assert_nonnull(manager->mm_location);
|
g_assert_nonnull(manager->mm_location);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void interface_added_cb (struct EG25Manager *manager,
|
static void interface_added_cb(struct EG25Manager *manager,
|
||||||
GDBusObject *object,
|
GDBusObject *object,
|
||||||
GDBusInterface *interface)
|
GDBusInterface *interface)
|
||||||
{
|
{
|
||||||
GDBusInterfaceInfo *info;
|
GDBusInterfaceInfo *info;
|
||||||
|
|
||||||
info = g_dbus_interface_get_info(interface);
|
info = g_dbus_interface_get_info(interface);
|
||||||
g_message("ModemManager interface `%s' found on object `%s'",
|
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)
|
if (g_strcmp0(info->name, MM_DBUS_INTERFACE_MODEM) == 0)
|
||||||
add_modem(manager, object);
|
add_modem(manager, object);
|
||||||
@@ -87,12 +88,11 @@ static void interface_added_cb (struct EG25Manager *manager,
|
|||||||
add_modem_location(manager, object);
|
add_modem_location(manager, object);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static void interface_removed_cb(struct EG25Manager *manager,
|
static void interface_removed_cb(struct EG25Manager *manager,
|
||||||
GDBusObject *object,
|
GDBusObject *object,
|
||||||
GDBusInterface *interface)
|
GDBusInterface *interface)
|
||||||
{
|
{
|
||||||
const gchar *path;
|
const gchar *path;
|
||||||
GDBusInterfaceInfo *info;
|
GDBusInterfaceInfo *info;
|
||||||
|
|
||||||
path = g_dbus_object_get_object_path(object);
|
path = g_dbus_object_get_object_path(object);
|
||||||
@@ -104,7 +104,6 @@ static void interface_removed_cb(struct EG25Manager *manager,
|
|||||||
manager->mm_modem = NULL;
|
manager->mm_modem = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static void add_mm_object(struct EG25Manager *manager, GDBusObject *object)
|
static void add_mm_object(struct EG25Manager *manager, GDBusObject *object)
|
||||||
{
|
{
|
||||||
GList *ifaces, *node;
|
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);
|
g_list_free_full(ifaces, g_object_unref);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static void add_mm_objects(struct EG25Manager *manager)
|
static void add_mm_objects(struct EG25Manager *manager)
|
||||||
{
|
{
|
||||||
GList *objects, *node;
|
GList *objects, *node;
|
||||||
@@ -128,14 +126,12 @@ static void add_mm_objects(struct EG25Manager *manager)
|
|||||||
g_list_free_full(objects, g_object_unref);
|
g_list_free_full(objects, g_object_unref);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static void object_added_cb(struct EG25Manager *manager, GDBusObject *object)
|
static void object_added_cb(struct EG25Manager *manager, GDBusObject *object)
|
||||||
{
|
{
|
||||||
g_message("ModemManager object `%s' added", g_dbus_object_get_object_path(object));
|
g_message("ModemManager object `%s' added", g_dbus_object_get_object_path(object));
|
||||||
add_mm_object(manager, object);
|
add_mm_object(manager, object);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static void object_removed_cb(struct EG25Manager *manager, GDBusObject *object)
|
static void object_removed_cb(struct EG25Manager *manager, GDBusObject *object)
|
||||||
{
|
{
|
||||||
const gchar *path;
|
const gchar *path;
|
||||||
@@ -146,25 +142,32 @@ static void object_removed_cb(struct EG25Manager *manager, GDBusObject *object)
|
|||||||
manager->mm_modem = NULL;
|
manager->mm_modem = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static void mm_manager_new_cb(GDBusConnection *connection,
|
static void mm_manager_new_cb(GDBusConnection *connection,
|
||||||
GAsyncResult *res,
|
GAsyncResult *res,
|
||||||
struct EG25Manager *manager)
|
struct EG25Manager *manager)
|
||||||
{
|
{
|
||||||
g_autoptr (GError) error = NULL;
|
g_autoptr(GError) error = NULL;
|
||||||
|
|
||||||
manager->mm_manager = mm_manager_new_finish(res, &error);
|
manager->mm_manager = mm_manager_new_finish(res, &error);
|
||||||
if (!manager->mm_manager)
|
if (!manager->mm_manager)
|
||||||
g_critical("Error creating ModemManager Manager: %s", error->message);
|
g_critical("Error creating ModemManager Manager: %s", error->message);
|
||||||
|
|
||||||
g_signal_connect_swapped(G_DBUS_OBJECT_MANAGER(manager->mm_manager),
|
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),
|
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),
|
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),
|
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);
|
add_mm_objects(manager);
|
||||||
}
|
}
|
||||||
@@ -177,13 +180,18 @@ static void mm_appeared_cb(GDBusConnection *connection,
|
|||||||
g_message("ModemManager appeared on D-Bus");
|
g_message("ModemManager appeared on D-Bus");
|
||||||
|
|
||||||
if (manager->modem_iface != MODEM_IFACE_NONE) {
|
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;
|
return;
|
||||||
}
|
}
|
||||||
manager->modem_iface = MODEM_IFACE_MODEMMANAGER;
|
manager->modem_iface = MODEM_IFACE_MODEMMANAGER;
|
||||||
|
|
||||||
mm_manager_new(connection, G_DBUS_OBJECT_MANAGER_CLIENT_FLAGS_NONE,
|
mm_manager_new(connection,
|
||||||
NULL, (GAsyncReadyCallback)mm_manager_new_cb, manager);
|
G_DBUS_OBJECT_MANAGER_CLIENT_FLAGS_NONE,
|
||||||
|
NULL,
|
||||||
|
(GAsyncReadyCallback)mm_manager_new_cb,
|
||||||
|
manager);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void mm_iface_clean(struct EG25Manager *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,
|
static void mm_vanished_cb(GDBusConnection *connection, const gchar *name, struct EG25Manager *manager)
|
||||||
const gchar *name,
|
|
||||||
struct EG25Manager *manager)
|
|
||||||
{
|
{
|
||||||
g_message("ModemManager vanished from D-Bus");
|
g_message("ModemManager vanished from D-Bus");
|
||||||
mm_iface_clean(manager);
|
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[])
|
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,
|
G_BUS_NAME_WATCHER_FLAGS_AUTO_START,
|
||||||
(GBusNameAppearedCallback)mm_appeared_cb,
|
(GBusNameAppearedCallback)mm_appeared_cb,
|
||||||
(GBusNameVanishedCallback)mm_vanished_cb,
|
(GBusNameVanishedCallback)mm_vanished_cb,
|
||||||
manager, NULL);
|
manager,
|
||||||
|
NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
void mm_iface_destroy(struct EG25Manager *manager)
|
void mm_iface_destroy(struct EG25Manager *manager)
|
||||||
|
@@ -14,9 +14,9 @@
|
|||||||
|
|
||||||
#define OFONO_SERVICE "org.ofono"
|
#define OFONO_SERVICE "org.ofono"
|
||||||
|
|
||||||
static void modem_added_cb(GDBOManager *manager_proxy,
|
static void modem_added_cb(GDBOManager *manager_proxy,
|
||||||
const gchar *path,
|
const gchar *path,
|
||||||
GVariant *properties,
|
GVariant *properties,
|
||||||
struct EG25Manager *manager)
|
struct EG25Manager *manager)
|
||||||
{
|
{
|
||||||
GVariant *modem_path;
|
GVariant *modem_path;
|
||||||
@@ -43,33 +43,28 @@ static void modem_added_cb(GDBOManager *manager_proxy,
|
|||||||
manager->modem_usb_id = g_strdup(strrchr(g_variant_dup_string(modem_path, NULL), '/') + 1);
|
manager->modem_usb_id = g_strdup(strrchr(g_variant_dup_string(modem_path, NULL), '/') + 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void modem_removed_cb(GDBOManager *manager_proxy,
|
static void modem_removed_cb(GDBOManager *manager_proxy, const gchar *path, struct EG25Manager *manager)
|
||||||
const gchar *path,
|
|
||||||
struct EG25Manager *manager)
|
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
static void get_modems_cb(GDBOManager *manager_proxy,
|
static void get_modems_cb(GDBOManager *manager_proxy, GAsyncResult *res, struct EG25Manager *manager)
|
||||||
GAsyncResult *res,
|
|
||||||
struct EG25Manager *manager)
|
|
||||||
{
|
{
|
||||||
gboolean ok;
|
gboolean ok;
|
||||||
GVariant *modems;
|
GVariant *modems;
|
||||||
GVariantIter *modems_iter = NULL;
|
GVariantIter *modems_iter = NULL;
|
||||||
g_autoptr(GError) error = NULL;
|
g_autoptr(GError) error = NULL;
|
||||||
|
|
||||||
const gchar *path;
|
const gchar *path;
|
||||||
GVariant *properties;
|
GVariant *properties;
|
||||||
|
|
||||||
ok = gdbo_manager_call_get_modems_finish(manager_proxy, &modems,
|
ok = gdbo_manager_call_get_modems_finish(manager_proxy, &modems, res, &error);
|
||||||
res, &error);
|
|
||||||
if (!ok) {
|
if (!ok) {
|
||||||
g_warning("Error getting modems from ofono manager: %s", error->message);
|
g_warning("Error getting modems from ofono manager: %s", error->message);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
g_variant_get(modems, "a(oa{sv})", &modems_iter);
|
g_variant_get(modems, "a(oa{sv})", &modems_iter);
|
||||||
while(g_variant_iter_loop(modems_iter, "(&o@a{sv})", &path, &properties)) {
|
while (g_variant_iter_loop(modems_iter, "(&o@a{sv})", &path, &properties)) {
|
||||||
g_debug("Got modem object path '%s'", path);
|
g_debug("Got modem object path '%s'", path);
|
||||||
modem_added_cb(manager_proxy, path, properties, manager);
|
modem_added_cb(manager_proxy, path, properties, manager);
|
||||||
}
|
}
|
||||||
@@ -87,7 +82,9 @@ static void ofono_appeared_cb(GDBusConnection *connection,
|
|||||||
g_message("oFono appeared on D-Bus");
|
g_message("oFono appeared on D-Bus");
|
||||||
|
|
||||||
if (manager->modem_iface != MODEM_IFACE_NONE) {
|
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;
|
return;
|
||||||
}
|
}
|
||||||
/* now connect to oFono! */
|
/* now connect to oFono! */
|
||||||
@@ -105,14 +102,12 @@ static void ofono_appeared_cb(GDBusConnection *connection,
|
|||||||
|
|
||||||
manager->modem_iface = MODEM_IFACE_OFONO;
|
manager->modem_iface = MODEM_IFACE_OFONO;
|
||||||
|
|
||||||
g_signal_connect(manager->ofono_manager, "modem-added",
|
g_signal_connect(manager->ofono_manager, "modem-added", G_CALLBACK(modem_added_cb), manager);
|
||||||
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-removed",
|
|
||||||
G_CALLBACK(modem_removed_cb), manager);
|
|
||||||
|
|
||||||
gdbo_manager_call_get_modems(manager->ofono_manager,
|
gdbo_manager_call_get_modems(manager->ofono_manager,
|
||||||
NULL,
|
NULL,
|
||||||
(GAsyncReadyCallback) get_modems_cb,
|
(GAsyncReadyCallback)get_modems_cb,
|
||||||
manager);
|
manager);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -130,11 +125,13 @@ static void ofono_vanished_cb(GDBusConnection *connection,
|
|||||||
|
|
||||||
void ofono_iface_init(struct EG25Manager *manager, toml_table_t *config[])
|
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,
|
G_BUS_NAME_WATCHER_FLAGS_AUTO_START,
|
||||||
(GBusNameAppearedCallback)ofono_appeared_cb,
|
(GBusNameAppearedCallback)ofono_appeared_cb,
|
||||||
(GBusNameVanishedCallback)ofono_vanished_cb,
|
(GBusNameVanishedCallback)ofono_vanished_cb,
|
||||||
manager, NULL);
|
manager,
|
||||||
|
NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ofono_iface_destroy(struct EG25Manager *manager)
|
void ofono_iface_destroy(struct EG25Manager *manager)
|
||||||
|
143
src/suspend.c
143
src/suspend.c
@@ -14,14 +14,28 @@
|
|||||||
|
|
||||||
#include <gio/gunixfdlist.h>
|
#include <gio/gunixfdlist.h>
|
||||||
|
|
||||||
#define SD_NAME "org.freedesktop.login1"
|
#define SD_NAME "org.freedesktop.login1"
|
||||||
#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;
|
||||||
@@ -36,8 +50,7 @@ static gboolean drop_inhibitor(struct EG25Manager *manager, gboolean block)
|
|||||||
manager->suspend_block_fd = -1;
|
manager->suspend_block_fd = -1;
|
||||||
return TRUE;
|
return TRUE;
|
||||||
}
|
}
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
if (manager->suspend_delay_fd >= 0) {
|
if (manager->suspend_delay_fd >= 0) {
|
||||||
g_message("dropping systemd sleep delay inhibitor");
|
g_message("dropping systemd sleep delay inhibitor");
|
||||||
close(manager->suspend_delay_fd);
|
close(manager->suspend_delay_fd);
|
||||||
@@ -48,18 +61,15 @@ static gboolean drop_inhibitor(struct EG25Manager *manager, gboolean block)
|
|||||||
return FALSE;
|
return FALSE;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void inhibit_done_delay(GObject *source,
|
static void inhibit_done_delay(GObject *source, GAsyncResult *result, gpointer user_data)
|
||||||
GAsyncResult *result,
|
|
||||||
gpointer user_data)
|
|
||||||
{
|
{
|
||||||
GDBusProxy *suspend_proxy = G_DBUS_PROXY(source);
|
GDBusProxy *suspend_proxy = G_DBUS_PROXY(source);
|
||||||
struct EG25Manager *manager = user_data;
|
struct EG25Manager *manager = user_data;
|
||||||
g_autoptr (GError) error = NULL;
|
g_autoptr(GError) error = NULL;
|
||||||
GVariant *res;
|
GVariant *res;
|
||||||
GUnixFDList *fd_list;
|
GUnixFDList *fd_list;
|
||||||
|
|
||||||
res = g_dbus_proxy_call_with_unix_fd_list_finish(suspend_proxy, &fd_list,
|
res = g_dbus_proxy_call_with_unix_fd_list_finish(suspend_proxy, &fd_list, result, &error);
|
||||||
result, &error);
|
|
||||||
if (!res) {
|
if (!res) {
|
||||||
g_warning("inhibit failed: %s", error->message);
|
g_warning("inhibit failed: %s", error->message);
|
||||||
} else {
|
} else {
|
||||||
@@ -74,18 +84,15 @@ static void inhibit_done_delay(GObject *source,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void inhibit_done_block(GObject *source,
|
static void inhibit_done_block(GObject *source, GAsyncResult *result, gpointer user_data)
|
||||||
GAsyncResult *result,
|
|
||||||
gpointer user_data)
|
|
||||||
{
|
{
|
||||||
GDBusProxy *suspend_proxy = G_DBUS_PROXY(source);
|
GDBusProxy *suspend_proxy = G_DBUS_PROXY(source);
|
||||||
struct EG25Manager *manager = user_data;
|
struct EG25Manager *manager = user_data;
|
||||||
g_autoptr (GError) error = NULL;
|
g_autoptr(GError) error = NULL;
|
||||||
GVariant *res;
|
GVariant *res;
|
||||||
GUnixFDList *fd_list;
|
GUnixFDList *fd_list;
|
||||||
|
|
||||||
res = g_dbus_proxy_call_with_unix_fd_list_finish(suspend_proxy, &fd_list,
|
res = g_dbus_proxy_call_with_unix_fd_list_finish(suspend_proxy, &fd_list, result, &error);
|
||||||
result, &error);
|
|
||||||
if (!res) {
|
if (!res) {
|
||||||
g_warning("inhibit failed: %s", error->message);
|
g_warning("inhibit failed: %s", error->message);
|
||||||
} else {
|
} else {
|
||||||
@@ -119,44 +126,59 @@ static void take_inhibitor(struct EG25Manager *manager, gboolean block)
|
|||||||
GVariant *variant_arg;
|
GVariant *variant_arg;
|
||||||
|
|
||||||
if (block) {
|
if (block) {
|
||||||
if(manager->suspend_block_fd != -1)
|
if (manager->suspend_block_fd != -1)
|
||||||
drop_inhibitor(manager, TRUE);
|
drop_inhibitor(manager, TRUE);
|
||||||
|
|
||||||
variant_arg = g_variant_new ("(ssss)", "sleep", "eg25manager",
|
variant_arg = g_variant_new("(ssss)",
|
||||||
"eg25manager needs to wait for modem to be fully booted",
|
"sleep",
|
||||||
"block");
|
"eg25manager",
|
||||||
|
"eg25manager needs to wait for modem to be fully booted",
|
||||||
|
"block");
|
||||||
|
|
||||||
g_message("taking systemd sleep inhibitor (blocking)");
|
g_message("taking systemd sleep inhibitor (blocking)");
|
||||||
g_dbus_proxy_call_with_unix_fd_list(manager->suspend_proxy, "Inhibit",
|
g_dbus_proxy_call_with_unix_fd_list(manager->suspend_proxy,
|
||||||
variant_arg, 0, G_MAXINT, NULL, NULL,
|
"Inhibit",
|
||||||
inhibit_done_block, manager);
|
variant_arg,
|
||||||
|
0,
|
||||||
|
G_MAXINT,
|
||||||
|
NULL,
|
||||||
|
NULL,
|
||||||
|
inhibit_done_block,
|
||||||
|
manager);
|
||||||
manager->modem_boot_timer = g_timeout_add_seconds(manager->modem_boot_timeout,
|
manager->modem_boot_timer = g_timeout_add_seconds(manager->modem_boot_timeout,
|
||||||
G_SOURCE_FUNC(modem_fully_booted),
|
G_SOURCE_FUNC(modem_fully_booted),
|
||||||
manager);
|
manager);
|
||||||
}
|
} else {
|
||||||
else {
|
if (manager->suspend_delay_fd != -1)
|
||||||
if(manager->suspend_delay_fd != -1)
|
|
||||||
drop_inhibitor(manager, FALSE);
|
drop_inhibitor(manager, FALSE);
|
||||||
|
|
||||||
variant_arg = g_variant_new ("(ssss)", "sleep", "eg25manager",
|
variant_arg = g_variant_new("(ssss)",
|
||||||
"eg25manager needs to prepare modem for sleep",
|
"sleep",
|
||||||
"delay");
|
"eg25manager",
|
||||||
|
"eg25manager needs to prepare modem for sleep",
|
||||||
|
"delay");
|
||||||
|
|
||||||
g_message("taking systemd sleep inhibitor");
|
g_message("taking systemd sleep inhibitor");
|
||||||
g_dbus_proxy_call_with_unix_fd_list(manager->suspend_proxy, "Inhibit",
|
g_dbus_proxy_call_with_unix_fd_list(manager->suspend_proxy,
|
||||||
variant_arg, 0, G_MAXINT, NULL, NULL,
|
"Inhibit",
|
||||||
inhibit_done_delay, manager);
|
variant_arg,
|
||||||
|
0,
|
||||||
|
G_MAXINT,
|
||||||
|
NULL,
|
||||||
|
NULL,
|
||||||
|
inhibit_done_delay,
|
||||||
|
manager);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void signal_cb(GDBusProxy *proxy,
|
static void signal_cb(GDBusProxy *proxy,
|
||||||
const gchar *sendername,
|
const gchar *sendername,
|
||||||
const gchar *signalname,
|
const gchar *signalname,
|
||||||
GVariant *args,
|
GVariant *args,
|
||||||
gpointer user_data)
|
gpointer user_data)
|
||||||
{
|
{
|
||||||
struct EG25Manager *manager = user_data;
|
struct EG25Manager *manager = user_data;
|
||||||
gboolean is_about_to_suspend;
|
gboolean is_about_to_suspend;
|
||||||
|
|
||||||
if (strcmp(signalname, "PrepareForSleep") != 0)
|
if (strcmp(signalname, "PrepareForSleep") != 0)
|
||||||
return;
|
return;
|
||||||
@@ -184,8 +206,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,
|
||||||
@@ -195,13 +216,11 @@ static void signal_cb(GDBusProxy *proxy,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void name_owner_cb(GObject *object,
|
static void name_owner_cb(GObject *object, GParamSpec *pspec, gpointer user_data)
|
||||||
GParamSpec *pspec,
|
|
||||||
gpointer user_data)
|
|
||||||
{
|
{
|
||||||
GDBusProxy *proxy = G_DBUS_PROXY(object);
|
GDBusProxy *proxy = G_DBUS_PROXY(object);
|
||||||
struct EG25Manager *manager = user_data;
|
struct EG25Manager *manager = user_data;
|
||||||
char *owner;
|
char *owner;
|
||||||
|
|
||||||
g_assert(proxy == manager->suspend_proxy);
|
g_assert(proxy == manager->suspend_proxy);
|
||||||
|
|
||||||
@@ -214,11 +233,9 @@ static void name_owner_cb(GObject *object,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void on_proxy_acquired(GObject *object,
|
static void on_proxy_acquired(GObject *object, GAsyncResult *res, struct EG25Manager *manager)
|
||||||
GAsyncResult *res,
|
|
||||||
struct EG25Manager *manager)
|
|
||||||
{
|
{
|
||||||
g_autoptr (GError) error = NULL;
|
g_autoptr(GError) error = NULL;
|
||||||
char *owner;
|
char *owner;
|
||||||
|
|
||||||
manager->suspend_proxy = g_dbus_proxy_new_for_bus_finish(res, &error);
|
manager->suspend_proxy = g_dbus_proxy_new_for_bus_finish(res, &error);
|
||||||
@@ -227,10 +244,8 @@ static void on_proxy_acquired(GObject *object,
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
g_signal_connect(manager->suspend_proxy, "notify::g-name-owner",
|
g_signal_connect(manager->suspend_proxy, "notify::g-name-owner", G_CALLBACK(name_owner_cb), manager);
|
||||||
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, "g-signal",
|
|
||||||
G_CALLBACK(signal_cb), manager);
|
|
||||||
|
|
||||||
owner = g_dbus_proxy_get_name_owner(manager->suspend_proxy);
|
owner = g_dbus_proxy_get_name_owner(manager->suspend_proxy);
|
||||||
if (owner) {
|
if (owner) {
|
||||||
@@ -246,7 +261,7 @@ void suspend_init(struct EG25Manager *manager, toml_table_t *config[])
|
|||||||
for (int i = 0; i < EG25_CONFIG_COUNT; i++)
|
for (int i = 0; i < EG25_CONFIG_COUNT; i++)
|
||||||
suspend_config[i] = config[i] ? toml_table_in(config[i], "suspend") : NULL;
|
suspend_config[i] = config[i] ? toml_table_in(config[i], "suspend") : NULL;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* The `suspend` section is optional in both the user and system config files,
|
* The `suspend` section is optional in both the user and system config files,
|
||||||
* so let's make sure suspend_config[EG25_CONFIG_SYS] is valid if one of the
|
* so let's make sure suspend_config[EG25_CONFIG_SYS] is valid if one of the
|
||||||
* files has it.
|
* files has it.
|
||||||
@@ -267,10 +282,16 @@ void suspend_init(struct EG25Manager *manager, toml_table_t *config[])
|
|||||||
manager->modem_recovery_timeout = 9;
|
manager->modem_recovery_timeout = 9;
|
||||||
|
|
||||||
g_dbus_proxy_new_for_bus(G_BUS_TYPE_SYSTEM,
|
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_AUTO_START |
|
||||||
G_DBUS_PROXY_FLAGS_DO_NOT_LOAD_PROPERTIES,
|
G_DBUS_PROXY_FLAGS_DO_NOT_LOAD_PROPERTIES,
|
||||||
NULL, SD_NAME, SD_PATH, SD_INTERFACE, NULL,
|
NULL,
|
||||||
(GAsyncReadyCallback)on_proxy_acquired, manager);
|
SD_NAME,
|
||||||
|
SD_PATH,
|
||||||
|
SD_INTERFACE,
|
||||||
|
NULL,
|
||||||
|
(GAsyncReadyCallback)on_proxy_acquired,
|
||||||
|
manager);
|
||||||
}
|
}
|
||||||
|
|
||||||
void suspend_destroy(struct EG25Manager *manager)
|
void suspend_destroy(struct EG25Manager *manager)
|
||||||
|
@@ -8,7 +8,7 @@
|
|||||||
|
|
||||||
#include "manager.h"
|
#include "manager.h"
|
||||||
|
|
||||||
void suspend_init (struct EG25Manager *data, toml_table_t *config[]);
|
void suspend_init(struct EG25Manager *data, toml_table_t *config[]);
|
||||||
void suspend_destroy (struct EG25Manager *data);
|
void suspend_destroy(struct EG25Manager *data);
|
||||||
|
|
||||||
void suspend_inhibit (struct EG25Manager *data, gboolean inhibit, gboolean block);
|
void suspend_inhibit(struct EG25Manager *data, gboolean inhibit, gboolean block);
|
||||||
|
3688
src/toml.c
3688
src/toml.c
File diff suppressed because it is too large
Load Diff
159
src/toml.h
159
src/toml.h
@@ -1,19 +1,19 @@
|
|||||||
/*
|
/*
|
||||||
MIT License
|
MIT License
|
||||||
|
|
||||||
Copyright (c) 2017 - 2019 CK Tan
|
Copyright (c) 2017 - 2019 CK Tan
|
||||||
https://github.com/cktan/tomlc99
|
https://github.com/cktan/tomlc99
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
of this software and associated documentation files (the "Software"), to deal
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
in the Software without restriction, including without limitation the rights
|
in the Software without restriction, including without limitation the rights
|
||||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
copies of the Software, and to permit persons to whom the Software is
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
furnished to do so, subject to the following conditions:
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
The above copyright notice and this permission notice shall be included in all
|
The above copyright notice and this permission notice shall be included in all
|
||||||
copies or substantial portions of the Software.
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
@@ -25,10 +25,8 @@
|
|||||||
#ifndef TOML_H
|
#ifndef TOML_H
|
||||||
#define TOML_H
|
#define TOML_H
|
||||||
|
|
||||||
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
#define TOML_EXTERN extern "C"
|
#define TOML_EXTERN extern "C"
|
||||||
@@ -37,139 +35,130 @@
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
typedef struct toml_timestamp_t toml_timestamp_t;
|
typedef struct toml_timestamp_t toml_timestamp_t;
|
||||||
typedef struct toml_table_t toml_table_t;
|
typedef struct toml_table_t toml_table_t;
|
||||||
typedef struct toml_array_t toml_array_t;
|
typedef struct toml_array_t toml_array_t;
|
||||||
typedef struct toml_datum_t toml_datum_t;
|
typedef struct toml_datum_t toml_datum_t;
|
||||||
|
|
||||||
/* Parse a file. Return a table on success, or 0 otherwise.
|
/* Parse a file. Return a table on success, or 0 otherwise.
|
||||||
* Caller must toml_free(the-return-value) after use.
|
* Caller must toml_free(the-return-value) after use.
|
||||||
*/
|
*/
|
||||||
TOML_EXTERN toml_table_t* toml_parse_file(FILE* fp,
|
TOML_EXTERN toml_table_t *toml_parse_file(FILE *fp, char *errbuf, int errbufsz);
|
||||||
char* errbuf,
|
|
||||||
int errbufsz);
|
|
||||||
|
|
||||||
/* Parse a string containing the full config.
|
/* Parse a string containing the full config.
|
||||||
* Return a table on success, or 0 otherwise.
|
* Return a table on success, or 0 otherwise.
|
||||||
* Caller must toml_free(the-return-value) after use.
|
* Caller must toml_free(the-return-value) after use.
|
||||||
*/
|
*/
|
||||||
TOML_EXTERN toml_table_t* toml_parse(char* conf, /* NUL terminated, please. */
|
TOML_EXTERN toml_table_t *toml_parse(char *conf, /* NUL terminated, please. */
|
||||||
char* errbuf,
|
char *errbuf,
|
||||||
int errbufsz);
|
int errbufsz);
|
||||||
|
|
||||||
/* Free the table returned by toml_parse() or toml_parse_file(). Once
|
/* Free the table returned by toml_parse() or toml_parse_file(). Once
|
||||||
* this function is called, any handles accessed through this tab
|
* this function is called, any handles accessed through this tab
|
||||||
* directly or indirectly are no longer valid.
|
* directly or indirectly are no longer valid.
|
||||||
*/
|
*/
|
||||||
TOML_EXTERN void toml_free(toml_table_t* tab);
|
TOML_EXTERN void toml_free(toml_table_t *tab);
|
||||||
|
|
||||||
|
/* Timestamp types. The year, month, day, hour, minute, second, z
|
||||||
/* Timestamp types. The year, month, day, hour, minute, second, z
|
|
||||||
* fields may be NULL if they are not relevant. e.g. In a DATE
|
* 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.
|
* type, the hour, minute, second and z fields will be NULLs.
|
||||||
*/
|
*/
|
||||||
struct toml_timestamp_t {
|
struct toml_timestamp_t {
|
||||||
struct { /* internal. do not use. */
|
struct { /* internal. do not use. */
|
||||||
int year, month, day;
|
int year, month, day;
|
||||||
int hour, minute, second, millisec;
|
int hour, minute, second, millisec;
|
||||||
char z[10];
|
char z[10];
|
||||||
} __buffer;
|
} __buffer;
|
||||||
int *year, *month, *day;
|
int *year, *month, *day;
|
||||||
int *hour, *minute, *second, *millisec;
|
int *hour, *minute, *second, *millisec;
|
||||||
char* z;
|
char *z;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
/*-----------------------------------------------------------------
|
/*-----------------------------------------------------------------
|
||||||
* Enhanced access methods
|
* Enhanced access methods
|
||||||
*/
|
*/
|
||||||
struct toml_datum_t {
|
struct toml_datum_t {
|
||||||
int ok;
|
int ok;
|
||||||
union {
|
union {
|
||||||
toml_timestamp_t* ts; /* ts must be freed after use */
|
toml_timestamp_t *ts; /* ts must be freed after use */
|
||||||
char* s; /* string value. s must be freed after use */
|
char *s; /* string value. s must be freed after use */
|
||||||
int b; /* bool value */
|
int b; /* bool value */
|
||||||
int64_t i; /* int value */
|
int64_t i; /* int value */
|
||||||
double d; /* double value */
|
double d; /* double value */
|
||||||
} u;
|
} u;
|
||||||
};
|
};
|
||||||
|
|
||||||
/* on arrays: */
|
/* on arrays: */
|
||||||
/* ... retrieve size of array. */
|
/* ... retrieve size of array. */
|
||||||
TOML_EXTERN int toml_array_nelem(const toml_array_t* arr);
|
TOML_EXTERN int toml_array_nelem(const toml_array_t *arr);
|
||||||
/* ... retrieve values using index. */
|
/* ... retrieve values using index. */
|
||||||
TOML_EXTERN toml_datum_t toml_string_at(const toml_array_t* arr, int idx);
|
TOML_EXTERN toml_datum_t toml_string_at(const toml_array_t *arr, int idx);
|
||||||
TOML_EXTERN toml_datum_t toml_bool_at(const toml_array_t* arr, int idx);
|
TOML_EXTERN toml_datum_t toml_bool_at(const toml_array_t *arr, int idx);
|
||||||
TOML_EXTERN toml_datum_t toml_int_at(const toml_array_t* arr, int idx);
|
TOML_EXTERN toml_datum_t toml_int_at(const toml_array_t *arr, int idx);
|
||||||
TOML_EXTERN toml_datum_t toml_double_at(const toml_array_t* arr, int idx);
|
TOML_EXTERN toml_datum_t toml_double_at(const toml_array_t *arr, int idx);
|
||||||
TOML_EXTERN toml_datum_t toml_timestamp_at(const toml_array_t* arr, int idx);
|
TOML_EXTERN toml_datum_t toml_timestamp_at(const toml_array_t *arr, int idx);
|
||||||
/* ... retrieve array or table using index. */
|
/* ... retrieve array or table using index. */
|
||||||
TOML_EXTERN toml_array_t* toml_array_at(const toml_array_t* arr, int idx);
|
TOML_EXTERN toml_array_t *toml_array_at(const toml_array_t *arr, int idx);
|
||||||
TOML_EXTERN toml_table_t* toml_table_at(const toml_array_t* arr, int idx);
|
TOML_EXTERN toml_table_t *toml_table_at(const toml_array_t *arr, int idx);
|
||||||
|
|
||||||
/* on tables: */
|
/* on tables: */
|
||||||
/* ... retrieve the key in table at keyidx. Return 0 if out of range. */
|
/* ... retrieve the key in table at keyidx. Return 0 if out of range. */
|
||||||
TOML_EXTERN const char* toml_key_in(const toml_table_t* tab, int keyidx);
|
TOML_EXTERN const char *toml_key_in(const toml_table_t *tab, int keyidx);
|
||||||
/* ... retrieve values using key. */
|
/* ... retrieve values using key. */
|
||||||
TOML_EXTERN toml_datum_t toml_string_in(const toml_table_t* arr, const char* key);
|
TOML_EXTERN toml_datum_t toml_string_in(const toml_table_t *arr, const char *key);
|
||||||
TOML_EXTERN toml_datum_t toml_bool_in(const toml_table_t* arr, const char* key);
|
TOML_EXTERN toml_datum_t toml_bool_in(const toml_table_t *arr, const char *key);
|
||||||
TOML_EXTERN toml_datum_t toml_int_in(const toml_table_t* arr, const char* key);
|
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_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);
|
TOML_EXTERN toml_datum_t toml_timestamp_in(const toml_table_t *arr, const char *key);
|
||||||
/* .. retrieve array or table using key. */
|
/* .. retrieve array or table using key. */
|
||||||
TOML_EXTERN toml_array_t* toml_array_in(const toml_table_t* tab,
|
TOML_EXTERN toml_array_t *toml_array_in(const toml_table_t *tab, const char *key);
|
||||||
const char* key);
|
TOML_EXTERN toml_table_t *toml_table_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
|
* lesser used
|
||||||
*/
|
*/
|
||||||
/* Return the array kind: 't'able, 'a'rray, 'v'alue */
|
/* Return the array kind: 't'able, 'a'rray, 'v'alue */
|
||||||
TOML_EXTERN char toml_array_kind(const toml_array_t* arr);
|
TOML_EXTERN char toml_array_kind(const toml_array_t *arr);
|
||||||
|
|
||||||
/* For array kind 'v'alue, return the type of values
|
/* For array kind 'v'alue, return the type of values
|
||||||
i:int, d:double, b:bool, s:string, t:time, D:date, T:timestamp
|
i:int, d:double, b:bool, s:string, t:time, D:date, T:timestamp
|
||||||
0 if unknown
|
0 if unknown
|
||||||
*/
|
*/
|
||||||
TOML_EXTERN char toml_array_type(const toml_array_t* arr);
|
TOML_EXTERN char toml_array_type(const toml_array_t *arr);
|
||||||
|
|
||||||
/* Return the key of an array */
|
/* Return the key of an array */
|
||||||
TOML_EXTERN const char* toml_array_key(const toml_array_t* arr);
|
TOML_EXTERN const char *toml_array_key(const toml_array_t *arr);
|
||||||
|
|
||||||
/* Return the number of key-values in a table */
|
/* Return the number of key-values in a table */
|
||||||
TOML_EXTERN int toml_table_nkval(const toml_table_t* tab);
|
TOML_EXTERN int toml_table_nkval(const toml_table_t *tab);
|
||||||
|
|
||||||
/* Return the number of arrays in a table */
|
/* Return the number of arrays in a table */
|
||||||
TOML_EXTERN int toml_table_narr(const toml_table_t* tab);
|
TOML_EXTERN int toml_table_narr(const toml_table_t *tab);
|
||||||
|
|
||||||
/* Return the number of sub-tables in a table */
|
/* Return the number of sub-tables in a table */
|
||||||
TOML_EXTERN int toml_table_ntab(const toml_table_t* tab);
|
TOML_EXTERN int toml_table_ntab(const toml_table_t *tab);
|
||||||
|
|
||||||
/* Return the key of a table*/
|
/* Return the key of a table*/
|
||||||
TOML_EXTERN const char* toml_table_key(const toml_table_t* tab);
|
TOML_EXTERN const char *toml_table_key(const toml_table_t *tab);
|
||||||
|
|
||||||
/*--------------------------------------------------------------
|
/*--------------------------------------------------------------
|
||||||
* misc
|
* misc
|
||||||
*/
|
*/
|
||||||
TOML_EXTERN int toml_utf8_to_ucs(const char* orig, int len, int64_t* ret);
|
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 int toml_ucs_to_utf8(int64_t code, char buf[6]);
|
||||||
TOML_EXTERN void toml_set_memutil(void* (*xxmalloc)(size_t),
|
TOML_EXTERN void toml_set_memutil(void *(*xxmalloc)(size_t), void (*xxfree)(void *));
|
||||||
void (*xxfree)(void*));
|
|
||||||
|
|
||||||
|
|
||||||
/*--------------------------------------------------------------
|
/*--------------------------------------------------------------
|
||||||
* deprecated
|
* deprecated
|
||||||
*/
|
*/
|
||||||
/* A raw value, must be processed by toml_rto* before using. */
|
/* A raw value, must be processed by toml_rto* before using. */
|
||||||
typedef const char* toml_raw_t;
|
typedef const char *toml_raw_t;
|
||||||
TOML_EXTERN toml_raw_t toml_raw_in(const toml_table_t* tab, const char* key);
|
TOML_EXTERN toml_raw_t toml_raw_in(const toml_table_t *tab, const char *key);
|
||||||
TOML_EXTERN toml_raw_t toml_raw_at(const toml_array_t* arr, int idx);
|
TOML_EXTERN toml_raw_t toml_raw_at(const toml_array_t *arr, int idx);
|
||||||
TOML_EXTERN int toml_rtos(toml_raw_t s, char** ret);
|
TOML_EXTERN int toml_rtos(toml_raw_t s, char **ret);
|
||||||
TOML_EXTERN int toml_rtob(toml_raw_t s, int* ret);
|
TOML_EXTERN int toml_rtob(toml_raw_t s, int *ret);
|
||||||
TOML_EXTERN int toml_rtoi(toml_raw_t s, int64_t* ret);
|
TOML_EXTERN int toml_rtoi(toml_raw_t s, int64_t *ret);
|
||||||
TOML_EXTERN int toml_rtod(toml_raw_t s, double* ret);
|
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_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);
|
TOML_EXTERN int toml_rtots(toml_raw_t s, toml_timestamp_t *ret);
|
||||||
|
|
||||||
|
|
||||||
#endif /* TOML_H */
|
#endif /* TOML_H */
|
||||||
|
50
src/udev.c
50
src/udev.c
@@ -11,23 +11,49 @@
|
|||||||
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 &&
|
|
||||||
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[])
|
void udev_init(struct EG25Manager *manager, toml_table_t *config[])
|
||||||
{
|
{
|
||||||
const char * const subsystems[] = { "usb", NULL };
|
const char *const subsystems[] = {"usb", NULL};
|
||||||
|
|
||||||
manager->udev = g_udev_client_new(subsystems);
|
manager->udev = g_udev_client_new(subsystems);
|
||||||
g_signal_connect(manager->udev, "uevent", G_CALLBACK(udev_event_cb), manager);
|
g_signal_connect(manager->udev, "uevent", G_CALLBACK(udev_event_cb), manager);
|
||||||
@@ -35,7 +61,7 @@ void udev_init (struct EG25Manager *manager, toml_table_t *config[])
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
void udev_destroy (struct EG25Manager *manager)
|
void udev_destroy(struct EG25Manager *manager)
|
||||||
{
|
{
|
||||||
if (manager->udev) {
|
if (manager->udev) {
|
||||||
g_object_unref(manager->udev);
|
g_object_unref(manager->udev);
|
||||||
|
@@ -8,5 +8,5 @@
|
|||||||
|
|
||||||
#include "manager.h"
|
#include "manager.h"
|
||||||
|
|
||||||
void udev_init (struct EG25Manager *data, toml_table_t *config[]);
|
void udev_init(struct EG25Manager *data, toml_table_t *config[]);
|
||||||
void udev_destroy (struct EG25Manager *data);
|
void udev_destroy(struct EG25Manager *data);
|
||||||
|
@@ -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