23 Commits
0.2.0 ... 0.3.0

Author SHA1 Message Date
Arnaud Ferraris
73e16f7699 meson.build: release version 0.3.0 2021-04-09 11:29:26 +02:00
Arnaud Ferraris
3d21b0fc9e Merge branch 'direct-udev' into 'master'
udev: use the udev rules directly to set attr

See merge request mobian1/devices/eg25-manager!12
2021-04-09 09:23:51 +00:00
Bhushan Shah
0d46f42d78 udev: match only USB device and not USB interfaces
Interfaces can not have a power control, only usb devices.
2021-04-09 14:39:53 +05:30
Arnaud Ferraris
1fd9d7d634 Merge branch 'no-urc-cache' into 'master'
config: drop URC cache

Closes #9

See merge request mobian1/devices/eg25-manager!14
2021-04-09 09:08:22 +00:00
Dylan Van Assche
d665ea9639 config: drop URC cache
In some internal discussions between me,
the ModemManager maintainer and a Quectel engineer,
the URC cache we use is unnecessary.
I did some testing and removed the URC cache
from the configs and it works reliable without affecting
the functionality.
2021-04-09 10:35:27 +02:00
Arnaud Ferraris
68e4b2c5b4 Merge branch 'sfos' into 'master'
Allow to build without mmglib

See merge request mobian1/devices/eg25-manager!13
2021-04-09 07:53:32 +00:00
Adam Pigg
dfeab4c79b Re-order mmglib checking as per suggestion 2021-04-09 08:44:17 +01:00
Adam Pigg
40136c2a52 Allow to build without mmglib 2021-04-05 17:11:17 +01:00
Bhushan Shah
84a0ae603d udev: use the udev rules directly to set attr
We don't need complicated script for this, we can just set required
attributes using udev rules.
2021-03-26 13:56:37 +05:30
Arnaud Ferraris
ea19b0271c Merge branch 'master' into 'master'
Add a 100ms delay before PWRKEY sequence

See merge request mobian1/devices/eg25-manager!11
2021-03-17 21:37:53 +00:00
Djhg2000
528fe7e07c Add a 100ms delay before PWRKEY sequence
This brings the power on sequence in line with the EG25-G Hardware Design
datasheet, which states we need to wait at least 30 ms after VBAT becomes stable
before pulling PWRKEY low (first action of the power on sequene).

After this change the sequence becomes as follows:
- Set RESET_N high
- Wait 60 ms (double 30 ms)
- Execute PWRKEY sequence

60 ms was choosen because we don't know when VBAT becomes stable, but it should
be much less than an additional 30 ms. Empirical evidence suggests PinePhone
units with a healthy battery do not see serious side effects from not doing
this, while the modem will fail to boot and/or throw random errors on boot with
a worn out battery.
2021-03-17 19:11:36 +01:00
Arnaud Ferraris
bd5b5a5cf8 Merge branch 'ofono' into 'master'
add ofono-iface

See merge request mobian1/devices/eg25-manager!6
2021-03-15 10:24:35 +00:00
Arnaud Ferraris
ef707c9a33 Merge branch 'qurccfg_all' into 'master'
Set URC config to 'all' instead of 'usbat'

See merge request mobian1/devices/eg25-manager!10
2021-03-15 10:21:25 +00:00
Bhushan Shah
2e7a5a696b ofono-iface: add spdx copyright info 2021-03-15 12:30:16 +05:30
Bhushan Shah
52488d2e2a suspend: if we are using ofono, mark modem as resumed immediately 2021-03-15 12:30:16 +05:30
Bhushan Shah
032bd4651c at: if we are using ofono, don't query modem manager for state 2021-03-15 12:30:16 +05:30
Bhushan Shah
060dc9ae32 src: watch ofono service for new modem
If system is using ofono, use ofono dbus service to figure out the
modem's USB id.
2021-03-15 12:30:16 +05:30
Oliver Smith
dcb1a9a050 src: add ofono-iface
Start work on new ofono interface. So far, this detects ofono on dbus
and complains if both mm and ofono are running.
2021-03-15 12:29:08 +05:30
Biktor
04eed2496d Set URC config to 'all' instead of 'usbat'
dquote> When using a custom kernel, if this setting is set to 'usbat' only, no RING urc is reported on any interface. Changing QURCCFG to 'all' makes the modem report RINGs on all supported interfaces, making receiving calls possible when using a custom firmware
2021-02-24 12:32:46 +01:00
Arnaud Ferraris
3d076e8bc8 README: add details about config files 2021-02-21 16:40:36 +01:00
Arnaud Ferraris
2e7bf44f2d Merge branch 'at-value-expected-switched' into 'master'
at: fix argument order

See merge request mobian1/devices/eg25-manager!9
2021-02-21 15:37:36 +00:00
Dylan Van Assche
3bf2d785bb at: fix argument order
'expected' and 'value' parts of the AT command were switched.
2021-02-21 16:27:30 +01:00
Arnaud Ferraris
9e40ae11d6 data: add AT command reporting firmware version
Fixes #7
2021-02-20 20:12:21 +01:00
21 changed files with 654 additions and 52 deletions

View File

