Group together all connection related logic (e.g. context) and define
the context steps directly within the connection sequence processing.
Also, don't initially run a disconnection before the connection; if that
logic is ever needed we should likely have it in the generic modem, not
done per plugin.
And error out early if not asking for IPv4.
The mm_base_modem_grab_port() now receives a MMKernelDevice directly from the
plugin, which is then stored in the MMPort corresponding to the port.
This means that we have direct access to e.g. all properties set by udev rules
everywhere, and we don't need additional GUdevClient objects (e.g. like the one
used in the Huawei plugin to detect NDISDUP support during runtime).
For virtual ports (e.g. generated during unit tests), we have a new 'generic'
kernel device object which just provides the values from the kernel device
properties given during its creation.
Instead of relying constantly on GUdevDevice objects reported by GUdev, we now
use a new generic object (MMKernelDevice) for which we provide an initial GUdev
based backend.
All ports of the same modem reported by the kernel will all be associated with
a common 'uid' (unique id), which uniquely identifies the physical device. This
logic was already in place, what we do now is avoid calling it the 'sysfs
path' of the physical device, because we may not want to use that to identify
a device.
This logic now also enables the possibility of "naming" the modems in a unique
way by setting the "ID_MM_PHYSDEV_UID" property in the "usb_device" that owns
all the ports.
E.g. a custom device has 4 modems in 4 different USB ports. The device path of
each USB device will always be the same, so the naming rules could go like this:
$ vim /usr/lib/udev/rules.d/78-mm-naming.rules
ACTION!="add|change|move", GOTO="mm_naming_rules_end"
DEVPATH=="/devices/pci0000:00/0000:00:1d.0/usb4/4-1/4-1.5/4-1.5.1", ENV{ID_MM_PHYSDEV_UID}="USB-MODEM-1"
DEVPATH=="/devices/pci0000:00/0000:00:1d.0/usb4/4-1/4-1.5/4-1.5.2", ENV{ID_MM_PHYSDEV_UID}="USB-MODEM-2"
DEVPATH=="/devices/pci0000:00/0000:00:1d.0/usb4/4-1/4-1.5/4-1.5.3", ENV{ID_MM_PHYSDEV_UID}="USB-MODEM-3"
DEVPATH=="/devices/pci0000:00/0000:00:1d.0/usb4/4-1/4-1.5/4-1.5.4", ENV{ID_MM_PHYSDEV_UID}="USB-MODEM-4"
LABEL="mm_naming_rules_end"
Each of the modems found will have a unique UID retrieved from the previous list
of rules. Then, "mmcli" has also been updated to allow using the UID instead of
the modem DBus path or index, e.g.:
$ sudo mmcli -m USB-MODEM-1
/org/freedesktop/ModemManager1/Modem/0 (device id '988d83252c0598f670c2d69d5f41e077204a92fd')
-------------------------
Hardware | manufacturer: 'ZTE CORPORATION'
| model: 'MF637'
| revision: 'BD_W7P673A3F3V1.0.0B04'
| supported: 'gsm-umts'
| current: 'gsm-umts'
| equipment id: '356516027657837'
-------------------------
System | device: 'USB-MODEM-1'
| drivers: 'option'
| plugin: 'ZTE'
| primary port: 'ttyUSB5'
| ports: 'ttyUSB5 (at)'
...
$ sudo mmcli -m USB-MODEM-1 --enable
...
Commit 7ff57f9808 introduced a change to try to
use ATTRS{bInterfaceNumber} as a common way to match by interface number, but
this logic is broken because all the rules that we use to match by interface
number (attribute in the interface device) also require matching by idVendor
and idProduct (attributes in the physdev device), and udev rules forbid matches
from more than one parent device at a time.
We could use ATTR{bInterfaceNumber} (instead of ATTRS) to tag the actual USB
interface device, but that would require a change in all the plugins to look for
the tag not in the TTY device, but in its parent.
So, recover the original behavior, where a hidden property is created containing
the first bInterfaceNumber found in the list of parent devices, and then run
the matches against idVendor and idProduct only if the hidden property is found
with the expected value.
Rules with a single condition where a parent property is checked with != don't
work properly. E.g.:
SUBSYSTEMS!="usb", GOTO="end"
or:
ATTRS{idVendor}!="abcd", GOTO="end"
Instead, we can mix both those previous parent rules and match them:
SUBSYSTEMS=="usb",ATTRS{idVendor}=="abcd", GOTO="next"
GOTO="end"
LABEL="next"
# Apply rules here
LABEL="end"
In this case both SUBSYSTEMS and ATTRS conditions apply to the parent usb_device
(idVendor attribute is only available in the usb_device), so they apply to all
ports of the same device.
This patch makes declarations bind to definitions within the same module
to prevent the potential ambiguity if referenced directly.
AddressSanitizer think they violated one definition rule, although
those symbols are accessed by address through their modules and do
not depend on the order of the libararies loaded.
The PHS8 in QMI-mode doesn't support GPS location retrieval via QMI, so we will
fallback to use the AT-based setup and the TTY for reading NMEA traces.
The setup in Cinterion modems goes as follows:
AT+CNMI=<mode>[, <mt>[, <bm>[, <ds>[, <bfr>]]]]
For each field, several setups are available, so we could use a default value,
as we were doing until now (+CNMI=2,1,2,2,1).
BUT, not every Cinterion modem allows the same set of settings. For example, the
new PHS8 only allows '0' for the <ds> value:
AT+CNMI=?
+CNMI: (0,1,2),(0,1),(0,2),(0),(1)
So, instead of hardcoding the setup, try to find the best suitable one for each
modem. We'll parse the +CNMI=? test response to know which values are supported
during the messaging support check, which is run once during initialization.
When automatic mode (i.e. 2G+3G+none preferred) is selected, we need to
explicitly request it by giving the AT+COPS write command with values (i.e. not
just empty fields).
Powering off Cinterion modems is done with the AT^SMSO command. Once this
command is sent to the device, ModemManager still needs to wait to get an
unsolicited ^SHUTDOWN message from the modem, which is when the shutdown
is considered complete.
Newer Cinterion devices, though, don't seem to follow this rule and just
reply OK to AT^SMSO (i.e. no ^SHUTDOWN), but instead, they will remove the
tty ports.
The new PHS8 or PXS8 devices may expose multiple tty ports, but only one is
supposed to be used for PPP. So, query which port that is and flag it before
grabbing it in the modem.
We now have a single 'CurrentModes' property which contains both values in a
tuple with signature "(uu)".
Also, rename 'SetAllowedModes()' to 'SetCurrentModes()', and update the list of
arguments expected to have a single "(uu)" tuple.