mirror of
https://gitlab.com/mobian1/eg25-manager.git
synced 2025-08-30 15:52:11 +02:00
Compare commits
25 Commits
Author | SHA1 | Date | |
---|---|---|---|
|
3d076e8bc8 | ||
|
2e7bf44f2d | ||
|
3bf2d785bb | ||
|
9e40ae11d6 | ||
|
11c9cee111 | ||
|
89ce52f709 | ||
|
8a41a3203b | ||
|
042de572ea | ||
|
cfbbbd0167 | ||
|
f276d9cf9e | ||
|
433982e4f7 | ||
|
d9256251fd | ||
|
c92746e875 | ||
|
19d00bee3b | ||
|
b929c6a380 | ||
|
4962fcd13e | ||
|
b8d269cf2f | ||
|
4ff727a7d4 | ||
|
7765e6c541 | ||
|
94cbd03921 | ||
|
4c832a00dc | ||
|
ded092ef04 | ||
|
235eff02ac | ||
|
3bb6e15de0 | ||
|
692f9134f8 |
@@ -29,13 +29,20 @@ $ ninja -C ../eg25-build
|
|||||||
# ninja -C ../eg25-build install
|
# ninja -C ../eg25-build install
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## Configuration
|
||||||
|
|
||||||
|
`eg25-manager` uses device-specific configuration files, named after the
|
||||||
|
device-tree `compatible` field. These files are installed to
|
||||||
|
`/usr/share/eg25-manager`. They can be copied to `/etc/eg25-manager` then
|
||||||
|
modified, that way they won't be overwritten during an upgrade.
|
||||||
|
|
||||||
## Running
|
## Running
|
||||||
|
|
||||||
`eg25-manager` is usually run as a systemd service, but can also be
|
`eg25-manager` is usually run as a systemd service, but can also be
|
||||||
manually started from the command-line (requires root privileges):
|
manually started from the command-line (requires root privileges):
|
||||||
|
|
||||||
```
|
```
|
||||||
# eg25-manager
|
# eg25manager
|
||||||
```
|
```
|
||||||
|
|
||||||
## License
|
## License
|
||||||
|
13
data/meson.build
Normal file
13
data/meson.build
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
#
|
||||||
|
# Copyright (C) 2020 Arnaud Ferraris <arnaud.ferraris@gmail.com>
|
||||||
|
#
|
||||||
|
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
#
|
||||||
|
|
||||||
|
conf_files = [
|
||||||
|
'pine64,pinephone-1.0.toml',
|
||||||
|
'pine64,pinephone-1.1.toml',
|
||||||
|
'pine64,pinephone-1.2.toml',
|
||||||
|
]
|
||||||
|
|
||||||
|
install_data(conf_files)
|
55
data/pine64,pinephone-1.0.toml
Normal file
55
data/pine64,pinephone-1.0.toml
Normal file
@@ -0,0 +1,55 @@
|
|||||||
|
[manager]
|
||||||
|
need_libusb = true
|
||||||
|
usb_vid = 0x2c7c
|
||||||
|
usb_pid = 0x0125
|
||||||
|
|
||||||
|
# Uncomment the following if you need to change the modem detection timeout on
|
||||||
|
# resume and/or the time during which suspend is blocked after modem boot
|
||||||
|
#[suspend]
|
||||||
|
#boot_timeout = 120
|
||||||
|
#recovery_timeout = 9
|
||||||
|
|
||||||
|
[gpio]
|
||||||
|
dtr = 358
|
||||||
|
pwrkey = 35
|
||||||
|
reset = 68
|
||||||
|
apready = 231
|
||||||
|
disable = 232
|
||||||
|
|
||||||
|
[at]
|
||||||
|
uart = "/dev/ttyS2"
|
||||||
|
configure = [
|
||||||
|
# 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 (optional)
|
||||||
|
# * `value` : the commands, argument, usually used to set the value of
|
||||||
|
# a specific parameter (optional)
|
||||||
|
# * `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 configured value (optional)
|
||||||
|
# A command can have `expect` OR `value` configured, but it shouldn't have both
|
||||||
|
{ cmd = "QGMR" },
|
||||||
|
{ cmd = "QDAI", expect = "1,1,0,1,0,0,1,1" },
|
||||||
|
{ cmd = "QCFG", subcmd = "risignaltype", expect = "\"physical\"" },
|
||||||
|
{ cmd = "QCFG", subcmd = "ims", expect = "1" },
|
||||||
|
{ cmd = "QCFG", subcmd = "urc/ri/ring", expect = "\"pulse\",2000,1000,5000,\"off\",1" },
|
||||||
|
{ cmd = "QCFG", subcmd = "urc/ri/smsincoming", expect = "\"pulse\",2000" },
|
||||||
|
{ cmd = "QCFG", subcmd = "urc/ri/other", expect = "\"off\",1" },
|
||||||
|
{ cmd = "QCFG", subcmd = "urc/delay", expect = "1" },
|
||||||
|
{ cmd = "QURCCFG", subcmd = "urcport", expect = "\"usbat\"" },
|
||||||
|
{ cmd = "QGPS", value = "1" },
|
||||||
|
{ cmd = "QSCLK", value = "1" },
|
||||||
|
{ cmd = "QCFG", subcmd = "urc/cache", value = "0" }
|
||||||
|
]
|
||||||
|
suspend = [
|
||||||
|
{ cmd = "QGPSEND" },
|
||||||
|
{ cmd = "QCFG", subcmd = "urc/cache", value = "1" }
|
||||||
|
]
|
||||||
|
resume = [
|
||||||
|
{ cmd = "QCFG", subcmd = "urc/cache", value = "0" },
|
||||||
|
{ cmd = "QGPS", value = "1" }
|
||||||
|
]
|
||||||
|
reset = [ { cmd = "CFUN", value = "1,1" } ]
|
55
data/pine64,pinephone-1.1.toml
Normal file
55
data/pine64,pinephone-1.1.toml
Normal file
@@ -0,0 +1,55 @@
|
|||||||
|
[manager]
|
||||||
|
need_libusb = true
|
||||||
|
usb_vid = 0x2c7c
|
||||||
|
usb_pid = 0x0125
|
||||||
|
|
||||||
|
# Uncomment the following if you need to change the modem detection timeout on
|
||||||
|
# resume and/or the time during which suspend is blocked after modem boot
|
||||||
|
#[suspend]
|
||||||
|
#boot_timeout = 120
|
||||||
|
#recovery_timeout = 9
|
||||||
|
|
||||||
|
[gpio]
|
||||||
|
dtr = 358
|
||||||
|
pwrkey = 35
|
||||||
|
reset = 68
|
||||||
|
apready = 231
|
||||||
|
disable = 232
|
||||||
|
|
||||||
|
[at]
|
||||||
|
uart = "/dev/ttyS2"
|
||||||
|
configure = [
|
||||||
|
# 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 (optional)
|
||||||
|
# * `value` : the commands, argument, usually used to set the value of
|
||||||
|
# a specific parameter (optional)
|
||||||
|
# * `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 configured value (optional)
|
||||||
|
# A command can have `expect` OR `value` configured, but it shouldn't have both
|
||||||
|
{ cmd = "QGMR" },
|
||||||
|
{ cmd = "QDAI", expect = "1,1,0,1,0,0,1,1" },
|
||||||
|
{ cmd = "QCFG", subcmd = "risignaltype", expect = "\"physical\"" },
|
||||||
|
{ cmd = "QCFG", subcmd = "ims", expect = "1" },
|
||||||
|
{ cmd = "QCFG", subcmd = "urc/ri/ring", expect = "\"pulse\",2000,1000,5000,\"off\",1" },
|
||||||
|
{ cmd = "QCFG", subcmd = "urc/ri/smsincoming", expect = "\"pulse\",2000" },
|
||||||
|
{ cmd = "QCFG", subcmd = "urc/ri/other", expect = "\"off\",1" },
|
||||||
|
{ cmd = "QCFG", subcmd = "urc/delay", expect = "1" },
|
||||||
|
{ cmd = "QURCCFG", subcmd = "urcport", expect = "\"usbat\"" },
|
||||||
|
{ cmd = "QGPS", value = "1" },
|
||||||
|
{ cmd = "QSCLK", value = "1" },
|
||||||
|
{ cmd = "QCFG", subcmd = "urc/cache", value = "0" }
|
||||||
|
]
|
||||||
|
suspend = [
|
||||||
|
{ cmd = "QGPSEND" },
|
||||||
|
{ cmd = "QCFG", subcmd = "urc/cache", value = "1" }
|
||||||
|
]
|
||||||
|
resume = [
|
||||||
|
{ cmd = "QCFG", subcmd = "urc/cache", value = "0" },
|
||||||
|
{ cmd = "QGPS", value = "1" }
|
||||||
|
]
|
||||||
|
reset = [ { cmd = "CFUN", value = "1,1" } ]
|
48
data/pine64,pinephone-1.2.toml
Normal file
48
data/pine64,pinephone-1.2.toml
Normal file
@@ -0,0 +1,48 @@
|
|||||||
|
# Uncomment the following if you need to change the modem detection timeout on
|
||||||
|
# resume and/or the time during which suspend is blocked after modem boot
|
||||||
|
#[suspend]
|
||||||
|
#boot_timeout = 120
|
||||||
|
#recovery_timeout = 9
|
||||||
|
|
||||||
|
[gpio]
|
||||||
|
dtr = 34
|
||||||
|
pwrkey = 35
|
||||||
|
reset = 68
|
||||||
|
apready = 231
|
||||||
|
disable = 232
|
||||||
|
status = 233
|
||||||
|
|
||||||
|
[at]
|
||||||
|
uart = "/dev/ttyS2"
|
||||||
|
configure = [
|
||||||
|
# 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 (optional)
|
||||||
|
# * `value` : the commands, argument, usually used to set the value of
|
||||||
|
# a specific parameter (optional)
|
||||||
|
# * `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 configured value (optional)
|
||||||
|
# A command can have `expect` OR `value` configured, but it shouldn't have both
|
||||||
|
{ cmd = "QGMR" },
|
||||||
|
{ cmd = "QDAI", expect = "1,1,0,1,0,0,1,1" },
|
||||||
|
{ cmd = "QCFG", subcmd = "risignaltype", expect = "\"physical\"" },
|
||||||
|
{ cmd = "QCFG", subcmd = "ims", expect = "1" },
|
||||||
|
{ cmd = "QCFG", subcmd = "apready", expect = "1,0,500" },
|
||||||
|
{ cmd = "QURCCFG", subcmd = "urcport", expect = "\"usbat\"" },
|
||||||
|
{ cmd = "QGPS", value = "1" },
|
||||||
|
{ cmd = "QSCLK", value = "1" },
|
||||||
|
{ cmd = "QCFG", subcmd = "urc/cache", value = "0" }
|
||||||
|
]
|
||||||
|
suspend = [
|
||||||
|
{ cmd = "QGPSEND" },
|
||||||
|
{ cmd = "QCFG", subcmd = "urc/cache", value = "1" }
|
||||||
|
]
|
||||||
|
resume = [
|
||||||
|
{ cmd = "QCFG", subcmd = "urc/cache", value = "0" },
|
||||||
|
{ cmd = "QGPS", value = "1" }
|
||||||
|
]
|
||||||
|
reset = [ { cmd = "CFUN", value = "1,1" } ]
|
13
meson.build
13
meson.build
@@ -6,9 +6,9 @@
|
|||||||
#
|
#
|
||||||
|
|
||||||
project (
|
project (
|
||||||
'eg25manager',
|
'eg25-manager',
|
||||||
'c',
|
'c',
|
||||||
version : '0.1.2',
|
version : '0.2.0',
|
||||||
license : 'GPLv3+',
|
license : 'GPLv3+',
|
||||||
meson_version : '>= 0.50.0',
|
meson_version : '>= 0.50.0',
|
||||||
default_options :
|
default_options :
|
||||||
@@ -27,6 +27,7 @@ prefix = get_option('prefix')
|
|||||||
datadir = get_option('datadir')
|
datadir = get_option('datadir')
|
||||||
sysconfdir = get_option('sysconfdir')
|
sysconfdir = get_option('sysconfdir')
|
||||||
bindir = join_paths(prefix, get_option('bindir'))
|
bindir = join_paths(prefix, get_option('bindir'))
|
||||||
|
udevrulesdir = join_paths(prefix, 'lib/udev/rules.d')
|
||||||
|
|
||||||
if datadir.startswith('/')
|
if datadir.startswith('/')
|
||||||
full_datadir = datadir
|
full_datadir = datadir
|
||||||
@@ -40,6 +41,12 @@ else
|
|||||||
full_sysconfdir = join_paths(prefix, sysconfdir)
|
full_sysconfdir = join_paths(prefix, sysconfdir)
|
||||||
endif
|
endif
|
||||||
|
|
||||||
|
eg25_confdir = join_paths(full_sysconfdir, meson.project_name())
|
||||||
|
eg25_datadir = join_paths(full_datadir, meson.project_name())
|
||||||
|
|
||||||
|
add_global_arguments('-D@0@="@1@"'.format('EG25_CONFDIR', eg25_confdir), language : 'c')
|
||||||
|
add_global_arguments('-D@0@="@1@"'.format('EG25_DATADIR', eg25_datadir), language : 'c')
|
||||||
|
|
||||||
mgr_deps = [
|
mgr_deps = [
|
||||||
dependency('glib-2.0'),
|
dependency('glib-2.0'),
|
||||||
dependency('gio-unix-2.0'),
|
dependency('gio-unix-2.0'),
|
||||||
@@ -49,4 +56,6 @@ mgr_deps = [
|
|||||||
dependency('mm-glib'),
|
dependency('mm-glib'),
|
||||||
]
|
]
|
||||||
|
|
||||||
|
subdir('data')
|
||||||
subdir('src')
|
subdir('src')
|
||||||
|
subdir('udev')
|
||||||
|
148
src/at.c
148
src/at.c
@@ -16,8 +16,6 @@
|
|||||||
|
|
||||||
#include <glib-unix.h>
|
#include <glib-unix.h>
|
||||||
|
|
||||||
#define MODEM_UART "/dev/ttyS2"
|
|
||||||
|
|
||||||
struct AtCommand {
|
struct AtCommand {
|
||||||
char *cmd;
|
char *cmd;
|
||||||
char *subcmd;
|
char *subcmd;
|
||||||
@@ -26,6 +24,11 @@ struct AtCommand {
|
|||||||
int retries;
|
int retries;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static GArray *configure_commands = NULL;
|
||||||
|
static GArray *suspend_commands = NULL;
|
||||||
|
static GArray *resume_commands = NULL;
|
||||||
|
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;
|
||||||
@@ -202,8 +205,10 @@ static gboolean modem_response(gint fd,
|
|||||||
|
|
||||||
g_message("Response: [%s]", response);
|
g_message("Response: [%s]", response);
|
||||||
|
|
||||||
if (strcmp(response, "RDY") == 0)
|
if (strcmp(response, "RDY") == 0) {
|
||||||
|
suspend_inhibit(manager, TRUE, TRUE);
|
||||||
manager->modem_state = EG25_STATE_STARTED;
|
manager->modem_state = EG25_STATE_STARTED;
|
||||||
|
}
|
||||||
else if (strstr(response, "ERROR"))
|
else if (strstr(response, "ERROR"))
|
||||||
retry_at_command(manager);
|
retry_at_command(manager);
|
||||||
else if (strstr(response, "OK"))
|
else if (strstr(response, "OK"))
|
||||||
@@ -216,16 +221,86 @@ static gboolean modem_response(gint fd,
|
|||||||
return TRUE;
|
return TRUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
int at_init(struct EG25Manager *manager)
|
static void parse_commands_list(toml_array_t *array, GArray **cmds)
|
||||||
{
|
{
|
||||||
manager->at_fd = configure_serial(MODEM_UART);
|
int len;
|
||||||
|
|
||||||
|
len = toml_array_nelem(array);
|
||||||
|
*cmds = g_array_new(FALSE, TRUE, sizeof(struct AtCommand));
|
||||||
|
g_array_set_size(*cmds, (guint)len);
|
||||||
|
for (int i = 0; i < len; i++) {
|
||||||
|
struct AtCommand *cmd = &g_array_index(*cmds, struct AtCommand, i);
|
||||||
|
toml_table_t *table = toml_table_at(array, i);
|
||||||
|
toml_datum_t value;
|
||||||
|
|
||||||
|
if (!table)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
value = toml_string_in(table, "cmd");
|
||||||
|
if (value.ok) {
|
||||||
|
cmd->cmd = g_strdup(value.u.s);
|
||||||
|
free(value.u.s);
|
||||||
|
}
|
||||||
|
|
||||||
|
value = toml_string_in(table, "subcmd");
|
||||||
|
if (value.ok) {
|
||||||
|
cmd->subcmd = g_strdup(value.u.s);
|
||||||
|
free(value.u.s);
|
||||||
|
}
|
||||||
|
|
||||||
|
value = toml_string_in(table, "value");
|
||||||
|
if (value.ok) {
|
||||||
|
cmd->value = g_strdup(value.u.s);
|
||||||
|
free(value.u.s);
|
||||||
|
}
|
||||||
|
|
||||||
|
value = toml_string_in(table, "expect");
|
||||||
|
if (value.ok) {
|
||||||
|
cmd->expected = g_strdup(value.u.s);
|
||||||
|
free(value.u.s);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int at_init(struct EG25Manager *manager, toml_table_t *config)
|
||||||
|
{
|
||||||
|
toml_array_t *commands;
|
||||||
|
toml_datum_t uart_port;
|
||||||
|
|
||||||
|
uart_port = toml_string_in(config, "uart");
|
||||||
|
if (!uart_port.ok)
|
||||||
|
g_error("Configuration file lacks UART port definition");
|
||||||
|
|
||||||
|
manager->at_fd = configure_serial(uart_port.u.s);
|
||||||
if (manager->at_fd < 0) {
|
if (manager->at_fd < 0) {
|
||||||
g_critical("Unable to configure %s", MODEM_UART);
|
g_critical("Unable to configure %s", uart_port.u.s);
|
||||||
|
free(uart_port.u.s);
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
free(uart_port.u.s);
|
||||||
|
|
||||||
manager->at_source = g_unix_fd_add(manager->at_fd, G_IO_IN, modem_response, manager);
|
manager->at_source = g_unix_fd_add(manager->at_fd, G_IO_IN, modem_response, manager);
|
||||||
|
|
||||||
|
commands = toml_array_in(config, "configure");
|
||||||
|
if (!commands)
|
||||||
|
g_error("Configuration file lacks initial AT commands list");
|
||||||
|
parse_commands_list(commands, &configure_commands);
|
||||||
|
|
||||||
|
commands = toml_array_in(config, "suspend");
|
||||||
|
if (!commands)
|
||||||
|
g_error("Configuration file lacks suspend AT commands list");
|
||||||
|
parse_commands_list(commands, &suspend_commands);
|
||||||
|
|
||||||
|
commands = toml_array_in(config, "resume");
|
||||||
|
if (!commands)
|
||||||
|
g_error("Configuration file lacks resume AT commands list");
|
||||||
|
parse_commands_list(commands, &resume_commands);
|
||||||
|
|
||||||
|
commands = toml_array_in(config, "reset");
|
||||||
|
if (!commands)
|
||||||
|
g_error("Configuration file lacks reset AT commands list");
|
||||||
|
parse_commands_list(commands, &reset_commands);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -234,66 +309,45 @@ void at_destroy(struct EG25Manager *manager)
|
|||||||
g_source_remove(manager->at_source);
|
g_source_remove(manager->at_source);
|
||||||
if (manager->at_fd > 0)
|
if (manager->at_fd > 0)
|
||||||
close(manager->at_fd);
|
close(manager->at_fd);
|
||||||
|
|
||||||
|
g_array_free(configure_commands, TRUE);
|
||||||
|
g_array_free(suspend_commands, TRUE);
|
||||||
|
g_array_free(resume_commands, TRUE);
|
||||||
|
g_array_free(reset_commands, TRUE);
|
||||||
}
|
}
|
||||||
|
|
||||||
void at_sequence_configure(struct EG25Manager *manager)
|
void at_sequence_configure(struct EG25Manager *manager)
|
||||||
{
|
{
|
||||||
/*
|
for (guint i = 0; i < configure_commands->len; i++) {
|
||||||
* Default parameters in megi's driver which differ with our own:
|
struct AtCommand *cmd = &g_array_index(configure_commands, struct AtCommand, i);
|
||||||
* - urc/ri/x are always set the same way on both BH and CE
|
append_at_command(manager, cmd->cmd, cmd->subcmd, cmd->value, cmd->expected);
|
||||||
* - urc/ri/x pulse duration is 1 ms and urc/delay is 0 (no need to delay
|
|
||||||
* URCs if the pulse is that short, so this is expected)
|
|
||||||
* - apready is disabled
|
|
||||||
*
|
|
||||||
* Parameters set in megi's kernel but not here:
|
|
||||||
* - sleepind/level = 0
|
|
||||||
* - wakeupin/level = 0
|
|
||||||
* - ApRstLevel = 0
|
|
||||||
* - ModemRstLevel = 0
|
|
||||||
* - airplanecontrol = 1
|
|
||||||
* - fast/poweroff = 1
|
|
||||||
* (those would need to be researched to check their usefulness for our
|
|
||||||
* use-case)
|
|
||||||
*/
|
|
||||||
|
|
||||||
append_at_command(manager, "QDAI", NULL, NULL, "1,1,0,1,0,0,1,1");
|
|
||||||
append_at_command(manager, "QCFG", "risignaltype", NULL, "\"physical\"");
|
|
||||||
append_at_command(manager, "QCFG", "ims", NULL, "1");
|
|
||||||
if (manager->braveheart) {
|
|
||||||
append_at_command(manager, "QCFG", "urc/ri/ring", NULL, "\"pulse\",2000,1000,5000,\"off\",1");
|
|
||||||
append_at_command(manager, "QCFG", "urc/ri/smsincoming", NULL, "\"pulse\",2000");
|
|
||||||
append_at_command(manager, "QCFG", "urc/ri/other", NULL, "\"off\",1");
|
|
||||||
append_at_command(manager, "QCFG", "urc/delay", NULL, "1");
|
|
||||||
} else {
|
|
||||||
append_at_command(manager, "QCFG", "apready", NULL, "1,0,500");
|
|
||||||
}
|
}
|
||||||
append_at_command(manager, "QURCCFG", "urcport", NULL, "\"usbat\"");
|
|
||||||
if (manager->manage_gnss)
|
|
||||||
append_at_command(manager, "QGPS", NULL, NULL, "1");
|
|
||||||
append_at_command(manager, "QSCLK", NULL, "1", NULL);
|
|
||||||
// Make sure URC cache is disabled
|
|
||||||
append_at_command(manager, "QCFG", "urc/cache", "0", NULL);
|
|
||||||
send_at_command(manager);
|
send_at_command(manager);
|
||||||
}
|
}
|
||||||
|
|
||||||
void at_sequence_suspend(struct EG25Manager *manager)
|
void at_sequence_suspend(struct EG25Manager *manager)
|
||||||
{
|
{
|
||||||
if (manager->manage_gnss)
|
for (guint i = 0; i < suspend_commands->len; i++) {
|
||||||
append_at_command(manager, "QGPSEND", NULL, NULL, NULL);
|
struct AtCommand *cmd = &g_array_index(suspend_commands, struct AtCommand, i);
|
||||||
append_at_command(manager, "QCFG", "urc/cache", "1", NULL);
|
append_at_command(manager, cmd->cmd, cmd->subcmd, cmd->value, cmd->expected);
|
||||||
|
}
|
||||||
send_at_command(manager);
|
send_at_command(manager);
|
||||||
}
|
}
|
||||||
|
|
||||||
void at_sequence_resume(struct EG25Manager *manager)
|
void at_sequence_resume(struct EG25Manager *manager)
|
||||||
{
|
{
|
||||||
append_at_command(manager, "QCFG", "urc/cache", "0", NULL);
|
for (guint i = 0; i < resume_commands->len; i++) {
|
||||||
if (manager->manage_gnss)
|
struct AtCommand *cmd = &g_array_index(resume_commands, struct AtCommand, i);
|
||||||
append_at_command(manager, "QGPS", NULL, "1", NULL);
|
append_at_command(manager, cmd->cmd, cmd->subcmd, cmd->value, cmd->expected);
|
||||||
|
}
|
||||||
send_at_command(manager);
|
send_at_command(manager);
|
||||||
}
|
}
|
||||||
|
|
||||||
void at_sequence_reset(struct EG25Manager *manager)
|
void at_sequence_reset(struct EG25Manager *manager)
|
||||||
{
|
{
|
||||||
append_at_command(manager, "CFUN", NULL, "1,1", NULL);
|
for (guint i = 0; i < reset_commands->len; i++) {
|
||||||
|
struct AtCommand *cmd = &g_array_index(reset_commands, struct AtCommand, i);
|
||||||
|
append_at_command(manager, cmd->cmd, cmd->subcmd, cmd->value, cmd->expected);
|
||||||
|
}
|
||||||
send_at_command(manager);
|
send_at_command(manager);
|
||||||
}
|
}
|
||||||
|
2
src/at.h
2
src/at.h
@@ -8,7 +8,7 @@
|
|||||||
|
|
||||||
#include "manager.h"
|
#include "manager.h"
|
||||||
|
|
||||||
int at_init(struct EG25Manager *data);
|
int at_init(struct EG25Manager *data, toml_table_t *config);
|
||||||
void at_destroy(struct EG25Manager *data);
|
void at_destroy(struct EG25Manager *data);
|
||||||
|
|
||||||
void at_sequence_configure(struct EG25Manager *data);
|
void at_sequence_configure(struct EG25Manager *data);
|
||||||
|
59
src/gpio.c
59
src/gpio.c
@@ -22,35 +22,12 @@ enum {
|
|||||||
GPIO_OUT_COUNT
|
GPIO_OUT_COUNT
|
||||||
};
|
};
|
||||||
|
|
||||||
static unsigned gpio_out_idx_bh[] = {
|
|
||||||
358,
|
|
||||||
35,
|
|
||||||
68,
|
|
||||||
231,
|
|
||||||
232
|
|
||||||
};
|
|
||||||
|
|
||||||
static unsigned gpio_out_idx_ce[] = {
|
|
||||||
34,
|
|
||||||
35,
|
|
||||||
68,
|
|
||||||
231,
|
|
||||||
232
|
|
||||||
};
|
|
||||||
|
|
||||||
enum {
|
enum {
|
||||||
GPIO_IN_STATUS = 0,
|
GPIO_IN_STATUS = 0,
|
||||||
GPIO_IN_COUNT
|
GPIO_IN_COUNT
|
||||||
};
|
};
|
||||||
|
|
||||||
static unsigned gpio_in_idx_bh[] = {
|
|
||||||
GPIO_IDX_INVAL,
|
|
||||||
};
|
|
||||||
|
|
||||||
static unsigned gpio_in_idx_ce[] = {
|
|
||||||
233,
|
|
||||||
};
|
|
||||||
|
|
||||||
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);
|
gpiod_line_set_value(manager->gpio_out[GPIO_OUT_PWRKEY], 1);
|
||||||
@@ -92,11 +69,24 @@ int gpio_sequence_resume(struct EG25Manager *manager)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int gpio_init(struct EG25Manager *manager)
|
static guint get_config_gpio(toml_table_t *config, const char *id)
|
||||||
|
{
|
||||||
|
toml_datum_t value = toml_int_in(config, id);
|
||||||
|
guint gpio;
|
||||||
|
|
||||||
|
if (!value.ok)
|
||||||
|
return GPIO_IDX_INVAL;
|
||||||
|
|
||||||
|
gpio = (guint)value.u.i;
|
||||||
|
|
||||||
|
return gpio;
|
||||||
|
}
|
||||||
|
|
||||||
|
int gpio_init(struct EG25Manager *manager, toml_table_t *config)
|
||||||
{
|
{
|
||||||
int i, ret;
|
int i, ret;
|
||||||
unsigned *gpio_in_idx;
|
guint gpio_out_idx[GPIO_OUT_COUNT];
|
||||||
unsigned *gpio_out_idx;
|
guint gpio_in_idx[GPIO_IN_COUNT];
|
||||||
|
|
||||||
manager->gpiochip[0] = gpiod_chip_open_by_label(GPIO_CHIP1_LABEL);
|
manager->gpiochip[0] = gpiod_chip_open_by_label(GPIO_CHIP1_LABEL);
|
||||||
if (!manager->gpiochip[0]) {
|
if (!manager->gpiochip[0]) {
|
||||||
@@ -110,16 +100,15 @@ int gpio_init(struct EG25Manager *manager)
|
|||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (manager->braveheart) {
|
gpio_out_idx[GPIO_OUT_DTR] = get_config_gpio(config, "dtr");
|
||||||
gpio_in_idx = gpio_in_idx_bh;
|
gpio_out_idx[GPIO_OUT_PWRKEY] = get_config_gpio(config, "pwrkey");
|
||||||
gpio_out_idx = gpio_out_idx_bh;
|
gpio_out_idx[GPIO_OUT_RESET] = get_config_gpio(config, "reset");
|
||||||
} else {
|
gpio_out_idx[GPIO_OUT_APREADY] = get_config_gpio(config, "apready");
|
||||||
gpio_in_idx = gpio_in_idx_ce;
|
gpio_out_idx[GPIO_OUT_DISABLE] = get_config_gpio(config, "disable");
|
||||||
gpio_out_idx = gpio_out_idx_ce;
|
gpio_in_idx[GPIO_IN_STATUS] = get_config_gpio(config, "status");
|
||||||
}
|
|
||||||
|
|
||||||
for (i = 0; i < GPIO_OUT_COUNT; i++) {
|
for (i = 0; i < GPIO_OUT_COUNT; i++) {
|
||||||
unsigned int offset, chipidx;
|
guint offset, chipidx;
|
||||||
|
|
||||||
if (gpio_out_idx[i] < MAX_GPIOCHIP_LINES) {
|
if (gpio_out_idx[i] < MAX_GPIOCHIP_LINES) {
|
||||||
offset = gpio_out_idx[i];
|
offset = gpio_out_idx[i];
|
||||||
@@ -143,7 +132,7 @@ int gpio_init(struct EG25Manager *manager)
|
|||||||
}
|
}
|
||||||
|
|
||||||
for (i = 0; i < GPIO_IN_COUNT; i++) {
|
for (i = 0; i < GPIO_IN_COUNT; i++) {
|
||||||
unsigned int offset, chipidx;
|
guint offset, chipidx;
|
||||||
|
|
||||||
if (gpio_in_idx[i] == GPIO_IDX_INVAL)
|
if (gpio_in_idx[i] == GPIO_IDX_INVAL)
|
||||||
continue;
|
continue;
|
||||||
|
@@ -8,7 +8,7 @@
|
|||||||
|
|
||||||
#include "manager.h"
|
#include "manager.h"
|
||||||
|
|
||||||
int gpio_init(struct EG25Manager *state);
|
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);
|
||||||
|
130
src/manager.c
130
src/manager.c
@@ -19,8 +19,13 @@
|
|||||||
#include <glib-unix.h>
|
#include <glib-unix.h>
|
||||||
#include <libusb.h>
|
#include <libusb.h>
|
||||||
|
|
||||||
#define EG25_USB_VID 0x2c7c
|
#ifndef EG25_CONFDIR
|
||||||
#define EG25_USB_PID 0x0125
|
#define EG25_CONFDIR "/etc/eg25-manager"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef EG25_DATADIR
|
||||||
|
#define EG25_DATADIR "/usr/share/eg25-manager"
|
||||||
|
#endif
|
||||||
|
|
||||||
static gboolean quit_app(struct EG25Manager *manager)
|
static gboolean quit_app(struct EG25Manager *manager)
|
||||||
{
|
{
|
||||||
@@ -58,14 +63,14 @@ static gboolean modem_start(struct EG25Manager *manager)
|
|||||||
libusb_device **devices = NULL;
|
libusb_device **devices = NULL;
|
||||||
struct libusb_device_descriptor desc;
|
struct libusb_device_descriptor desc;
|
||||||
|
|
||||||
if (manager->braveheart) {
|
if (manager->use_libusb) {
|
||||||
// BH don't have the STATUS line connected, so check if USB device is present
|
// BH don't have the STATUS line connected, so check if USB device is present
|
||||||
libusb_init(&ctx);
|
libusb_init(&ctx);
|
||||||
|
|
||||||
count = libusb_get_device_list(ctx, &devices);
|
count = libusb_get_device_list(ctx, &devices);
|
||||||
for (i = 0; i < count; i++) {
|
for (i = 0; i < count; i++) {
|
||||||
libusb_get_device_descriptor(devices[i], &desc);
|
libusb_get_device_descriptor(devices[i], &desc);
|
||||||
if (desc.idVendor == EG25_USB_VID && desc.idProduct == EG25_USB_PID) {
|
if (desc.idVendor == manager->usb_vid && desc.idProduct == manager->usb_pid) {
|
||||||
g_message("Found corresponding USB device, modem already powered");
|
g_message("Found corresponding USB device, modem already powered");
|
||||||
should_boot = FALSE;
|
should_boot = FALSE;
|
||||||
break;
|
break;
|
||||||
@@ -126,9 +131,9 @@ void modem_reset(struct EG25Manager *manager)
|
|||||||
if (manager->reset_timer)
|
if (manager->reset_timer)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (manager->suspend_timer) {
|
if (manager->modem_recovery_timer) {
|
||||||
g_source_remove(manager->suspend_timer);
|
g_source_remove(manager->modem_recovery_timer);
|
||||||
manager->suspend_timer = 0;
|
manager->modem_recovery_timer = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!manager->modem_usb_id) {
|
if (!manager->modem_usb_id) {
|
||||||
@@ -165,7 +170,14 @@ void modem_reset(struct EG25Manager *manager)
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
error:
|
error:
|
||||||
// Everything else failed, reset the modem
|
// Release blocking sleep inhibitor
|
||||||
|
if (manager->suspend_block_fd >= 0)
|
||||||
|
suspend_inhibit(manager, FALSE, TRUE);
|
||||||
|
if (manager->modem_boot_timer) {
|
||||||
|
g_source_remove(manager->modem_boot_timer);
|
||||||
|
manager->modem_boot_timer = 0;
|
||||||
|
}
|
||||||
|
// Everything else failed, reboot the modem
|
||||||
at_sequence_reset(manager);
|
at_sequence_reset(manager);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -178,7 +190,7 @@ void modem_suspend_post(struct EG25Manager *manager)
|
|||||||
{
|
{
|
||||||
gpio_sequence_suspend(manager);
|
gpio_sequence_suspend(manager);
|
||||||
g_message("suspend sequence is over, drop inhibitor");
|
g_message("suspend sequence is over, drop inhibitor");
|
||||||
suspend_inhibit(manager, FALSE);
|
suspend_inhibit(manager, FALSE, FALSE);
|
||||||
}
|
}
|
||||||
|
|
||||||
void modem_resume_pre(struct EG25Manager *manager)
|
void modem_resume_pre(struct EG25Manager *manager)
|
||||||
@@ -191,21 +203,78 @@ void modem_resume_post(struct EG25Manager *manager)
|
|||||||
at_sequence_resume(manager);
|
at_sequence_resume(manager);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static toml_table_t *parse_config_file(char *config_file)
|
||||||
|
{
|
||||||
|
toml_table_t *toml_config;
|
||||||
|
gchar *compatible;
|
||||||
|
gchar error[256];
|
||||||
|
gsize len;
|
||||||
|
FILE *f = NULL;
|
||||||
|
|
||||||
|
if (config_file) {
|
||||||
|
f = fopen(config_file, "r");
|
||||||
|
} else if (g_file_get_contents("/proc/device-tree/compatible", &compatible, &len, NULL)) {
|
||||||
|
g_autoptr (GPtrArray) compat = g_ptr_array_new();
|
||||||
|
gsize pos = 0;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* `compatible` file is a list of NULL-terminated strings, convert it
|
||||||
|
* to an array
|
||||||
|
*/
|
||||||
|
do {
|
||||||
|
g_ptr_array_add(compat, &compatible[pos]);
|
||||||
|
pos += strlen(&compatible[pos]) + 1;
|
||||||
|
} while (pos < len);
|
||||||
|
|
||||||
|
for (pos = 0; pos < compat->len; pos++) {
|
||||||
|
g_autofree gchar *filename = g_strdup_printf(EG25_CONFDIR "/%s.toml", (gchar *)g_ptr_array_index(compat, pos));
|
||||||
|
if (access(filename, F_OK) == 0) {
|
||||||
|
g_message("Opening config file: %s", filename);
|
||||||
|
f = fopen(filename, "r");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!f) {
|
||||||
|
for (pos = 0; pos < compat->len; pos++) {
|
||||||
|
g_autofree gchar *filename = g_strdup_printf(EG25_DATADIR "/%s.toml", (gchar *)g_ptr_array_index(compat, pos));
|
||||||
|
if (access(filename, F_OK) == 0) {
|
||||||
|
g_message("Opening config file: %s", filename);
|
||||||
|
f = fopen(filename, "r");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!f)
|
||||||
|
g_error("unable to find a suitable config file!");
|
||||||
|
|
||||||
|
toml_config = toml_parse_file(f, error, sizeof(error));
|
||||||
|
if (!toml_config)
|
||||||
|
g_error("unable to parse config file: %s", error);
|
||||||
|
|
||||||
|
return toml_config;
|
||||||
|
}
|
||||||
|
|
||||||
int main(int argc, char *argv[])
|
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;
|
||||||
char compatible[32];
|
gchar *config_file = NULL;
|
||||||
int fd, ret;
|
toml_table_t *toml_config;
|
||||||
|
toml_table_t *toml_manager;
|
||||||
|
toml_datum_t toml_value;
|
||||||
const GOptionEntry options[] = {
|
const GOptionEntry options[] = {
|
||||||
{ "gnss", 'g', 0, G_OPTION_ARG_NONE, &manager.manage_gnss, "Manage the GNSS feature.", NULL },
|
{ "config", 'c', 0, G_OPTION_ARG_STRING, &config_file, "Config file to use.", 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));
|
||||||
manager.at_fd = -1;
|
manager.at_fd = -1;
|
||||||
manager.suspend_inhibit_fd = -1;
|
manager.suspend_delay_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);
|
||||||
@@ -216,21 +285,28 @@ int main(int argc, char *argv[])
|
|||||||
|
|
||||||
manager.loop = g_main_loop_new(NULL, FALSE);
|
manager.loop = g_main_loop_new(NULL, FALSE);
|
||||||
|
|
||||||
fd = open("/proc/device-tree/compatible", O_RDONLY);
|
toml_config = parse_config_file(config_file);
|
||||||
if (fd < 0) {
|
|
||||||
g_critical("Unable to read 'compatible' string from device tree");
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
ret = read(fd, compatible, sizeof(compatible));
|
|
||||||
if (ret > 0 && !strstr(compatible, "pine64,pinephone-1.2"))
|
|
||||||
manager.braveheart = TRUE;
|
|
||||||
close(fd);
|
|
||||||
|
|
||||||
at_init(&manager);
|
toml_manager = toml_table_in(toml_config, "manager");
|
||||||
gpio_init(&manager);
|
if (toml_manager) {
|
||||||
mm_iface_init(&manager);
|
toml_value = toml_bool_in(toml_manager, "need_libusb");
|
||||||
suspend_init(&manager);
|
if (toml_value.ok)
|
||||||
udev_init(&manager);
|
manager.use_libusb = toml_value.u.b;
|
||||||
|
|
||||||
|
toml_value = toml_int_in(toml_manager, "usb_vid");
|
||||||
|
if (toml_value.ok)
|
||||||
|
manager.usb_vid = toml_value.u.i;
|
||||||
|
|
||||||
|
toml_value = toml_int_in(toml_manager, "usb_pid");
|
||||||
|
if (toml_value.ok)
|
||||||
|
manager.usb_pid = toml_value.u.i;
|
||||||
|
}
|
||||||
|
|
||||||
|
at_init(&manager, toml_table_in(toml_config, "at"));
|
||||||
|
gpio_init(&manager, toml_table_in(toml_config, "gpio"));
|
||||||
|
mm_iface_init(&manager, toml_table_in(toml_config, "mm-iface"));
|
||||||
|
suspend_init(&manager, toml_table_in(toml_config, "suspend"));
|
||||||
|
udev_init(&manager, toml_table_in(toml_config, "udev"));
|
||||||
|
|
||||||
g_idle_add(G_SOURCE_FUNC(modem_start), &manager);
|
g_idle_add(G_SOURCE_FUNC(modem_start), &manager);
|
||||||
|
|
||||||
|
@@ -11,6 +11,8 @@
|
|||||||
#include <gudev/gudev.h>
|
#include <gudev/gudev.h>
|
||||||
#include <libmm-glib.h>
|
#include <libmm-glib.h>
|
||||||
|
|
||||||
|
#include "toml.h"
|
||||||
|
|
||||||
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
|
||||||
@@ -28,7 +30,9 @@ enum EG25State {
|
|||||||
struct EG25Manager {
|
struct EG25Manager {
|
||||||
GMainLoop *loop;
|
GMainLoop *loop;
|
||||||
guint reset_timer;
|
guint reset_timer;
|
||||||
gboolean manage_gnss;
|
gboolean use_libusb;
|
||||||
|
guint usb_vid;
|
||||||
|
guint usb_pid;
|
||||||
|
|
||||||
int at_fd;
|
int at_fd;
|
||||||
guint at_source;
|
guint at_source;
|
||||||
@@ -36,15 +40,19 @@ struct EG25Manager {
|
|||||||
|
|
||||||
enum EG25State modem_state;
|
enum EG25State modem_state;
|
||||||
gchar *modem_usb_id;
|
gchar *modem_usb_id;
|
||||||
gboolean braveheart;
|
|
||||||
|
|
||||||
guint mm_watch;
|
guint mm_watch;
|
||||||
MMManager *mm_manager;
|
MMManager *mm_manager;
|
||||||
MMModem *mm_modem;
|
MMModem *mm_modem;
|
||||||
|
|
||||||
GDBusProxy *suspend_proxy;
|
GDBusProxy *suspend_proxy;
|
||||||
int suspend_inhibit_fd;
|
int suspend_delay_fd;
|
||||||
guint suspend_timer;
|
int suspend_block_fd;
|
||||||
|
|
||||||
|
guint modem_recovery_timer;
|
||||||
|
guint modem_recovery_timeout;
|
||||||
|
guint modem_boot_timer;
|
||||||
|
guint modem_boot_timeout;
|
||||||
|
|
||||||
GUdevClient *udev;
|
GUdevClient *udev;
|
||||||
|
|
||||||
|
@@ -12,6 +12,7 @@ executable (
|
|||||||
'manager.c', 'manager.h',
|
'manager.c', 'manager.h',
|
||||||
'mm-iface.c', 'mm-iface.h',
|
'mm-iface.c', 'mm-iface.h',
|
||||||
'suspend.c', 'suspend.h',
|
'suspend.c', 'suspend.h',
|
||||||
|
'toml.c', 'toml.h',
|
||||||
'udev.c', 'udev.h',
|
'udev.c', 'udev.h',
|
||||||
],
|
],
|
||||||
dependencies : mgr_deps,
|
dependencies : mgr_deps,
|
||||||
|
@@ -35,9 +35,9 @@ static void add_modem(struct EG25Manager *manager, GDBusObject *object)
|
|||||||
g_assert(manager->mm_modem != NULL);
|
g_assert(manager->mm_modem != NULL);
|
||||||
|
|
||||||
if (manager->modem_state == EG25_STATE_RESUMING) {
|
if (manager->modem_state == EG25_STATE_RESUMING) {
|
||||||
if (manager->suspend_timer) {
|
if (manager->modem_recovery_timer) {
|
||||||
g_source_remove(manager->suspend_timer);
|
g_source_remove(manager->modem_recovery_timer);
|
||||||
manager->suspend_timer = 0;
|
manager->modem_recovery_timer = 0;
|
||||||
}
|
}
|
||||||
modem_resume_post(manager);
|
modem_resume_post(manager);
|
||||||
manager->modem_state = EG25_STATE_CONFIGURED;
|
manager->modem_state = EG25_STATE_CONFIGURED;
|
||||||
@@ -167,15 +167,27 @@ static void mm_appeared_cb(GDBusConnection *connection,
|
|||||||
NULL, (GAsyncReadyCallback)mm_manager_new_cb, manager);
|
NULL, (GAsyncReadyCallback)mm_manager_new_cb, manager);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void mm_iface_clean(struct EG25Manager *manager)
|
||||||
|
{
|
||||||
|
if (manager->mm_manager) {
|
||||||
|
g_clear_object(&manager->mm_manager);
|
||||||
|
manager->mm_manager = NULL;
|
||||||
|
}
|
||||||
|
if (manager->modem_usb_id) {
|
||||||
|
g_free(manager->modem_usb_id);
|
||||||
|
manager->modem_usb_id = NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static void mm_vanished_cb(GDBusConnection *connection,
|
static void mm_vanished_cb(GDBusConnection *connection,
|
||||||
const gchar *name,
|
const gchar *name,
|
||||||
struct EG25Manager *manager)
|
struct EG25Manager *manager)
|
||||||
{
|
{
|
||||||
g_message("ModemManager vanished from D-Bus");
|
g_message("ModemManager vanished from D-Bus");
|
||||||
mm_iface_destroy(manager);
|
mm_iface_clean(manager);
|
||||||
}
|
}
|
||||||
|
|
||||||
void mm_iface_init(struct EG25Manager *manager)
|
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,
|
||||||
@@ -186,14 +198,7 @@ void mm_iface_init(struct EG25Manager *manager)
|
|||||||
|
|
||||||
void mm_iface_destroy(struct EG25Manager *manager)
|
void mm_iface_destroy(struct EG25Manager *manager)
|
||||||
{
|
{
|
||||||
if (manager->mm_manager) {
|
mm_iface_clean(manager);
|
||||||
g_clear_object(&manager->mm_manager);
|
|
||||||
manager->mm_manager = NULL;
|
|
||||||
}
|
|
||||||
if (manager->modem_usb_id) {
|
|
||||||
g_free(manager->modem_usb_id);
|
|
||||||
manager->modem_usb_id = NULL;
|
|
||||||
}
|
|
||||||
if (manager->mm_watch != 0) {
|
if (manager->mm_watch != 0) {
|
||||||
g_bus_unwatch_name(manager->mm_watch);
|
g_bus_unwatch_name(manager->mm_watch);
|
||||||
manager->mm_watch = 0;
|
manager->mm_watch = 0;
|
||||||
|
@@ -8,5 +8,5 @@
|
|||||||
|
|
||||||
#include "manager.h"
|
#include "manager.h"
|
||||||
|
|
||||||
void mm_iface_init(struct EG25Manager *data);
|
void mm_iface_init(struct EG25Manager *data, toml_table_t *config);
|
||||||
void mm_iface_destroy(struct EG25Manager *data);
|
void mm_iface_destroy(struct EG25Manager *data);
|
||||||
|
187
src/suspend.c
187
src/suspend.c
@@ -13,33 +13,43 @@
|
|||||||
|
|
||||||
#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 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!");
|
g_message("Modem wasn't probed in time, restart it!");
|
||||||
manager->suspend_timer = 0;
|
manager->modem_recovery_timer = 0;
|
||||||
modem_reset(manager);
|
modem_reset(manager);
|
||||||
|
|
||||||
return FALSE;
|
return FALSE;
|
||||||
}
|
}
|
||||||
|
|
||||||
static gboolean drop_inhibitor(struct EG25Manager *manager)
|
static gboolean drop_inhibitor(struct EG25Manager *manager, gboolean block)
|
||||||
{
|
{
|
||||||
if (manager->suspend_inhibit_fd >= 0) {
|
if (block) {
|
||||||
g_message("dropping systemd sleep inhibitor");
|
if (manager->suspend_block_fd >= 0) {
|
||||||
close(manager->suspend_inhibit_fd);
|
g_message("dropping systemd sleep block inhibitor");
|
||||||
manager->suspend_inhibit_fd = -1;
|
close(manager->suspend_block_fd);
|
||||||
return TRUE;
|
manager->suspend_block_fd = -1;
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
if (manager->suspend_delay_fd >= 0) {
|
||||||
|
g_message("dropping systemd sleep delay inhibitor");
|
||||||
|
close(manager->suspend_delay_fd);
|
||||||
|
manager->suspend_delay_fd = -1;
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return FALSE;
|
return FALSE;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void inhibit_done(GObject *source,
|
static void inhibit_done_delay(GObject *source,
|
||||||
GAsyncResult *result,
|
GAsyncResult *result,
|
||||||
gpointer user_data)
|
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;
|
||||||
@@ -47,34 +57,95 @@ static void inhibit_done(GObject *source,
|
|||||||
GVariant *res;
|
GVariant *res;
|
||||||
GUnixFDList *fd_list;
|
GUnixFDList *fd_list;
|
||||||
|
|
||||||
res = g_dbus_proxy_call_with_unix_fd_list_finish(suspend_proxy, &fd_list, result, &error);
|
res = g_dbus_proxy_call_with_unix_fd_list_finish(suspend_proxy, &fd_list,
|
||||||
|
result, &error);
|
||||||
if (!res) {
|
if (!res) {
|
||||||
g_warning("inhibit failed: %s", error->message);
|
g_warning("inhibit failed: %s", error->message);
|
||||||
} else {
|
} else {
|
||||||
if (!fd_list || g_unix_fd_list_get_length(fd_list) != 1)
|
if (!fd_list || g_unix_fd_list_get_length(fd_list) != 1)
|
||||||
g_warning("didn't get a single fd back");
|
g_warning("didn't get a single fd back");
|
||||||
|
|
||||||
manager->suspend_inhibit_fd = g_unix_fd_list_get(fd_list, 0, NULL);
|
manager->suspend_delay_fd = g_unix_fd_list_get(fd_list, 0, NULL);
|
||||||
|
|
||||||
g_message("inhibitor fd is %d", manager->suspend_inhibit_fd);
|
g_message("inhibitor sleep fd is %d", manager->suspend_delay_fd);
|
||||||
g_object_unref(fd_list);
|
g_object_unref(fd_list);
|
||||||
g_variant_unref(res);
|
g_variant_unref(res);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void take_inhibitor(struct EG25Manager *manager)
|
static void inhibit_done_block(GObject *source,
|
||||||
|
GAsyncResult *result,
|
||||||
|
gpointer user_data)
|
||||||
|
{
|
||||||
|
GDBusProxy *suspend_proxy = G_DBUS_PROXY(source);
|
||||||
|
struct EG25Manager *manager = user_data;
|
||||||
|
g_autoptr (GError) error = NULL;
|
||||||
|
GVariant *res;
|
||||||
|
GUnixFDList *fd_list;
|
||||||
|
|
||||||
|
res = g_dbus_proxy_call_with_unix_fd_list_finish(suspend_proxy, &fd_list,
|
||||||
|
result, &error);
|
||||||
|
if (!res) {
|
||||||
|
g_warning("inhibit failed: %s", error->message);
|
||||||
|
} else {
|
||||||
|
if (!fd_list || g_unix_fd_list_get_length(fd_list) != 1)
|
||||||
|
g_warning("didn't get a single fd back");
|
||||||
|
|
||||||
|
manager->suspend_block_fd = g_unix_fd_list_get(fd_list, 0, NULL);
|
||||||
|
|
||||||
|
g_message("inhibitor block fd is %d", manager->suspend_block_fd);
|
||||||
|
g_object_unref(fd_list);
|
||||||
|
g_variant_unref(res);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* After the EG25 modem sends 'RDY', it takes up to 2 minutes before all
|
||||||
|
* capabilities are operational. If the modem is suspended before that,
|
||||||
|
* calls and texts may be not recognized properly.
|
||||||
|
*/
|
||||||
|
static gboolean modem_fully_booted(struct EG25Manager *manager)
|
||||||
|
{
|
||||||
|
g_message("Modem is up for %u seconds and fully ready", manager->modem_boot_timeout);
|
||||||
|
manager->modem_boot_timer = 0;
|
||||||
|
drop_inhibitor(manager, TRUE);
|
||||||
|
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void take_inhibitor(struct EG25Manager *manager, gboolean block)
|
||||||
{
|
{
|
||||||
GVariant *variant_arg;
|
GVariant *variant_arg;
|
||||||
|
|
||||||
if(manager->suspend_inhibit_fd != -1)
|
if (block) {
|
||||||
drop_inhibitor(manager);
|
if(manager->suspend_block_fd != -1)
|
||||||
|
drop_inhibitor(manager, TRUE);
|
||||||
|
|
||||||
variant_arg = g_variant_new ("(ssss)", "sleep", "eg25manager",
|
variant_arg = g_variant_new ("(ssss)", "sleep", "eg25manager",
|
||||||
"eg25manager needs to prepare modem for sleep", "delay");
|
"eg25manager needs to wait for modem to be fully booted",
|
||||||
|
"block");
|
||||||
|
|
||||||
g_message("taking systemd sleep inhibitor");
|
g_message("taking systemd sleep inhibitor (blocking)");
|
||||||
g_dbus_proxy_call_with_unix_fd_list(manager->suspend_proxy, "Inhibit", variant_arg,
|
g_dbus_proxy_call_with_unix_fd_list(manager->suspend_proxy, "Inhibit",
|
||||||
0, G_MAXINT, NULL, NULL, inhibit_done, manager);
|
variant_arg, 0, G_MAXINT, NULL, NULL,
|
||||||
|
inhibit_done_block, manager);
|
||||||
|
manager->modem_boot_timer = g_timeout_add_seconds(manager->modem_boot_timeout,
|
||||||
|
G_SOURCE_FUNC(modem_fully_booted),
|
||||||
|
manager);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
if(manager->suspend_delay_fd != -1)
|
||||||
|
drop_inhibitor(manager, FALSE);
|
||||||
|
|
||||||
|
variant_arg = g_variant_new ("(ssss)", "sleep", "eg25manager",
|
||||||
|
"eg25manager needs to prepare modem for sleep",
|
||||||
|
"delay");
|
||||||
|
|
||||||
|
g_message("taking systemd sleep inhibitor");
|
||||||
|
g_dbus_proxy_call_with_unix_fd_list(manager->suspend_proxy, "Inhibit",
|
||||||
|
variant_arg, 0, G_MAXINT, NULL, NULL,
|
||||||
|
inhibit_done_delay, manager);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void signal_cb(GDBusProxy *proxy,
|
static void signal_cb(GDBusProxy *proxy,
|
||||||
@@ -97,10 +168,22 @@ static void signal_cb(GDBusProxy *proxy,
|
|||||||
modem_suspend_pre(manager);
|
modem_suspend_pre(manager);
|
||||||
} else {
|
} else {
|
||||||
g_message("system is resuming");
|
g_message("system is resuming");
|
||||||
take_inhibitor(manager);
|
take_inhibitor(manager, FALSE);
|
||||||
modem_resume_pre(manager);
|
modem_resume_pre(manager);
|
||||||
manager->modem_state = EG25_STATE_RESUMING;
|
if (manager->mm_modem) {
|
||||||
manager->suspend_timer = g_timeout_add_seconds(9, G_SOURCE_FUNC(check_modem_resume), manager);
|
/*
|
||||||
|
* On some systems ModemManager doesn't handle suspend/resume, so
|
||||||
|
* we still have a valid/managed modem when resuming. In this case,
|
||||||
|
* do the whole resume sequence immediately.
|
||||||
|
*/
|
||||||
|
manager->modem_state = EG25_STATE_CONFIGURED;
|
||||||
|
modem_resume_post(manager);
|
||||||
|
} else {
|
||||||
|
manager->modem_state = EG25_STATE_RESUMING;
|
||||||
|
manager->modem_recovery_timer = g_timeout_add_seconds(manager->modem_recovery_timeout,
|
||||||
|
G_SOURCE_FUNC(check_modem_resume),
|
||||||
|
manager);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -116,10 +199,10 @@ static void name_owner_cb(GObject *object,
|
|||||||
|
|
||||||
owner = g_dbus_proxy_get_name_owner(proxy);
|
owner = g_dbus_proxy_get_name_owner(proxy);
|
||||||
if (owner) {
|
if (owner) {
|
||||||
take_inhibitor(manager);
|
take_inhibitor(manager, FALSE);
|
||||||
g_free(owner);
|
g_free(owner);
|
||||||
} else {
|
} else {
|
||||||
drop_inhibitor(manager);
|
drop_inhibitor(manager, FALSE);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -136,18 +219,37 @@ static void on_proxy_acquired(GObject *object,
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
g_signal_connect(manager->suspend_proxy, "notify::g-name-owner", G_CALLBACK(name_owner_cb), manager);
|
g_signal_connect(manager->suspend_proxy, "notify::g-name-owner",
|
||||||
g_signal_connect(manager->suspend_proxy, "g-signal", G_CALLBACK(signal_cb), manager);
|
G_CALLBACK(name_owner_cb), manager);
|
||||||
|
g_signal_connect(manager->suspend_proxy, "g-signal",
|
||||||
|
G_CALLBACK(signal_cb), manager);
|
||||||
|
|
||||||
owner = g_dbus_proxy_get_name_owner(manager->suspend_proxy);
|
owner = g_dbus_proxy_get_name_owner(manager->suspend_proxy);
|
||||||
if (owner) {
|
if (owner) {
|
||||||
take_inhibitor(manager);
|
take_inhibitor(manager, FALSE);
|
||||||
g_free(owner);
|
g_free(owner);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void suspend_init(struct EG25Manager *manager)
|
void suspend_init(struct EG25Manager *manager, toml_table_t *config)
|
||||||
{
|
{
|
||||||
|
toml_datum_t timeout_value;
|
||||||
|
|
||||||
|
if (config) {
|
||||||
|
timeout_value = toml_int_in(config, "boot_timeout");
|
||||||
|
if (timeout_value.ok)
|
||||||
|
manager->modem_boot_timeout = (guint)timeout_value.u.i;
|
||||||
|
|
||||||
|
timeout_value = toml_int_in(config, "recovery_timeout");
|
||||||
|
if (timeout_value.ok)
|
||||||
|
manager->modem_recovery_timeout = (guint)timeout_value.u.i;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (manager->modem_boot_timeout == 0)
|
||||||
|
manager->modem_boot_timeout = 120;
|
||||||
|
if (manager->modem_recovery_timeout == 0)
|
||||||
|
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,
|
||||||
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,
|
||||||
@@ -157,10 +259,15 @@ void suspend_init(struct EG25Manager *manager)
|
|||||||
|
|
||||||
void suspend_destroy(struct EG25Manager *manager)
|
void suspend_destroy(struct EG25Manager *manager)
|
||||||
{
|
{
|
||||||
drop_inhibitor(manager);
|
drop_inhibitor(manager, FALSE);
|
||||||
if (manager->suspend_timer) {
|
drop_inhibitor(manager, TRUE);
|
||||||
g_source_remove(manager->suspend_timer);
|
if (manager->modem_recovery_timer) {
|
||||||
manager->suspend_timer = 0;
|
g_source_remove(manager->modem_recovery_timer);
|
||||||
|
manager->modem_recovery_timer = 0;
|
||||||
|
}
|
||||||
|
if (manager->modem_boot_timer) {
|
||||||
|
g_source_remove(manager->modem_boot_timer);
|
||||||
|
manager->modem_boot_timer = 0;
|
||||||
}
|
}
|
||||||
if (manager->suspend_proxy) {
|
if (manager->suspend_proxy) {
|
||||||
g_object_unref(manager->suspend_proxy);
|
g_object_unref(manager->suspend_proxy);
|
||||||
@@ -168,10 +275,10 @@ void suspend_destroy(struct EG25Manager *manager)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void suspend_inhibit(struct EG25Manager *manager, gboolean inhibit)
|
void suspend_inhibit(struct EG25Manager *manager, gboolean inhibit, gboolean block)
|
||||||
{
|
{
|
||||||
if (inhibit)
|
if (inhibit)
|
||||||
take_inhibitor(manager);
|
take_inhibitor(manager, block);
|
||||||
else
|
else
|
||||||
drop_inhibitor(manager);
|
drop_inhibitor(manager, block);
|
||||||
}
|
}
|
||||||
|
@@ -8,7 +8,7 @@
|
|||||||
|
|
||||||
#include "manager.h"
|
#include "manager.h"
|
||||||
|
|
||||||
void suspend_init (struct EG25Manager *data);
|
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);
|
void suspend_inhibit (struct EG25Manager *data, gboolean inhibit, gboolean block);
|
||||||
|
2249
src/toml.c
Normal file
2249
src/toml.c
Normal file
File diff suppressed because it is too large
Load Diff
175
src/toml.h
Normal file
175
src/toml.h
Normal file
@@ -0,0 +1,175 @@
|
|||||||
|
/*
|
||||||
|
MIT License
|
||||||
|
|
||||||
|
Copyright (c) 2017 - 2019 CK Tan
|
||||||
|
https://github.com/cktan/tomlc99
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
||||||
|
*/
|
||||||
|
#ifndef TOML_H
|
||||||
|
#define TOML_H
|
||||||
|
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
#define TOML_EXTERN extern "C"
|
||||||
|
#else
|
||||||
|
#define TOML_EXTERN extern
|
||||||
|
#endif
|
||||||
|
|
||||||
|
typedef struct toml_timestamp_t toml_timestamp_t;
|
||||||
|
typedef struct toml_table_t toml_table_t;
|
||||||
|
typedef struct toml_array_t toml_array_t;
|
||||||
|
typedef struct toml_datum_t toml_datum_t;
|
||||||
|
|
||||||
|
/* Parse a file. Return a table on success, or 0 otherwise.
|
||||||
|
* Caller must toml_free(the-return-value) after use.
|
||||||
|
*/
|
||||||
|
TOML_EXTERN toml_table_t* toml_parse_file(FILE* fp,
|
||||||
|
char* errbuf,
|
||||||
|
int errbufsz);
|
||||||
|
|
||||||
|
/* Parse a string containing the full config.
|
||||||
|
* Return a table on success, or 0 otherwise.
|
||||||
|
* Caller must toml_free(the-return-value) after use.
|
||||||
|
*/
|
||||||
|
TOML_EXTERN toml_table_t* toml_parse(char* conf, /* NUL terminated, please. */
|
||||||
|
char* errbuf,
|
||||||
|
int errbufsz);
|
||||||
|
|
||||||
|
/* Free the table returned by toml_parse() or toml_parse_file(). Once
|
||||||
|
* this function is called, any handles accessed through this tab
|
||||||
|
* directly or indirectly are no longer valid.
|
||||||
|
*/
|
||||||
|
TOML_EXTERN void toml_free(toml_table_t* tab);
|
||||||
|
|
||||||
|
|
||||||
|
/* Timestamp types. The year, month, day, hour, minute, second, z
|
||||||
|
* fields may be NULL if they are not relevant. e.g. In a DATE
|
||||||
|
* type, the hour, minute, second and z fields will be NULLs.
|
||||||
|
*/
|
||||||
|
struct toml_timestamp_t {
|
||||||
|
struct { /* internal. do not use. */
|
||||||
|
int year, month, day;
|
||||||
|
int hour, minute, second, millisec;
|
||||||
|
char z[10];
|
||||||
|
} __buffer;
|
||||||
|
int *year, *month, *day;
|
||||||
|
int *hour, *minute, *second, *millisec;
|
||||||
|
char* z;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/*-----------------------------------------------------------------
|
||||||
|
* Enhanced access methods
|
||||||
|
*/
|
||||||
|
struct toml_datum_t {
|
||||||
|
int ok;
|
||||||
|
union {
|
||||||
|
toml_timestamp_t* ts; /* ts must be freed after use */
|
||||||
|
char* s; /* string value. s must be freed after use */
|
||||||
|
int b; /* bool value */
|
||||||
|
int64_t i; /* int value */
|
||||||
|
double d; /* double value */
|
||||||
|
} u;
|
||||||
|
};
|
||||||
|
|
||||||
|
/* on arrays: */
|
||||||
|
/* ... retrieve size of array. */
|
||||||
|
TOML_EXTERN int toml_array_nelem(const toml_array_t* arr);
|
||||||
|
/* ... 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_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_double_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. */
|
||||||
|
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);
|
||||||
|
|
||||||
|
/* on tables: */
|
||||||
|
/* ... 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);
|
||||||
|
/* ... 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_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_double_in(const toml_table_t* arr, const char* key);
|
||||||
|
TOML_EXTERN toml_datum_t toml_timestamp_in(const toml_table_t* arr, const char* key);
|
||||||
|
/* .. retrieve array or table using key. */
|
||||||
|
TOML_EXTERN toml_array_t* toml_array_in(const toml_table_t* tab,
|
||||||
|
const char* key);
|
||||||
|
TOML_EXTERN toml_table_t* toml_table_in(const toml_table_t* tab,
|
||||||
|
const char* key);
|
||||||
|
|
||||||
|
/*-----------------------------------------------------------------
|
||||||
|
* lesser used
|
||||||
|
*/
|
||||||
|
/* Return the array kind: 't'able, 'a'rray, 'v'alue */
|
||||||
|
TOML_EXTERN char toml_array_kind(const toml_array_t* arr);
|
||||||
|
|
||||||
|
/* For array kind 'v'alue, return the type of values
|
||||||
|
i:int, d:double, b:bool, s:string, t:time, D:date, T:timestamp
|
||||||
|
0 if unknown
|
||||||
|
*/
|
||||||
|
TOML_EXTERN char toml_array_type(const toml_array_t* arr);
|
||||||
|
|
||||||
|
/* Return the key of an array */
|
||||||
|
TOML_EXTERN const char* toml_array_key(const toml_array_t* arr);
|
||||||
|
|
||||||
|
/* Return the number of key-values in a table */
|
||||||
|
TOML_EXTERN int toml_table_nkval(const toml_table_t* tab);
|
||||||
|
|
||||||
|
/* Return the number of arrays in a table */
|
||||||
|
TOML_EXTERN int toml_table_narr(const toml_table_t* tab);
|
||||||
|
|
||||||
|
/* Return the number of sub-tables in a table */
|
||||||
|
TOML_EXTERN int toml_table_ntab(const toml_table_t* tab);
|
||||||
|
|
||||||
|
/* Return the key of a table*/
|
||||||
|
TOML_EXTERN const char* toml_table_key(const toml_table_t* tab);
|
||||||
|
|
||||||
|
/*--------------------------------------------------------------
|
||||||
|
* misc
|
||||||
|
*/
|
||||||
|
TOML_EXTERN int toml_utf8_to_ucs(const char* orig, int len, int64_t* ret);
|
||||||
|
TOML_EXTERN int toml_ucs_to_utf8(int64_t code, char buf[6]);
|
||||||
|
TOML_EXTERN void toml_set_memutil(void* (*xxmalloc)(size_t),
|
||||||
|
void (*xxfree)(void*));
|
||||||
|
|
||||||
|
|
||||||
|
/*--------------------------------------------------------------
|
||||||
|
* deprecated
|
||||||
|
*/
|
||||||
|
/* A raw value, must be processed by toml_rto* before using. */
|
||||||
|
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_at(const toml_array_t* arr, int idx);
|
||||||
|
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_rtoi(toml_raw_t s, int64_t* 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_rtots(toml_raw_t s, toml_timestamp_t* ret);
|
||||||
|
|
||||||
|
|
||||||
|
#endif /* TOML_H */
|
@@ -12,8 +12,11 @@ static void udev_event_cb(GUdevClient *client, gchar *action, GUdevDevice *devic
|
|||||||
{
|
{
|
||||||
struct EG25Manager *manager = data;
|
struct EG25Manager *manager = data;
|
||||||
|
|
||||||
if (strcmp(action, "unbind") != 0 || manager->modem_state == EG25_STATE_RESETTING || !manager->modem_usb_id)
|
if (strcmp(action, "unbind") != 0 ||
|
||||||
|
manager->modem_state == EG25_STATE_RESETTING ||
|
||||||
|
!manager->modem_usb_id) {
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (strncmp(g_udev_device_get_name(device), manager->modem_usb_id, strlen(manager->modem_usb_id)) == 0 &&
|
if (strncmp(g_udev_device_get_name(device), manager->modem_usb_id, strlen(manager->modem_usb_id)) == 0 &&
|
||||||
manager->reset_timer == 0) {
|
manager->reset_timer == 0) {
|
||||||
@@ -22,7 +25,7 @@ static void udev_event_cb(GUdevClient *client, gchar *action, GUdevDevice *devic
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void udev_init (struct EG25Manager *manager)
|
void udev_init (struct EG25Manager *manager, toml_table_t *config)
|
||||||
{
|
{
|
||||||
const char * const subsystems[] = { "usb", NULL };
|
const char * const subsystems[] = { "usb", NULL };
|
||||||
|
|
||||||
|
@@ -8,5 +8,5 @@
|
|||||||
|
|
||||||
#include "manager.h"
|
#include "manager.h"
|
||||||
|
|
||||||
void udev_init (struct EG25Manager *data);
|
void udev_init (struct EG25Manager *data, toml_table_t *config);
|
||||||
void udev_destroy (struct EG25Manager *data);
|
void udev_destroy (struct EG25Manager *data);
|
||||||
|
1
udev/80-modem-eg25.rules
Normal file
1
udev/80-modem-eg25.rules
Normal file
@@ -0,0 +1 @@
|
|||||||
|
ACTION=="add", SUBSYSTEM=="tty", ATTRS{idVendor}=="2c7c", ATTRS{idProduct}=="0125", ENV{MINOR}=="0", RUN+="/usr/bin/eg25-configure-usb %p"
|
21
udev/eg25-configure-usb
Executable file
21
udev/eg25-configure-usb
Executable file
@@ -0,0 +1,21 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
DEVPATH=/sys/$1
|
||||||
|
USB_ID=
|
||||||
|
|
||||||
|
[ -d ${DEVPATH} ] || exit 1
|
||||||
|
|
||||||
|
while [ ! "${USB_ID}" ]; do
|
||||||
|
if [ -f ${DEVPATH}/avoid_reset_quirk ]; then
|
||||||
|
USB_ID=$(basename ${DEVPATH})
|
||||||
|
break
|
||||||
|
fi
|
||||||
|
DEVPATH=$(dirname ${DEVPATH})
|
||||||
|
done
|
||||||
|
|
||||||
|
# Avoid USB resets
|
||||||
|
echo "auto" > /sys/bus/usb/devices/${USB_ID}/power/control
|
||||||
|
echo "3000" > /sys/bus/usb/devices/${USB_ID}/power/autosuspend_delay_ms
|
||||||
|
echo "enabled" > /sys/bus/usb/devices/${USB_ID}/power/wakeup
|
||||||
|
echo "1" > /sys/bus/usb/devices/${USB_ID}/avoid_reset_quirk
|
||||||
|
echo "0" > /sys/bus/usb/devices/${USB_ID}/power/persist
|
8
udev/meson.build
Normal file
8
udev/meson.build
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
#
|
||||||
|
# Copyright (C) 2020 Arnaud Ferraris <arnaud.ferraris@gmail.com>
|
||||||
|
#
|
||||||
|
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
#
|
||||||
|
|
||||||
|
install_data ('eg25-configure-usb', install_dir: bindir)
|
||||||
|
install_data ('80-modem-eg25.rules', install_dir: udevrulesdir)
|
Reference in New Issue
Block a user