@@ -29,13 +29,20 @@ $ ninja -C ../eg25-build
# 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
`eg25-manager` is usually run as a systemd service, but can also be
manually started from the command-line (requires root privileges):
```
# eg25-manager
# eg25manager
```
## License

View File

@@ -3,6 +3,9 @@ need_libusb = true
usb_vid = 0x2c7c
usb_pid = 0x0125
# Delay between setting GPIO and PWRKEY sequence, set in microseconds
poweron_delay = 100000
# 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]
@@ -31,6 +34,7 @@ configure = [
# 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" },
@@ -38,17 +42,14 @@ configure = [
{ 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 = "QURCCFG", subcmd = "urcport", expect = "\"all\"" },
{ 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" } ]

View File

@@ -3,6 +3,9 @@ need_libusb = true
usb_vid = 0x2c7c
usb_pid = 0x0125
# Delay between setting GPIO and PWRKEY sequence, set in microseconds
poweron_delay = 100000
# 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]
@@ -31,6 +34,7 @@ configure = [
# 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" },
@@ -38,17 +42,14 @@ configure = [
{ 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 = "QURCCFG", subcmd = "urcport", expect = "\"all\"" },
{ 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" } ]

View File

@@ -1,3 +1,7 @@
[manager]
# Delay between setting GPIO and PWRKEY sequence, set in microseconds
poweron_delay = 100000
# 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]
@@ -27,21 +31,19 @@ configure = [
# 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 = "QURCCFG", subcmd = "urcport", expect = "\"all\"" },
{ 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" } ]

View File

@@ -8,7 +8,7 @@
project (
'eg25-manager',
'c',
version : '0.2.0',
version : '0.3.0',
license : 'GPLv3+',
meson_version : '>= 0.50.0',
default_options :
@@ -47,13 +47,18 @@ 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')
mmglib_dep = dependency('mm-glib', required : false)
if mmglib_dep.found()
add_global_arguments('-DHAVE_MMGLIB=1', language : 'c')
endif
mgr_deps = [
dependency('glib-2.0'),
dependency('gio-unix-2.0'),
dependency('gudev-1.0'),
dependency('libgpiod'),
dependency('libusb-1.0'),
dependency('mm-glib'),
mmglib_dep,
]
subdir('data')

View File

@@ -76,12 +76,18 @@ static gboolean send_at_command(struct EG25Manager *manager)
g_message("Sending command: %s", g_strstrip(command));
} else if (manager->modem_state < EG25_STATE_CONFIGURED) {
MMModemState modem_state = mm_modem_get_state(manager->mm_modem);
if (manager->modem_iface == MODEM_IFACE_MODEMMANAGER) {
#ifdef HAVE_MMGLIB
MMModemState modem_state = mm_modem_get_state(manager->mm_modem);
if (manager->mm_modem && modem_state >= MM_MODEM_STATE_REGISTERED)
modem_update_state(manager, modem_state);
else
if (manager->mm_modem && modem_state >= MM_MODEM_STATE_REGISTERED)
modem_update_state(manager, modem_state);
else
manager->modem_state = EG25_STATE_CONFIGURED;
#endif
} else {
manager->modem_state = EG25_STATE_CONFIGURED;
}
} else if (manager->modem_state == EG25_STATE_SUSPENDING) {
modem_suspend_post(manager);
} else if (manager->modem_state == EG25_STATE_RESETTING) {
@@ -320,7 +326,7 @@ void at_sequence_configure(struct EG25Manager *manager)
{
for (guint i = 0; i < configure_commands->len; i++) {
struct AtCommand *cmd = &g_array_index(configure_commands, struct AtCommand, i);
append_at_command(manager, cmd->cmd, cmd->subcmd, cmd->expected, cmd->value);
append_at_command(manager, cmd->cmd, cmd->subcmd, cmd->value, cmd->expected);
}
send_at_command(manager);
}
@@ -329,7 +335,7 @@ void at_sequence_suspend(struct EG25Manager *manager)
{
for (guint i = 0; i < suspend_commands->len; i++) {
struct AtCommand *cmd = &g_array_index(suspend_commands, struct AtCommand, i);
append_at_command(manager, cmd->cmd, cmd->subcmd, cmd->expected, cmd->value);
append_at_command(manager, cmd->cmd, cmd->subcmd, cmd->value, cmd->expected);
}
send_at_command(manager);
}
@@ -338,7 +344,7 @@ void at_sequence_resume(struct EG25Manager *manager)
{
for (guint i = 0; i < resume_commands->len; i++) {
struct AtCommand *cmd = &g_array_index(resume_commands, struct AtCommand, i);
append_at_command(manager, cmd->cmd, cmd->subcmd, cmd->expected, cmd->value);
append_at_command(manager, cmd->cmd, cmd->subcmd, cmd->value, cmd->expected);
}
send_at_command(manager);
}
@@ -347,7 +353,7 @@ void at_sequence_reset(struct EG25Manager *manager)
{
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->expected, cmd->value);
append_at_command(manager, cmd->cmd, cmd->subcmd, cmd->value, cmd->expected);
}
send_at_command(manager);
}

View File

@@ -0,0 +1,11 @@
#!/bin/bash
DEST="$1"
OBJ_PATH="$2"
METHOD="$3"
shift 3
dbus-send "$@" --print-reply --dest="$DEST" "$OBJ_PATH" "$METHOD" | \
grep -v '^method return' | \
sed -e 's/^[[:space:]]\+string "</</' \
-e 's_</node>"_</node>_'

View File

@@ -0,0 +1,13 @@
<!DOCTYPE node PUBLIC "-//freedesktop//DTD D-BUS Object Introspection 1.0//EN"
"http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd">
<node>
<interface name="org.ofono.Manager"><method name="GetModems"><arg name="modems" type="a(oa{sv})" direction="out"/>
</method><signal name="ModemAdded"><arg name="path" type="o"/>
<arg name="properties" type="a{sv}"/>
</signal>
<signal name="ModemRemoved"><arg name="path" type="o"/>
</signal>
</interface>
</node>

View File

@@ -0,0 +1,50 @@
#
# Copyright (C) 2018 Purism SPC
#
# This file is part of Calls.
#
# Calls is free software: you can redistribute it and/or modify it
# under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# Calls is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with Calls. If not, see <http://www.gnu.org/licenses/>.
#
# Author: Bob Ham <bob.ham@puri.sm>
#
# SPDX-License-Identifier: GPL-3.0-or-later
#
gnome = import('gnome')
dbus_interfaces = ['manager', 'modem']
gdbofono_src = []
gdbofono_headers = []
foreach iface: dbus_interfaces
src = gnome.gdbus_codegen(
'gdbo-' + iface,
iface + '.xml',
interface_prefix: 'org.ofono.',
namespace: 'GDBO'
)
gdbofono_src += src
gdbofono_headers += src[1]
endforeach
gdbofono_deps = [
dependency('gio-2.0'),
dependency('gio-unix-2.0'),
]
gdbofono_lib = static_library(
'gdbofono',
gdbofono_src,
dependencies: gdbofono_deps
)

View File

@@ -0,0 +1,249 @@
<!DOCTYPE node PUBLIC "-//freedesktop//DTD D-BUS Object Introspection 1.0//EN"
"http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd">
<node>
<interface name="org.ofono.Modem"><method name="GetProperties"><arg name="properties" type="a{sv}" direction="out"/>
</method><method name="SetProperty"><arg name="property" type="s" direction="in"/>
<arg name="value" type="v" direction="in"/>
</method><signal name="PropertyChanged"><arg name="name" type="s"/>
<arg name="value" type="v"/>
</signal>
</interface>
<interface name="org.ofono.SimManager"><method name="GetProperties"><arg name="properties" type="a{sv}" direction="out"/>
</method><method name="SetProperty"><arg name="property" type="s" direction="in"/>
<arg name="value" type="v" direction="in"/>
</method><method name="ChangePin"><arg name="type" type="s" direction="in"/>
<arg name="oldpin" type="s" direction="in"/>
<arg name="newpin" type="s" direction="in"/>
</method><method name="EnterPin"><arg name="type" type="s" direction="in"/>
<arg name="pin" type="s" direction="in"/>
</method><method name="ResetPin"><arg name="type" type="s" direction="in"/>
<arg name="puk" type="s" direction="in"/>
<arg name="newpin" type="s" direction="in"/>
</method><method name="LockPin"><arg name="type" type="s" direction="in"/>
<arg name="pin" type="s" direction="in"/>
</method><method name="UnlockPin"><arg name="type" type="s" direction="in"/>
<arg name="pin" type="s" direction="in"/>
</method><method name="GetIcon"><arg name="id" type="y" direction="in"/>
<arg name="icon" type="ay" direction="out"/>
</method><signal name="PropertyChanged"><arg name="name" type="s"/>
<arg name="value" type="v"/>
</signal>
</interface>
<interface name="org.ofono.VoiceCallManager"><method name="GetProperties"><arg name="properties" type="a{sv}" direction="out"/>
</method><method name="Dial"><arg name="number" type="s" direction="in"/>
<arg name="hide_callerid" type="s" direction="in"/>
<arg name="path" type="o" direction="out"/>
</method><method name="DialLast"></method><method name="DialMemory"><arg name="memory_location" type="u" direction="in"/>
</method><method name="Transfer"></method><method name="SwapCalls"></method><method name="ReleaseAndAnswer"></method><method name="ReleaseAndSwap"></method><method name="HoldAndAnswer"></method><method name="HangupAll"></method><method name="PrivateChat"><arg name="call" type="o" direction="in"/>
<arg name="calls" type="ao" direction="out"/>
</method><method name="CreateMultiparty"><arg name="calls" type="ao" direction="out"/>
</method><method name="HangupMultiparty"></method><method name="SendTones"><arg name="SendTones" type="s" direction="in"/>
</method><method name="GetCalls"><arg name="calls_with_properties" type="a(oa{sv})" direction="out"/>
</method><signal name="Forwarded"><arg name="type" type="s"/>
</signal>
<signal name="BarringActive"><arg name="type" type="s"/>
</signal>
<signal name="PropertyChanged"><arg name="name" type="s"/>
<arg name="value" type="v"/>
</signal>
<signal name="CallAdded"><arg name="path" type="o"/>
<arg name="properties" type="a{sv}"/>
</signal>
<signal name="CallRemoved"><arg name="path" type="o"/>
</signal>
</interface>
<interface name="org.ofono.AllowedAccessPoints"><method name="GetAllowedAccessPoints"><arg name="apnlist" type="as" direction="out"/>
</method></interface>
<interface name="org.ofono.SimAuthentication"><method name="GetApplications"><arg name="applications" type="a{oa{sv}}" direction="out"/>
</method><method name="GetProperties"><arg name="properties" type="a{sv}" direction="out"/>
</method></interface>
<interface name="org.ofono.SimToolkit"><method name="GetProperties"><arg name="properties" type="a{sv}" direction="out"/>
</method><method name="SelectItem"><arg name="item" type="y" direction="in"/>
<arg name="agent" type="o" direction="in"/>
</method><method name="RegisterAgent"><arg name="path" type="o" direction="in"/>
</method><method name="UnregisterAgent"><arg name="path" type="o" direction="in"/>
</method><signal name="PropertyChanged"><arg name="name" type="s"/>
<arg name="value" type="v"/>
</signal>
</interface>
<interface name="org.ofono.CallForwarding"><method name="GetProperties"><arg name="properties" type="a{sv}" direction="out"/>
</method><method name="SetProperty"><arg name="property" type="s" direction="in"/>
<arg name="value" type="v" direction="in"/>
</method><method name="DisableAll"><arg name="type" type="s" direction="in"/>
</method><signal name="PropertyChanged"><arg name="name" type="s"/>
<arg name="value" type="v"/>
</signal>
</interface>
<interface name="org.ofono.RadioSettings"><method name="GetProperties"><arg name="properties" type="a{sv}" direction="out"/>
</method><method name="SetProperty"><arg name="property" type="s" direction="in"/>
<arg name="value" type="v" direction="in"/>
</method><signal name="PropertyChanged"><arg name="name" type="s"/>
<arg name="value" type="v"/>
</signal>
</interface>
<interface name="org.ofono.TextTelephony"><method name="GetProperties"><arg name="properties" type="a{sv}" direction="out"/>
</method><method name="SetProperty"><arg name="property" type="s" direction="in"/>
<arg name="value" type="v" direction="in"/>
</method><signal name="PropertyChanged"><arg name="name" type="s"/>
<arg name="value" type="v"/>
</signal>
</interface>
<interface name="org.ofono.Phonebook"><method name="Import"><arg name="entries" type="s" direction="out"/>
</method></interface>
<interface name="org.ofono.MessageManager"><method name="GetProperties"><arg name="properties" type="a{sv}" direction="out"/>
</method><method name="SetProperty"><arg name="property" type="s" direction="in"/>
<arg name="value" type="v" direction="in"/>
</method><method name="SendMessage"><arg name="to" type="s" direction="in"/>
<arg name="text" type="s" direction="in"/>
<arg name="path" type="o" direction="out"/>
</method><method name="GetMessages"><arg name="messages" type="a(oa{sv})" direction="out"/>
</method><signal name="PropertyChanged"><arg name="name" type="s"/>
<arg name="value" type="v"/>
</signal>
<signal name="IncomingMessage"><arg name="message" type="s"/>
<arg name="info" type="a{sv}"/>
</signal>
<signal name="ImmediateMessage"><arg name="message" type="s"/>
<arg name="info" type="a{sv}"/>
</signal>
<signal name="MessageAdded"><arg name="path" type="o"/>
<arg name="properties" type="a{sv}"/>
</signal>
<signal name="MessageRemoved"><arg name="path" type="o"/>
</signal>
</interface>
<interface name="org.ofono.PushNotification"><method name="RegisterAgent"><arg name="path" type="o" direction="in"/>
</method><method name="UnregisterAgent"><arg name="path" type="o" direction="in"/>
</method></interface>
<interface name="org.ofono.SmartMessaging"><method name="RegisterAgent"><arg name="path" type="o" direction="in"/>
</method><method name="UnregisterAgent"><arg name="path" type="o" direction="in"/>
</method><method name="SendBusinessCard"><arg name="to" type="s" direction="in"/>
<arg name="card" type="ay" direction="in"/>
<arg name="path" type="o" direction="out"/>
</method><method name="SendAppointment"><arg name="to" type="s" direction="in"/>
<arg name="appointment" type="ay" direction="in"/>
<arg name="path" type="o" direction="out"/>
</method></interface>
<interface name="org.ofono.MessageWaiting"><method name="GetProperties"><arg name="properties" type="a{sv}" direction="out"/>
</method><method name="SetProperty"><arg name="property" type="s" direction="in"/>
<arg name="value" type="v" direction="in"/>
</method><signal name="PropertyChanged"><arg name="name" type="s"/>
<arg name="value" type="v"/>
</signal>
</interface>
<interface name="org.ofono.CallSettings"><method name="GetProperties"><arg name="properties" type="a{sv}" direction="out"/>
</method><method name="SetProperty"><arg name="property" type="s" direction="in"/>
<arg name="value" type="v" direction="in"/>
</method><signal name="PropertyChanged"><arg name="property" type="s"/>
<arg name="value" type="v"/>
</signal>
</interface>
<interface name="org.ofono.CallBarring"><method name="GetProperties"><arg name="properties" type="a{sv}" direction="out"/>
</method><method name="SetProperty"><arg name="property" type="s" direction="in"/>
<arg name="value" type="v" direction="in"/>
<arg name="pin2" type="s" direction="in"/>
</method><method name="DisableAll"><arg name="password" type="s" direction="in"/>
</method><method name="DisableAllIncoming"><arg name="password" type="s" direction="in"/>
</method><method name="DisableAllOutgoing"><arg name="password" type="s" direction="in"/>
</method><method name="ChangePassword"><arg name="old" type="s" direction="in"/>
<arg name="new" type="s" direction="in"/>
</method><signal name="PropertyChanged"><arg name="name" type="s"/>
<arg name="value" type="v"/>
</signal>
</interface>
<interface name="org.ofono.SupplementaryServices"><method name="Initiate"><arg name="command" type="s" direction="in"/>
<arg name="result_name" type="s" direction="out"/>
<arg name="value" type="v" direction="out"/>
</method><method name="Respond"><arg name="reply" type="s" direction="in"/>
<arg name="result" type="s" direction="out"/>
</method><method name="Cancel"></method><method name="GetProperties"><arg name="properties" type="a{sv}" direction="out"/>
</method><signal name="NotificationReceived"><arg name="message" type="s"/>
</signal>
<signal name="RequestReceived"><arg name="message" type="s"/>
</signal>
<signal name="PropertyChanged"><arg name="name" type="s"/>
<arg name="value" type="v"/>
</signal>
</interface>
<interface name="org.ofono.CallMeter"><method name="GetProperties"><arg name="properties" type="a{sv}" direction="out"/>
</method><method name="SetProperty"><arg name="property" type="s" direction="in"/>
<arg name="value" type="v" direction="in"/>
<arg name="password" type="s" direction="in"/>
</method><method name="Reset"><arg name="passoword" type="s" direction="in"/>
</method><signal name="PropertyChanged"><arg name="property" type="s"/>
<arg name="value" type="v"/>
</signal>
<signal name="NearMaximumWarning"></signal>
</interface>
<interface name="org.ofono.CallVolume"><method name="GetProperties"><arg name="properties" type="a{sv}" direction="out"/>
</method><method name="SetProperty"><arg name="property" type="s" direction="in"/>
<arg name="value" type="v" direction="in"/>
</method><signal name="PropertyChanged"><arg name="property" type="s"/>
<arg name="value" type="v"/>
</signal>
</interface>
<interface name="org.ofono.NetworkRegistration"><method name="GetProperties"><arg name="properties" type="a{sv}" direction="out"/>
</method><method name="Register"></method><method name="GetOperators"><arg name="operators_with_properties" type="a(oa{sv})" direction="out"/>
</method><method name="Scan"><arg name="operators_with_properties" type="a(oa{sv})" direction="out"/>
</method><signal name="PropertyChanged"><arg name="name" type="s"/>
<arg name="value" type="v"/>
</signal>
</interface>
<interface name="org.ofono.CellBroadcast"><method name="GetProperties"><arg name="properties" type="a{sv}" direction="out"/>
</method><method name="SetProperty"><arg name="property" type="s" direction="in"/>
<arg name="value" type="v" direction="in"/>
</method><signal name="PropertyChanged"><arg name="property" type="s"/>
<arg name="value" type="v"/>
</signal>
<signal name="IncomingBroadcast"><arg name="message" type="s"/>
<arg name="channel" type="q"/>
</signal>
<signal name="EmergencyBroadcast"><arg name="message" type="s"/>
<arg name="dict" type="a{sv}"/>
</signal>
</interface>
<interface name="org.ofono.AssistedSatelliteNavigation"><method name="SendPositioningElement"><arg name="xml_elements" type="(null)" direction="in"/>
</method><method name="RegisterPositioningRequestAgent"><arg name="agent" type="o" direction="in"/>
</method><method name="UnregisterPositioningRequestAgent"><arg name="agent" type="o" direction="in"/>
</method></interface>
<interface name="org.ofono.ConnectionManager"><method name="GetProperties"><arg name="properties" type="a{sv}" direction="out"/>
</method><method name="SetProperty"><arg name="property" type="s" direction="in"/>
<arg name="value" type="v" direction="in"/>
</method><method name="AddContext"><arg name="type" type="s" direction="in"/>
<arg name="path" type="o" direction="out"/>
</method><method name="RemoveContext"><arg name="path" type="o" direction="in"/>
</method><method name="DeactivateAll"></method><method name="GetContexts"><arg name="contexts_with_properties" type="a(oa{sv})" direction="out"/>
</method><method name="ResetContexts"></method><signal name="PropertyChanged"><arg name="name" type="s"/>
<arg name="value" type="v"/>
</signal>
<signal name="ContextAdded"><arg name="path" type="o"/>
<arg name="properties" type="a{sv}"/>
</signal>
<signal name="ContextRemoved"><arg name="path" type="o"/>
</signal>
</interface>
</node>

37
src/libgdbofono/modem.xml Normal file
View File

@@ -0,0 +1,37 @@
<!DOCTYPE node PUBLIC "-//freedesktop//DTD D-BUS Object Introspection 1.0//EN"
"http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd">
<node>
<interface name="org.ofono.Modem"><method name="GetProperties"><arg name="properties" type="a{sv}" direction="out"/>
</method><method name="SetProperty"><arg name="property" type="s" direction="in"/>
<arg name="value" type="v" direction="in"/>
</method><signal name="PropertyChanged"><arg name="name" type="s"/>
<arg name="value" type="v"/>
</signal>
</interface>
<interface name="org.ofono.VoiceCallManager"><method name="GetProperties"><arg name="properties" type="a{sv}" direction="out"/>
</method><method name="Dial"><arg name="number" type="s" direction="in"/>
<arg name="hide_callerid" type="s" direction="in"/>
<arg name="path" type="o" direction="out"/>
</method><method name="DialLast"></method><method name="DialMemory"><arg name="memory_location" type="u" direction="in"/>
</method><method name="Transfer"></method><method name="SwapCalls"></method><method name="ReleaseAndAnswer"></method><method name="ReleaseAndSwap"></method><method name="HoldAndAnswer"></method><method name="HangupAll"></method><method name="PrivateChat"><arg name="call" type="o" direction="in"/>
<arg name="calls" type="ao" direction="out"/>
</method><method name="CreateMultiparty"><arg name="calls" type="ao" direction="out"/>
</method><method name="HangupMultiparty"></method><method name="SendTones"><arg name="SendTones" type="s" direction="in"/>
</method><method name="GetCalls"><arg name="calls_with_properties" type="a(oa{sv})" direction="out"/>
</method><signal name="Forwarded"><arg name="type" type="s"/>
</signal>
<signal name="BarringActive"><arg name="type" type="s"/>
</signal>
<signal name="PropertyChanged"><arg name="name" type="s"/>
<arg name="value" type="v"/>
</signal>
<signal name="CallAdded"><arg name="path" type="o"/>
<arg name="properties" type="a{sv}"/>
</signal>
<signal name="CallRemoved"><arg name="path" type="o"/>
</signal>
</interface>
</node>

View File

@@ -7,7 +7,12 @@
#include "at.h"
#include "gpio.h"
#include "manager.h"
#ifdef HAVE_MMGLIB
#include "mm-iface.h"
#endif
#include "ofono-iface.h"
#include "suspend.h"
#include "udev.h"
@@ -34,7 +39,10 @@ static gboolean quit_app(struct EG25Manager *manager)
g_message("Request to quit...");
at_destroy(manager);
#ifdef HAVE_MMGLIB
mm_iface_destroy(manager);
#endif
ofono_iface_destroy(manager);
suspend_destroy(manager);
udev_destroy(manager);
@@ -86,6 +94,9 @@ static gboolean modem_start(struct EG25Manager *manager)
if (should_boot) {
g_message("Starting modem...");
// Modem might crash on boot (especially with worn battery) if we don't delay here
if (manager->poweron_delay > 0)
g_usleep(manager->poweron_delay);
gpio_sequence_poweron(manager);
manager->modem_state = EG25_STATE_POWERED;
} else {
@@ -95,6 +106,7 @@ static gboolean modem_start(struct EG25Manager *manager)
return FALSE;
}
#ifdef HAVE_MMGLIB
void modem_update_state(struct EG25Manager *manager, MMModemState state)
{
switch (state) {
@@ -111,6 +123,7 @@ void modem_update_state(struct EG25Manager *manager, MMModemState state)
break;
}
}
#endif
void modem_configure(struct EG25Manager *manager)
{
@@ -131,6 +144,14 @@ void modem_reset(struct EG25Manager *manager)
if (manager->reset_timer)
return;
/*
* If we are managing the modem through lets say ofono, we should not
* reset the modem based on the availability of USB ID
* TODO: Improve ofono plugin and add support for fetching USB ID
*/
if (manager->modem_iface != MODEM_IFACE_MODEMMANAGER)
return;
if (manager->modem_recovery_timer) {
g_source_remove(manager->modem_recovery_timer);
manager->modem_recovery_timer = 0;
@@ -300,11 +321,25 @@ int main(int argc, char *argv[])
toml_value = toml_int_in(toml_manager, "usb_pid");
if (toml_value.ok)
manager.usb_pid = toml_value.u.i;
toml_value = toml_int_in(toml_manager, "poweron_delay");
if (toml_value.ok) {
if (toml_value.u.i >= 0 && toml_value.u.i <= G_MAXULONG) {
// Safe to cast into gulong
manager.poweron_delay = (gulong) toml_value.u.i;
} else {
// Changed from initialized default value but not in range
g_message("Configured poweron_delay out of range, using default");
}
}
}
at_init(&manager, toml_table_in(toml_config, "at"));
gpio_init(&manager, toml_table_in(toml_config, "gpio"));
#ifdef HAVE_MMGLIB
mm_iface_init(&manager, toml_table_in(toml_config, "mm-iface"));
#endif
ofono_iface_init(&manager);
suspend_init(&manager, toml_table_in(toml_config, "suspend"));
udev_init(&manager, toml_table_in(toml_config, "udev"));

View File

@@ -9,7 +9,10 @@
#include <glib.h>
#include <gpiod.h>
#include <gudev/gudev.h>
#ifdef HAVE_MMGLIB
#include <libmm-glib.h>
#endif
#include <libgdbofono/gdbo-manager.h>
#include "toml.h"
@@ -27,12 +30,19 @@ enum EG25State {
EG25_STATE_FINISHING
};
enum ModemIface {
MODEM_IFACE_NONE = 0,
MODEM_IFACE_MODEMMANAGER,
MODEM_IFACE_OFONO
};
struct EG25Manager {
GMainLoop *loop;
guint reset_timer;
gboolean use_libusb;
guint usb_vid;
guint usb_pid;
gulong poweron_delay;
int at_fd;
guint at_source;
@@ -41,9 +51,15 @@ struct EG25Manager {
enum EG25State modem_state;
gchar *modem_usb_id;
enum ModemIface modem_iface;
guint mm_watch;
#ifdef HAVE_MMGLIB
MMManager *mm_manager;
MMModem *mm_modem;
#endif
guint ofono_watch;
GDBOManager *ofono_manager;
GDBusConnection *ofono_connection;
GDBusProxy *suspend_proxy;
int suspend_delay_fd;
@@ -67,4 +83,6 @@ void modem_suspend_pre(struct EG25Manager *data);
void modem_suspend_post(struct EG25Manager *data);
void modem_resume_pre(struct EG25Manager *data);
void modem_resume_post(struct EG25Manager *data);
#ifdef HAVE_MMGLIB
void modem_update_state(struct EG25Manager *data, MMModemState state);
#endif

View File

@@ -4,17 +4,27 @@
# SPDX-License-Identifier: GPL-3.0-or-later
#
executable (
'eg25manager',
[
subdir('libgdbofono')
src = [
'at.c', 'at.h',
'gpio.c', 'gpio.h',
'manager.c', 'manager.h',
'mm-iface.c', 'mm-iface.h',
'ofono-iface.c', 'ofono-iface.h',
'suspend.c', 'suspend.h',
'toml.c', 'toml.h',
'udev.c', 'udev.h',
],
]
if mmglib_dep.found()
src += ['mm-iface.c', 'mm-iface.h']
endif
executable (
'eg25manager',
src,
dependencies : mgr_deps,
link_with: gdbofono_lib,
install : true
)

View File

@@ -163,6 +163,12 @@ static void mm_appeared_cb(GDBusConnection *connection,
{
g_message("ModemManager appeared on D-Bus");
if (manager->modem_iface != MODEM_IFACE_NONE) {
g_critical("Modem interface already found! Make sure to only run either of ModemManager or oFono.");
return;
}
manager->modem_iface = MODEM_IFACE_MODEMMANAGER;
mm_manager_new(connection, G_DBUS_OBJECT_MANAGER_CLIENT_FLAGS_NONE,
NULL, (GAsyncReadyCallback)mm_manager_new_cb, manager);
}

150
src/ofono-iface.c Normal file
View File

@@ -0,0 +1,150 @@
/*
* Copyright (C) 2020 Oliver Smith <ollieparanoid@postmarketos.org>
* Copyright (C) 2021 Bhushan Shah <bshah@kde.org>
*
* SPDX-License-Identifier: GPL-3.0-or-later
*/
#include "ofono-iface.h"
#include <string.h>
#include <libgdbofono/gdbo-manager.h>
#include <libgdbofono/gdbo-modem.h>
#define OFONO_SERVICE "org.ofono"
static void modem_added_cb(GDBOManager *manager_proxy,
const gchar *path,
GVariant *properties,
struct EG25Manager *manager)
{
GVariant *modem_path;
g_debug("Adding ofono modem '%s'", path);
if (manager->modem_state == EG25_STATE_RESUMING) {
if (manager->modem_recovery_timer) {
g_source_remove(manager->modem_recovery_timer);
manager->modem_recovery_timer = 0;
}
modem_resume_post(manager);
manager->modem_state = EG25_STATE_CONFIGURED;
}
if (manager->modem_state < EG25_STATE_ACQUIRED)
manager->modem_state = EG25_STATE_ACQUIRED;
if (manager->modem_state < EG25_STATE_CONFIGURED)
modem_configure(manager);
modem_path = g_variant_lookup_value(properties, "SystemPath", G_VARIANT_TYPE_STRING);
if (manager->modem_usb_id)
g_free(manager->modem_usb_id);
manager->modem_usb_id = g_strdup(strrchr(g_variant_dup_string(modem_path, NULL), '/') + 1);
}
static void modem_removed_cb(GDBOManager *manager_proxy,
const gchar *path,
struct EG25Manager *manager)
{
}
static void get_modems_cb(GDBOManager *manager_proxy,
GAsyncResult *res,
struct EG25Manager *manager)
{
gboolean ok;
GVariant *modems;
GVariantIter *modems_iter = NULL;
g_autoptr(GError) error = NULL;
const gchar *path;
GVariant *properties;
ok = gdbo_manager_call_get_modems_finish(manager_proxy, &modems,
res, &error);
if (!ok) {
g_warning("Error getting modems from ofono manager: %s", error->message);
return;
}
g_variant_get(modems, "a(oa{sv})", &modems_iter);
while(g_variant_iter_loop(modems_iter, "(&o@a{sv})", &path, &properties)) {
g_debug("Got modem object path '%s'", path);
modem_added_cb(manager_proxy, path, properties, manager);
}
g_variant_iter_free(modems_iter);
g_variant_unref(modems);
}
static void ofono_appeared_cb(GDBusConnection *connection,
const gchar *name,
const gchar *name_owner,
struct EG25Manager *manager)
{
GError *error = NULL;
g_message("oFono appeared on D-Bus");
if (manager->modem_iface != MODEM_IFACE_NONE) {
g_critical("Modem interface already found! Make sure to only run either of ModemManager or oFono.");
return;
}
/* now connect to oFono! */
manager->ofono_connection = connection;
manager->ofono_manager = gdbo_manager_proxy_new_sync(connection,
G_DBUS_PROXY_FLAGS_NONE,
OFONO_SERVICE,
"/",
NULL,
&error);
if (!manager->ofono_manager) {
g_critical("Error creating ofono object manager proxy: %s", error->message);
return;
}
manager->modem_iface = MODEM_IFACE_OFONO;
g_signal_connect(manager->ofono_manager, "modem-added",
G_CALLBACK(modem_added_cb), manager);
g_signal_connect(manager->ofono_manager, "modem-removed",
G_CALLBACK(modem_removed_cb), manager);
gdbo_manager_call_get_modems(manager->ofono_manager,
NULL,
(GAsyncReadyCallback) get_modems_cb,
manager);
}
static void ofono_vanished_cb(GDBusConnection *connection,
const gchar *name,
struct EG25Manager *manager)
{
g_message("oFono vanished from D-Bus");
if (manager->modem_iface == MODEM_IFACE_OFONO) {
manager->modem_iface = MODEM_IFACE_NONE;
ofono_iface_destroy(manager);
}
}
void ofono_iface_init(struct EG25Manager *manager)
{
manager->ofono_watch = g_bus_watch_name(G_BUS_TYPE_SYSTEM, OFONO_SERVICE,
G_BUS_NAME_WATCHER_FLAGS_AUTO_START,
(GBusNameAppearedCallback)ofono_appeared_cb,
(GBusNameVanishedCallback)ofono_vanished_cb,
manager, NULL);
}
void ofono_iface_destroy(struct EG25Manager *manager)
{
if (manager->modem_usb_id) {
g_free(manager->modem_usb_id);
manager->modem_usb_id = NULL;
}
if (manager->ofono_watch != 0) {
g_bus_unwatch_name(manager->ofono_watch);
manager->ofono_watch = 0;
}
}

12
src/ofono-iface.h Normal file
View File

@@ -0,0 +1,12 @@
/*
* Copyright (C) 2020 Oliver Smith <ollieparanoid@postmarketos.org>
*
* SPDX-License-Identifier: GPL-3.0-or-later
*/
#pragma once
#include "manager.h"
void ofono_iface_init(struct EG25Manager *data);
void ofono_iface_destroy(struct EG25Manager *data);

View File

@@ -170,11 +170,18 @@ static void signal_cb(GDBusProxy *proxy,
g_message("system is resuming");
take_inhibitor(manager, FALSE);
modem_resume_pre(manager);
if (manager->mm_modem) {
if (
#ifdef HAVE_MMGLIB
manager->mm_modem ||
#endif
manager->modem_iface == MODEM_IFACE_OFONO) {
/*
* 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.
*
* If modem is managed by ofono, we also do resume sequence immediately
* as ofono handles resuming from sleep itself.
*/
manager->modem_state = EG25_STATE_CONFIGURED;
modem_resume_post(manager);

View File

@@ -1 +1,5 @@
ACTION=="add", SUBSYSTEM=="tty", ATTRS{idVendor}=="2c7c", ATTRS{idProduct}=="0125", ENV{MINOR}=="0", RUN+="/usr/bin/eg25-configure-usb %p"
ACTION=="add", SUBSYSTEM=="usb", DRIVERS=="usb", ENV{DEVTYPE}=="usb_device", ATTRS{idVendor}=="2c7c", ATTRS{idProduct}=="0125", ATTR{power/control}="auto"
ACTION=="add", SUBSYSTEM=="usb", DRIVERS=="usb", ENV{DEVTYPE}=="usb_device", ATTRS{idVendor}=="2c7c", ATTRS{idProduct}=="0125", ATTR{power/autosuspend_delay_ms}="3000"
ACTION=="add", SUBSYSTEM=="usb", DRIVERS=="usb", ENV{DEVTYPE}=="usb_device", ATTRS{idVendor}=="2c7c", ATTRS{idProduct}=="0125", ATTR{power/wakeup}="enabled"
ACTION=="add", SUBSYSTEM=="usb", DRIVERS=="usb", ENV{DEVTYPE}=="usb_device", ATTRS{idVendor}=="2c7c", ATTRS{idProduct}=="0125", ATTR{avoid_reset_quirk}="1"
ACTION=="add", SUBSYSTEM=="usb", DRIVERS=="usb", ENV{DEVTYPE}=="usb_device", ATTRS{idVendor}=="2c7c", ATTRS{idProduct}=="0125", ATTR{power/persist}="0"

View File

@@ -1,21 +0,0 @@
#!/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

View File

@@ -4,5 +4,4 @@
# 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)