Merge cros/upstream to cros/master
Contains the following commits:
8bc90b71 broadband-modem-qmi: Enable AT URCs and QMI indications (Dylan Van Assche)
438ff54d libmm-glib,tests: include string.h explicitly (Aleksander Morgado)
da0e610f modem-helpers-mbim: include string.h explicitly (Aleksander Morgado)
a9611c62 utils: import ptr array lookup with GEqualFunc from GLib 2.54 (Aleksander Morgado)
e1372a71 iface-modem: detect hotswap on all slots (Pavan Holla)
fbc16360 docs: add api index for 1.18 (Aleksander Morgado)
a4aba0a6 mmcli,sim: add preferred networks list to SIM properties (Teijo Kinnunen)
816beeff libmm-glib,modem-helpers,mm-base-sim: implement Sim.PreferredNetworks (Teijo Kinnunen)
c7d36667 shared-qmi: network registration cancellation logic with asserts disabled (Aleksander Morgado)
f10e4af9 libmm-glib,bearer-properties: fix 'allow roaming' comparison (Aleksander Morgado)
297a8c85 examples: sms: resolve PEP8 issues (Yegor Yefremov)
aba237df broadband-modem-qmi: allow lookup of QMI for data without SIO port (Aleksander Morgado)
381e2f38 base-modem: separate method to lookup exact port by name (Aleksander Morgado)
b8e076f9 kernel-device-udev: keep track of the client object (Aleksander Morgado)
1b35d74c kernel-device: add get_interface_number() method (Aleksander Morgado)
cc07d214 examples: network-scan: get rid of global variables (Yegor Yefremov)
62506034 build: improve releasing notes (Aleksander Morgado)
4a06a027 charsets: detect iconv() support in runtime (Aleksander Morgado)
8a8e0016 charsets: define common translit fallback character (Aleksander Morgado)
c84454c1 charsets: remove charset_hex_to_utf8() (Aleksander Morgado)
0ff3eb7e charsets: remove take_and_convert methods (Aleksander Morgado)
ab4c31ec cinterion: rework mno decoding to use str_to_utf8() (Aleksander Morgado)
6bc07b4b cinterion: rework band encoding to use str_to_utf8() (Aleksander Morgado)
16df1e17 helpers: rework normalize_operator() to use str_to_utf8() (Aleksander Morgado)
63fa9eee charsets,tests: update take_and_convert tests to str_from/to (Aleksander Morgado)
3ac248a7 cinterion: move sequence to set bands to private ctx (Aleksander Morgado)
e5363b54 charsets: use new str_from_utf8() instead of take_and_convert_to_current_charset() (Aleksander Morgado)
395ab06c charsets: use new bytearray_to_utf8() instead of hex_to_utf8() (Aleksander Morgado)
5ea4a591 charsets: use new bytearray_to_utf8() instead of byte_array_to_utf8() (Aleksander Morgado)
033e174e charsets: make charset_gsm_unpacked_to_utf8() private (Aleksander Morgado)
8bfdfb18 charsets: use new bytearray_from_utf8() instead of byte_array_append() (Aleksander Morgado)
75b37e16 charsets: make charset_utf8_to_unpacked_gsm() private (Aleksander Morgado)
9c613d33 charsets: new common APIs to convert from/to charsets and UTF-8 (Aleksander Morgado)
6f32c8d3 charsets: avoid //TRANSLIT when converting to/from charsets (Aleksander Morgado)
bc449cbe charsets: make translit optional in utf8_to_unpacked_gsm() (Aleksander Morgado)
5ce97abd charsets: make translit optional in gsm_unpacked_to_utf8() (Aleksander Morgado)
5480cb67 libmm-glib,tests: add ishexstr/hexstr2bin/bin2hexstr unit tests (Aleksander Morgado)
34de613d libmm-glib,common-helpers: make hexstr2bin() return a guint8 array (Aleksander Morgado)
6d8610d6 libmm-glib,common-helpers: ishexstr() fails on empty input string (Aleksander Morgado)
8c30a6b6 libmm-glib,common-helpers: hexstr2bin fails on empty input string (Aleksander Morgado)
a211981d libmm-glib,common-helpers: make hexstr2bin() accept input string length (Aleksander Morgado)
657cabcf libmm-glib,common-helpers: make hexstr2bin() return a GError (Aleksander Morgado)
dbdf67e9 charsets: remove unused charset_utf8_to_hex() method (Aleksander Morgado)
8b590721 charsets: don't allow quoting in byte_array_append() (Aleksander Morgado)
38a4a9c8 charsets: remove HEX charset type (Aleksander Morgado)
a025e83e charsets: define charset enum explicitly as flags (Aleksander Morgado)
19e5d5f9 build: post-release version bump to 1.17.0 (Aleksander Morgado)
7a5a49b7 release: bump version to 1.16.0 (Aleksander Morgado)
7a5eae2a NEWS: update for 1.16.0 (Aleksander Morgado)
bbd3638d build: require libqmi 1.28.0 (Aleksander Morgado)
a5462014 bearer-mbim: IP type may be reported as deactivated and still have IP settings (Aleksander Morgado)
Cq-Depend: chromium:2729495
Change-Id: Ib418fd49adc23055e82037d69469da794442425e
diff --git a/NEWS b/NEWS
index a57854b..2ded561 100644
--- a/NEWS
+++ b/NEWS
@@ -1,4 +1,149 @@
+ModemManager 1.16.0
+-------------------------------------------
+This is a new stable release of ModemManager.
+
+The following notes are directed to package maintainers:
+
+ * This version now requires:
+ ** libqmi >= 1.28.0 (for the optional QMI support)
+
+ * The 1.16.x branch will be the last one supporting the 'LEGACY' and 'PARANOID'
+ filter modes; standard distributions are advised to use the default 'STRICT'
+ mode if they aren't using it already (i.e. running the daemon without any
+ explicit '--filter-policy' option).
+
+ * A new 'qcom-soc' plugin is implemented to be able to use ModemManager in
+ Qualcomm SoCs like the MSM8916 or MSM8974. This plugin uses a combination of
+ RPMSG based control ports plus BAM-DMUX based network ports. This plugin is
+ disabled by default, even when `--enable-all-plugins` is used, and if wanted
+ it must be explicitly enabled with `--enable-plugin-qcom-soc`. Systems
+ targeting this kind of SoCs, like postmarketos, should enable it. Standard
+ distributions may or may not include it, up to the targeted hardware in each
+ distribution.
+
+ * Gentoo's 'libelogind' library may now be used to detect the systemd
+ suspend/resume support.
+
+The API is backwards compatible with the previous releases, the only updates
+are the following:
+
+ * Modem interface:
+ ** Updated the 'Ports' property so that it exposes all ports that are
+ owned by the modem even if they are explicitly ignored and not used.
+ ** New 'SimSlots' property that exposes the available SIM slots in the modem,
+ including the SIM object paths in each of them if the cards are present.
+ ** New 'PrimarySimSlot' property indicating which of the slots in the
+ 'SimSlots' array is the one currently active.
+ ** New 'SetPrimarySimSlot' method to select which SIM slot in the 'SimSlots'
+ array should be considered active. When the switch happens, the modem will
+ be fully re-probed.
+
+ * Signal interface:
+ ** New 'Nr5g' dictionary property including signal information for the 5GNR
+ access technology.
+
+ * SIM interface:
+ ** New 'Active' boolean property, indicating whether the SIM object is the
+ currently active one.
+ ** New 'Eid' string property, indicating the EID of the card, if any.
+
+ * New udev tags:
+ ** New 'ID_MM_PORT_TYPE_QMI' tag to explicitly flag a port as being QMI, when
+ there is no other way to guess the type of port; e.g. this tag is not
+ needed for ports exposed by the qmi_wwan driver.
+ ** New 'ID_MM_PORT_TYPE_MBIM' tag to explicitly flag a port as being MBIM,
+ when there is no other way to guess the type of port; e.g. this tag is not
+ needed for ports exposed by the cdc_mbim driver.
+
+The most important features and changes in this release are the following:
+
+ * Implemented support for Multi SIM Single Standby support, for systems that
+ have multiple SIM slots and they can select which of them (only one) is
+ active at any given time. Currently implemented for QMI modems only.
+
+ * If the modem enabling phase fails in a fatal way, an implicit disabling
+ sequence is now run, in order to avoid leaving the modem in an inconsistent
+ state.
+
+ * If the connection attempt includes user/password information but no explicit
+ authentication type, CHAP will now be used by default instead of PAP.
+
+ * Full USB device removal events reported via udev are no longer used. The
+ device removal logic relies exclusively on independent port removal events,
+ as that logic is supported for all subsystems and kernel device backends
+ (e.g. also working for non-USB devices and for systems without udev like
+ OpenWRT).
+
+ * Added support to monitor the 'rpmsg' subsystem, but only in plugins that
+ explicitly require its use (e.g. the 'qcom-soc' plugin).
+
+ * New options in the ModemManager daemon:
+ ** Added new '--test-no-suspend-resume' option to disable the runtime
+ suspend/resume support even if the daemon was built with it.
+ ** Added new '--test-no-udev' option to disable the runtime udev support even
+ if the daemon was built with it.
+
+ * Serial:
+ ** Also match OK or ERROR responses that are not at end of line.
+
+ * SIM:
+ ** Force reprobing the modem if a new SIM is detected in a modem that
+ initially started in Failed state without SIM.
+ ** Force reprobing the modem if the lock status cannot be read after sending
+ SIM-PUK, so that it transitions to the Failed state.
+ ** Force reprobing the modem if a PUK lock is discovered after sending
+ SIM-PIN, so that it transitions to the Failed state.
+
+ * QMI:
+ ** The logic no longer depends on the service version reported by each
+ client, the support for each feature is explicitly probed instead.
+ ** Implemented SIM profile (eUICC) change detection.
+ ** Support for QMI modems on kernels < 3.6 is dropped. Only kernels where the
+ QMI control ports are exposed in the 'usbmisc' subsystem are supported.
+ ** Implemented additional step in the connection logic to allow binding the
+ WDS client to a given SIO port, required in the BAM-DMUX driver setup.
+ ** Implemented support for the initial EPS bearer settings logic.
+ ** Disabled explicit signal and access technology polling if indications have
+ been correctly enabled.
+
+ * MBIM:
+ ** Enable SIM hot swap detection logic with QMI over MBIM.
+ ** Allow plugins to specify explicitly that QMI over MBIM is not supported.
+
+ * libmm-glib:
+ ** Added missing APIs to get/set RM protocol in the Simple connect settings.
+
+ * Plugins:
+ ** gosuncn: new plugin, for now just with port type hints for the GM800.
+ ** quectel: implemented GPS support with +QGPS.
+ ** quectel: implemented custom time support check to prefer +CTZU=3 instead
+ of +CTZU=1 so that the modem reports localtime instead of UTC in +CCLK.
+ ** sierra: added support for XMM-specific features (e.g. EM7345).
+ ** cinterion: implemented support for the initial EPS bearer settings logic.
+ ** cinterion: added SIM hot swap support to AT-based modems.
+ ** huawei: updated to avoid applying multiple port type hint methods.
+ ** huawei: updated the ^GETPORTMODE logic so that we don't assume the hints
+ in the response apply to specific USB interfaces.
+
+The following features which were backported to 1.14.x releases are also present
+in ModemManager 1.16.0:
+
+ * location: allow CID only updates.
+ * sms: allow sending/receiving UTF-16 as if it were UCS-2.
+ * modem: don't consider charset setup failure as fatal.
+ * QMI: fix reporting signal strength indications.
+ * QMI: fix parsing of USSD indications with UTF-16 data.
+ * QMI: run network registration with NAS Set System Selection Preference.
+ * QMI: when connection aborted, ensure network handles are released.
+ * MBIM: don't fail IPv4v6 connection attempt if only IPv4 succeeds.
+ * cinterion: improve user/password handling in AT^SGAUTH calls.
+ * cinterion: removed limitation to IPv4 only PDP contexts.
+ * cinterion: configure the PLAS9 to send URCs correctly.
+ * quectel: add support for MBIM devices.
+ * telit: add initial delay for AT ports to become responsive.
+
+
ModemManager 1.14.0
-------------------------------------------
This is a new stable release of ModemManager.
diff --git a/RELEASING b/RELEASING
index 8ae9a6e..cfe3ea2 100644
--- a/RELEASING
+++ b/RELEASING
@@ -2,6 +2,14 @@
The ModemManager releases are generated using the GNU autotools.
+0.1) For major releases:
+ * Increment mm_minor_version and reset mm_micro_version.
+ * Assuming API/ABI compatibility in libmm-glib, increment both
+ mm_glib_lt_current and mm_glib_lt_age.
+
+0.2) For stable branch releases:
+ * Increment mm_micro_version.
+
1) Configure and build the whole project, making sure gtk-doc is enabled:
$ NOCONFIGURE=1 ./autogen.sh
@@ -24,9 +32,44 @@
$ gpg --verify ModemManager-${VERSION}.tar.xz.asc ModemManager-${VERSION}.tar.xz
5) Upload source tarball (.tar.xz) and signature (.tar.xz.asc) to
- freedesktop.org
+ freedesktop.org.
+ $ scp ModemManager-${VERSION}.tar.xz* fd.o:${ModemManager}/
-TODO: manpages and gtk-doc references
+6) Create directories for the manpages and gtk-doc documentation in
+ freedesktop.org, and also update the 'latest' links:
+ $ ssh fd.o
+ [fd.o] $ cd ${ModemManager}/man/
+ [fd.o] $ rm latest
+ [fd.o] $ mkdir -p ${VERSION}
+ [fd.o] $ ln -s ${VERSION} latest
+ [fd.o] $ cd ${ModemManager}/doc/
+ [fd.o] $ rm latest
+ [fd.o] $ mkdir -p ${VERSION}/ModemManager
+ [fd.o] $ mkdir -p ${VERSION}/libmm-glib
+ [fd.o] $ ln -s ${VERSION} latest
+
+7) Generate HTML for the manpages:
+ $ roffit < docs/man/mmcli.1 > mmcli.1.html
+ $ roffit < docs/man/ModemManager.8 > ModemManager.8.html
+
+8) Upload manpages in HTML to freedesktop.org:
+ $ scp *.html fd.o:${ModemManager}/man/${VERSION}/
+
+9) Upload the gtk-doc in HTML available inside the source tarball to
+ freedesktop.org. It must be the one inside the tarball because it contains
+ the correct fixed refs to the online documentation of the dependencies
+ (e.g. the glib/gobject/gio documentation URLs in http://developer.gnome.org)
+ $ tar -Jxvf ModemManager-${VERSION}.tar.xz
+ $ scp ModemManager-${VERSION}/docs/reference/api/html/* fd.o:${ModemManager}/doc/${VERSION}/ModemManager/
+ $ scp ModemManager-${VERSION}/docs/reference/libmm-glib/html/* fd.o:${ModemManager}/doc/${VERSION}/libmm-glib/
+
+10.1) For major releases:
+ * Fork new stable branch (e.g. mm-${MAJOR}-${MINOR})
+ * Post-release version bump in the master branch, increment mm_minor_version.
+ * Post-release version bump in the stable branch, increment mm_micro_version.
+
+10.2) For stable branch releases:
+ * Post-release version bump, increment mm_micro_version.
-------------------------------------------------------------------------------
@@ -34,4 +77,3 @@
signed it, e.g.:
$ curl https://www.freedesktop.org/software/ModemManager/0x3CAD53398973FFFA.asc | gpg --import
-
diff --git a/cli/mmcli-output.c b/cli/mmcli-output.c
index 275e706..3ef7fd1 100644
--- a/cli/mmcli-output.c
+++ b/cli/mmcli-output.c
@@ -273,6 +273,7 @@
[MMC_F_SIM_PROPERTIES_OPERATOR_ID] = { "sim.properties.operator-code", "operator id", MMC_S_SIM_PROPERTIES, },
[MMC_F_SIM_PROPERTIES_OPERATOR_NAME] = { "sim.properties.operator-name", "operator name", MMC_S_SIM_PROPERTIES, },
[MMC_F_SIM_PROPERTIES_EMERGENCY_NUMBERS] = { "sim.properties.emergency-numbers", "emergency numbers", MMC_S_SIM_PROPERTIES, },
+ [MMC_F_SIM_PROPERTIES_PREFERRED_NETWORKS] = { "sim.properties.preferred-networks", "preferred networks", MMC_S_SIM_PROPERTIES, },
[MMC_F_MODEM_LIST_DBUS_PATH] = { "modem-list", "modems", MMC_S_UNKNOWN, },
[MMC_F_SMS_LIST_DBUS_PATH] = { "modem.messaging.sms", "sms messages", MMC_S_UNKNOWN, },
[MMC_F_CALL_LIST_DBUS_PATH] = { "modem.voice.call", "calls", MMC_S_UNKNOWN, },
@@ -798,6 +799,44 @@
}
/******************************************************************************/
+/* (Custom) Preferred networks output */
+
+void
+mmcli_output_preferred_networks (GList *preferred_nets_list)
+{
+ if (preferred_nets_list) {
+ GPtrArray *aux;
+
+ aux = g_ptr_array_new ();
+ for (;preferred_nets_list; preferred_nets_list = g_list_next (preferred_nets_list)) {
+ const MMSimPreferredNetwork *preferred_net;
+ gchar *access_technologies;
+ gchar *out;
+ const gchar *operator_code;
+
+ preferred_net = (const MMSimPreferredNetwork *)preferred_nets_list->data;
+ operator_code = mm_sim_preferred_network_get_operator_code (preferred_net);
+ access_technologies = mm_modem_access_technology_build_string_from_mask (mm_sim_preferred_network_get_access_technology (preferred_net));
+
+ if (selected_type == MMC_OUTPUT_TYPE_HUMAN)
+ out = g_strdup_printf ("%s (%s)",
+ operator_code,
+ access_technologies);
+ else
+ out = g_strdup_printf ("operator-code: %s, access-technologies: %s",
+ operator_code,
+ access_technologies);
+
+ g_ptr_array_add (aux, out);
+ g_free (access_technologies);
+ }
+ g_ptr_array_add (aux, NULL);
+
+ mmcli_output_string_array_take (MMC_F_SIM_PROPERTIES_PREFERRED_NETWORKS, (gchar **) g_ptr_array_free (aux, FALSE), TRUE);
+ }
+}
+
+/******************************************************************************/
/* Human-friendly output */
#define HUMAN_MAX_VALUE_LENGTH 60
diff --git a/cli/mmcli-output.h b/cli/mmcli-output.h
index 928bcd3..dea575b 100644
--- a/cli/mmcli-output.h
+++ b/cli/mmcli-output.h
@@ -290,6 +290,7 @@
MMC_F_SIM_PROPERTIES_OPERATOR_ID,
MMC_F_SIM_PROPERTIES_OPERATOR_NAME,
MMC_F_SIM_PROPERTIES_EMERGENCY_NUMBERS,
+ MMC_F_SIM_PROPERTIES_PREFERRED_NETWORKS,
/* Lists */
MMC_F_MODEM_LIST_DBUS_PATH,
MMC_F_SMS_LIST_DBUS_PATH,
@@ -341,16 +342,17 @@
/******************************************************************************/
/* Custom output management */
-void mmcli_output_signal_quality (guint value,
- gboolean recent);
-void mmcli_output_state (MMModemState state,
- MMModemStateFailedReason reason);
-void mmcli_output_sim_slots (gchar **sim_slot_paths,
- guint primary_sim_slot);
-void mmcli_output_scan_networks (GList *network_list);
-void mmcli_output_firmware_list (GList *firmware_list,
- MMFirmwareProperties *selected);
-void mmcli_output_pco_list (GList *pco_list);
+void mmcli_output_signal_quality (guint value,
+ gboolean recent);
+void mmcli_output_state (MMModemState state,
+ MMModemStateFailedReason reason);
+void mmcli_output_sim_slots (gchar **sim_slot_paths,
+ guint primary_sim_slot);
+void mmcli_output_scan_networks (GList *network_list);
+void mmcli_output_firmware_list (GList *firmware_list,
+ MMFirmwareProperties *selected);
+void mmcli_output_pco_list (GList *pco_list);
+void mmcli_output_preferred_networks (GList *preferred_nets_list);
/******************************************************************************/
/* Dump output */
diff --git a/cli/mmcli-sim.c b/cli/mmcli-sim.c
index 2a1fed3..68ae7f5 100644
--- a/cli/mmcli-sim.c
+++ b/cli/mmcli-sim.c
@@ -158,6 +158,8 @@
static void
print_sim_info (MMSim *sim)
{
+ GList *preferred_nets_list;
+
mmcli_output_string (MMC_F_SIM_GENERAL_DBUS_PATH, mm_sim_get_path (sim));
mmcli_output_string (MMC_F_SIM_PROPERTIES_ACTIVE, mm_sim_get_active (sim) ? "yes" : "no");
mmcli_output_string (MMC_F_SIM_PROPERTIES_IMSI, mm_sim_get_imsi (sim));
@@ -166,6 +168,9 @@
mmcli_output_string (MMC_F_SIM_PROPERTIES_OPERATOR_ID, mm_sim_get_operator_identifier (sim));
mmcli_output_string (MMC_F_SIM_PROPERTIES_OPERATOR_NAME, mm_sim_get_operator_name (sim));
mmcli_output_string_array (MMC_F_SIM_PROPERTIES_EMERGENCY_NUMBERS, (const gchar **) mm_sim_get_emergency_numbers (sim), FALSE);
+ preferred_nets_list = mm_sim_get_preferred_networks (sim);
+ mmcli_output_preferred_networks (preferred_nets_list);
+ g_list_free_full (preferred_nets_list, (GDestroyNotify) mm_sim_preferred_network_free);
mmcli_output_dump ();
}
diff --git a/configure.ac b/configure.ac
index 8c2711f..be1b33f 100644
--- a/configure.ac
+++ b/configure.ac
@@ -5,7 +5,7 @@
dnl
m4_define([mm_major_version], [1])
-m4_define([mm_minor_version], [15])
+m4_define([mm_minor_version], [17])
m4_define([mm_micro_version], [0])
m4_define([mm_version],
[mm_major_version.mm_minor_version.mm_micro_version])
@@ -18,9 +18,9 @@
dnl with old code), increment a.
dnl If the interface has changed in an incompatible way (that is,
dnl functions have changed or been removed), then zero a.
-m4_define([mm_glib_lt_current], [6])
+m4_define([mm_glib_lt_current], [7])
m4_define([mm_glib_lt_revision], [0])
-m4_define([mm_glib_lt_age], [6])
+m4_define([mm_glib_lt_age], [7])
dnl-----------------------------------------------------------------------------
dnl autoconf, automake, libtool initialization
@@ -401,7 +401,7 @@
dnl QMI support (enabled by default)
dnl
-LIBQMI_VERSION=1.27.3
+LIBQMI_VERSION=1.28.0
AC_ARG_WITH(qmi, AS_HELP_STRING([--without-qmi], [Build without QMI support]), [], [with_qmi=yes])
AM_CONDITIONAL(WITH_QMI, test "x$with_qmi" = "xyes")
diff --git a/docs/reference/libmm-glib/libmm-glib-docs.xml b/docs/reference/libmm-glib/libmm-glib-docs.xml
index 71e7440..a67a0e3 100644
--- a/docs/reference/libmm-glib/libmm-glib-docs.xml
+++ b/docs/reference/libmm-glib/libmm-glib-docs.xml
@@ -153,6 +153,7 @@
<chapter>
<title>The SIM object</title>
<xi:include href="xml/mm-sim.xml"/>
+ <xi:include href="xml/mm-sim-preferred-network.xml"/>
</chapter>
<chapter>
@@ -298,6 +299,10 @@
<title>Index of new symbols in 1.16</title>
<xi:include href="xml/api-index-1.16.xml"></xi:include>
</chapter>
+ <chapter id="api-index-1-18" role="1.18">
+ <title>Index of new symbols in 1.18</title>
+ <xi:include href="xml/api-index-1.18.xml"></xi:include>
+ </chapter>
<xi:include href="xml/annotation-glossary.xml"></xi:include>
</book>
diff --git a/docs/reference/libmm-glib/libmm-glib-sections.txt b/docs/reference/libmm-glib/libmm-glib-sections.txt
index 0a72bef..c1daf23 100644
--- a/docs/reference/libmm-glib/libmm-glib-sections.txt
+++ b/docs/reference/libmm-glib/libmm-glib-sections.txt
@@ -1215,6 +1215,25 @@
</SECTION>
<SECTION>
+<FILE>mm-sim-preferred-network</FILE>
+<TITLE>MMSimPreferredNetwork</TITLE>
+MMSimPreferredNetwork
+mm_sim_preferred_network_get_operator_code
+mm_sim_preferred_network_get_access_technology
+mm_sim_preferred_network_free
+<SUBSECTION Private>
+mm_sim_preferred_network_new
+mm_sim_preferred_network_new_from_variant
+mm_sim_preferred_network_set_access_technology
+mm_sim_preferred_network_set_operator_code
+mm_sim_preferred_network_get_tuple
+mm_sim_preferred_network_list_get_variant
+<SUBSECTION Standard>
+MM_TYPE_SIM_PREFERRED_NETWORK
+mm_sim_preferred_network_get_type
+</SECTION>
+
+<SECTION>
<FILE>mm-sim</FILE>
<TITLE>MMSim</TITLE>
MMSim
@@ -1234,6 +1253,7 @@
mm_sim_dup_operator_name
mm_sim_get_emergency_numbers
mm_sim_dup_emergency_numbers
+mm_sim_get_preferred_networks
<SUBSECTION Methods>
mm_sim_send_pin
mm_sim_send_pin_finish
@@ -3125,6 +3145,8 @@
mm_gdbus_sim_dup_operator_name
mm_gdbus_sim_get_emergency_numbers
mm_gdbus_sim_dup_emergency_numbers
+mm_gdbus_sim_dup_preferred_networks
+mm_gdbus_sim_get_preferred_networks
<SUBSECTION Methods>
mm_gdbus_sim_call_send_pin
mm_gdbus_sim_call_send_pin_finish
@@ -3146,6 +3168,7 @@
mm_gdbus_sim_set_operator_name
mm_gdbus_sim_set_sim_identifier
mm_gdbus_sim_set_emergency_numbers
+mm_gdbus_sim_set_preferred_networks
mm_gdbus_sim_complete_change_pin
mm_gdbus_sim_complete_enable_pin
mm_gdbus_sim_complete_send_pin
diff --git a/examples/network-scan-python/network-scan-python b/examples/network-scan-python/network-scan-python
index 12ee02f..21dd89f 100755
--- a/examples/network-scan-python/network-scan-python
+++ b/examples/network-scan-python/network-scan-python
@@ -1,4 +1,4 @@
-#!/usr/bin/env python
+#!/usr/bin/env python3
# -*- Mode: python; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
#
# This program is free software; you can redistribute it and/or modify it under
@@ -26,7 +26,8 @@
from gi.repository import Gio, GLib, GObject, ModemManager
-if __name__ == "__main__":
+def main():
+ """Main routine."""
# Process input arguments
if len(sys.argv) != 1:
@@ -67,3 +68,7 @@
network.get_availability())))
else:
print('no networks found')
+
+
+if __name__ == "__main__":
+ main()
diff --git a/examples/sms-python/sms-python b/examples/sms-python/sms-python
index 569db37..46d5e69 100755
--- a/examples/sms-python/sms-python
+++ b/examples/sms-python/sms-python
@@ -1,4 +1,4 @@
-#!/usr/bin/env python
+#!/usr/bin/env python3
# -*- Mode: python; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
#
# This program is free software; you can redistribute it and/or modify it under
@@ -25,7 +25,8 @@
from gi.repository import Gio, GLib, GObject, ModemManager
-if __name__ == "__main__":
+def main():
+ """Main routine."""
# Process input arguments
if len(sys.argv) != 3:
@@ -34,14 +35,15 @@
sys.exit(1)
# Prepare SMS properties
- sms_properties = ModemManager.SmsProperties.new ()
+ sms_properties = ModemManager.SmsProperties.new()
sms_properties.set_number(sys.argv[1])
sms_properties.set_text(sys.argv[2])
# Connection to ModemManager
- connection = Gio.bus_get_sync (Gio.BusType.SYSTEM, None)
- manager = ModemManager.Manager.new_sync (connection, Gio.DBusObjectManagerClientFlags.DO_NOT_AUTO_START, None)
- if manager.get_name_owner() is None:
+ connection = Gio.bus_get_sync(Gio.BusType.SYSTEM, None)
+ manager = ModemManager.Manager.new_sync(
+ connection, Gio.DBusObjectManagerClientFlags.DO_NOT_AUTO_START, None)
+ if not manager.get_name_owner():
sys.stderr.write('ModemManager not found in bus')
sys.exit(2)
@@ -51,3 +53,7 @@
sms = messaging.create_sync(sms_properties)
sms.send_sync()
print('%s: sms sent' % messaging.get_object_path())
+
+
+if __name__ == "__main__":
+ main()
diff --git a/introspection/org.freedesktop.ModemManager1.Sim.xml b/introspection/org.freedesktop.ModemManager1.Sim.xml
index 87891e3..63d0e9b 100644
--- a/introspection/org.freedesktop.ModemManager1.Sim.xml
+++ b/introspection/org.freedesktop.ModemManager1.Sim.xml
@@ -127,5 +127,19 @@
-->
<property name="EmergencyNumbers" type="as" access="read" />
+ <!--
+ PreferredNetworks:
+
+ List of preferred networks with access technologies configured in the SIM card.
+
+ Each entry contains an operator id string (<literal>"MCCMNC"</literal>)
+ consisting of 5 or 6 digits, and an
+ <link linkend="MMModemAccessTechnology">MMModemAccessTechnology</link> mask.
+ If the SIM card does not support access technology storage, the mask will be
+ set to <link linkend="MM-MODEM-ACCESS-TECHNOLOGY-UNKNOWN:CAPS">
+ MM_MODEM_ACCESS_TECHNOLOGY_UNKNOWN</link>.
+ -->
+ <property name="PreferredNetworks" type="a(su)" access="read" />
+
</interface>
</node>
diff --git a/libmm-glib/Makefile.am b/libmm-glib/Makefile.am
index 49f1e83..f33836f 100644
--- a/libmm-glib/Makefile.am
+++ b/libmm-glib/Makefile.am
@@ -89,6 +89,8 @@
mm-pco.c \
mm-call-audio-format.h \
mm-call-audio-format.c \
+ mm-sim-preferred-network.h \
+ mm-sim-preferred-network.c \
$(NULL)
libmm_glib_la_CPPFLAGS = \
@@ -160,6 +162,7 @@
mm-kernel-event-properties.h \
mm-pco.h \
mm-call-audio-format.h \
+ mm-sim-preferred-network.h \
$(NULL)
CLEANFILES =
diff --git a/libmm-glib/libmm-glib.h b/libmm-glib/libmm-glib.h
index e450489..6572b2d 100644
--- a/libmm-glib/libmm-glib.h
+++ b/libmm-glib/libmm-glib.h
@@ -80,6 +80,7 @@
#include <mm-signal.h>
#include <mm-kernel-event-properties.h>
#include <mm-pco.h>
+#include <mm-sim-preferred-network.h>
/* generated */
#include <mm-errors-types.h>
diff --git a/libmm-glib/mm-bearer-properties.c b/libmm-glib/mm-bearer-properties.c
index f3ccddb..4305cec 100644
--- a/libmm-glib/mm-bearer-properties.c
+++ b/libmm-glib/mm-bearer-properties.c
@@ -761,7 +761,7 @@
if (!(flags & MM_BEARER_PROPERTIES_CMP_FLAGS_NO_ALLOW_ROAMING)) {
if (a->priv->allow_roaming != b->priv->allow_roaming)
return FALSE;
- if (a->priv->allow_roaming_set != b->priv->allow_roaming)
+ if (a->priv->allow_roaming_set != b->priv->allow_roaming_set)
return FALSE;
}
if (a->priv->rm_protocol != b->priv->rm_protocol)
diff --git a/libmm-glib/mm-common-helpers.c b/libmm-glib/mm-common-helpers.c
index da1cc1c..43fff92 100644
--- a/libmm-glib/mm-common-helpers.c
+++ b/libmm-glib/mm-common-helpers.c
@@ -1686,33 +1686,48 @@
return (a << 4) | b;
}
-gchar *
-mm_utils_hexstr2bin (const gchar *hex, gsize *out_len)
+guint8 *
+mm_utils_hexstr2bin (const gchar *hex,
+ gssize len,
+ gsize *out_len,
+ GError **error)
{
const gchar *ipos = hex;
- gchar *buf = NULL;
+ g_autofree guint8 *buf = NULL;
gsize i;
gint a;
- gchar *opos;
- gsize len;
+ guint8 *opos;
- len = strlen (hex);
+ if (len < 0)
+ len = strlen (hex);
+
+ if (len == 0) {
+ g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_INVALID_ARGS,
+ "Hex conversion failed: empty string");
+ return NULL;
+ }
/* Length must be a multiple of 2 */
- g_return_val_if_fail ((len % 2) == 0, NULL);
+ if ((len % 2) != 0) {
+ g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_INVALID_ARGS,
+ "Hex conversion failed: invalid input length");
+ return NULL;
+ }
- opos = buf = g_malloc0 ((len / 2) + 1);
+ opos = buf = g_malloc0 (len / 2);
for (i = 0; i < len; i += 2) {
a = mm_utils_hex2byte (ipos);
if (a < 0) {
- g_free (buf);
+ g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_INVALID_ARGS,
+ "Hex byte conversion from '%c%c' failed",
+ ipos[0], ipos[1]);
return NULL;
}
- *opos++ = a;
+ *opos++ = (guint8)a;
ipos += 2;
}
*out_len = len / 2;
- return buf;
+ return g_steal_pointer (&buf);
}
/* End from hostap */
@@ -1723,9 +1738,9 @@
gsize len;
gsize i;
- /* Length not multiple of 2? */
+ /* Empty string or length not multiple of 2? */
len = strlen (hex);
- if (len % 2 != 0)
+ if (len == 0 || (len % 2) != 0)
return FALSE;
for (i = 0; i < len; i++) {
diff --git a/libmm-glib/mm-common-helpers.h b/libmm-glib/mm-common-helpers.h
index 65d0e70..4e6d978 100644
--- a/libmm-glib/mm-common-helpers.h
+++ b/libmm-glib/mm-common-helpers.h
@@ -181,7 +181,7 @@
const gchar *mm_sms_delivery_state_get_string_extended (guint delivery_state);
gint mm_utils_hex2byte (const gchar *hex);
-gchar *mm_utils_hexstr2bin (const gchar *hex, gsize *out_len);
+guint8 *mm_utils_hexstr2bin (const gchar *hex, gssize len, gsize *out_len, GError **error);
gchar *mm_utils_bin2hexstr (const guint8 *bin, gsize len);
gboolean mm_utils_ishexstr (const gchar *hex);
diff --git a/libmm-glib/mm-sim-preferred-network.c b/libmm-glib/mm-sim-preferred-network.c
new file mode 100644
index 0000000..c53239d
--- /dev/null
+++ b/libmm-glib/mm-sim-preferred-network.c
@@ -0,0 +1,167 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This program 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 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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:
+ *
+ * Copyright (C) 2021 UROS Ltd
+ */
+
+#include "mm-sim-preferred-network.h"
+
+struct _MMSimPreferredNetwork {
+ gchar *operator_code;
+ MMModemAccessTechnology access_technology;
+};
+
+static MMSimPreferredNetwork *
+mm_sim_preferred_network_copy (MMSimPreferredNetwork *preferred_network)
+{
+ MMSimPreferredNetwork *preferred_network_copy;
+
+ preferred_network_copy = g_slice_new0 (MMSimPreferredNetwork);
+ preferred_network_copy->operator_code = g_strdup (preferred_network->operator_code);
+ preferred_network_copy->access_technology = preferred_network->access_technology;
+
+ return preferred_network_copy;
+}
+
+G_DEFINE_BOXED_TYPE (MMSimPreferredNetwork, mm_sim_preferred_network, (GBoxedCopyFunc) mm_sim_preferred_network_copy, (GBoxedFreeFunc) mm_sim_preferred_network_free)
+
+/**
+ * mm_sim_preferred_network_free:
+ * @self: A #MMSimPreferredNetwork.
+ *
+ * Frees a #MMSimPreferredNetwork.
+ *
+ * Since: 1.18
+ */
+void
+mm_sim_preferred_network_free (MMSimPreferredNetwork *self)
+{
+ if (!self)
+ return;
+
+ g_free (self->operator_code);
+ g_slice_free (MMSimPreferredNetwork, self);
+}
+
+/**
+ * mm_sim_preferred_network_get_operator_code:
+ * @self: A #MMSimPreferredNetwork.
+ *
+ * Get the operator code (MCCMNC) of the preferred network.
+ *
+ * Returns: (transfer none): The operator code, or %NULL if none available.
+ *
+ * Since: 1.18
+ */
+const gchar *
+mm_sim_preferred_network_get_operator_code (const MMSimPreferredNetwork *self)
+{
+ g_return_val_if_fail (self != NULL, NULL);
+
+ return self->operator_code;
+}
+
+/**
+ * mm_sim_preferred_network_get_access_technology:
+ * @self: A #MMSimPreferredNetwork.
+ *
+ * Get the access technology mask of the preferred network.
+ *
+ * Returns: A #MMModemAccessTechnology.
+ *
+ * Since: 1.18
+ */
+MMModemAccessTechnology
+mm_sim_preferred_network_get_access_technology (const MMSimPreferredNetwork *self)
+{
+ g_return_val_if_fail (self != NULL, MM_MODEM_ACCESS_TECHNOLOGY_UNKNOWN);
+
+ return self->access_technology;
+}
+
+/**
+ * mm_sim_preferred_network_set_operator_code: (skip)
+ */
+void
+mm_sim_preferred_network_set_operator_code (MMSimPreferredNetwork *self,
+ const gchar *operator_code)
+{
+ g_return_if_fail (self != NULL);
+
+ g_free (self->operator_code);
+ self->operator_code = g_strdup (operator_code);
+}
+
+/**
+ * mm_sim_preferred_network_set_access_technology: (skip)
+ */
+void
+mm_sim_preferred_network_set_access_technology (MMSimPreferredNetwork *self,
+ MMModemAccessTechnology access_technology)
+{
+ g_return_if_fail (self != NULL);
+
+ self->access_technology = access_technology;
+}
+
+/**
+ * mm_sim_preferred_network_new: (skip)
+ */
+MMSimPreferredNetwork *
+mm_sim_preferred_network_new (void)
+{
+ return g_slice_new0 (MMSimPreferredNetwork);
+}
+
+/**
+ * mm_sim_preferred_network_new_from_variant: (skip)
+ */
+MMSimPreferredNetwork *
+mm_sim_preferred_network_new_from_variant (GVariant *variant)
+{
+ MMSimPreferredNetwork *preferred_net;
+
+ g_return_val_if_fail (g_variant_is_of_type (variant, G_VARIANT_TYPE ("(su)")),
+ NULL);
+
+ preferred_net = mm_sim_preferred_network_new ();
+ g_variant_get (variant, "(su)", &preferred_net->operator_code, &preferred_net->access_technology);
+ return preferred_net;
+}
+
+/**
+ * mm_sim_preferred_network_get_tuple: (skip)
+ */
+GVariant *
+mm_sim_preferred_network_get_tuple (const MMSimPreferredNetwork *self)
+{
+ return g_variant_new ("(su)",
+ self->operator_code,
+ self->access_technology);
+}
+
+/**
+ * mm_sim_preferred_network_list_get_variant: (skip)
+ */
+GVariant *
+mm_sim_preferred_network_list_get_variant (const GList *preferred_network_list)
+{
+ GVariantBuilder builder;
+ const GList *iter;
+
+ g_variant_builder_init (&builder, G_VARIANT_TYPE ("a(su)"));
+ for (iter = preferred_network_list; iter; iter = g_list_next (iter)) {
+ g_variant_builder_add_value (&builder,
+ mm_sim_preferred_network_get_tuple ((const MMSimPreferredNetwork *) iter->data));
+ }
+ return g_variant_builder_end (&builder);
+}
diff --git a/libmm-glib/mm-sim-preferred-network.h b/libmm-glib/mm-sim-preferred-network.h
new file mode 100644
index 0000000..e604258
--- /dev/null
+++ b/libmm-glib/mm-sim-preferred-network.h
@@ -0,0 +1,68 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This program 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 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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:
+ *
+ * Copyright (C) 2021 UROS Ltd
+ */
+
+#ifndef MM_SIM_PREFERRED_NETWORK_H
+#define MM_SIM_PREFERRED_NETWORK_H
+
+#if !defined (__LIBMM_GLIB_H_INSIDE__) && !defined (LIBMM_GLIB_COMPILATION)
+#error "Only <libmm-glib.h> can be included directly."
+#endif
+
+#include <ModemManager.h>
+#include <glib-object.h>
+
+G_BEGIN_DECLS
+
+/**
+ * MMSimPreferredNetwork:
+ *
+ * The #MMSimPreferredNetwork structure contains private data and should only be accessed
+ * using the provided API.
+ */
+typedef struct _MMSimPreferredNetwork MMSimPreferredNetwork;
+
+#define MM_TYPE_SIM_PREFERRED_NETWORK (mm_sim_preferred_network_get_type ())
+GType mm_sim_preferred_network_get_type (void);
+
+const gchar *mm_sim_preferred_network_get_operator_code (const MMSimPreferredNetwork *self);
+MMModemAccessTechnology mm_sim_preferred_network_get_access_technology (const MMSimPreferredNetwork *self);
+
+void mm_sim_preferred_network_free (MMSimPreferredNetwork *self);
+
+G_DEFINE_AUTOPTR_CLEANUP_FUNC (MMSimPreferredNetwork, mm_sim_preferred_network_free)
+
+/*****************************************************************************/
+/* ModemManager/libmm-glib/mmcli specific methods */
+
+#if defined (_LIBMM_INSIDE_MM) || \
+ defined (_LIBMM_INSIDE_MMCLI) || \
+ defined (LIBMM_GLIB_COMPILATION)
+
+MMSimPreferredNetwork * mm_sim_preferred_network_new (void);
+MMSimPreferredNetwork * mm_sim_preferred_network_new_from_variant (GVariant *variant);
+
+void mm_sim_preferred_network_set_operator_code (MMSimPreferredNetwork *self,
+ const gchar *operator_code);
+void mm_sim_preferred_network_set_access_technology (MMSimPreferredNetwork *self,
+ MMModemAccessTechnology access_technology);
+
+GVariant *mm_sim_preferred_network_get_tuple (const MMSimPreferredNetwork *self);
+GVariant *mm_sim_preferred_network_list_get_variant (const GList *preferred_network_list);
+
+#endif
+
+G_END_DECLS
+
+#endif /* MM_SIM_PREFERRED_NETWORK_H */
diff --git a/libmm-glib/mm-sim.c b/libmm-glib/mm-sim.c
index 0a3b607..ac509d3 100644
--- a/libmm-glib/mm-sim.c
+++ b/libmm-glib/mm-sim.c
@@ -23,6 +23,7 @@
#include "mm-helpers.h"
#include "mm-sim.h"
+#include "mm-sim-preferred-network.h"
/**
* SECTION: mm-sim
@@ -860,6 +861,47 @@
/*****************************************************************************/
+/**
+ * mm_sim_get_preferred_networks:
+ * @self: A #MMSim.
+ *
+ * Gets the list of #MMSimPreferredNetwork objects exposed by this
+ * #MMSim.
+ *
+ * Returns: (transfer full) (element-type ModemManager.SimPreferredNetwork): a list of
+ * #MMSimPreferredNetwork objects, or #NULL. The returned value should
+ * be freed with g_list_free_full() using mm_sim_preferred_network_free() as #GDestroyNotify
+ * function.
+ *
+ * Since: 1.18
+ */
+GList *
+mm_sim_get_preferred_networks (MMSim *self)
+{
+ GList *network_list = NULL;
+ GVariant *container, *child;
+ GVariantIter iter;
+
+ g_return_val_if_fail (MM_IS_SIM (self), NULL);
+
+ container = mm_gdbus_sim_get_preferred_networks (MM_GDBUS_SIM (self));
+ g_return_val_if_fail (g_variant_is_of_type (container, G_VARIANT_TYPE ("a(su)")), NULL);
+
+ g_variant_iter_init (&iter, container);
+ while ((child = g_variant_iter_next_value (&iter))) {
+ MMSimPreferredNetwork *preferred_net;
+
+ preferred_net = mm_sim_preferred_network_new_from_variant (child);
+ if (preferred_net)
+ network_list = g_list_append (network_list, preferred_net);
+ g_variant_unref (child);
+ }
+
+ return network_list;
+}
+
+/*****************************************************************************/
+
static void
mm_sim_init (MMSim *self)
{
diff --git a/libmm-glib/mm-sim.h b/libmm-glib/mm-sim.h
index 3449e28..8ebf069 100644
--- a/libmm-glib/mm-sim.h
+++ b/libmm-glib/mm-sim.h
@@ -87,6 +87,8 @@
const gchar * const *mm_sim_get_emergency_numbers (MMSim *self);
gchar **mm_sim_dup_emergency_numbers (MMSim *self);
+GList* mm_sim_get_preferred_networks (MMSim *self);
+
void mm_sim_send_pin (MMSim *self,
const gchar *pin,
GCancellable *cancellable,
diff --git a/libmm-glib/tests/test-common-helpers.c b/libmm-glib/tests/test-common-helpers.c
index 78432be..19e1122 100644
--- a/libmm-glib/tests/test-common-helpers.c
+++ b/libmm-glib/tests/test-common-helpers.c
@@ -13,6 +13,7 @@
* Copyright (C) 2012 Google, Inc.
*/
+#include <string.h>
#include <glib-object.h>
#include <libmm-glib.h>
@@ -502,6 +503,102 @@
}
/**************************************************************/
+/* hexstr2bin & bin2hexstr */
+
+static void
+common_hexstr2bin_test_failure (const gchar *input_hex)
+{
+ g_autoptr(GError) error = NULL;
+ g_autofree guint8 *bin = NULL;
+ gsize bin_len = 0;
+
+ g_assert (mm_utils_ishexstr (input_hex) == FALSE);
+
+ bin = mm_utils_hexstr2bin (input_hex, -1, &bin_len, &error);
+ g_assert_null (bin);
+ g_assert_nonnull (error);
+}
+
+static void
+common_hexstr2bin_test_success_len (const gchar *input_hex,
+ gssize input_hex_len)
+{
+ g_autoptr(GError) error = NULL;
+ g_autofree guint8 *bin = NULL;
+ gsize bin_len = 0;
+ g_autofree gchar *hex = NULL;
+
+ bin = mm_utils_hexstr2bin (input_hex, input_hex_len, &bin_len, &error);
+ g_assert_nonnull (bin);
+ g_assert_no_error (error);
+
+ hex = mm_utils_bin2hexstr (bin, bin_len);
+ g_assert_nonnull (hex);
+
+ if (input_hex_len == -1)
+ g_assert (g_ascii_strcasecmp (input_hex, hex) == 0);
+ else
+ g_assert (g_ascii_strncasecmp (input_hex, hex, (gsize)input_hex_len) == 0);
+}
+
+static void
+common_hexstr2bin_test_success (const gchar *input_hex)
+{
+ gsize input_hex_len;
+ gssize i;
+
+ g_assert (mm_utils_ishexstr (input_hex) == TRUE);
+
+ common_hexstr2bin_test_success_len (input_hex, -1);
+
+ input_hex_len = strlen (input_hex);
+ for (i = input_hex_len; i >= 2; i-=2)
+ common_hexstr2bin_test_success_len (input_hex, i);
+}
+
+static void
+hexstr_lower_case (void)
+{
+ common_hexstr2bin_test_success ("000123456789abcdefff");
+}
+
+static void
+hexstr_upper_case (void)
+{
+ common_hexstr2bin_test_success ("000123456789ABCDEFFF");
+}
+
+static void
+hexstr_mixed_case (void)
+{
+ common_hexstr2bin_test_success ("000123456789AbcDefFf");
+}
+
+static void
+hexstr_empty (void)
+{
+ common_hexstr2bin_test_failure ("");
+}
+
+static void
+hexstr_missing_digits (void)
+{
+ common_hexstr2bin_test_failure ("012");
+}
+
+static void
+hexstr_wrong_digits_all (void)
+{
+ common_hexstr2bin_test_failure ("helloworld");
+}
+
+static void
+hexstr_wrong_digits_some (void)
+{
+ common_hexstr2bin_test_failure ("012345k7");
+}
+
+/**************************************************************/
int main (int argc, char **argv)
{
@@ -539,5 +636,13 @@
g_test_add_func ("/MM/Common/FieldParsers/Uint", field_parser_uint);
g_test_add_func ("/MM/Common/FieldParsers/Double", field_parser_double);
+ g_test_add_func ("/MM/Common/HexStr/lower-case", hexstr_lower_case);
+ g_test_add_func ("/MM/Common/HexStr/upper-case", hexstr_upper_case);
+ g_test_add_func ("/MM/Common/HexStr/mixed-case", hexstr_mixed_case);
+ g_test_add_func ("/MM/Common/HexStr/missing-empty", hexstr_empty);
+ g_test_add_func ("/MM/Common/HexStr/missing-digits", hexstr_missing_digits);
+ g_test_add_func ("/MM/Common/HexStr/wrong-digits-all", hexstr_wrong_digits_all);
+ g_test_add_func ("/MM/Common/HexStr/wrong-digits-some", hexstr_wrong_digits_some);
+
return g_test_run ();
}
diff --git a/plugins/altair/mm-broadband-modem-altair-lte.c b/plugins/altair/mm-broadband-modem-altair-lte.c
index d26335f..6aebbd9 100644
--- a/plugins/altair/mm-broadband-modem-altair-lte.c
+++ b/plugins/altair/mm-broadband-modem-altair-lte.c
@@ -1067,7 +1067,7 @@
error))
return NULL;
- mm_3gpp_normalize_operator (&operator_name, MM_MODEM_CHARSET_UNKNOWN);
+ mm_3gpp_normalize_operator (&operator_name, MM_MODEM_CHARSET_UNKNOWN, self);
return operator_name;
}
diff --git a/plugins/altair/mm-modem-helpers-altair-lte.c b/plugins/altair/mm-modem-helpers-altair-lte.c
index 278f31e..d2fd9af 100644
--- a/plugins/altair/mm-modem-helpers-altair-lte.c
+++ b/plugins/altair/mm-modem-helpers-altair-lte.c
@@ -155,9 +155,9 @@
G_REGEX_DOLLAR_ENDONLY | G_REGEX_RAW,
0, NULL);
g_assert (regex);
- if (!g_regex_match_full (regex, pco_info, strlen (pco_info), 0, 0, &match_info, error)) {
+
+ if (!g_regex_match_full (regex, pco_info, strlen (pco_info), 0, 0, &match_info, error))
return NULL;
- }
num_matches = g_match_info_get_match_count (match_info);
if (num_matches != 5) {
@@ -170,22 +170,18 @@
}
while (g_match_info_matches (match_info)) {
- guint pco_cid;
- gchar *pco_id;
- gchar *pco_payload;
- gsize pco_payload_len;
- gchar *pco_payload_bytes = NULL;
- gsize pco_payload_bytes_len;
- guint8 pco_prefix[6];
- GByteArray *pco_raw;
- gsize pco_raw_len;
+ guint pco_cid;
+ g_autofree gchar *pco_id = NULL;
+ g_autofree gchar *pco_payload = NULL;
+ g_autofree guint8 *pco_payload_bytes = NULL;
+ gsize pco_payload_bytes_len;
+ guint8 pco_prefix[6];
+ GByteArray *pco_raw;
+ gsize pco_raw_len;
if (!mm_get_uint_from_match_info (match_info, 1, &pco_cid)) {
- g_set_error (error,
- MM_CORE_ERROR,
- MM_CORE_ERROR_FAILED,
- "Couldn't parse CID from PCO info: '%s'",
- pco_info);
+ g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED,
+ "Couldn't parse CID from PCO info: '%s'", pco_info);
break;
}
@@ -197,42 +193,26 @@
pco_id = mm_get_string_unquoted_from_match_info (match_info, 3);
if (!pco_id) {
- g_set_error (error,
- MM_CORE_ERROR, MM_CORE_ERROR_FAILED,
- "Couldn't parse PCO ID from PCO info: '%s'",
- pco_info);
+ g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED,
+ "Couldn't parse PCO ID from PCO info: '%s'", pco_info);
break;
}
if (g_strcmp0 (pco_id, "FF00")) {
- g_free (pco_id);
g_match_info_next (match_info, error);
continue;
}
- g_free (pco_id);
pco_payload = mm_get_string_unquoted_from_match_info (match_info, 4);
if (!pco_payload) {
- g_set_error (error,
- MM_CORE_ERROR,
- MM_CORE_ERROR_FAILED,
- "Couldn't parse PCO payload from PCO info: '%s'",
- pco_info);
+ g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED,
+ "Couldn't parse PCO payload from PCO info: '%s'", pco_info);
break;
}
- pco_payload_len = strlen (pco_payload);
- if (pco_payload_len % 2 == 0)
- pco_payload_bytes = mm_utils_hexstr2bin (pco_payload, &pco_payload_bytes_len);
-
- g_free (pco_payload);
-
+ pco_payload_bytes = mm_utils_hexstr2bin (pco_payload, -1, &pco_payload_bytes_len, error);
if (!pco_payload_bytes) {
- g_set_error (error,
- MM_CORE_ERROR,
- MM_CORE_ERROR_FAILED,
- "Invalid PCO payload from PCO info: '%s'",
- pco_info);
+ g_prefix_error (error, "Invalid PCO payload from PCO info '%s': ", pco_info);
break;
}
@@ -266,14 +246,12 @@
pco_raw = g_byte_array_sized_new (pco_raw_len);
g_byte_array_append (pco_raw, pco_prefix, sizeof (pco_prefix));
- g_byte_array_append (pco_raw, (guint8 *)pco_payload_bytes, pco_payload_bytes_len);
- g_free (pco_payload_bytes);
+ g_byte_array_append (pco_raw, pco_payload_bytes, pco_payload_bytes_len);
pco = mm_pco_new ();
mm_pco_set_session_id (pco, pco_cid);
mm_pco_set_complete (pco, TRUE);
mm_pco_set_data (pco, pco_raw->data, pco_raw->len);
- g_byte_array_unref (pco_raw);
break;
}
diff --git a/plugins/cinterion/mm-broadband-bearer-cinterion.c b/plugins/cinterion/mm-broadband-bearer-cinterion.c
index 464e75e..4c59d56 100644
--- a/plugins/cinterion/mm-broadband-bearer-cinterion.c
+++ b/plugins/cinterion/mm-broadband-bearer-cinterion.c
@@ -63,7 +63,7 @@
guint usb_iface_num;
guint i;
- usb_iface_num = mm_kernel_device_get_property_as_int_hex (mm_port_peek_kernel_device (data), "ID_USB_INTERFACE_NUM");
+ usb_iface_num = (guint) mm_kernel_device_get_interface_number (mm_port_peek_kernel_device (data));
for (i = 0; i < G_N_ELEMENTS (usb_interface_configs); i++) {
if (usb_interface_configs[i].usb_iface_num == usb_iface_num)
diff --git a/plugins/cinterion/mm-broadband-modem-cinterion.c b/plugins/cinterion/mm-broadband-modem-cinterion.c
index 4986eea..7eeb8ad 100644
--- a/plugins/cinterion/mm-broadband-modem-cinterion.c
+++ b/plugins/cinterion/mm-broadband-modem-cinterion.c
@@ -107,9 +107,6 @@
/* Initial EPS bearer context number */
gint initial_eps_bearer_cid;
-
- /* Command sequence */
- MMBaseModemAtCommandAlloc *cmds;
};
/*****************************************************************************/
@@ -2070,6 +2067,23 @@
/*****************************************************************************/
/* Set current bands (Modem interface) */
+typedef struct {
+ MMBaseModemAtCommandAlloc *cmds;
+} SetCurrentBandsContext;
+
+static void
+set_current_bands_context_free (SetCurrentBandsContext *ctx)
+{
+ if (ctx->cmds) {
+ guint i;
+
+ for (i = 0; ctx->cmds[i].command; i++)
+ mm_base_modem_at_command_alloc_clear (&ctx->cmds[i]);
+ g_free (ctx->cmds);
+ }
+ g_slice_free (SetCurrentBandsContext, ctx);
+}
+
static gboolean
set_current_bands_finish (MMIfaceModem *self,
GAsyncResult *res,
@@ -2093,23 +2107,17 @@
}
static void
-scfg_set_ready_sequence (MMBaseModem *_self,
+scfg_set_ready_sequence (MMBaseModem *self,
GAsyncResult *res,
GTask *task)
{
GError *error = NULL;
- gpointer ctx = NULL;
- guint i;
- MMBroadbandModemCinterion *self;
- self = g_task_get_source_object (task);
- for (i = 0; self->priv->cmds[i].command; i++)
- mm_base_modem_at_command_alloc_clear (&self->priv->cmds[i]);
- g_free (self->priv->cmds);
- self->priv->cmds = NULL;
-
- mm_base_modem_at_sequence_finish (_self, res, &ctx, &error);
- g_task_return_boolean (task, TRUE);
+ mm_base_modem_at_sequence_finish (self, res, NULL, &error);
+ if (error)
+ g_task_return_error (task, error);
+ else
+ g_task_return_boolean (task, TRUE);
g_object_unref (task);
}
@@ -2154,39 +2162,83 @@
FALSE,
(GAsyncReadyCallback)scfg_set_ready,
task);
- } else { /* self->priv->rb_format == MM_CINTERION_RADIO_BAND_FORMAT_MULTIPLE */
+ return;
+ }
+
+ if (self->priv->rb_format == MM_CINTERION_RADIO_BAND_FORMAT_MULTIPLE) {
+ SetCurrentBandsContext *ctx;
+
+ ctx = g_slice_new0 (SetCurrentBandsContext);
+ g_task_set_task_data (task, ctx, (GDestroyNotify)set_current_bands_context_free);
+
if (self->priv->modem_family == MM_CINTERION_MODEM_FAMILY_IMT) {
g_autofree gchar *bandstr2G = NULL;
g_autofree gchar *bandstr3G = NULL;
g_autofree gchar *bandstr4G = NULL;
+ g_autofree gchar *bandstr2G_enc = NULL;
+ g_autofree gchar *bandstr3G_enc = NULL;
+ g_autofree gchar *bandstr4G_enc = NULL;
bandstr2G = g_strdup_printf ("0x%08X", band[MM_CINTERION_RB_BLOCK_GSM]);
bandstr3G = g_strdup_printf ("0x%08X", band[MM_CINTERION_RB_BLOCK_UMTS]);
bandstr4G = g_strdup_printf ("0x%08X", band[MM_CINTERION_RB_BLOCK_LTE_LOW]);
- bandstr2G = mm_broadband_modem_take_and_convert_to_current_charset (MM_BROADBAND_MODEM (self), bandstr2G);
- bandstr3G = mm_broadband_modem_take_and_convert_to_current_charset (MM_BROADBAND_MODEM (self), bandstr3G);
- bandstr4G = mm_broadband_modem_take_and_convert_to_current_charset (MM_BROADBAND_MODEM (self), bandstr4G);
- self->priv->cmds = g_new0 (MMBaseModemAtCommandAlloc, 3 + 1);
- self->priv->cmds[0].command = g_strdup_printf ("^SCFG=\"Radio/Band/2G\",\"%s\"", bandstr2G);
- self->priv->cmds[1].command = g_strdup_printf ("^SCFG=\"Radio/Band/3G\",\"%s\"", bandstr3G);
- self->priv->cmds[2].command = g_strdup_printf ("^SCFG=\"Radio/Band/4G\",\"%s\"", bandstr4G);
- self->priv->cmds[0].timeout = self->priv->cmds[1].timeout = self->priv->cmds[2].timeout = 60;
+
+ bandstr2G_enc = mm_modem_charset_str_from_utf8 (bandstr2G,
+ mm_broadband_modem_get_current_charset (MM_BROADBAND_MODEM (self)),
+ FALSE,
+ &error);
+ if (!bandstr2G_enc) {
+ g_prefix_error (&error, "Couldn't convert 2G band string to current charset: ");
+ g_task_return_error (task, error);
+ g_object_unref (task);
+ return;
+ }
+
+ bandstr3G_enc = mm_modem_charset_str_from_utf8 (bandstr3G,
+ mm_broadband_modem_get_current_charset (MM_BROADBAND_MODEM (self)),
+ FALSE,
+ &error);
+ if (!bandstr3G_enc) {
+ g_prefix_error (&error, "Couldn't convert 3G band string to current charset: ");
+ g_task_return_error (task, error);
+ g_object_unref (task);
+ return;
+ }
+
+ bandstr4G_enc = mm_modem_charset_str_from_utf8 (bandstr4G,
+ mm_broadband_modem_get_current_charset (MM_BROADBAND_MODEM (self)),
+ FALSE,
+ &error);
+ if (!bandstr4G_enc) {
+ g_prefix_error (&error, "Couldn't convert 4G band string to current charset: ");
+ g_task_return_error (task, error);
+ g_object_unref (task);
+ return;
+ }
+
+ ctx->cmds = g_new0 (MMBaseModemAtCommandAlloc, 3 + 1);
+ ctx->cmds[0].command = g_strdup_printf ("^SCFG=\"Radio/Band/2G\",\"%s\"", bandstr2G_enc);
+ ctx->cmds[1].command = g_strdup_printf ("^SCFG=\"Radio/Band/3G\",\"%s\"", bandstr3G_enc);
+ ctx->cmds[2].command = g_strdup_printf ("^SCFG=\"Radio/Band/4G\",\"%s\"", bandstr4G_enc);
+ ctx->cmds[0].timeout = ctx->cmds[1].timeout = ctx->cmds[2].timeout = 60;
} else {
- self->priv->cmds = g_new0 (MMBaseModemAtCommandAlloc, 3 + 1);
- self->priv->cmds[0].command = g_strdup_printf ("^SCFG=\"Radio/Band/2G\",\"%08x\",,1", band[MM_CINTERION_RB_BLOCK_GSM]);
- self->priv->cmds[1].command = g_strdup_printf ("^SCFG=\"Radio/Band/3G\",\"%08x\",,1", band[MM_CINTERION_RB_BLOCK_UMTS]);
- self->priv->cmds[2].command = g_strdup_printf ("^SCFG=\"Radio/Band/4G\",\"%08x\",\"%08x\",1", band[MM_CINTERION_RB_BLOCK_LTE_LOW], band[MM_CINTERION_RB_BLOCK_LTE_HIGH]);
- self->priv->cmds[0].timeout = self->priv->cmds[1].timeout = self->priv->cmds[2].timeout = 15;
+ ctx->cmds = g_new0 (MMBaseModemAtCommandAlloc, 3 + 1);
+ ctx->cmds[0].command = g_strdup_printf ("^SCFG=\"Radio/Band/2G\",\"%08x\",,1", band[MM_CINTERION_RB_BLOCK_GSM]);
+ ctx->cmds[1].command = g_strdup_printf ("^SCFG=\"Radio/Band/3G\",\"%08x\",,1", band[MM_CINTERION_RB_BLOCK_UMTS]);
+ ctx->cmds[2].command = g_strdup_printf ("^SCFG=\"Radio/Band/4G\",\"%08x\",\"%08x\",1", band[MM_CINTERION_RB_BLOCK_LTE_LOW], band[MM_CINTERION_RB_BLOCK_LTE_HIGH]);
+ ctx->cmds[0].timeout = ctx->cmds[1].timeout = ctx->cmds[2].timeout = 15;
}
mm_base_modem_at_sequence (MM_BASE_MODEM (self),
- (const MMBaseModemAtCommand *)self->priv->cmds,
+ (const MMBaseModemAtCommand *)ctx->cmds,
NULL,
NULL,
(GAsyncReadyCallback)scfg_set_ready_sequence,
task);
+ return;
}
+ g_assert_not_reached ();
}
static void
@@ -2196,8 +2248,9 @@
MMBroadbandModemCinterion *self;
GError *error = NULL;
guint band[MM_CINTERION_RB_BLOCK_N] = { 0 };
- gchar *cmd;
- gchar *bandstr;
+ g_autofree gchar *cmd = NULL;
+ g_autofree gchar *bandstr = NULL;
+ g_autofree gchar *bandstr_enc = NULL;
self = g_task_get_source_object (task);
@@ -2215,12 +2268,13 @@
/* Build string with the value, in the proper charset */
bandstr = g_strdup_printf ("%u", band[MM_CINTERION_RB_BLOCK_LEGACY]);
- bandstr = mm_broadband_modem_take_and_convert_to_current_charset (MM_BROADBAND_MODEM (self), bandstr);
- if (!bandstr) {
- g_task_return_new_error (task,
- MM_CORE_ERROR,
- MM_CORE_ERROR_UNSUPPORTED,
- "Couldn't convert band set to current charset");
+ bandstr_enc = mm_modem_charset_str_from_utf8 (bandstr,
+ mm_broadband_modem_get_current_charset (MM_BROADBAND_MODEM (self)),
+ FALSE,
+ &error);
+ if (!bandstr_enc) {
+ g_prefix_error (&error, "Couldn't convert band string to current charset: ");
+ g_task_return_error (task, error);
g_object_unref (task);
return;
}
@@ -2231,17 +2285,13 @@
* the modem to connect at that specific frequency only. Note that we will be
* passing double-quote enclosed strings here!
*/
- cmd = g_strdup_printf ("^SCFG=\"Radio/Band\",\"%s\",\"%s\"", bandstr, bandstr);
-
+ cmd = g_strdup_printf ("^SCFG=\"Radio/Band\",\"%s\",\"%s\"", bandstr_enc, bandstr_enc);
mm_base_modem_at_command (MM_BASE_MODEM (self),
cmd,
15,
FALSE,
(GAsyncReadyCallback)scfg_set_ready,
task);
-
- g_free (cmd);
- g_free (bandstr);
}
static void
diff --git a/plugins/cinterion/mm-modem-helpers-cinterion.c b/plugins/cinterion/mm-modem-helpers-cinterion.c
index 0522881..cf18ea6 100644
--- a/plugins/cinterion/mm-modem-helpers-cinterion.c
+++ b/plugins/cinterion/mm-modem-helpers-cinterion.c
@@ -217,20 +217,34 @@
}
static guint
-take_and_convert_from_matched_string (gchar *str,
- MMModemCharset charset,
- MMCinterionModemFamily modem_family)
+take_and_convert_from_matched_string (gchar *str,
+ MMModemCharset charset,
+ MMCinterionModemFamily modem_family,
+ GError **error)
{
- guint val = 0;
+ guint val = 0;
+ g_autofree gchar *utf8 = NULL;
+ g_autofree gchar *taken_str = str;
- if (!str)
+ if (!taken_str) {
+ g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_INVALID_ARGS,
+ "Couldn't convert to integer number: no input string");
return 0;
+ }
- if (modem_family == MM_CINTERION_MODEM_FAMILY_IMT)
- str = mm_charset_take_and_convert_to_utf8 (str, charset);
+ if (modem_family == MM_CINTERION_MODEM_FAMILY_IMT) {
+ utf8 = mm_modem_charset_str_to_utf8 (taken_str, -1, charset, FALSE, error);
+ if (!utf8) {
+ g_prefix_error (error, "Couldn't convert to integer number: ");
+ return 0;
+ }
+ }
- mm_get_uint_from_hex_str (str, &val);
- g_free (str);
+ if (!mm_get_uint_from_hex_str (utf8 ? utf8 : taken_str, &val)) {
+ g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED,
+ "Couldn't convert to integer number: wrong hex encoding: %s", utf8 ? utf8 : taken_str);
+ return 0;
+ }
return val;
}
@@ -292,12 +306,16 @@
goto finish;
}
- r2 = g_regex_new ("\\^SCFG:\\s*\"Radio/Band/([234]G)\",\\(\"?([0-9A-Fa-fx]*)\"?-\"?([0-9A-Fa-fx]*)\"?\\)(,*\\(\"?([0-9A-Fa-fx]*)\"?-\"?([0-9A-Fa-fx]*)\"?\\))?",
+ r2 = g_regex_new ("\\^SCFG:\\s*\"Radio/Band/([234]G)\","
+ "\\(\"?([0-9A-Fa-fx]*)\"?-\"?([0-9A-Fa-fx]*)\"?\\)"
+ "(,*\\(\"?([0-9A-Fa-fx]*)\"?-\"?([0-9A-Fa-fx]*)\"?\\))?",
0, 0, NULL);
g_assert (r2 != NULL);
+
g_regex_match_full (r2, response, strlen (response), 0, 0, &match_info2, &inner_error);
if (inner_error)
goto finish;
+
while (g_match_info_matches (match_info2)) {
g_autofree gchar *techstr = NULL;
guint maxband;
@@ -306,16 +324,28 @@
techstr = mm_get_string_unquoted_from_match_info (match_info2, 1);
if (g_strcmp0 (techstr, "2G") == 0) {
- maxband = take_and_convert_from_matched_string (mm_get_string_unquoted_from_match_info (match_info2, 3), charset, modem_family);
+ maxband = take_and_convert_from_matched_string (mm_get_string_unquoted_from_match_info (match_info2, 3),
+ charset, modem_family, &inner_error);
+ if (inner_error)
+ break;
parse_bands (maxband, &bands, MM_CINTERION_RB_BLOCK_GSM, modem_family);
} else if (g_strcmp0 (techstr, "3G") == 0) {
- maxband = take_and_convert_from_matched_string (mm_get_string_unquoted_from_match_info (match_info2, 3), charset, modem_family);
+ maxband = take_and_convert_from_matched_string (mm_get_string_unquoted_from_match_info (match_info2, 3),
+ charset, modem_family, &inner_error);
+ if (inner_error)
+ break;
parse_bands (maxband, &bands, MM_CINTERION_RB_BLOCK_UMTS, modem_family);
} else if (g_strcmp0 (techstr, "4G") == 0) {
- maxband = take_and_convert_from_matched_string (mm_get_string_unquoted_from_match_info (match_info2, 3), charset, modem_family);
+ maxband = take_and_convert_from_matched_string (mm_get_string_unquoted_from_match_info (match_info2, 3),
+ charset, modem_family, &inner_error);
+ if (inner_error)
+ break;
parse_bands (maxband, &bands, MM_CINTERION_RB_BLOCK_LTE_LOW, modem_family);
if (modem_family == MM_CINTERION_MODEM_FAMILY_DEFAULT) {
- maxband = take_and_convert_from_matched_string (mm_get_string_unquoted_from_match_info (match_info2, 6), charset, modem_family);
+ maxband = take_and_convert_from_matched_string (mm_get_string_unquoted_from_match_info (match_info2, 6),
+ charset, modem_family, &inner_error);
+ if (inner_error)
+ break;
parse_bands (maxband, &bands, MM_CINTERION_RB_BLOCK_LTE_HIGH, modem_family);
}
} else {
@@ -407,9 +437,11 @@
if (format == MM_CINTERION_RADIO_BAND_FORMAT_SINGLE) {
r = g_regex_new ("\\^SCFG:\\s*\"Radio/Band\",\\s*\"?([0-9a-fA-F]*)\"?", 0, 0, NULL);
g_assert (r != NULL);
+
g_regex_match_full (r, response, strlen (response), 0, 0, &match_info, &inner_error);
if (inner_error)
goto finish;
+
if (g_match_info_matches (match_info)) {
g_autofree gchar *currentstr = NULL;
guint current = 0;
@@ -438,26 +470,40 @@
r = g_regex_new ("\\^SCFG:\\s*\"Radio/Band/([234]G)\",\"?([0-9A-Fa-fx]*)\"?,?\"?([0-9A-Fa-fx]*)?\"?",
0, 0, NULL);
g_assert (r != NULL);
+
g_regex_match_full (r, response, strlen (response), 0, 0, &match_info, &inner_error);
if (inner_error)
goto finish;
+
while (g_match_info_matches (match_info)) {
g_autofree gchar *techstr = NULL;
guint current;
techstr = mm_get_string_unquoted_from_match_info (match_info, 1);
- if (g_strcmp0(techstr, "2G") == 0) {
- current = take_and_convert_from_matched_string (mm_get_string_unquoted_from_match_info (match_info, 2), charset, modem_family);
+ if (g_strcmp0 (techstr, "2G") == 0) {
+ current = take_and_convert_from_matched_string (mm_get_string_unquoted_from_match_info (match_info, 2),
+ charset, modem_family, &inner_error);
+ if (inner_error)
+ break;
parse_bands (current, &bands, MM_CINTERION_RB_BLOCK_GSM, modem_family);
- } else if (g_strcmp0(techstr, "3G") == 0) {
- current = take_and_convert_from_matched_string (mm_get_string_unquoted_from_match_info (match_info, 2), charset, modem_family);
+ } else if (g_strcmp0 (techstr, "3G") == 0) {
+ current = take_and_convert_from_matched_string (mm_get_string_unquoted_from_match_info (match_info, 2),
+ charset, modem_family, &inner_error);
+ if (inner_error)
+ break;
parse_bands (current, &bands, MM_CINTERION_RB_BLOCK_UMTS, modem_family);
- } else if (g_strcmp0(techstr, "4G") == 0) {
- current = take_and_convert_from_matched_string (mm_get_string_unquoted_from_match_info (match_info, 2), charset, modem_family);
+ } else if (g_strcmp0 (techstr, "4G") == 0) {
+ current = take_and_convert_from_matched_string (mm_get_string_unquoted_from_match_info (match_info, 2),
+ charset, modem_family, &inner_error);
+ if (inner_error)
+ break;
parse_bands (current, &bands, MM_CINTERION_RB_BLOCK_LTE_LOW, modem_family);
if (modem_family == MM_CINTERION_MODEM_FAMILY_DEFAULT) {
- current = take_and_convert_from_matched_string (mm_get_string_unquoted_from_match_info (match_info, 3), charset, modem_family);
+ current = take_and_convert_from_matched_string (mm_get_string_unquoted_from_match_info (match_info, 3),
+ charset, modem_family, &inner_error);
+ if (inner_error)
+ break;
parse_bands (current, &bands, MM_CINTERION_RB_BLOCK_LTE_HIGH, modem_family);
}
} else {
@@ -1477,9 +1523,15 @@
mno = mm_get_string_unquoted_from_match_info (match_info, 1);
if (mno && modem_family == MM_CINTERION_MODEM_FAMILY_IMT) {
- mno = mm_charset_take_and_convert_to_utf8 (mno, charset);
- mm_obj_dbg (log_object, "current mno: %s", mno);
+ gchar *mno_utf8;
+
+ mno_utf8 = mm_modem_charset_str_to_utf8 (mno, -1, charset, FALSE, error);
+ if (!mno_utf8)
+ return FALSE;
+ g_free (mno);
+ mno = mno_utf8;
}
+ mm_obj_dbg (log_object, "current mno: %s", mno ? mno : "none");
/* for Cinterion LTE modules, some CID numbers have special meaning.
* This is dictated by the chipset and by the MNO:
diff --git a/plugins/huawei/mm-broadband-modem-huawei.c b/plugins/huawei/mm-broadband-modem-huawei.c
index 19249a8..2c341d3 100644
--- a/plugins/huawei/mm-broadband-modem-huawei.c
+++ b/plugins/huawei/mm-broadband-modem-huawei.c
@@ -177,8 +177,7 @@
/* Additional cdc-wdm ports used for dialing */
cdc_wdm_at_ports = mm_base_modem_find_ports (MM_BASE_MODEM (self),
MM_PORT_SUBSYS_USBMISC,
- MM_PORT_TYPE_AT,
- NULL);
+ MM_PORT_TYPE_AT);
return g_list_concat (out, cdc_wdm_at_ports);
}
@@ -2201,8 +2200,7 @@
/* Find the CDC-WDM port on the same USB interface as the given net port */
cdc_wdm_at_ports = mm_base_modem_find_ports (MM_BASE_MODEM (self),
MM_PORT_SUBSYS_USBMISC,
- MM_PORT_TYPE_AT,
- NULL);
+ MM_PORT_TYPE_AT);
for (l = cdc_wdm_at_ports; l && !found; l = g_list_next (l)) {
const gchar *wdm_port_parent_path;
@@ -2301,28 +2299,27 @@
guint *scheme,
GError **error)
{
- gchar *hex;
- guint8 *gsm, *packed;
- guint32 len = 0, packed_len = 0;
+ g_autoptr(GByteArray) gsm = NULL;
+ g_autofree guint8 *packed = NULL;
+ guint32 packed_len = 0;
+
+ gsm = mm_modem_charset_bytearray_from_utf8 (command, MM_MODEM_CHARSET_GSM, FALSE, error);
+ if (!gsm)
+ return NULL;
*scheme = MM_MODEM_GSM_USSD_SCHEME_7BIT;
- gsm = mm_charset_utf8_to_unpacked_gsm (command, &len);
/* If command is a multiple of 7 characters long, Huawei firmwares
* apparently want that padded. Maybe all modems?
*/
- if (len % 7 == 0) {
- gsm = g_realloc (gsm, len + 1);
- gsm[len] = 0x0d;
- len++;
+ if (gsm->len % 7 == 0) {
+ static const guint8 padding = 0x0d;
+
+ g_byte_array_append (gsm, &padding, 1);
}
- packed = mm_charset_gsm_pack (gsm, len, 0, &packed_len);
- hex = mm_utils_bin2hexstr (packed, packed_len);
- g_free (packed);
- g_free (gsm);
-
- return hex;
+ packed = mm_charset_gsm_pack (gsm->data, gsm->len, 0, &packed_len);
+ return mm_utils_bin2hexstr (packed, packed_len);
}
static gchar *
@@ -2330,21 +2327,25 @@
const gchar *reply,
GError **error)
{
- gchar *bin, *utf8;
- guint8 *unpacked;
- gsize bin_len;
- guint32 unpacked_len;
+ g_autofree guint8 *bin = NULL;
+ gsize bin_len = 0;
+ g_autofree guint8 *unpacked = NULL;
+ guint32 unpacked_len;
+ g_autoptr(GByteArray) unpacked_array = NULL;
- bin = mm_utils_hexstr2bin (reply, &bin_len);
- unpacked = mm_charset_gsm_unpack ((guint8*) bin, (bin_len * 8) / 7, 0, &unpacked_len);
+ bin = mm_utils_hexstr2bin (reply, -1, &bin_len, error);
+ if (!bin)
+ return NULL;
+
+ unpacked = mm_charset_gsm_unpack (bin, (bin_len * 8) / 7, 0, &unpacked_len);
/* if the last character in a 7-byte block is padding, then drop it */
if ((bin_len % 7 == 0) && (unpacked[unpacked_len - 1] == 0x0d))
unpacked_len--;
- utf8 = (char*) mm_charset_gsm_unpacked_to_utf8 (unpacked, unpacked_len);
- g_free (bin);
- g_free (unpacked);
- return utf8;
+ unpacked_array = g_byte_array_sized_new (unpacked_len);
+ g_byte_array_append (unpacked_array, unpacked, unpacked_len);
+
+ return mm_modem_charset_bytearray_to_utf8 (unpacked_array, MM_MODEM_CHARSET_GSM, FALSE, error);
}
/*****************************************************************************/
diff --git a/plugins/huawei/mm-modem-helpers-huawei.c b/plugins/huawei/mm-modem-helpers-huawei.c
index 2cd94e6..1b44305 100644
--- a/plugins/huawei/mm-modem-helpers-huawei.c
+++ b/plugins/huawei/mm-modem-helpers-huawei.c
@@ -157,14 +157,15 @@
static gboolean
match_info_to_ip4_addr (GMatchInfo *match_info,
- guint match_index,
- guint *out_addr)
+ guint match_index,
+ guint *out_addr)
{
- gchar *s, *bin = NULL;
- gchar buf[9];
- gsize len, bin_len;
- gboolean success = FALSE;
- guint32 aux;
+ g_autofree gchar *s = NULL;
+ g_autofree guint8 *bin = NULL;
+ gchar buf[9];
+ gsize len;
+ gsize bin_len;
+ guint32 aux;
s = g_match_info_fetch (match_info, match_index);
g_return_val_if_fail (s != NULL, FALSE);
@@ -172,11 +173,11 @@
len = strlen (s);
if (len == 1 && s[0] == '0') {
*out_addr = 0;
- success = TRUE;
- goto done;
+ return TRUE;
}
+
if (len < 7 || len > 8)
- goto done;
+ return FALSE;
/* Handle possibly missing leading zero */
memset (buf, 0, sizeof (buf));
@@ -188,18 +189,13 @@
else
g_assert_not_reached ();
- bin = mm_utils_hexstr2bin (buf, &bin_len);
+ bin = mm_utils_hexstr2bin (buf, -1, &bin_len, NULL);
if (!bin || bin_len != 4)
- goto done;
+ return FALSE;
memcpy (&aux, bin, 4);
*out_addr = GUINT32_SWAP_LE_BE (aux);
- success = TRUE;
-
-done:
- g_free (s);
- g_free (bin);
- return success;
+ return TRUE;
}
gboolean
diff --git a/plugins/huawei/mm-plugin-huawei.c b/plugins/huawei/mm-plugin-huawei.c
index 4c5c39e..07652ec 100644
--- a/plugins/huawei/mm-plugin-huawei.c
+++ b/plugins/huawei/mm-plugin-huawei.c
@@ -56,7 +56,7 @@
typedef struct {
MMPortProbe *probe;
- guint first_usbif;
+ gint first_usbif;
guint timeout_id;
gboolean custom_init_run;
} FirstInterfaceContext;
@@ -177,23 +177,23 @@
MMDevice *device)
{
FirstInterfaceContext *fi_ctx;
- GList *l;
- guint closest;
+ GList *l;
+ gint closest;
fi_ctx = g_object_get_data (G_OBJECT (device), TAG_FIRST_INTERFACE_CONTEXT);
g_assert (fi_ctx != NULL);
/* Look for the next closest one among the list of interfaces in the device,
* and enable that one as being first */
- closest = G_MAXUINT;
+ closest = G_MAXINT;
for (l = mm_device_peek_port_probe_list (device); l; l = g_list_next (l)) {
MMPortProbe *iter = MM_PORT_PROBE (l->data);
/* Only expect ttys for next probing attempt */
if (g_str_equal (mm_port_probe_get_port_subsys (iter), "tty")) {
- guint usbif;
+ gint usbif;
- usbif = mm_kernel_device_get_property_as_int_hex (mm_port_probe_peek_port (iter), "ID_USB_INTERFACE_NUM");
+ usbif = mm_kernel_device_get_interface_number (mm_port_probe_peek_port (iter));
if (usbif == fi_ctx->first_usbif) {
/* This is the one we just probed, which wasn't yet removed, so just skip it */
} else if (usbif > fi_ctx->first_usbif &&
@@ -203,7 +203,7 @@
}
}
- if (closest == G_MAXUINT) {
+ if (closest == G_MAXINT) {
/* No more ttys to try! Just return something */
closest = 0;
mm_obj_dbg (probe, "no more ports to run initial probing");
@@ -359,9 +359,7 @@
g_task_set_task_data (task, ctx, (GDestroyNotify)huawei_custom_init_context_free);
/* Custom init only to be run in the first interface */
- if (mm_kernel_device_get_property_as_int_hex (mm_port_probe_peek_port (probe),
- "ID_USB_INTERFACE_NUM") != fi_ctx->first_usbif) {
-
+ if (mm_kernel_device_get_interface_number (mm_port_probe_peek_port (probe)) != fi_ctx->first_usbif) {
if (fi_ctx->custom_init_run)
/* If custom init was run already, we can consider this as successfully run */
g_task_return_boolean (task, TRUE);
@@ -391,8 +389,8 @@
probe_cmp_by_usbif (MMPortProbe *a,
MMPortProbe *b)
{
- return ((gint) mm_kernel_device_get_property_as_int_hex (mm_port_probe_peek_port (a), "ID_USB_INTERFACE_NUM") -
- (gint) mm_kernel_device_get_property_as_int_hex (mm_port_probe_peek_port (b), "ID_USB_INTERFACE_NUM"));
+ return (mm_kernel_device_get_interface_number (mm_port_probe_peek_port (a)) -
+ mm_kernel_device_get_interface_number (mm_port_probe_peek_port (b)));
}
static guint
diff --git a/plugins/mbm/mm-broadband-bearer-mbm.c b/plugins/mbm/mm-broadband-bearer-mbm.c
index b4bba2d..8de7a09 100644
--- a/plugins/mbm/mm-broadband-bearer-mbm.c
+++ b/plugins/mbm/mm-broadband-bearer-mbm.c
@@ -359,22 +359,35 @@
/* Both user and password are required; otherwise firmware returns an error */
if (user || password) {
- gchar *command;
- gchar *encoded_user;
- gchar *encoded_password;
+ g_autofree gchar *command = NULL;
+ g_autofree gchar *user_enc = NULL;
+ g_autofree gchar *password_enc = NULL;
+ GError *error = NULL;
- encoded_user = mm_broadband_modem_take_and_convert_to_current_charset (MM_BROADBAND_MODEM (ctx->modem),
- g_strdup (user));
- encoded_password = mm_broadband_modem_take_and_convert_to_current_charset (MM_BROADBAND_MODEM (ctx->modem),
- g_strdup (password));
+ user_enc = mm_modem_charset_str_from_utf8 (user,
+ mm_broadband_modem_get_current_charset (MM_BROADBAND_MODEM (ctx->modem)),
+ FALSE,
+ &error);
+ if (!user_enc) {
+ g_prefix_error (&error, "Couldn't convert user to current charset: ");
+ g_task_return_error (task, error);
+ g_object_unref (task);
+ return;
+ }
+
+ password_enc = mm_modem_charset_str_from_utf8 (password,
+ mm_broadband_modem_get_current_charset (MM_BROADBAND_MODEM (ctx->modem)),
+ FALSE,
+ &error);
+ if (!password_enc) {
+ g_prefix_error (&error, "Couldn't convert password to current charset: ");
+ g_task_return_error (task, error);
+ g_object_unref (task);
+ return;
+ }
command = g_strdup_printf ("AT*EIAAUW=%d,1,\"%s\",\"%s\"",
- ctx->cid,
- encoded_user ? encoded_user : "",
- encoded_password ? encoded_password : "");
- g_free (encoded_user);
- g_free (encoded_password);
-
+ ctx->cid, user_enc, password_enc);
mm_base_modem_at_command_full (ctx->modem,
ctx->primary,
command,
@@ -384,7 +397,6 @@
g_task_get_cancellable (task),
(GAsyncReadyCallback) authenticate_ready,
task);
- g_free (command);
return;
}
diff --git a/plugins/option/mm-plugin-option.c b/plugins/option/mm-plugin-option.c
index 3ba183e..1f72325 100644
--- a/plugins/option/mm-plugin-option.c
+++ b/plugins/option/mm-plugin-option.c
@@ -56,7 +56,7 @@
{
MMPortSerialAtFlag pflags = MM_PORT_SERIAL_AT_FLAG_NONE;
MMKernelDevice *port;
- guint usbif;
+ gint usbif;
/* The Option plugin cannot do anything with non-AT ports */
if (!mm_port_probe_is_at (probe)) {
@@ -73,7 +73,7 @@
* the modem/data port, per mail with Option engineers. Only this port
* will emit responses to dialing commands.
*/
- usbif = mm_kernel_device_get_property_as_int_hex (port, "ID_USB_INTERFACE_NUM");
+ usbif = mm_kernel_device_get_interface_number (port);
if (usbif == 0)
pflags = MM_PORT_SERIAL_AT_FLAG_PRIMARY | MM_PORT_SERIAL_AT_FLAG_PPP;
diff --git a/plugins/qcom-soc/mm-broadband-modem-qmi-qcom-soc.c b/plugins/qcom-soc/mm-broadband-modem-qmi-qcom-soc.c
index 58a6e5e..0256903 100644
--- a/plugins/qcom-soc/mm-broadband-modem-qmi-qcom-soc.c
+++ b/plugins/qcom-soc/mm-broadband-modem-qmi-qcom-soc.c
@@ -81,8 +81,7 @@
/* Find one QMI port, we don't care which one */
rpmsg_qmi_ports = mm_base_modem_find_ports (MM_BASE_MODEM (self),
MM_PORT_SUBSYS_RPMSG,
- MM_PORT_TYPE_QMI,
- NULL);
+ MM_PORT_TYPE_QMI);
if (!rpmsg_qmi_ports) {
g_set_error (error,
MM_CORE_ERROR,
@@ -93,7 +92,8 @@
}
/* Set outputs */
- *out_sio_port = sio_port_per_port_number[net_port_number];
+ if (out_sio_port)
+ *out_sio_port = sio_port_per_port_number[net_port_number];
found = MM_PORT_QMI (rpmsg_qmi_ports->data);
g_list_free_full (rpmsg_qmi_ports, g_object_unref);
diff --git a/plugins/telit/mm-common-telit.c b/plugins/telit/mm-common-telit.c
index f53f5b75..2e0b380 100644
--- a/plugins/telit/mm-common-telit.c
+++ b/plugins/telit/mm-common-telit.c
@@ -59,7 +59,7 @@
if (g_object_get_data (G_OBJECT (device), TAG_GETPORTCFG_SUPPORTED) != NULL) {
guint usbif;
- usbif = mm_kernel_device_get_property_as_int_hex (port, "ID_USB_INTERFACE_NUM");
+ usbif = (guint) mm_kernel_device_get_interface_number (port);
if (usbif == GPOINTER_TO_UINT (g_object_get_data (G_OBJECT (device), TAG_TELIT_MODEM_PORT))) {
mm_obj_dbg (self, "AT port '%s/%s' flagged as primary",
mm_port_probe_get_port_subsys (probe),
@@ -257,9 +257,7 @@
* is always linked to an AT port
*/
port = mm_port_probe_peek_port (probe);
- if (!ctx->getportcfg_done &&
- g_strcmp0 (mm_kernel_device_get_property (port, "ID_USB_INTERFACE_NUM"), "00") == 0) {
-
+ if (!ctx->getportcfg_done && mm_kernel_device_get_interface_number (port) == 0) {
if (ctx->getportcfg_retries == 0)
goto out;
ctx->getportcfg_retries--;
diff --git a/src/Makefile.am b/src/Makefile.am
index e80c10a..05fc6d4 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -287,7 +287,7 @@
main.c \
mm-context.h \
mm-context.c \
- mm-utils.h \
+ mm-utils.h mm-utils.c \
mm-private-boxed-types.h \
mm-private-boxed-types.c \
mm-auth-provider.h \
diff --git a/src/kerneldevice/mm-kernel-device-generic.c b/src/kerneldevice/mm-kernel-device-generic.c
index b1a3bf9..3428563 100644
--- a/src/kerneldevice/mm-kernel-device-generic.c
+++ b/src/kerneldevice/mm-kernel-device-generic.c
@@ -27,6 +27,7 @@
#include "mm-kernel-device-generic.h"
#include "mm-kernel-device-generic-rules.h"
#include "mm-log-object.h"
+#include "mm-utils.h"
#if !defined UDEVRULESDIR
# error UDEVRULESDIR is not defined
@@ -551,6 +552,12 @@
}
static gint
+kernel_device_get_interface_number (MMKernelDevice *self)
+{
+ return (gint) MM_KERNEL_DEVICE_GENERIC (self)->priv->interface_number;
+}
+
+static gint
kernel_device_get_interface_class (MMKernelDevice *self)
{
return (gint) MM_KERNEL_DEVICE_GENERIC (self)->priv->interface_class;
@@ -1172,6 +1179,7 @@
kernel_device_class->get_physdev_subsystem = kernel_device_get_physdev_subsystem;
kernel_device_class->get_physdev_manufacturer = kernel_device_get_physdev_manufacturer;
kernel_device_class->get_physdev_product = kernel_device_get_physdev_product;
+ kernel_device_class->get_interface_number = kernel_device_get_interface_number;
kernel_device_class->get_interface_class = kernel_device_get_interface_class;
kernel_device_class->get_interface_subclass = kernel_device_get_interface_subclass;
kernel_device_class->get_interface_protocol = kernel_device_get_interface_protocol;
diff --git a/src/kerneldevice/mm-kernel-device-udev.c b/src/kerneldevice/mm-kernel-device-udev.c
index f75104b..0687ec6 100644
--- a/src/kerneldevice/mm-kernel-device-udev.c
+++ b/src/kerneldevice/mm-kernel-device-udev.c
@@ -31,6 +31,7 @@
enum {
PROP_0,
+ PROP_UDEV_CLIENT,
PROP_UDEV_DEVICE,
PROP_PROPERTIES,
PROP_LAST
@@ -39,6 +40,7 @@
static GParamSpec *properties[PROP_LAST];
struct _MMKernelDeviceUdevPrivate {
+ GUdevClient *client;
GUdevDevice *device;
GUdevDevice *interface;
@@ -403,6 +405,15 @@
}
static gint
+kernel_device_get_interface_number (MMKernelDevice *_self)
+{
+ MMKernelDeviceUdev *self;
+
+ self = MM_KERNEL_DEVICE_UDEV (_self);
+ return (self->priv->interface ? (gint) udev_device_get_sysfs_attr_as_hex (self->priv->interface, "bInterfaceNumber") : -1);
+}
+
+static gint
kernel_device_get_interface_class (MMKernelDevice *_self)
{
MMKernelDeviceUdev *self;
@@ -556,7 +567,8 @@
/*****************************************************************************/
MMKernelDevice *
-mm_kernel_device_udev_new (GUdevDevice *udev_device)
+mm_kernel_device_udev_new (GUdevClient *udev_client,
+ GUdevDevice *udev_device)
{
GError *error = NULL;
MMKernelDevice *self;
@@ -565,6 +577,7 @@
self = MM_KERNEL_DEVICE (g_initable_new (MM_TYPE_KERNEL_DEVICE_UDEV,
NULL,
&error,
+ "udev-client", udev_client,
"udev-device", udev_device,
NULL));
g_assert_no_error (error);
@@ -574,13 +587,15 @@
/*****************************************************************************/
MMKernelDevice *
-mm_kernel_device_udev_new_from_properties (MMKernelEventProperties *props,
+mm_kernel_device_udev_new_from_properties (GUdevClient *udev_client,
+ MMKernelEventProperties *props,
GError **error)
{
return MM_KERNEL_DEVICE (g_initable_new (MM_TYPE_KERNEL_DEVICE_UDEV,
NULL,
error,
- "properties", props,
+ "udev-client", udev_client,
+ "properties", props,
NULL));
}
@@ -602,6 +617,10 @@
MMKernelDeviceUdev *self = MM_KERNEL_DEVICE_UDEV (object);
switch (prop_id) {
+ case PROP_UDEV_CLIENT:
+ g_assert (!self->priv->client);
+ self->priv->client = g_value_dup_object (value);
+ break;
case PROP_UDEV_DEVICE:
g_assert (!self->priv->device);
self->priv->device = g_value_dup_object (value);
@@ -625,6 +644,9 @@
MMKernelDeviceUdev *self = MM_KERNEL_DEVICE_UDEV (object);
switch (prop_id) {
+ case PROP_UDEV_CLIENT:
+ g_value_set_object (value, self->priv->client);
+ break;
case PROP_UDEV_DEVICE:
g_value_set_object (value, self->priv->device);
break;
@@ -646,6 +668,12 @@
const gchar *subsystem;
const gchar *name;
+ if (!self->priv->client) {
+ g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_INVALID_ARGS,
+ "missing client in kernel device");
+ return FALSE;
+ }
+
/* When created from a GUdevDevice, we're done */
if (self->priv->device) {
preload_contents (self);
@@ -675,23 +703,15 @@
/* On remove events, we don't look for the GUdevDevice */
if (g_strcmp0 (mm_kernel_event_properties_get_action (self->priv->properties), "remove")) {
- GUdevClient *client;
- GUdevDevice *device;
-
- client = g_udev_client_new (NULL);
- device = g_udev_client_query_by_subsystem_and_name (client, subsystem, name);
- if (!device) {
+ g_assert (!self->priv->device);
+ self->priv->device = g_udev_client_query_by_subsystem_and_name (self->priv->client, subsystem, name);
+ if (!self->priv->device) {
g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_INVALID_ARGS,
"device %s/%s not found",
subsystem,
name);
- g_object_unref (client);
return FALSE;
}
-
- /* Store device */
- self->priv->device = device;
- g_object_unref (client);
}
if (self->priv->device)
@@ -708,6 +728,7 @@
g_clear_object (&self->priv->physdev);
g_clear_object (&self->priv->interface);
g_clear_object (&self->priv->device);
+ g_clear_object (&self->priv->client);
g_clear_object (&self->priv->properties);
G_OBJECT_CLASS (mm_kernel_device_udev_parent_class)->dispose (object);
@@ -743,6 +764,7 @@
kernel_device_class->get_physdev_subsystem = kernel_device_get_physdev_subsystem;
kernel_device_class->get_physdev_manufacturer = kernel_device_get_physdev_manufacturer;
kernel_device_class->get_physdev_product = kernel_device_get_physdev_product;
+ kernel_device_class->get_interface_number = kernel_device_get_interface_number;
kernel_device_class->get_interface_class = kernel_device_get_interface_class;
kernel_device_class->get_interface_subclass = kernel_device_get_interface_subclass;
kernel_device_class->get_interface_protocol = kernel_device_get_interface_protocol;
@@ -764,6 +786,14 @@
G_PARAM_READWRITE);
g_object_class_install_property (object_class, PROP_UDEV_DEVICE, properties[PROP_UDEV_DEVICE]);
+ properties[PROP_UDEV_CLIENT] =
+ g_param_spec_object ("udev-client",
+ "udev client",
+ "GUdev client",
+ G_UDEV_TYPE_CLIENT,
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY);
+ g_object_class_install_property (object_class, PROP_UDEV_CLIENT, properties[PROP_UDEV_CLIENT]);
+
properties[PROP_PROPERTIES] =
g_param_spec_object ("properties",
"Properties",
diff --git a/src/kerneldevice/mm-kernel-device-udev.h b/src/kerneldevice/mm-kernel-device-udev.h
index 34445b2..93153b3 100644
--- a/src/kerneldevice/mm-kernel-device-udev.h
+++ b/src/kerneldevice/mm-kernel-device-udev.h
@@ -48,8 +48,10 @@
GType mm_kernel_device_udev_get_type (void);
G_DEFINE_AUTOPTR_CLEANUP_FUNC (MMKernelDeviceUdev, g_object_unref)
-MMKernelDevice *mm_kernel_device_udev_new (GUdevDevice *udev_device);
-MMKernelDevice *mm_kernel_device_udev_new_from_properties (MMKernelEventProperties *properties,
+MMKernelDevice *mm_kernel_device_udev_new (GUdevClient *udev_client,
+ GUdevDevice *udev_device);
+MMKernelDevice *mm_kernel_device_udev_new_from_properties (GUdevClient *udev_client,
+ MMKernelEventProperties *properties,
GError **error);
#endif /* MM_KERNEL_DEVICE_UDEV_H */
diff --git a/src/kerneldevice/mm-kernel-device.c b/src/kerneldevice/mm-kernel-device.c
index 464a380..86251bc 100644
--- a/src/kerneldevice/mm-kernel-device.c
+++ b/src/kerneldevice/mm-kernel-device.c
@@ -126,6 +126,14 @@
}
gint
+mm_kernel_device_get_interface_number (MMKernelDevice *self)
+{
+ return (MM_KERNEL_DEVICE_GET_CLASS (self)->get_interface_number ?
+ MM_KERNEL_DEVICE_GET_CLASS (self)->get_interface_number (self) :
+ -1);
+}
+
+gint
mm_kernel_device_get_interface_class (MMKernelDevice *self)
{
return (MM_KERNEL_DEVICE_GET_CLASS (self)->get_interface_class ?
diff --git a/src/kerneldevice/mm-kernel-device.h b/src/kerneldevice/mm-kernel-device.h
index 45b270d..f1d869b 100644
--- a/src/kerneldevice/mm-kernel-device.h
+++ b/src/kerneldevice/mm-kernel-device.h
@@ -41,6 +41,7 @@
const gchar * (* get_driver) (MMKernelDevice *self);
const gchar * (* get_sysfs_path) (MMKernelDevice *self);
+ gint (* get_interface_number) (MMKernelDevice *self);
gint (* get_interface_class) (MMKernelDevice *self);
gint (* get_interface_subclass) (MMKernelDevice *self);
gint (* get_interface_protocol) (MMKernelDevice *self);
@@ -74,6 +75,7 @@
const gchar *mm_kernel_device_get_driver (MMKernelDevice *self);
const gchar *mm_kernel_device_get_sysfs_path (MMKernelDevice *self);
+gint mm_kernel_device_get_interface_number (MMKernelDevice *self);
gint mm_kernel_device_get_interface_class (MMKernelDevice *self);
gint mm_kernel_device_get_interface_subclass (MMKernelDevice *self);
gint mm_kernel_device_get_interface_protocol (MMKernelDevice *self);
diff --git a/src/main.c b/src/main.c
index 928078a..d11383c 100644
--- a/src/main.c
+++ b/src/main.c
@@ -182,6 +182,9 @@
mm_info ("ModemManager (version " MM_DIST_VERSION ") starting in %s bus...",
mm_context_get_test_session () ? "session" : "system");
+ /* Detect runtime charset conversion support */
+ mm_modem_charsets_init ();
+
/* Acquire name, don't allow replacement */
name_id = g_bus_own_name (mm_context_get_test_session () ? G_BUS_TYPE_SESSION : G_BUS_TYPE_SYSTEM,
MM_DBUS_SERVICE,
diff --git a/src/mm-base-manager.c b/src/mm-base-manager.c
index 41236c4..f3f7e51 100644
--- a/src/mm-base-manager.c
+++ b/src/mm-base-manager.c
@@ -407,7 +407,7 @@
g_autoptr(MMKernelDevice) kernel_device = NULL;
#if defined WITH_UDEV
if (!mm_context_get_test_no_udev ())
- kernel_device = mm_kernel_device_udev_new_from_properties (properties, error);
+ kernel_device = mm_kernel_device_udev_new_from_properties (self->priv->udev, properties, error);
else
#endif
kernel_device = mm_kernel_device_generic_new (properties, error);
@@ -436,7 +436,7 @@
if (g_str_equal (action, "add") || g_str_equal (action, "move") || g_str_equal (action, "change")) {
g_autoptr(MMKernelDevice) kernel_device = NULL;
- kernel_device = mm_kernel_device_udev_new (device);
+ kernel_device = mm_kernel_device_udev_new (self->priv->udev, device);
device_added (self, kernel_device, TRUE, FALSE);
return;
}
@@ -458,7 +458,7 @@
{
MMKernelDevice *kernel_device;
- kernel_device = mm_kernel_device_udev_new (ctx->device);
+ kernel_device = mm_kernel_device_udev_new (ctx->self->priv->udev, ctx->device);
device_added (ctx->self, kernel_device, FALSE, ctx->manual_scan);
g_object_unref (kernel_device);
@@ -1493,13 +1493,12 @@
return FALSE;
#if defined WITH_UDEV
- if (!mm_context_get_test_no_udev ()) {
- /* Create udev client based on the subsystems requested by the plugins */
- self->priv->udev = g_udev_client_new (mm_plugin_manager_get_subsystems (self->priv->plugin_manager));
- /* If autoscan enabled, list for udev events */
- if (self->priv->auto_scan)
- g_signal_connect_swapped (self->priv->udev, "uevent", G_CALLBACK (handle_uevent), initable);
- }
+ /* Create udev client based on the subsystems requested by the plugins */
+ self->priv->udev = g_udev_client_new (mm_plugin_manager_get_subsystems (self->priv->plugin_manager));
+
+ /* If autoscan enabled, list for udev events */
+ if (!mm_context_get_test_no_udev () && self->priv->auto_scan)
+ g_signal_connect_swapped (self->priv->udev, "uevent", G_CALLBACK (handle_uevent), initable);
#endif
/* Export the manager interface */
diff --git a/src/mm-base-modem.c b/src/mm-base-modem.c
index 78720f9..cfa50a2 100644
--- a/src/mm-base-modem.c
+++ b/src/mm-base-modem.c
@@ -834,21 +834,18 @@
}
GList *
-mm_base_modem_find_ports (MMBaseModem *self,
- MMPortSubsys subsys,
- MMPortType type,
- const gchar *name)
+mm_base_modem_find_ports (MMBaseModem *self,
+ MMPortSubsys subsys,
+ MMPortType type)
{
- GList *out = NULL;
- GHashTableIter iter;
- gpointer value;
- gpointer key;
+ GList *out = NULL;
+ GHashTableIter iter;
+ gpointer value;
+ gpointer key;
if (!self->priv->ports)
return NULL;
- /* We'll iterate the ht of ports, looking for any port which is matches
- * the compare function */
g_hash_table_iter_init (&iter, self->priv->ports);
while (g_hash_table_iter_next (&iter, &key, &value)) {
MMPort *port = MM_PORT (value);
@@ -859,15 +856,44 @@
if (type != MM_PORT_TYPE_UNKNOWN && mm_port_get_port_type (port) != type)
continue;
- if (name != NULL && !g_str_equal (mm_port_get_device (port), name))
- continue;
-
out = g_list_append (out, g_object_ref (port));
}
return g_list_sort (out, (GCompareFunc) port_cmp);
}
+MMPort *
+mm_base_modem_peek_port (MMBaseModem *self,
+ const gchar *name)
+{
+ GHashTableIter iter;
+ gpointer value;
+ gpointer key;
+
+ if (!self->priv->ports)
+ return NULL;
+
+ g_hash_table_iter_init (&iter, self->priv->ports);
+ while (g_hash_table_iter_next (&iter, &key, &value)) {
+ MMPort *port = MM_PORT (value);
+
+ if (g_str_equal (mm_port_get_device (port), name))
+ return port;
+ }
+
+ return NULL;
+}
+
+MMPort *
+mm_base_modem_get_port (MMBaseModem *self,
+ const gchar *name)
+{
+ MMPort *port;
+
+ port = mm_base_modem_peek_port (self, name);
+ return (port ? g_object_ref (port) : NULL);
+}
+
static void
initialize_ready (MMBaseModem *self,
GAsyncResult *res)
diff --git a/src/mm-base-modem.h b/src/mm-base-modem.h
index 2463481..11c3993 100644
--- a/src/mm-base-modem.h
+++ b/src/mm-base-modem.h
@@ -142,10 +142,13 @@
MMModemPortInfo *mm_base_modem_get_port_infos (MMBaseModem *self,
guint *n_port_infos);
-GList *mm_base_modem_find_ports (MMBaseModem *self,
- MMPortSubsys subsys,
- MMPortType type,
- const gchar *name);
+GList *mm_base_modem_find_ports (MMBaseModem *self,
+ MMPortSubsys subsys,
+ MMPortType type);
+MMPort *mm_base_modem_peek_port (MMBaseModem *self,
+ const gchar *name);
+MMPort *mm_base_modem_get_port (MMBaseModem *self,
+ const gchar *name);
void mm_base_modem_set_hotplugged (MMBaseModem *self,
gboolean hotplugged);
diff --git a/src/mm-base-sim.c b/src/mm-base-sim.c
index 8505119..a3275fc 100644
--- a/src/mm-base-sim.c
+++ b/src/mm-base-sim.c
@@ -1141,6 +1141,101 @@
}
/*****************************************************************************/
+/* Preferred networks */
+
+static GList *
+parse_preferred_networks (const gchar *response,
+ GError **error)
+{
+ gchar **entries;
+ gchar **iter;
+ GList *result = NULL;
+
+ entries = g_strsplit_set (response, "\r\n", -1);
+ for (iter = entries; iter && *iter; iter++) {
+ gchar *operator_code = NULL;
+ gboolean gsm_act;
+ gboolean gsm_compact_act;
+ gboolean utran_act;
+ gboolean eutran_act;
+ gboolean ngran_act;
+ MMSimPreferredNetwork *preferred_network = NULL;
+ MMModemAccessTechnology act = MM_MODEM_ACCESS_TECHNOLOGY_UNKNOWN;
+
+ g_strstrip (*iter);
+ if (strlen (*iter) == 0)
+ continue;
+
+ if (mm_sim_parse_cpol_query_response (*iter,
+ &operator_code,
+ &gsm_act,
+ &gsm_compact_act,
+ &utran_act,
+ &eutran_act,
+ &ngran_act,
+ error)) {
+ preferred_network = mm_sim_preferred_network_new ();
+ mm_sim_preferred_network_set_operator_code (preferred_network, operator_code);
+ if (gsm_act)
+ act |= MM_MODEM_ACCESS_TECHNOLOGY_GSM;
+ if (gsm_compact_act)
+ act |= MM_MODEM_ACCESS_TECHNOLOGY_GSM_COMPACT;
+ if (utran_act)
+ act |= MM_MODEM_ACCESS_TECHNOLOGY_UMTS;
+ if (eutran_act)
+ act |= MM_MODEM_ACCESS_TECHNOLOGY_LTE;
+ if (ngran_act)
+ act |= MM_MODEM_ACCESS_TECHNOLOGY_5GNR;
+ mm_sim_preferred_network_set_access_technology (preferred_network, act);
+ result = g_list_append (result, preferred_network);
+ } else
+ break;
+ g_free (operator_code);
+ }
+ g_strfreev (entries);
+
+ return result;
+}
+
+static GList *
+load_preferred_networks_finish (MMBaseSim *self,
+ GAsyncResult *res,
+ GError **error)
+{
+ gchar *result;
+ GList *preferred_network_list;
+
+ result = g_task_propagate_pointer (G_TASK (res), error);
+ if (!result)
+ return NULL;
+
+ preferred_network_list = parse_preferred_networks (result, error);
+ mm_obj_dbg (self, "loaded %u preferred networks", g_list_length (preferred_network_list));
+
+ g_free (result);
+
+ return preferred_network_list;
+}
+
+STR_REPLY_READY_FN (load_preferred_networks)
+
+static void
+load_preferred_networks (MMBaseSim *self,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ mm_obj_dbg (self, "loading preferred networks...");
+
+ mm_base_modem_at_command (
+ self->priv->modem,
+ "+CPOL?",
+ 20,
+ FALSE,
+ (GAsyncReadyCallback)load_preferred_networks_command_ready,
+ g_task_new (self, NULL, callback, user_data));
+}
+
+/*****************************************************************************/
/* ICCID */
static gchar *
@@ -1291,9 +1386,9 @@
parse_mnc_length (const gchar *response,
GError **error)
{
- guint sw1 = 0;
- guint sw2 = 0;
- gchar *hex = 0;
+ guint sw1 = 0;
+ guint sw2 = 0;
+ g_autofree gchar *hex = NULL;
if (!mm_3gpp_parse_crsm_response (response,
&sw1,
@@ -1306,47 +1401,34 @@
(sw1 == 0x91) ||
(sw1 == 0x92) ||
(sw1 == 0x9f)) {
- gsize buflen = 0;
- guint32 mnc_len;
- gchar *bin;
+ gsize buflen = 0;
+ guint32 mnc_len;
+ g_autofree guint8 *bin = NULL;
/* Convert hex string to binary */
- bin = mm_utils_hexstr2bin (hex, &buflen);
- if (!bin || buflen < 4) {
- g_set_error (error,
- MM_CORE_ERROR,
- MM_CORE_ERROR_FAILED,
- "SIM returned malformed response '%s'",
- hex);
- g_free (bin);
- g_free (hex);
+ bin = mm_utils_hexstr2bin (hex, -1, &buflen, error);
+ if (!bin) {
+ g_prefix_error (error, "SIM returned malformed response '%s': ", hex);
+ return 0;
+ }
+ if (buflen < 4) {
+ g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED,
+ "SIM returned malformed response '%s': too short", hex);
return 0;
}
- g_free (hex);
-
/* MNC length is byte 4 of this SIM file */
- mnc_len = bin[3] & 0xFF;
- if (mnc_len == 2 || mnc_len == 3) {
- g_free (bin);
+ mnc_len = bin[3];
+ if (mnc_len == 2 || mnc_len == 3)
return mnc_len;
- }
- g_set_error (error,
- MM_CORE_ERROR,
- MM_CORE_ERROR_FAILED,
- "SIM returned invalid MNC length %d (should be either 2 or 3)",
- mnc_len);
- g_free (bin);
+ g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED,
+ "SIM returned invalid MNC length %d (should be either 2 or 3)", mnc_len);
return 0;
}
- g_free (hex);
- g_set_error (error,
- MM_CORE_ERROR,
- MM_CORE_ERROR_FAILED,
- "SIM failed to handle CRSM request (sw1 %d sw2 %d)",
- sw1, sw2);
+ g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED,
+ "SIM failed to handle CRSM request (sw1 %d sw2 %d)", sw1, sw2);
return 0;
}
@@ -1410,9 +1492,9 @@
parse_spn (const gchar *response,
GError **error)
{
- guint sw1 = 0;
- guint sw2 = 0;
- gchar *hex = 0;
+ guint sw1 = 0;
+ guint sw2 = 0;
+ g_autofree gchar *hex = NULL;
if (!mm_3gpp_parse_crsm_response (response,
&sw1,
@@ -1425,40 +1507,35 @@
(sw1 == 0x91) ||
(sw1 == 0x92) ||
(sw1 == 0x9f)) {
- gsize buflen = 0;
- gchar *bin;
- gchar *utf8;
+ g_autoptr(GByteArray) bin_array = NULL;
+ g_autofree guint8 *bin = NULL;
+ gsize binlen = 0;
/* Convert hex string to binary */
- bin = mm_utils_hexstr2bin (hex, &buflen);
+ bin = mm_utils_hexstr2bin (hex, -1, &binlen, error);
if (!bin) {
- g_set_error (error,
- MM_CORE_ERROR,
- MM_CORE_ERROR_FAILED,
- "SIM returned malformed response '%s'",
- hex);
- g_free (hex);
+ g_prefix_error (error, "SIM returned malformed response '%s': ", hex);
return NULL;
}
- g_free (hex);
-
/* Remove the FF filler at the end */
- while (buflen > 1 && bin[buflen - 1] == (char)0xff)
- buflen--;
+ while (binlen > 1 && bin[binlen - 1] == 0xff)
+ binlen--;
+ if (binlen <= 1) {
+ g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED,
+ "SIM returned empty response '%s'", hex);
+ return NULL;
+ }
+ /* Setup as bytearray.
+ * First byte is metadata; remainder is GSM-7 unpacked into octets; convert to UTF8 */
+ bin_array = g_byte_array_sized_new (binlen - 1);
+ g_byte_array_append (bin_array, bin + 1, binlen - 1);
- /* First byte is metadata; remainder is GSM-7 unpacked into octets; convert to UTF8 */
- utf8 = (gchar *)mm_charset_gsm_unpacked_to_utf8 ((guint8 *)bin + 1, buflen - 1);
- g_free (bin);
- return utf8;
+ return mm_modem_charset_bytearray_to_utf8 (bin_array, MM_MODEM_CHARSET_GSM, FALSE, error);
}
- g_free (hex);
- g_set_error (error,
- MM_CORE_ERROR,
- MM_CORE_ERROR_FAILED,
- "SIM failed to handle CRSM request (sw1 %d sw2 %d)",
- sw1, sw2);
+ g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED,
+ "SIM failed to handle CRSM request (sw1 %d sw2 %d)", sw1, sw2);
return NULL;
}
@@ -1543,6 +1620,7 @@
INITIALIZATION_STEP_OPERATOR_ID,
INITIALIZATION_STEP_OPERATOR_NAME,
INITIALIZATION_STEP_EMERGENCY_NUMBERS,
+ INITIALIZATION_STEP_PREFERRED_NETWORKS,
INITIALIZATION_STEP_LAST
} InitializationStep;
@@ -1641,6 +1719,31 @@
interface_initialization_step (task);
}
+static void
+init_load_preferred_networks_ready (MMBaseSim *self,
+ GAsyncResult *res,
+ GTask *task)
+{
+ InitAsyncContext *ctx;
+ GError *error = NULL;
+ GList *preferred_nets_list;
+
+ preferred_nets_list = MM_BASE_SIM_GET_CLASS (self)->load_preferred_networks_finish (self, res, &error);
+ if (error) {
+ mm_obj_warn (self, "couldn't load list of preferred networks: %s", error->message);
+ g_error_free (error);
+ }
+
+ mm_gdbus_sim_set_preferred_networks (MM_GDBUS_SIM (self),
+ mm_sim_preferred_network_list_get_variant (preferred_nets_list));
+ g_list_free_full (preferred_nets_list, (GDestroyNotify) mm_sim_preferred_network_free);
+
+ /* Go on to next step */
+ ctx = g_task_get_task_data (task);
+ ctx->step++;
+ interface_initialization_step (task);
+}
+
#undef STR_REPLY_READY_FN
#define STR_REPLY_READY_FN(NAME,DISPLAY) \
static void \
@@ -1816,6 +1919,18 @@
ctx->step++;
/* Fall through */
+ case INITIALIZATION_STEP_PREFERRED_NETWORKS:
+ if (MM_BASE_SIM_GET_CLASS (self)->load_preferred_networks &&
+ MM_BASE_SIM_GET_CLASS (self)->load_preferred_networks_finish) {
+ MM_BASE_SIM_GET_CLASS (self)->load_preferred_networks (
+ self,
+ (GAsyncReadyCallback)init_load_preferred_networks_ready,
+ task);
+ return;
+ }
+ ctx->step++;
+ /* Fall through */
+
case INITIALIZATION_STEP_LAST:
/* We are done without errors! */
g_task_return_boolean (task, TRUE);
@@ -2069,6 +2184,8 @@
klass->load_operator_name_finish = load_operator_name_finish;
klass->load_emergency_numbers = load_emergency_numbers;
klass->load_emergency_numbers_finish = load_emergency_numbers_finish;
+ klass->load_preferred_networks = load_preferred_networks;
+ klass->load_preferred_networks_finish = load_preferred_networks_finish;
klass->send_pin = send_pin;
klass->send_pin_finish = common_send_pin_puk_finish;
klass->send_puk = send_puk;
diff --git a/src/mm-base-sim.h b/src/mm-base-sim.h
index ed58252..67f2690 100644
--- a/src/mm-base-sim.h
+++ b/src/mm-base-sim.h
@@ -150,6 +150,14 @@
/* Signals */
void (* pin_lock_enabled) (MMBaseSim *self,
gboolean enabled);
+
+ /* Load preferred networks (async) */
+ void (* load_preferred_networks) (MMBaseSim *self,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+ GList * (* load_preferred_networks_finish) (MMBaseSim *self,
+ GAsyncResult *res,
+ GError **error);
};
GType mm_base_sim_get_type (void);
diff --git a/src/mm-bearer-mbim.c b/src/mm-bearer-mbim.c
index e53529e..b808f54 100644
--- a/src/mm-bearer-mbim.c
+++ b/src/mm-bearer-mbim.c
@@ -223,7 +223,8 @@
MMBearerProperties *properties;
ConnectStep step;
MMPort *data;
- MbimContextIpType ip_type;
+ MbimContextIpType requested_ip_type;
+ MbimContextIpType activated_ip_type;
MMBearerConnectResult *connect_result;
} ConnectContext;
@@ -396,9 +397,11 @@
/* Build connection results */
/* Build IPv4 config */
- if (ctx->ip_type == MBIM_CONTEXT_IP_TYPE_IPV4 ||
- ctx->ip_type == MBIM_CONTEXT_IP_TYPE_IPV4V6 ||
- ctx->ip_type == MBIM_CONTEXT_IP_TYPE_IPV4_AND_IPV6) {
+ if (ctx->requested_ip_type == MBIM_CONTEXT_IP_TYPE_IPV4 ||
+ ctx->requested_ip_type == MBIM_CONTEXT_IP_TYPE_IPV4V6 ||
+ ctx->requested_ip_type == MBIM_CONTEXT_IP_TYPE_IPV4_AND_IPV6) {
+ gboolean address_set = FALSE;
+
ipv4_config = mm_bearer_ip_config_new ();
/* We assume that if we have an IP we can use static configuration.
@@ -416,6 +419,7 @@
mm_bearer_ip_config_set_address (ipv4_config, str);
g_free (str);
g_object_unref (addr);
+ address_set = TRUE;
/* Netmask */
mm_bearer_ip_config_set_prefix (ipv4_config, ipv4address[0]->on_link_prefix_length);
@@ -451,13 +455,23 @@
/* MTU */
if (ipv4configurationavailable & MBIM_IP_CONFIGURATION_AVAILABLE_FLAG_MTU)
mm_bearer_ip_config_set_mtu (ipv4_config, ipv4mtu);
+
+ /* We requested IPv4, but it wasn't reported as activated. If there is no IP address
+ * provided by the modem, we assume the IPv4 bearer wasn't truly activated */
+ if (!address_set &&
+ ctx->activated_ip_type != MBIM_CONTEXT_IP_TYPE_IPV4 &&
+ ctx->activated_ip_type != MBIM_CONTEXT_IP_TYPE_IPV4V6 &&
+ ctx->activated_ip_type != MBIM_CONTEXT_IP_TYPE_IPV4_AND_IPV6) {
+ mm_obj_dbg (self, "IPv4 requested but no IPv4 activated and no IPv4 address set: ignoring");
+ g_clear_object (&ipv4_config);
+ }
} else
ipv4_config = NULL;
/* Build IPv6 config */
- if (ctx->ip_type == MBIM_CONTEXT_IP_TYPE_IPV6 ||
- ctx->ip_type == MBIM_CONTEXT_IP_TYPE_IPV4V6 ||
- ctx->ip_type == MBIM_CONTEXT_IP_TYPE_IPV4_AND_IPV6) {
+ if (ctx->requested_ip_type == MBIM_CONTEXT_IP_TYPE_IPV6 ||
+ ctx->requested_ip_type == MBIM_CONTEXT_IP_TYPE_IPV4V6 ||
+ ctx->requested_ip_type == MBIM_CONTEXT_IP_TYPE_IPV4_AND_IPV6) {
gboolean address_set = FALSE;
gboolean gateway_set = FALSE;
gboolean dns_set = FALSE;
@@ -528,6 +542,16 @@
mm_bearer_ip_config_set_method (ipv6_config, MM_BEARER_IP_METHOD_STATIC);
else
mm_bearer_ip_config_set_method (ipv6_config, MM_BEARER_IP_METHOD_DHCP);
+
+ /* We requested IPv6, but it wasn't reported as activated. If there is no IPv6 address
+ * provided by the modem, we assume the IPv6 bearer wasn't truly activated */
+ if (!address_set &&
+ ctx->activated_ip_type != MBIM_CONTEXT_IP_TYPE_IPV6 &&
+ ctx->activated_ip_type != MBIM_CONTEXT_IP_TYPE_IPV4V6 &&
+ ctx->activated_ip_type != MBIM_CONTEXT_IP_TYPE_IPV4_AND_IPV6) {
+ mm_obj_dbg (self, "IPv6 requested but no IPv6 activated and no IPv6 address set: ignoring");
+ g_clear_object (&ipv6_config);
+ }
} else
ipv6_config = NULL;
@@ -570,7 +594,6 @@
GError *error = NULL;
MbimMessage *response;
guint32 session_id;
- MbimContextIpType ip_type;
MbimActivationState activation_state;
guint32 nw_error;
@@ -588,16 +611,16 @@
&session_id,
&activation_state,
NULL, /* voice_call_state */
- &ip_type,
+ &ctx->activated_ip_type,
NULL, /* context_type */
&nw_error,
&inner_error)) {
/* Report the IP type we asked for and the one returned by the modem */
- mm_obj_dbg (self, "session ID '%u': %s (requested IP type: %s, received IP type: %s, nw error: %s)",
+ mm_obj_dbg (self, "session ID '%u': %s (requested IP type: %s, activated IP type: %s, nw error: %s)",
session_id,
mbim_activation_state_get_string (activation_state),
- mbim_context_ip_type_get_string (ctx->ip_type),
- mbim_context_ip_type_get_string (ip_type),
+ mbim_context_ip_type_get_string (ctx->requested_ip_type),
+ mbim_context_ip_type_get_string (ctx->activated_ip_type),
nw_error ? mbim_nw_error_get_string (nw_error) : "none");
/* If the response reports an ACTIVATED state, we're good even if
* there is a nw_error set (e.g. asking for IPv4v6 may return a
@@ -613,11 +636,6 @@
"Unknown error: context activation failed");
}
}
- /* We're now connected, but we may have received an IP type different to the one
- * requested (e.g. asking for IPv4v6 but received IPv4 only). Handle that, so that
- * the next step getting IP details doesn't get confused. */
- else if (ctx->ip_type != ip_type)
- ctx->ip_type = ip_type;
} else {
/* Prefer the error from the result to the parsing error */
if (!error)
@@ -995,14 +1013,15 @@
g_free (str);
}
- ctx->ip_type = mm_bearer_ip_family_to_mbim_context_ip_type (ip_family, &error);
+ ctx->requested_ip_type = mm_bearer_ip_family_to_mbim_context_ip_type (ip_family, &error);
if (error) {
g_task_return_error (task, error);
g_object_unref (task);
return;
}
- mm_obj_dbg (self, "launching %s connection with APN '%s'...", mbim_context_ip_type_get_string (ctx->ip_type), apn);
+ mm_obj_dbg (self, "launching %s connection with APN '%s'...",
+ mbim_context_ip_type_get_string (ctx->requested_ip_type), apn);
message = (mbim_message_connect_set_new (
self->priv->session_id,
MBIM_ACTIVATION_COMMAND_ACTIVATE,
@@ -1011,7 +1030,7 @@
password ? password : "",
MBIM_COMPRESSION_NONE,
auth,
- ctx->ip_type,
+ ctx->requested_ip_type,
mbim_uuid_from_context_type (MBIM_CONTEXT_TYPE_INTERNET),
&error));
if (!message) {
@@ -1138,6 +1157,8 @@
ctx->device = g_object_ref (device);;
ctx->data = g_object_ref (data);
ctx->step = CONNECT_STEP_FIRST;
+ ctx->requested_ip_type = MBIM_CONTEXT_IP_TYPE_DEFAULT;
+ ctx->activated_ip_type = MBIM_CONTEXT_IP_TYPE_DEFAULT;
g_object_get (self,
MM_BASE_BEARER_CONFIG, &ctx->properties,
diff --git a/src/mm-broadband-modem-mbim.c b/src/mm-broadband-modem-mbim.c
index 02d524e..c524af4 100644
--- a/src/mm-broadband-modem-mbim.c
+++ b/src/mm-broadband-modem-mbim.c
@@ -238,8 +238,7 @@
mbim_ports = mm_base_modem_find_ports (MM_BASE_MODEM (self),
MM_PORT_SUBSYS_UNKNOWN,
- MM_PORT_TYPE_MBIM,
- NULL);
+ MM_PORT_TYPE_MBIM);
/* First MBIM port in the list is the primary one always */
if (mbim_ports)
@@ -303,8 +302,7 @@
/* Find the CDC-WDM port on the same USB interface as the given net port */
cdc_wdm_mbim_ports = mm_base_modem_find_ports (MM_BASE_MODEM (self),
MM_PORT_SUBSYS_USBMISC,
- MM_PORT_TYPE_MBIM,
- NULL);
+ MM_PORT_TYPE_MBIM);
for (l = cdc_wdm_mbim_ports; l && !found; l = g_list_next (l)) {
const gchar *wdm_port_parent_path;
@@ -4889,30 +4887,23 @@
g_autoptr(GByteArray) array = NULL;
if (mm_charset_can_convert_to (command, MM_MODEM_CHARSET_GSM)) {
- guint8 *gsm;
- guint8 *packed;
- guint32 len = 0;
- guint32 packed_len = 0;
+ g_autoptr(GByteArray) gsm = NULL;
+ guint8 *packed;
+ guint32 packed_len = 0;
*scheme = MM_MODEM_GSM_USSD_SCHEME_7BIT;
- gsm = mm_charset_utf8_to_unpacked_gsm (command, &len);
+ gsm = mm_modem_charset_bytearray_from_utf8 (command, MM_MODEM_CHARSET_GSM, FALSE, error);
if (!gsm) {
- g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_UNSUPPORTED,
- "Failed to encode USSD command in GSM7 charset");
+ g_prefix_error (error, "Failed to encode USSD command in GSM7 charset: ");
return NULL;
}
- packed = mm_charset_gsm_pack (gsm, len, 0, &packed_len);
- g_free (gsm);
-
+ packed = mm_charset_gsm_pack (gsm->data, gsm->len, 0, &packed_len);
array = g_byte_array_new_take (packed, packed_len);
} else {
- g_autoptr(GError) inner_error = NULL;
-
*scheme = MM_MODEM_GSM_USSD_SCHEME_UCS2;
- array = g_byte_array_sized_new (strlen (command) * 2);
- if (!mm_modem_charset_byte_array_append (array, command, FALSE, MM_MODEM_CHARSET_UCS2, &inner_error)) {
- g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_UNSUPPORTED,
- "Failed to encode USSD command in UCS2 charset: %s", inner_error->message);
+ array = mm_modem_charset_bytearray_from_utf8 (command, MM_MODEM_CHARSET_UCS2, FALSE, error);
+ if (!array) {
+ g_prefix_error (error, "Failed to encode USSD command in UCS2 charset: ");
return NULL;
}
}
@@ -4934,21 +4925,20 @@
gchar *decoded = NULL;
if (scheme == MM_MODEM_GSM_USSD_SCHEME_7BIT) {
- g_autofree guint8 *unpacked = NULL;
- guint32 unpacked_len;
+ g_autoptr(GByteArray) unpacked_array = NULL;
+ guint8 *unpacked = NULL;
+ guint32 unpacked_len;
unpacked = mm_charset_gsm_unpack ((const guint8 *)data->data, (data->len * 8) / 7, 0, &unpacked_len);
- decoded = (gchar *) mm_charset_gsm_unpacked_to_utf8 (unpacked, unpacked_len);
+ unpacked_array = g_byte_array_new_take (unpacked, unpacked_len);
+
+ decoded = mm_modem_charset_bytearray_to_utf8 (unpacked_array, MM_MODEM_CHARSET_GSM, FALSE, error);
if (!decoded)
- g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_UNSUPPORTED,
- "Error decoding USSD command in 0x%04x scheme (GSM7 charset)",
- scheme);
+ g_prefix_error (error, "Error decoding USSD command in 0x%04x scheme (GSM7 charset): ", scheme);
} else if (scheme == MM_MODEM_GSM_USSD_SCHEME_UCS2) {
- decoded = mm_modem_charset_byte_array_to_utf8 (data, MM_MODEM_CHARSET_UCS2);
+ decoded = mm_modem_charset_bytearray_to_utf8 (data, MM_MODEM_CHARSET_UCS2, FALSE, error);
if (!decoded)
- g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_UNSUPPORTED,
- "Error decoding USSD command in 0x%04x scheme (UCS2 charset)",
- scheme);
+ g_prefix_error (error, "Error decoding USSD command in 0x%04x scheme (UCS2 charset): ", scheme);
} else
g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_UNSUPPORTED,
"Failed to decode USSD command in unsupported 0x%04x scheme", scheme);
diff --git a/src/mm-broadband-modem-qmi.c b/src/mm-broadband-modem-qmi.c
index ba72f5f..f51146e 100644
--- a/src/mm-broadband-modem-qmi.c
+++ b/src/mm-broadband-modem-qmi.c
@@ -121,7 +121,7 @@
GTask *activation_task;
/* Messaging helpers */
- gboolean messaging_fallback_at;
+ gboolean messaging_fallback_at_only;
gboolean messaging_unsolicited_events_enabled;
gboolean messaging_unsolicited_events_setup;
guint messaging_event_report_indication_id;
@@ -206,8 +206,7 @@
qmi_ports = mm_base_modem_find_ports (MM_BASE_MODEM (self),
MM_PORT_SUBSYS_UNKNOWN,
- MM_PORT_TYPE_QMI,
- NULL);
+ MM_PORT_TYPE_QMI);
/* First QMI port in the list is the primary one always */
if (qmi_ports)
@@ -273,8 +272,7 @@
/* Find the CDC-WDM port on the same USB interface as the given net port */
cdc_wdm_qmi_ports = mm_base_modem_find_ports (MM_BASE_MODEM (self),
MM_PORT_SUBSYS_USBMISC,
- MM_PORT_TYPE_QMI,
- NULL);
+ MM_PORT_TYPE_QMI);
for (l = cdc_wdm_qmi_ports; l && !found; l = g_list_next (l)) {
const gchar *wdm_port_parent_path;
@@ -292,7 +290,7 @@
MM_CORE_ERROR_NOT_FOUND,
"Couldn't find associated QMI port for 'net/%s'",
mm_port_get_device (data));
- else
+ else if (out_sio_port)
*out_sio_port = QMI_SIO_PORT_NONE;
return found;
@@ -5555,9 +5553,9 @@
{
MMBroadbandModemQmi *self = MM_BROADBAND_MODEM_QMI (_self);
- self->priv->messaging_fallback_at = iface_modem_messaging_parent->check_support_finish (_self, res, NULL);
+ self->priv->messaging_fallback_at_only = iface_modem_messaging_parent->check_support_finish (_self, res, NULL);
- g_task_return_boolean (task, self->priv->messaging_fallback_at);
+ g_task_return_boolean (task, self->priv->messaging_fallback_at_only);
g_object_unref (task);
}
@@ -5602,8 +5600,8 @@
MMBroadbandModemQmi *self = MM_BROADBAND_MODEM_QMI (_self);
MMSmsStorage supported;
- /* Handle fallback */
- if (self->priv->messaging_fallback_at) {
+ /* Handle AT URC only fallback */
+ if (self->priv->messaging_fallback_at_only) {
return iface_modem_messaging_parent->load_supported_storages_finish (_self, res, mem1, mem2, mem3, error);
}
@@ -5630,8 +5628,8 @@
MMBroadbandModemQmi *self = MM_BROADBAND_MODEM_QMI (_self);
GTask *task;
- /* Handle fallback */
- if (self->priv->messaging_fallback_at) {
+ /* Handle AT URC only fallback */
+ if (self->priv->messaging_fallback_at_only) {
iface_modem_messaging_parent->load_supported_storages (_self, callback, user_data);
return;
}
@@ -5651,8 +5649,8 @@
{
MMBroadbandModemQmi *self = MM_BROADBAND_MODEM_QMI (_self);
- /* Handle fallback */
- if (self->priv->messaging_fallback_at) {
+ /* Handle AT URC only fallback */
+ if (self->priv->messaging_fallback_at_only) {
return iface_modem_messaging_parent->setup_sms_format_finish (_self, res, error);
}
@@ -5667,8 +5665,8 @@
MMBroadbandModemQmi *self = MM_BROADBAND_MODEM_QMI (_self);
GTask *task;
- /* Handle fallback */
- if (self->priv->messaging_fallback_at) {
+ /* Handle AT URC only fallback */
+ if (self->priv->messaging_fallback_at_only) {
return iface_modem_messaging_parent->setup_sms_format (_self, callback, user_data);
}
@@ -5688,8 +5686,8 @@
{
MMBroadbandModemQmi *self = MM_BROADBAND_MODEM_QMI (_self);
- /* Handle fallback */
- if (self->priv->messaging_fallback_at) {
+ /* Handle AT URC only fallback */
+ if (self->priv->messaging_fallback_at_only) {
return iface_modem_messaging_parent->set_default_storage_finish (_self, res, error);
}
@@ -5732,8 +5730,8 @@
GArray *routes_array;
QmiMessageWmsSetRoutesInputRouteListElement route;
- /* Handle fallback */
- if (self->priv->messaging_fallback_at) {
+ /* Handle AT URC only fallback */
+ if (self->priv->messaging_fallback_at_only) {
iface_modem_messaging_parent->set_default_storage (_self, storage, callback, user_data);
return;
}
@@ -5817,8 +5815,8 @@
{
MMBroadbandModemQmi *self = MM_BROADBAND_MODEM_QMI (_self);
- /* Handle fallback */
- if (self->priv->messaging_fallback_at) {
+ /* Handle AT URC only fallback */
+ if (self->priv->messaging_fallback_at_only) {
return iface_modem_messaging_parent->load_initial_sms_parts_finish (_self, res, error);
}
@@ -6196,8 +6194,8 @@
GTask *task;
QmiClient *client = NULL;
- /* Handle fallback */
- if (self->priv->messaging_fallback_at) {
+ /* Handle AT URC only fallback */
+ if (self->priv->messaging_fallback_at_only) {
return iface_modem_messaging_parent->load_initial_sms_parts (_self, storage, callback, user_data);
}
@@ -6218,7 +6216,7 @@
}
/*****************************************************************************/
-/* Setup/Cleanup unsolicited event handlers (Messaging interface) */
+/* Common setup/cleanup unsolicited event handlers (Messaging interface) */
typedef struct {
MMIfaceModemMessaging *self;
@@ -6331,57 +6329,23 @@
}
static gboolean
-messaging_cleanup_unsolicited_events_finish (MMIfaceModemMessaging *_self,
- GAsyncResult *res,
- GError **error)
+common_setup_cleanup_messaging_unsolicited_events (MMBroadbandModemQmi *self,
+ gboolean enable,
+ GError **error)
{
- MMBroadbandModemQmi *self = MM_BROADBAND_MODEM_QMI (_self);
-
- /* Handle fallback */
- if (self->priv->messaging_fallback_at) {
- return iface_modem_messaging_parent->cleanup_unsolicited_events_finish (_self, res, error);
- }
-
- return g_task_propagate_boolean (G_TASK (res), error);
-}
-
-static gboolean
-messaging_setup_unsolicited_events_finish (MMIfaceModemMessaging *_self,
- GAsyncResult *res,
- GError **error)
-{
- MMBroadbandModemQmi *self = MM_BROADBAND_MODEM_QMI (_self);
-
- /* Handle fallback */
- if (self->priv->messaging_fallback_at) {
- return iface_modem_messaging_parent->setup_unsolicited_events_finish (_self, res, error);
- }
-
- return g_task_propagate_boolean (G_TASK (res), error);
-}
-
-static void
-common_setup_cleanup_messaging_unsolicited_events (MMBroadbandModemQmi *self,
- gboolean enable,
- GAsyncReadyCallback callback,
- gpointer user_data)
-{
- GTask *task;
QmiClient *client = NULL;
- if (!mm_shared_qmi_ensure_client (MM_SHARED_QMI (self),
- QMI_SERVICE_WMS, &client,
- callback, user_data))
- return;
-
- task = g_task_new (self, NULL, callback, user_data);
+ client = mm_shared_qmi_peek_client (MM_SHARED_QMI (self),
+ QMI_SERVICE_WMS,
+ MM_PORT_QMI_FLAG_DEFAULT,
+ error);
+ if (!client)
+ return FALSE;
if (enable == self->priv->messaging_unsolicited_events_setup) {
mm_obj_dbg (self, "messaging unsolicited events already %s; skipping",
enable ? "setup" : "cleanup");
- g_task_return_boolean (task, TRUE);
- g_object_unref (task);
- return;
+ return TRUE;
}
/* Store new state */
@@ -6401,44 +6365,119 @@
self->priv->messaging_event_report_indication_id = 0;
}
- g_task_return_boolean (task, TRUE);
+ return TRUE;
+}
+
+/*****************************************************************************/
+/* Cleanup unsolicited event handlers (Messaging interface) */
+
+static gboolean
+messaging_cleanup_unsolicited_events_finish (MMIfaceModemMessaging *self,
+ GAsyncResult *res,
+ GError **error)
+{
+ return g_task_propagate_boolean (G_TASK (res), error);
+}
+
+static void
+parent_messaging_cleanup_unsolicited_events_ready (MMIfaceModemMessaging *_self,
+ GAsyncResult *res,
+ GTask *task)
+{
+ MMBroadbandModemQmi *self = MM_BROADBAND_MODEM_QMI (_self);
+ GError *error = NULL;
+
+ if (!iface_modem_messaging_parent->cleanup_unsolicited_events_finish (_self, res, &error)) {
+ if (self->priv->messaging_fallback_at_only) {
+ g_task_return_error (task, error);
+ g_object_unref (task);
+ return;
+ }
+ mm_obj_dbg (self, "cleaning up parent messaging unsolicited events failed: %s", error->message);
+ g_clear_error (&error);
+ }
+
+ /* handle AT URC only fallback */
+ if (self->priv->messaging_fallback_at_only) {
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
+ return;
+ }
+
+ /* Disable QMI indications */
+ if (!common_setup_cleanup_messaging_unsolicited_events (self, FALSE, &error))
+ g_task_return_error (task, error);
+ else
+ g_task_return_boolean (task, TRUE);
g_object_unref (task);
}
static void
-messaging_cleanup_unsolicited_events (MMIfaceModemMessaging *_self,
- GAsyncReadyCallback callback,
- gpointer user_data)
+messaging_cleanup_unsolicited_events (MMIfaceModemMessaging *self,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
{
- MMBroadbandModemQmi *self = MM_BROADBAND_MODEM_QMI (_self);
+ /* Disable AT URCs parent and chain QMI indications disabling */
+ iface_modem_messaging_parent->cleanup_unsolicited_events (
+ self,
+ (GAsyncReadyCallback)parent_messaging_cleanup_unsolicited_events_ready,
+ g_task_new (self, NULL, callback, user_data));
+}
- /* Handle fallback */
- if (self->priv->messaging_fallback_at) {
- return iface_modem_messaging_parent->cleanup_unsolicited_events (_self, callback, user_data);
- }
+/*****************************************************************************/
+/* Setup unsolicited event handlers (Messaging interface) */
- common_setup_cleanup_messaging_unsolicited_events (MM_BROADBAND_MODEM_QMI (self),
- FALSE,
- callback,
- user_data);
+static gboolean
+messaging_setup_unsolicited_events_finish (MMIfaceModemMessaging *self,
+ GAsyncResult *res,
+ GError **error)
+{
+ return g_task_propagate_boolean (G_TASK (res), error);
}
static void
-messaging_setup_unsolicited_events (MMIfaceModemMessaging *_self,
- GAsyncReadyCallback callback,
- gpointer user_data)
+parent_messaging_setup_unsolicited_events_ready (MMIfaceModemMessaging *_self,
+ GAsyncResult *res,
+ GTask *task)
{
MMBroadbandModemQmi *self = MM_BROADBAND_MODEM_QMI (_self);
+ GError *error = NULL;
- /* Handle fallback */
- if (self->priv->messaging_fallback_at) {
- return iface_modem_messaging_parent->setup_unsolicited_events (_self, callback, user_data);
+ if (!iface_modem_messaging_parent->setup_unsolicited_events_finish (_self, res, &error)) {
+ if (self->priv->messaging_fallback_at_only) {
+ g_task_return_error (task, error);
+ g_object_unref (task);
+ return;
+ }
+ mm_obj_dbg (self, "setting up parent messaging unsolicited events failed: %s", error->message);
+ g_clear_error (&error);
}
- common_setup_cleanup_messaging_unsolicited_events (MM_BROADBAND_MODEM_QMI (self),
- TRUE,
- callback,
- user_data);
+ /* handle AT URC only fallback */
+ if (self->priv->messaging_fallback_at_only) {
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
+ return;
+ }
+
+ /* Enable QMI indications */
+ if (!common_setup_cleanup_messaging_unsolicited_events (self, TRUE, &error))
+ g_task_return_error (task, error);
+ else
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
+}
+
+static void
+messaging_setup_unsolicited_events (MMIfaceModemMessaging *self,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ /* Enable AT URCs parent and chain QMI indication enabling */
+ iface_modem_messaging_parent->setup_unsolicited_events (
+ self,
+ (GAsyncReadyCallback)parent_messaging_setup_unsolicited_events_ready,
+ g_task_new (self, NULL, callback, user_data));
}
/*****************************************************************************/
@@ -6455,8 +6494,8 @@
{
MMBroadbandModemQmi *self = MM_BROADBAND_MODEM_QMI (_self);
- /* Handle fallback */
- if (self->priv->messaging_fallback_at && iface_modem_messaging_parent->disable_unsolicited_events_finish) {
+ /* Handle AT URC only fallback */
+ if (self->priv->messaging_fallback_at_only && iface_modem_messaging_parent->disable_unsolicited_events_finish) {
return iface_modem_messaging_parent->disable_unsolicited_events_finish (_self, res, error);
}
@@ -6470,8 +6509,8 @@
{
MMBroadbandModemQmi *self = MM_BROADBAND_MODEM_QMI (_self);
- /* Handle fallback */
- if (self->priv->messaging_fallback_at) {
+ /* Handle AT URC only fallback */
+ if (self->priv->messaging_fallback_at_only) {
return iface_modem_messaging_parent->enable_unsolicited_events_finish (_self, res, error);
}
@@ -6563,8 +6602,8 @@
{
MMBroadbandModemQmi *self = MM_BROADBAND_MODEM_QMI (_self);
- /* Handle fallback */
- if (self->priv->messaging_fallback_at) {
+ /* Handle AT URC only fallback */
+ if (self->priv->messaging_fallback_at_only) {
/* Generic implementation doesn't actually have a method to disable
* unsolicited messaging events */
if (!iface_modem_messaging_parent->disable_unsolicited_events) {
@@ -6592,8 +6631,8 @@
{
MMBroadbandModemQmi *self = MM_BROADBAND_MODEM_QMI (_self);
- /* Handle fallback */
- if (self->priv->messaging_fallback_at) {
+ /* Handle AT URC only fallback */
+ if (self->priv->messaging_fallback_at_only) {
return iface_modem_messaging_parent->enable_unsolicited_events (_self, callback, user_data);
}
@@ -6611,8 +6650,8 @@
{
MMBroadbandModemQmi *self = MM_BROADBAND_MODEM_QMI (_self);
- /* Handle fallback */
- if (self->priv->messaging_fallback_at) {
+ /* Handle AT URC only fallback */
+ if (self->priv->messaging_fallback_at_only) {
return iface_modem_messaging_parent->create_sms (_self);
}
@@ -7477,8 +7516,8 @@
return (GArray *) g_steal_pointer (&barray);
}
- barray = g_byte_array_sized_new (command_len * 2);
- if (!mm_modem_charset_byte_array_append (barray, command, FALSE, MM_MODEM_CHARSET_UCS2, &inner_error)) {
+ barray = mm_modem_charset_bytearray_from_utf8 (command, MM_MODEM_CHARSET_UCS2, FALSE, &inner_error);
+ if (!barray) {
g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_UNSUPPORTED,
"Failed to encode USSD command in UCS2 charset: %s", inner_error->message);
return NULL;
@@ -7502,11 +7541,9 @@
"Error decoding USSD command in 0x%04x scheme (ASCII charset)",
scheme);
} else if (scheme == QMI_VOICE_USS_DATA_CODING_SCHEME_UCS2) {
- decoded = mm_modem_charset_byte_array_to_utf8 ((GByteArray *) data, MM_MODEM_CHARSET_UCS2);
+ decoded = mm_modem_charset_bytearray_to_utf8 ((GByteArray *) data, MM_MODEM_CHARSET_UCS2, FALSE, error);
if (!decoded)
- g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_UNSUPPORTED,
- "Error decoding USSD command in 0x%04x scheme (UCS2 charset)",
- scheme);
+ g_prefix_error (error, "Error decoding USSD command in 0x%04x scheme (UCS2 charset): ", scheme);
} else
g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_UNSUPPORTED,
"Failed to decode USSD command in unsupported 0x%04x scheme", scheme);
diff --git a/src/mm-broadband-modem.c b/src/mm-broadband-modem.c
index 97c60b6..a25883a 100644
--- a/src/mm-broadband-modem.c
+++ b/src/mm-broadband-modem.c
@@ -4465,7 +4465,7 @@
error))
return NULL;
- mm_3gpp_normalize_operator (&operator_code, MM_BROADBAND_MODEM (self)->priv->modem_current_charset);
+ mm_3gpp_normalize_operator (&operator_code, MM_BROADBAND_MODEM (self)->priv->modem_current_charset, self);
if (operator_code)
mm_obj_dbg (self, "loaded Operator Code: %s", operator_code);
return operator_code;
@@ -4504,7 +4504,7 @@
error))
return NULL;
- mm_3gpp_normalize_operator (&operator_name, MM_BROADBAND_MODEM (self)->priv->modem_current_charset);
+ mm_3gpp_normalize_operator (&operator_name, MM_BROADBAND_MODEM (self)->priv->modem_current_charset, self);
if (operator_name)
mm_obj_dbg (self, "loaded Operator Name: %s", operator_name);
return operator_name;
@@ -4800,27 +4800,37 @@
}
static void
-cops_ascii_set_ready (MMBaseModem *self,
+cops_ascii_set_ready (MMBaseModem *_self,
GAsyncResult *res,
GTask *task)
{
- GError *error = NULL;
+ MMBroadbandModem *self = MM_BROADBAND_MODEM (_self);
+ g_autoptr(GError) error = NULL;
- if (!mm_base_modem_at_command_full_finish (MM_BASE_MODEM (self), res, &error)) {
+ if (!mm_base_modem_at_command_full_finish (_self, res, &error)) {
/* If it failed with an unsupported error, retry with current modem charset */
if (g_error_matches (error, MM_MOBILE_EQUIPMENT_ERROR, MM_MOBILE_EQUIPMENT_ERROR_NOT_SUPPORTED)) {
- gchar *operator_id;
- gchar *operator_id_current_charset;
+ g_autoptr(GError) enc_error = NULL;
+ g_autofree gchar *operator_id_enc = NULL;
+ gchar *operator_id;
+ /* try to encode to current charset */
operator_id = g_task_get_task_data (task);
- operator_id_current_charset = mm_broadband_modem_take_and_convert_to_current_charset (MM_BROADBAND_MODEM (self), g_strdup (operator_id));
+ operator_id_enc = mm_modem_charset_str_from_utf8 (operator_id, self->priv->modem_current_charset, FALSE, &enc_error);
+ if (!operator_id_enc) {
+ mm_obj_dbg (self, "couldn't convert operator id to current charset: %s", enc_error->message);
+ g_task_return_error (task, g_steal_pointer (&error));
+ g_object_unref (task);
+ return;
+ }
- if (g_strcmp0 (operator_id, operator_id_current_charset) != 0) {
- gchar *command;
+ /* retry only if encoded string is different to the non-encoded one */
+ if (g_strcmp0 (operator_id, operator_id_enc) != 0) {
+ g_autofree gchar *command = NULL;
- command = g_strdup_printf ("+COPS=1,2,\"%s\"", operator_id_current_charset);
- mm_base_modem_at_command_full (MM_BASE_MODEM (self),
- mm_base_modem_peek_best_at_port (MM_BASE_MODEM (self), NULL),
+ command = g_strdup_printf ("+COPS=1,2,\"%s\"", operator_id_enc);
+ mm_base_modem_at_command_full (_self,
+ mm_base_modem_peek_best_at_port (_self, NULL),
command,
120,
FALSE,
@@ -4828,16 +4838,10 @@
g_task_get_cancellable (task),
(GAsyncReadyCallback)cops_set_ready,
task);
- g_error_free (error);
- g_free (operator_id_current_charset);
- g_free (command);
return;
}
- /* operator id string would be the same on the current charset,
- * so fallback and return the not supported error */
- g_free (operator_id_current_charset);
}
- g_task_return_error (task, error);
+ g_task_return_error (task, g_steal_pointer (&error));
} else
g_task_return_boolean (task, TRUE);
g_object_unref (task);
@@ -5877,47 +5881,53 @@
/* USSD Encode/Decode (3GPP/USSD interface) */
static gchar *
-modem_3gpp_ussd_encode (MMIfaceModem3gppUssd *self,
+modem_3gpp_ussd_encode (MMIfaceModem3gppUssd *_self,
const gchar *command,
guint *scheme,
GError **error)
{
- MMBroadbandModem *broadband = MM_BROADBAND_MODEM (self);
- gchar *hex = NULL;
+ MMBroadbandModem *self = MM_BROADBAND_MODEM (_self);
g_autoptr(GByteArray) ussd_command = NULL;
- ussd_command = g_byte_array_new ();
-
/* Encode to the current charset (as per AT+CSCS, which is what most modems
* (except for Huawei it seems) will ask for. */
- if (mm_modem_charset_byte_array_append (ussd_command,
- command,
- FALSE,
- broadband->priv->modem_current_charset,
- NULL)) {
- /* The scheme value does NOT represent the encoding used to encode the string
- * we're giving. This scheme reflects the encoding that the modem should use when
- * sending the data out to the network. We're hardcoding this to GSM-7 because
- * USSD commands fit well in GSM-7, unlike USSD responses that may contain code
- * points that may only be encoded in UCS-2. */
- *scheme = MM_MODEM_GSM_USSD_SCHEME_7BIT;
- /* convert to hex representation */
- hex = mm_utils_bin2hexstr (ussd_command->data, ussd_command->len);
+ ussd_command = mm_modem_charset_bytearray_from_utf8 (command, self->priv->modem_current_charset, FALSE, error);
+ if (!ussd_command) {
+ g_prefix_error (error, "Failed to encode USSD command: ");
+ return NULL;
}
- return hex;
+ /* The scheme value does NOT represent the encoding used to encode the string
+ * we're giving. This scheme reflects the encoding that the modem should use when
+ * sending the data out to the network. We're hardcoding this to GSM-7 because
+ * USSD commands fit well in GSM-7, unlike USSD responses that may contain code
+ * points that may only be encoded in UCS-2. */
+ *scheme = MM_MODEM_GSM_USSD_SCHEME_7BIT;
+
+ /* convert to hex representation */
+ return (gchar *) mm_utils_bin2hexstr (ussd_command->data, ussd_command->len);
}
static gchar *
-modem_3gpp_ussd_decode (MMIfaceModem3gppUssd *self,
- const gchar *reply,
- GError **error)
+modem_3gpp_ussd_decode (MMIfaceModem3gppUssd *self,
+ const gchar *reply,
+ GError **error)
{
- MMBroadbandModem *broadband = MM_BROADBAND_MODEM (self);
+ MMBroadbandModem *broadband = MM_BROADBAND_MODEM (self);
+ guint8 *bin = NULL;
+ gsize bin_len = 0;
+ g_autoptr(GByteArray) barray = NULL;
+
+ bin = (guint8 *) mm_utils_hexstr2bin (reply, -1, &bin_len, error);
+ if (!bin) {
+ g_prefix_error (error, "Couldn't convert HEX string to binary: ");
+ return NULL;
+ }
+ barray = g_byte_array_new_take (bin, bin_len);
/* Decode from current charset (as per AT+CSCS, which is what most modems
* (except for Huawei it seems) will ask for. */
- return mm_modem_charset_hex_to_utf8 (reply, broadband->priv->modem_current_charset);
+ return mm_modem_charset_bytearray_to_utf8 (barray, broadband->priv->modem_current_charset, FALSE, error);
}
/*****************************************************************************/
@@ -7305,11 +7315,17 @@
ctx = g_task_get_task_data (task);
while (g_match_info_matches (match_info)) {
- MMSmsPart *part;
- guint matches, idx;
- gchar *number, *timestamp, *text, *ucs2_text, *stat;
- gsize ucs2_len = 0;
- GByteArray *raw;
+ MMSmsPart *part;
+ guint matches;
+ guint idx;
+ g_autofree gchar *number_enc = NULL;
+ g_autofree gchar *number = NULL;
+ g_autofree gchar *timestamp = NULL;
+ g_autofree gchar *text_enc = NULL;
+ g_autofree gchar *text = NULL;
+ g_autofree gchar *stat = NULL;
+ g_autoptr(GByteArray) raw = NULL;
+ g_autoptr(GError) inner_error = NULL;
matches = g_match_info_get_match_count (match_info);
if (matches != 7) {
@@ -7330,39 +7346,44 @@
}
/* Get and parse number */
- number = mm_get_string_unquoted_from_match_info (match_info, 3);
- if (!number) {
+ number_enc = mm_get_string_unquoted_from_match_info (match_info, 3);
+ if (!number_enc) {
mm_obj_dbg (self, "failed to get message sender number");
- g_free (stat);
+ goto next;
+ }
+ number = mm_modem_charset_str_to_utf8 (number_enc, -1, self->priv->modem_current_charset, FALSE, &inner_error);
+ if (!number) {
+ mm_obj_dbg (self, "failed to convert message sender number to UTF-8: %s", inner_error->message);
goto next;
}
- number = mm_broadband_modem_take_and_convert_to_utf8 (MM_BROADBAND_MODEM (self),
- number);
-
/* Get and parse timestamp (always expected in ASCII) */
timestamp = mm_get_string_unquoted_from_match_info (match_info, 5);
+ if (timestamp && !g_str_is_ascii (timestamp)) {
+ mm_obj_dbg (self, "failed to parse input timestamp as ASCII");
+ goto next;
+ }
/* Get and parse text */
- text = mm_broadband_modem_take_and_convert_to_utf8 (MM_BROADBAND_MODEM (self),
- g_match_info_fetch (match_info, 6));
+ text_enc = g_match_info_fetch (match_info, 6);
+ text = mm_modem_charset_str_to_utf8 (text_enc, -1, self->priv->modem_current_charset, FALSE, &inner_error);
+ if (!text) {
+ mm_obj_dbg (self, "failed to convert message text to UTF-8: %s", inner_error->message);
+ goto next;
+ }
/* The raw SMS data can only be GSM, UCS2, or unknown (8-bit), so we
* need to convert to UCS2 here.
*/
- ucs2_text = g_convert (text, -1, "UCS-2BE//TRANSLIT", "UTF-8", NULL, &ucs2_len, NULL);
- g_assert (ucs2_text);
- raw = g_byte_array_sized_new (ucs2_len);
- g_byte_array_append (raw, (const guint8 *) ucs2_text, ucs2_len);
- g_free (ucs2_text);
+ raw = mm_modem_charset_bytearray_from_utf8 (text, MM_MODEM_CHARSET_UCS2, FALSE, NULL);
+ g_assert (raw);
/* all take() methods pass ownership of the value as well */
- part = mm_sms_part_new (idx,
- sms_pdu_type_from_str (stat));
- mm_sms_part_take_number (part, number);
- mm_sms_part_take_timestamp (part, timestamp);
- mm_sms_part_take_text (part, text);
- mm_sms_part_take_data (part, raw);
+ part = mm_sms_part_new (idx, sms_pdu_type_from_str (stat));
+ mm_sms_part_take_number (part, g_steal_pointer (&number));
+ mm_sms_part_take_timestamp (part, g_steal_pointer (×tamp));
+ mm_sms_part_take_text (part, g_steal_pointer (&text));
+ mm_sms_part_take_data (part, g_steal_pointer (&raw));
mm_sms_part_set_class (part, -1);
mm_obj_dbg (self, "correctly parsed SMS list entry (%d)", idx);
@@ -7370,7 +7391,6 @@
part,
sms_state_from_str (stat),
ctx->list_storage);
- g_free (stat);
next:
g_match_info_next (match_info, NULL);
}
@@ -11884,35 +11904,14 @@
/*****************************************************************************/
-gchar *
-mm_broadband_modem_take_and_convert_to_utf8 (MMBroadbandModem *self,
- gchar *str)
-{
- /* should only be used AFTER current charset is set */
- if (self->priv->modem_current_charset == MM_MODEM_CHARSET_UNKNOWN)
- return str;
-
- return mm_charset_take_and_convert_to_utf8 (str,
- self->priv->modem_current_charset);
-}
-
-gchar *
-mm_broadband_modem_take_and_convert_to_current_charset (MMBroadbandModem *self,
- gchar *str)
-{
- /* should only be used AFTER current charset is set */
- if (self->priv->modem_current_charset == MM_MODEM_CHARSET_UNKNOWN)
- return str;
-
- return mm_utf8_take_and_convert_to_charset (str, self->priv->modem_current_charset);
-}
-
MMModemCharset
mm_broadband_modem_get_current_charset (MMBroadbandModem *self)
{
return self->priv->modem_current_charset;
}
+/*****************************************************************************/
+
gchar *
mm_broadband_modem_create_device_identifier (MMBroadbandModem *self,
const gchar *ati,
@@ -11934,7 +11933,6 @@
MM_GDBUS_MODEM (self->priv->modem_dbus_skeleton))));
}
-
/*****************************************************************************/
void
diff --git a/src/mm-broadband-modem.h b/src/mm-broadband-modem.h
index eafca85..1f5acac 100644
--- a/src/mm-broadband-modem.h
+++ b/src/mm-broadband-modem.h
@@ -100,18 +100,6 @@
guint16 vendor_id,
guint16 product_id);
-/* Convert the given string, which comes in the charset currently set in the
- * modem, to UTF-8. Given in the API so that subclasses can also use it directly.
- */
-gchar *mm_broadband_modem_take_and_convert_to_utf8 (MMBroadbandModem *self,
- gchar *str);
-
-/* Convert the given string, which comes in UTF-8, to the charset currently set
- * in the modem. Given in the API so that subclasses can also use it directly.
- */
-gchar *mm_broadband_modem_take_and_convert_to_current_charset (MMBroadbandModem *self,
- gchar *str);
-
MMModemCharset mm_broadband_modem_get_current_charset (MMBroadbandModem *self);
/* Create a unique device identifier string using the ATI and ATI1 replies and some
diff --git a/src/mm-charsets.c b/src/mm-charsets.c
index 19d1874..a48da36 100644
--- a/src/mm-charsets.c
+++ b/src/mm-charsets.c
@@ -11,6 +11,7 @@
* GNU General Public License for more details:
*
* Copyright (C) 2010 Red Hat, Inc.
+ * Copyright (C) 2020 Aleksander Morgado <aleksander@aleksander.es>
*/
#include <config.h>
@@ -26,24 +27,28 @@
#include "mm-charsets.h"
#include "mm-log.h"
+/* Common fallback character when transliteration is enabled */
+static const gchar *translit_fallback = "?";
+
+/******************************************************************************/
+/* Expected charset settings */
+
typedef struct {
+ MMModemCharset charset;
const gchar *gsm_name;
const gchar *other_name;
- const gchar *iconv_from_name;
- const gchar *iconv_to_name;
- MMModemCharset charset;
-} CharsetEntry;
+ const gchar *iconv_name;
+} CharsetSettings;
-static const CharsetEntry charset_map[] = {
- { "UTF-8", "UTF8", "UTF-8", "UTF-8//TRANSLIT", MM_MODEM_CHARSET_UTF8 },
- { "UCS2", NULL, "UCS-2BE", "UCS-2BE//TRANSLIT", MM_MODEM_CHARSET_UCS2 },
- { "IRA", "ASCII", "ASCII", "ASCII//TRANSLIT", MM_MODEM_CHARSET_IRA },
- { "GSM", NULL, NULL, NULL, MM_MODEM_CHARSET_GSM },
- { "8859-1", NULL, "ISO8859-1", "ISO8859-1//TRANSLIT", MM_MODEM_CHARSET_8859_1 },
- { "PCCP437", "CP437", "CP437", "CP437//TRANSLIT", MM_MODEM_CHARSET_PCCP437 },
- { "PCDN", "CP850", "CP850", "CP850//TRANSLIT", MM_MODEM_CHARSET_PCDN },
- { "HEX", NULL, NULL, NULL, MM_MODEM_CHARSET_HEX },
- { "UTF-16", "UTF16", "UTF-16BE", "UTF-16BE//TRANSLIT", MM_MODEM_CHARSET_UTF16 },
+static const CharsetSettings charset_settings[] = {
+ { MM_MODEM_CHARSET_UTF8, "UTF-8", "UTF8", "UTF-8" },
+ { MM_MODEM_CHARSET_UCS2, "UCS2", NULL, "UCS-2BE" },
+ { MM_MODEM_CHARSET_IRA, "IRA", "ASCII", "ASCII" },
+ { MM_MODEM_CHARSET_GSM, "GSM", NULL, NULL },
+ { MM_MODEM_CHARSET_8859_1, "8859-1", NULL, "ISO8859-1" },
+ { MM_MODEM_CHARSET_PCCP437, "PCCP437", "CP437", "CP437" },
+ { MM_MODEM_CHARSET_PCDN, "PCDN", "CP850", "CP850" },
+ { MM_MODEM_CHARSET_UTF16, "UTF-16", "UTF16", "UTF-16BE" },
};
MMModemCharset
@@ -53,24 +58,24 @@
g_return_val_if_fail (string != NULL, MM_MODEM_CHARSET_UNKNOWN);
- for (i = 0; i < G_N_ELEMENTS (charset_map); i++) {
- if (strcasestr (string, charset_map[i].gsm_name))
- return charset_map[i].charset;
- if (charset_map[i].other_name && strcasestr (string, charset_map[i].other_name))
- return charset_map[i].charset;
+ for (i = 0; i < G_N_ELEMENTS (charset_settings); i++) {
+ if (strcasestr (string, charset_settings[i].gsm_name))
+ return charset_settings[i].charset;
+ if (charset_settings[i].other_name && strcasestr (string, charset_settings[i].other_name))
+ return charset_settings[i].charset;
}
return MM_MODEM_CHARSET_UNKNOWN;
}
-static const CharsetEntry *
-lookup_charset_by_id (MMModemCharset charset)
+static const CharsetSettings *
+lookup_charset_settings (MMModemCharset charset)
{
guint i;
g_return_val_if_fail (charset != MM_MODEM_CHARSET_UNKNOWN, NULL);
- for (i = 0; i < G_N_ELEMENTS (charset_map); i++) {
- if (charset_map[i].charset == charset)
- return &charset_map[i];
+ for (i = 0; i < G_N_ELEMENTS (charset_settings); i++) {
+ if (charset_settings[i].charset == charset)
+ return &charset_settings[i];
}
g_warn_if_reached ();
return NULL;
@@ -79,146 +84,13 @@
const gchar *
mm_modem_charset_to_string (MMModemCharset charset)
{
- const CharsetEntry *entry;
+ const CharsetSettings *settings;
- entry = lookup_charset_by_id (charset);
- return entry ? entry->gsm_name : NULL;
+ settings = lookup_charset_settings (charset);
+ return settings ? settings->gsm_name : NULL;
}
-static const gchar *
-charset_iconv_to (MMModemCharset charset)
-{
- const CharsetEntry *entry;
-
- entry = lookup_charset_by_id (charset);
- return entry ? entry->iconv_to_name : NULL;
-}
-
-static const gchar *
-charset_iconv_from (MMModemCharset charset)
-{
- const CharsetEntry *entry;
-
- entry = lookup_charset_by_id (charset);
- return entry ? entry->iconv_from_name : NULL;
-}
-
-gboolean
-mm_modem_charset_byte_array_append (GByteArray *array,
- const gchar *utf8,
- gboolean quoted,
- MMModemCharset charset,
- GError **error)
-{
- g_autofree gchar *converted = NULL;
- const gchar *iconv_to;
- gsize written = 0;
-
- g_return_val_if_fail (array != NULL, FALSE);
- g_return_val_if_fail (utf8 != NULL, FALSE);
-
- iconv_to = charset_iconv_to (charset);
- g_assert (iconv_to);
-
- converted = g_convert (utf8, -1, iconv_to, "UTF-8", NULL, &written, error);
- if (!converted) {
- g_prefix_error (error, "Failed to convert '%s' to %s character set",
- utf8, iconv_to);
- return FALSE;
- }
-
- if (quoted)
- g_byte_array_append (array, (const guint8 *) "\"", 1);
- g_byte_array_append (array, (const guint8 *) converted, written);
- if (quoted)
- g_byte_array_append (array, (const guint8 *) "\"", 1);
-
- return TRUE;
-}
-
-gchar *
-mm_modem_charset_byte_array_to_utf8 (GByteArray *array,
- MMModemCharset charset)
-{
- const gchar *iconv_from;
- g_autofree gchar *converted = NULL;
- g_autoptr(GError) error = NULL;
-
- g_return_val_if_fail (array != NULL, NULL);
- g_return_val_if_fail (charset != MM_MODEM_CHARSET_UNKNOWN, NULL);
-
- iconv_from = charset_iconv_from (charset);
- g_return_val_if_fail (iconv_from != NULL, FALSE);
-
- converted = g_convert ((const gchar *)array->data, array->len,
- "UTF-8//TRANSLIT", iconv_from,
- NULL, NULL, &error);
- if (!converted || error)
- return NULL;
-
- return g_steal_pointer (&converted);
-}
-
-gchar *
-mm_modem_charset_hex_to_utf8 (const gchar *src,
- MMModemCharset charset)
-{
- const gchar *iconv_from;
- g_autofree gchar *unconverted = NULL;
- g_autofree gchar *converted = NULL;
- g_autoptr(GError) error = NULL;
- gsize unconverted_len = 0;
-
- g_return_val_if_fail (src != NULL, NULL);
- g_return_val_if_fail (charset != MM_MODEM_CHARSET_UNKNOWN, NULL);
-
- iconv_from = charset_iconv_from (charset);
- g_return_val_if_fail (iconv_from != NULL, FALSE);
-
- unconverted = mm_utils_hexstr2bin (src, &unconverted_len);
- if (!unconverted)
- return NULL;
-
- if (charset == MM_MODEM_CHARSET_UTF8 || charset == MM_MODEM_CHARSET_IRA)
- return g_steal_pointer (&unconverted);
-
- converted = g_convert (unconverted, unconverted_len,
- "UTF-8//TRANSLIT", iconv_from,
- NULL, NULL, &error);
- if (!converted || error)
- return NULL;
-
- return g_steal_pointer (&converted);
-}
-
-gchar *
-mm_modem_charset_utf8_to_hex (const gchar *src,
- MMModemCharset charset)
-{
- const gchar *iconv_to;
- g_autofree gchar *converted = NULL;
- g_autoptr(GError) error = NULL;
- gsize converted_len = 0;
-
- g_return_val_if_fail (src != NULL, NULL);
- g_return_val_if_fail (charset != MM_MODEM_CHARSET_UNKNOWN, NULL);
-
- iconv_to = charset_iconv_from (charset);
- g_return_val_if_fail (iconv_to != NULL, FALSE);
-
- if (charset == MM_MODEM_CHARSET_UTF8 || charset == MM_MODEM_CHARSET_IRA)
- return g_strdup (src);
-
- converted = g_convert (src, strlen (src),
- iconv_to, "UTF-8//TRANSLIT",
- NULL, &converted_len, &error);
- if (!converted || error)
- return NULL;
-
- /* Get hex representation of the string */
- return mm_utils_bin2hexstr ((guint8 *)converted, converted_len);
-}
-
+/******************************************************************************/
/* GSM 03.38 encoding conversion stuff */
#define GSM_DEF_ALPHABET_SIZE 128
@@ -337,6 +209,22 @@
return FALSE;
}
+static gboolean
+translit_gsm_nul_byte (GByteArray *gsm)
+{
+ guint i;
+ guint n_replaces = 0;
+
+ for (i = 0; i < gsm->len; i++) {
+ if (gsm->data[i] == 0x00) {
+ utf8_to_gsm_def_char (translit_fallback, strlen (translit_fallback), &gsm->data[i]);
+ n_replaces++;
+ }
+ }
+
+ return (n_replaces > 0);
+}
+
#define EONE(a, g) { {a, 0x00, 0x00}, 1, g }
#define ETHR(a, b, c, g) { {a, b, c}, 3, g }
@@ -393,12 +281,14 @@
return FALSE;
}
-guint8 *
-mm_charset_gsm_unpacked_to_utf8 (const guint8 *gsm,
- guint32 len)
+static guint8 *
+charset_gsm_unpacked_to_utf8 (const guint8 *gsm,
+ guint32 len,
+ gboolean translit,
+ GError **error)
{
- guint i;
- GByteArray *utf8;
+ g_autoptr(GByteArray) utf8 = NULL;
+ guint i;
g_return_val_if_fail (gsm != NULL, NULL);
g_return_val_if_fail (len < 4096, NULL);
@@ -444,26 +334,36 @@
if (ulen)
g_byte_array_append (utf8, &uchars[0], ulen);
- else
- g_byte_array_append (utf8, (guint8 *) "?", 1);
+ else if (translit)
+ g_byte_array_append (utf8, (guint8 *) translit_fallback, strlen (translit_fallback));
+ else {
+ g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_INVALID_ARGS,
+ "Invalid conversion from GSM7");
+ return NULL;
+ }
}
/* Always make sure returned string is NUL terminated */
g_byte_array_append (utf8, (guint8 *) "\0", 1);
- return g_byte_array_free (utf8, FALSE);
+ return g_byte_array_free (g_steal_pointer (&utf8), FALSE);
}
-guint8 *
-mm_charset_utf8_to_unpacked_gsm (const gchar *utf8,
- guint32 *out_len)
+static guint8 *
+charset_utf8_to_unpacked_gsm (const gchar *utf8,
+ gboolean translit,
+ guint32 *out_len,
+ GError **error)
{
- GByteArray *gsm;
- const gchar *c;
- const gchar *next;
- static const guint8 gesc = GSM_ESCAPE_CHAR;
+ g_autoptr(GByteArray) gsm = NULL;
+ const gchar *c;
+ const gchar *next;
+ static const guint8 gesc = GSM_ESCAPE_CHAR;
- g_return_val_if_fail (utf8 != NULL, NULL);
- g_return_val_if_fail (g_utf8_validate (utf8, -1, NULL), NULL);
+ if (!utf8 || !g_utf8_validate (utf8, -1, NULL)) {
+ g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_INVALID_ARGS,
+ "Couldn't convert UTF-8 to GSM: input UTF-8 validation failed");
+ return NULL;
+ }
/* worst case initial length */
gsm = g_byte_array_sized_new (g_utf8_strlen (utf8, -1) * 2 + 1);
@@ -473,7 +373,7 @@
g_byte_array_append (gsm, (guint8 *) "\0", 1);
if (out_len)
*out_len = 0;
- return g_byte_array_free (gsm, FALSE);
+ return g_byte_array_free (g_steal_pointer (&gsm), FALSE);
}
next = utf8;
@@ -488,8 +388,16 @@
/* Add the escape char */
g_byte_array_append (gsm, &gesc, 1);
g_byte_array_append (gsm, &gch, 1);
- } else if (utf8_to_gsm_def_char (c, next - c, &gch))
+ } else if (utf8_to_gsm_def_char (c, next - c, &gch)) {
g_byte_array_append (gsm, &gch, 1);
+ } else if (translit) {
+ /* add ? */
+ g_byte_array_append (gsm, &gch, 1);
+ } else {
+ g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_INVALID_ARGS,
+ "Couldn't convert UTF-8 char to GSM");
+ return NULL;
+ }
c = next;
}
@@ -500,9 +408,13 @@
/* Always make sure returned string is NUL terminated */
g_byte_array_append (gsm, (guint8 *) "\0", 1);
- return g_byte_array_free (gsm, FALSE);
+ return g_byte_array_free (g_steal_pointer (&gsm), FALSE);
}
+/******************************************************************************/
+/* Checks to see whether conversion to a target charset may be done without
+ * any loss. */
+
static gboolean
gsm_is_subset (gunichar c,
const gchar *utf8,
@@ -632,13 +544,6 @@
{ MM_MODEM_CHARSET_PCDN, pcdn_is_subset },
};
-/**
- * mm_charset_can_covert_to:
- * @utf8: UTF-8 valid string.
- * @charset: the #MMModemCharset to validate the conversion from @utf8.
- *
- * Returns: %TRUE if the conversion is possible without errors, %FALSE otherwise.
- */
gboolean
mm_charset_can_convert_to (const gchar *utf8,
MMModemCharset charset)
@@ -682,6 +587,9 @@
return TRUE;
}
+/******************************************************************************/
+/* GSM-7 pack/unpack operations */
+
guint8 *
mm_charset_gsm_unpack (const guint8 *gsm,
guint32 num_septets,
@@ -754,217 +662,308 @@
return packed;
}
-/* We do all our best to get the given string, which is possibly given in the
- * specified charset, to UTF8. It may happen that the given string is really
- * the hex representation of the charset-encoded string, so we need to cope with
- * that case. */
-gchar *
-mm_charset_take_and_convert_to_utf8 (gchar *str,
- MMModemCharset charset)
+/*****************************************************************************/
+/* Main conversion functions */
+
+static guint8 *
+charset_iconv_from_utf8 (const gchar *utf8,
+ const CharsetSettings *settings,
+ gboolean translit,
+ guint *out_size,
+ GError **error)
{
- gchar *utf8 = NULL;
+ g_autoptr(GError) inner_error = NULL;
+ gsize bytes_written = 0;
+ g_autofree guint8 *encoded = NULL;
- if (!str)
+ encoded = (guint8 *) g_convert (utf8, -1,
+ settings->iconv_name, "UTF-8",
+ NULL, &bytes_written, &inner_error);
+ if (encoded) {
+ if (out_size)
+ *out_size = (guint) bytes_written;
+ return g_steal_pointer (&encoded);
+ }
+
+ if (!translit) {
+ g_propagate_error (error, g_steal_pointer (&inner_error));
+ g_prefix_error (error, "Couldn't convert from UTF-8 to %s: ", settings->gsm_name);
return NULL;
-
- switch (charset) {
- case MM_MODEM_CHARSET_UNKNOWN:
- g_warn_if_reached ();
- utf8 = str;
- break;
-
- case MM_MODEM_CHARSET_HEX:
- /* We'll assume that the HEX string is really valid ASCII at the end */
- utf8 = str;
- break;
-
- case MM_MODEM_CHARSET_GSM:
- utf8 = (gchar *) mm_charset_gsm_unpacked_to_utf8 ((const guint8 *) str, strlen (str));
- g_free (str);
- break;
-
- case MM_MODEM_CHARSET_8859_1:
- case MM_MODEM_CHARSET_PCCP437:
- case MM_MODEM_CHARSET_PCDN: {
- const gchar *iconv_from;
- GError *error = NULL;
-
- iconv_from = charset_iconv_from (charset);
- utf8 = g_convert (str, strlen (str),
- "UTF-8//TRANSLIT", iconv_from,
- NULL, NULL, &error);
- if (!utf8 || error) {
- g_clear_error (&error);
- utf8 = NULL;
- }
-
- g_free (str);
- break;
}
- case MM_MODEM_CHARSET_UCS2:
- case MM_MODEM_CHARSET_UTF16: {
- gsize len;
- gboolean possibly_hex = TRUE;
- gsize bread = 0, bwritten = 0;
-
- /* If the string comes in hex-UCS-2, len needs to be a multiple of 4 */
- len = strlen (str);
- if ((len < 4) || ((len % 4) != 0))
- possibly_hex = FALSE;
- else {
- const gchar *p = str;
-
- /* All chars in the string must be hex */
- while (*p && possibly_hex)
- possibly_hex = isxdigit (*p++);
- }
-
- /* If hex, then we expect hex-encoded UCS-2 */
- if (possibly_hex) {
- utf8 = mm_modem_charset_hex_to_utf8 (str, charset);
- if (utf8) {
- g_free (str);
- break;
- }
- }
-
- /* If not hex, then it might be raw UCS-2 (very unlikely) or ASCII/UTF-8
- * (much more likely). Try to convert to UTF-8 and if that fails, use
- * the partial conversion length to re-convert the part of the string
- * that is UTF-8, if any.
- */
- utf8 = g_convert (str, strlen (str),
- "UTF-8//TRANSLIT", "UTF-8//TRANSLIT",
- &bread, &bwritten, NULL);
-
- /* Valid conversion, or we didn't get enough valid UTF-8 */
- if (utf8 || (bwritten <= 2)) {
- g_free (str);
- break;
- }
-
- /* Last try; chop off the original string at the conversion failure
- * location and get what we can.
- */
- str[bread] = '\0';
- utf8 = g_convert (str, strlen (str),
- "UTF-8//TRANSLIT", "UTF-8//TRANSLIT",
- NULL, NULL, NULL);
- g_free (str);
- break;
+ encoded = (guint8 *) g_convert_with_fallback (utf8, -1,
+ settings->iconv_name, "UTF-8", translit_fallback,
+ NULL, &bytes_written, error);
+ if (encoded) {
+ if (out_size)
+ *out_size = (guint) bytes_written;
+ return g_steal_pointer (&encoded);
}
- /* If the given charset is ASCII or UTF8, we really expect the final string
- * already here */
- case MM_MODEM_CHARSET_IRA:
- case MM_MODEM_CHARSET_UTF8:
- utf8 = str;
- break;
-
- default:
- g_assert_not_reached ();
- }
-
- /* Validate UTF-8 always before returning. This result will be exposed in DBus
- * very likely... */
- if (utf8 && !g_utf8_validate (utf8, -1, NULL)) {
- /* Better return NULL than an invalid UTF-8 string */
- g_free (utf8);
- utf8 = NULL;
- }
-
- return utf8;
+ g_prefix_error (error, "Couldn't convert from UTF-8 to %s with translit: ", settings->gsm_name);
+ return NULL;
}
-/* We do all our best to convert the given string, which comes in UTF-8, to the
- * specified charset. It may be that the output string needs to be the hex
- * representation of the charset-encoded string, so we need to cope with that
- * case. */
-gchar *
-mm_utf8_take_and_convert_to_charset (gchar *str,
- MMModemCharset charset)
+GByteArray *
+mm_modem_charset_bytearray_from_utf8 (const gchar *utf8,
+ MMModemCharset charset,
+ gboolean translit,
+ GError **error)
{
- gchar *encoded = NULL;
+ const CharsetSettings *settings;
+ guint8 *encoded = NULL;
+ guint encoded_size = 0;
- if (!str)
- return NULL;
+ settings = lookup_charset_settings (charset);
- /* Validate UTF-8 always before converting */
- if (!g_utf8_validate (str, -1, NULL)) {
- /* Better return NULL than an invalid encoded string */
- g_free (str);
+ if (charset == MM_MODEM_CHARSET_UNKNOWN) {
+ g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_INVALID_ARGS,
+ "Cannot convert from UTF-8: unknown target charset");
return NULL;
}
switch (charset) {
- case MM_MODEM_CHARSET_UNKNOWN:
- g_warn_if_reached ();
- encoded = str;
- break;
+ case MM_MODEM_CHARSET_GSM:
+ encoded = charset_utf8_to_unpacked_gsm (utf8, translit, &encoded_size, error);
+ break;
+ case MM_MODEM_CHARSET_IRA:
+ case MM_MODEM_CHARSET_8859_1:
+ case MM_MODEM_CHARSET_UTF8:
+ case MM_MODEM_CHARSET_UCS2:
+ case MM_MODEM_CHARSET_PCCP437:
+ case MM_MODEM_CHARSET_PCDN:
+ case MM_MODEM_CHARSET_UTF16:
+ encoded = charset_iconv_from_utf8 (utf8, settings, translit, &encoded_size, error);
+ break;
+ case MM_MODEM_CHARSET_UNKNOWN:
+ default:
+ g_assert_not_reached ();
+ }
- case MM_MODEM_CHARSET_HEX:
- encoded = str;
- break;
+ return g_byte_array_new_take (encoded, encoded_size);
+}
- case MM_MODEM_CHARSET_GSM:
- encoded = (gchar *) mm_charset_utf8_to_unpacked_gsm (str, NULL);
- g_free (str);
- break;
+gchar *
+mm_modem_charset_str_from_utf8 (const gchar *utf8,
+ MMModemCharset charset,
+ gboolean translit,
+ GError **error)
+{
+ g_autoptr(GByteArray) bytearray = NULL;
- case MM_MODEM_CHARSET_8859_1:
- case MM_MODEM_CHARSET_PCCP437:
- case MM_MODEM_CHARSET_PCDN: {
- const gchar *iconv_to;
- GError *error = NULL;
+ if (charset == MM_MODEM_CHARSET_UNKNOWN) {
+ g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_INVALID_ARGS,
+ "Cannot convert from UTF-8: unknown target charset");
+ return NULL;
+ }
- iconv_to = charset_iconv_from (charset);
- encoded = g_convert (str, strlen (str),
- iconv_to, "UTF-8",
- NULL, NULL, &error);
- if (!encoded || error) {
- g_clear_error (&error);
- encoded = NULL;
+ bytearray = mm_modem_charset_bytearray_from_utf8 (utf8, charset, translit, error);
+ if (!bytearray)
+ return NULL;
+
+ switch (charset) {
+ case MM_MODEM_CHARSET_GSM:
+ /* Note: strings encoded in unpacked GSM-7 can be used as plain
+ * strings as long as the string doesn't contain character '@', which
+ * is the one encoded as 0x00. At this point, we perform transliteration
+ * of the NUL bytes in the GSM-7 bytearray, and we fail the operation
+ * if one or more replacements were done and transliteration wasn't
+ * requested */
+ if (translit_gsm_nul_byte (bytearray) && !translit) {
+ g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_INVALID_ARGS,
+ "Cannot convert to GSM-7 string: transliteration required for embedded '@'");
+ return NULL;
+ }
+ /* fall through */
+ case MM_MODEM_CHARSET_IRA:
+ case MM_MODEM_CHARSET_8859_1:
+ case MM_MODEM_CHARSET_UTF8:
+ case MM_MODEM_CHARSET_PCCP437:
+ case MM_MODEM_CHARSET_PCDN:
+ return (gchar *) g_byte_array_free (g_steal_pointer (&bytearray), FALSE);
+ case MM_MODEM_CHARSET_UCS2:
+ case MM_MODEM_CHARSET_UTF16:
+ return mm_utils_bin2hexstr (bytearray->data, bytearray->len);
+ default:
+ case MM_MODEM_CHARSET_UNKNOWN:
+ g_assert_not_reached ();
+ }
+}
+
+static gchar *
+charset_iconv_to_utf8 (const guint8 *data,
+ guint32 len,
+ const CharsetSettings *settings,
+ gboolean translit,
+ GError **error)
+{
+ g_autoptr(GError) inner_error = NULL;
+ g_autofree gchar *utf8 = NULL;
+
+ utf8 = g_convert ((const gchar *) data, len,
+ "UTF-8",
+ settings->iconv_name,
+ NULL, NULL, &inner_error);
+ if (utf8)
+ return g_steal_pointer (&utf8);
+
+ if (!translit) {
+ g_propagate_error (error, g_steal_pointer (&inner_error));
+ g_prefix_error (error, "Couldn't convert from %s to UTF-8: ", settings->gsm_name);
+ return NULL;
+ }
+
+ utf8 = g_convert_with_fallback ((const gchar *) data, len,
+ "UTF-8", settings->iconv_name, translit_fallback,
+ NULL, NULL, error);
+ if (utf8)
+ return g_steal_pointer (&utf8);
+
+ g_prefix_error (error, "Couldn't convert from %s to UTF-8 with translit: ", settings->gsm_name);
+ return NULL;
+}
+
+gchar *
+mm_modem_charset_bytearray_to_utf8 (GByteArray *bytearray,
+ MMModemCharset charset,
+ gboolean translit,
+ GError **error)
+{
+ const CharsetSettings *settings;
+ g_autofree gchar *utf8 = NULL;
+
+ if (charset == MM_MODEM_CHARSET_UNKNOWN) {
+ g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_INVALID_ARGS,
+ "Cannot convert from UTF-8: unknown target charset");
+ return NULL;
+ }
+
+ settings = lookup_charset_settings (charset);
+
+ switch (charset) {
+ case MM_MODEM_CHARSET_GSM:
+ utf8 = (gchar *) charset_gsm_unpacked_to_utf8 (bytearray->data,
+ bytearray->len,
+ translit,
+ error);
+ break;
+ case MM_MODEM_CHARSET_IRA:
+ case MM_MODEM_CHARSET_UTF8:
+ case MM_MODEM_CHARSET_8859_1:
+ case MM_MODEM_CHARSET_PCCP437:
+ case MM_MODEM_CHARSET_PCDN:
+ case MM_MODEM_CHARSET_UCS2:
+ case MM_MODEM_CHARSET_UTF16:
+ utf8 = charset_iconv_to_utf8 (bytearray->data,
+ bytearray->len,
+ settings,
+ translit,
+ error);
+ break;
+ case MM_MODEM_CHARSET_UNKNOWN:
+ default:
+ g_assert_not_reached ();
+ }
+
+ if (utf8 && g_utf8_validate (utf8, -1, NULL))
+ return g_steal_pointer (&utf8);
+
+ g_prefix_error (error, "Invalid conversion from %s to UTF-8: ", settings->gsm_name);
+ return NULL;
+}
+
+gchar *
+mm_modem_charset_str_to_utf8 (const gchar *str,
+ gssize len,
+ MMModemCharset charset,
+ gboolean translit,
+ GError **error)
+{
+ g_autoptr(GByteArray) bytearray = NULL;
+
+ if (charset == MM_MODEM_CHARSET_UNKNOWN) {
+ g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_INVALID_ARGS,
+ "Cannot convert from UTF-8: unknown target charset");
+ return NULL;
+ }
+
+ /* Note: if the input string is GSM-7 encoded and it contains the '@'
+ * character, using -1 to indicate string length won't work properly,
+ * as '@' is encoded as 0x00. Whenever possible, if using GSM-7,
+ * give a proper len value or otherwise use the bytearray_to_utf8()
+ * method instead. */
+ if (len < 0)
+ len = strlen (str);
+
+ switch (charset) {
+ case MM_MODEM_CHARSET_GSM:
+ case MM_MODEM_CHARSET_IRA:
+ case MM_MODEM_CHARSET_8859_1:
+ case MM_MODEM_CHARSET_UTF8:
+ case MM_MODEM_CHARSET_PCCP437:
+ case MM_MODEM_CHARSET_PCDN:
+ bytearray = g_byte_array_sized_new (len);
+ g_byte_array_append (bytearray, (const guint8 *)str, len);
+ break;
+ case MM_MODEM_CHARSET_UCS2:
+ case MM_MODEM_CHARSET_UTF16: {
+ guint8 *bin = NULL;
+ gsize bin_len;
+
+ bin = (guint8 *) mm_utils_hexstr2bin (str, len, &bin_len, error);
+ if (!bin)
+ return NULL;
+
+ bytearray = g_byte_array_new_take (bin, bin_len);
+ break;
+ }
+ case MM_MODEM_CHARSET_UNKNOWN:
+ default:
+ g_assert_not_reached ();
+ }
+
+ return mm_modem_charset_bytearray_to_utf8 (bytearray, charset, translit, error);
+}
+
+/******************************************************************************/
+/* Runtime charset support via iconv() */
+
+void
+mm_modem_charsets_init (void)
+{
+ /* As test string, something we can convert to/from all the encodings */
+ static const gchar *default_test_str = "ModemManager";
+ guint i;
+
+ mm_obj_dbg (NULL, "[charsets] detecting platform iconv() support...");
+ for (i = 0; i < G_N_ELEMENTS (charset_settings); i++) {
+ g_autofree guint8 *enc = NULL;
+ guint enc_size;
+ g_autofree gchar *dec = NULL;
+
+ if (!charset_settings[i].iconv_name)
+ continue;
+
+ enc = charset_iconv_from_utf8 (default_test_str,
+ &charset_settings[i],
+ FALSE,
+ &enc_size,
+ NULL);
+ if (!enc) {
+ mm_obj_dbg (NULL, "[charsets] %s: iconv conversion to charset not supported", charset_settings[i].iconv_name);
+ continue;
}
- g_free (str);
- break;
- }
-
- case MM_MODEM_CHARSET_UCS2:
- case MM_MODEM_CHARSET_UTF16: {
- const gchar *iconv_to;
- gsize encoded_len = 0;
- GError *error = NULL;
- gchar *hex;
-
- iconv_to = charset_iconv_from (charset);
- encoded = g_convert (str, strlen (str),
- iconv_to, "UTF-8",
- NULL, &encoded_len, &error);
- if (!encoded || error) {
- g_clear_error (&error);
- encoded = NULL;
+ dec = charset_iconv_to_utf8 (enc,
+ enc_size,
+ &charset_settings[i],
+ FALSE,
+ NULL);
+ if (!enc) {
+ mm_obj_dbg (NULL, "[charsets] %s: iconv conversion from charset not supported", charset_settings[i].iconv_name);
+ continue;
}
- /* Get hex representation of the string */
- hex = mm_utils_bin2hexstr ((guint8 *)encoded, encoded_len);
- g_free (encoded);
- encoded = hex;
- g_free (str);
- break;
+ mm_obj_dbg (NULL, "[charsets] %s: iconv conversion to/from charset is supported", charset_settings[i].iconv_name);
}
-
- /* If the given charset is ASCII or UTF8, we really expect the final string
- * already here. */
- case MM_MODEM_CHARSET_IRA:
- case MM_MODEM_CHARSET_UTF8:
- encoded = str;
- break;
-
- default:
- g_assert_not_reached ();
- }
-
- return encoded;
}
diff --git a/src/mm-charsets.h b/src/mm-charsets.h
index c064eef..3071f6b 100644
--- a/src/mm-charsets.h
+++ b/src/mm-charsets.h
@@ -18,53 +18,24 @@
#include <glib.h>
+/*****************************************************************************************/
+
typedef enum {
- MM_MODEM_CHARSET_UNKNOWN = 0x00000000,
- MM_MODEM_CHARSET_GSM = 0x00000001,
- MM_MODEM_CHARSET_IRA = 0x00000002,
- MM_MODEM_CHARSET_8859_1 = 0x00000004,
- MM_MODEM_CHARSET_UTF8 = 0x00000008,
- MM_MODEM_CHARSET_UCS2 = 0x00000010,
- MM_MODEM_CHARSET_PCCP437 = 0x00000020,
- MM_MODEM_CHARSET_PCDN = 0x00000040,
- MM_MODEM_CHARSET_HEX = 0x00000080,
- MM_MODEM_CHARSET_UTF16 = 0x00000100,
+ MM_MODEM_CHARSET_UNKNOWN = 0,
+ MM_MODEM_CHARSET_GSM = 1 << 0,
+ MM_MODEM_CHARSET_IRA = 1 << 1,
+ MM_MODEM_CHARSET_8859_1 = 1 << 2,
+ MM_MODEM_CHARSET_UTF8 = 1 << 3,
+ MM_MODEM_CHARSET_UCS2 = 1 << 4,
+ MM_MODEM_CHARSET_PCCP437 = 1 << 5,
+ MM_MODEM_CHARSET_PCDN = 1 << 6,
+ MM_MODEM_CHARSET_UTF16 = 1 << 7,
} MMModemCharset;
const gchar *mm_modem_charset_to_string (MMModemCharset charset);
MMModemCharset mm_modem_charset_from_string (const gchar *string);
-/* Append the given string to the given byte array but re-encode it
- * into the given charset first. The original string is assumed to be
- * UTF-8 encoded.
- */
-gboolean mm_modem_charset_byte_array_append (GByteArray *array,
- const gchar *utf8,
- gboolean quoted,
- MMModemCharset charset,
- GError **error);
-
-/* Take a string encoded in the given charset in binary form, and
- * convert it to UTF-8. */
-gchar *mm_modem_charset_byte_array_to_utf8 (GByteArray *array,
- MMModemCharset charset);
-
-/* Take a string in hex representation ("00430052" or "A4BE11" for example)
- * and convert it from the given character set to UTF-8.
- */
-gchar *mm_modem_charset_hex_to_utf8 (const gchar *src,
- MMModemCharset charset);
-
-/* Take a string in UTF-8 and convert it to the given charset in hex
- * representation.
- */
-gchar *mm_modem_charset_utf8_to_hex (const gchar *src,
- MMModemCharset charset);
-
-guint8 *mm_charset_utf8_to_unpacked_gsm (const gchar *utf8,
- guint32 *out_len);
-guint8 *mm_charset_gsm_unpacked_to_utf8 (const guint8 *gsm,
- guint32 len);
+/*****************************************************************************************/
/* Checks whether conversion to the given charset may be done without errors */
gboolean mm_charset_can_convert_to (const gchar *utf8,
@@ -80,9 +51,65 @@
guint8 start_offset, /* in bits */
guint32 *out_packed_len);
-gchar *mm_charset_take_and_convert_to_utf8 (gchar *str,
- MMModemCharset charset);
-gchar *mm_utf8_take_and_convert_to_charset (gchar *str,
- MMModemCharset charset);
+/*****************************************************************************************/
+
+/*
+ * Convert the given UTF-8 encoded string into the given charset.
+ *
+ * The output is given as a bytearray, because the target charset may allow
+ * embedded NUL bytes (e.g. UTF-16).
+ *
+ * The output encoded string is not guaranteed to be NUL-terminated, instead
+ * the bytearray length itself gives the correct string length.
+ */
+GByteArray *mm_modem_charset_bytearray_from_utf8 (const gchar *utf8,
+ MMModemCharset charset,
+ gboolean translit,
+ GError **error);
+
+/*
+ * Convert the given UTF-8 encoded string into the given charset.
+ *
+ * The output is given as a C string, and those charsets that allow
+ * embedded NUL bytes (e.g. UTF-16) will be hex-encoded.
+ *
+ * The output encoded string is guaranteed to be NUL-terminated, and so no
+ * explicit output length is returned.
+ */
+gchar *mm_modem_charset_str_from_utf8 (const gchar *utf8,
+ MMModemCharset charset,
+ gboolean translit,
+ GError **error);
+
+/*
+ * Convert into an UTF-8 encoded string the input byte array, which is
+ * encoded in the given charset.
+ *
+ * The output string is guaranteed to be valid UTF-8 and NUL-terminated.
+ */
+gchar *mm_modem_charset_bytearray_to_utf8 (GByteArray *bytearray,
+ MMModemCharset charset,
+ gboolean translit,
+ GError **error);
+
+/*
+ * Convert into an UTF-8 encoded string the input string, which is
+ * encoded in the given charset. Those charsets that allow embedded NUL
+ * bytes (e.g. UTF-16) need to be hex-encoded.
+ *
+ * If the input string is NUL-terminated, len may be given as -1; otherwise
+ * len needs to specify the number of valid bytes in the input string.
+ *
+ * The output string is guaranteed to be valid UTF-8 and NUL-terminated.
+ */
+gchar *mm_modem_charset_str_to_utf8 (const gchar *str,
+ gssize len,
+ MMModemCharset charset,
+ gboolean translit,
+ GError **error);
+
+/*****************************************************************************************/
+
+void mm_modem_charsets_init (void);
#endif /* MM_CHARSETS_H */
diff --git a/src/mm-modem-helpers-mbim.c b/src/mm-modem-helpers-mbim.c
index f9dc8e6..b70f379 100644
--- a/src/mm-modem-helpers-mbim.c
+++ b/src/mm-modem-helpers-mbim.c
@@ -19,6 +19,8 @@
#include "mm-errors-types.h"
#include "mm-log-object.h"
+#include <string.h>
+
/*****************************************************************************/
MMModemCapability
diff --git a/src/mm-modem-helpers-qmi.c b/src/mm-modem-helpers-qmi.c
index 012e657..6ab0adf 100644
--- a/src/mm-modem-helpers-qmi.c
+++ b/src/mm-modem-helpers-qmi.c
@@ -1845,25 +1845,19 @@
/* The length will be exactly EXPECTED_QMI_UNIQUE_ID_LENGTH*2 if given in HEX */
if (len == (2 * EXPECTED_QMI_UNIQUE_ID_LENGTH)) {
- guint8 *tmp;
- gsize tmp_len;
- guint i;
-
- for (i = 0; i < len; i++) {
- if (!g_ascii_isxdigit (unique_id[i])) {
- g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED,
- "Unexpected character found in unique id (not HEX): %c", unique_id[i]);
- return NULL;
- }
- }
+ g_autofree guint8 *tmp = NULL;
+ gsize tmp_len;
tmp_len = 0;
- tmp = (guint8 *) mm_utils_hexstr2bin (unique_id, &tmp_len);
+ tmp = mm_utils_hexstr2bin (unique_id, -1, &tmp_len, error);
+ if (!tmp) {
+ g_prefix_error (error, "Unexpected character found in unique id: ");
+ return NULL;
+ }
g_assert (tmp_len == EXPECTED_QMI_UNIQUE_ID_LENGTH);
qmi_unique_id = g_array_sized_new (FALSE, FALSE, sizeof (guint8), tmp_len);
g_array_insert_vals (qmi_unique_id, 0, tmp, tmp_len);
- g_free (tmp);
return qmi_unique_id;
}
diff --git a/src/mm-modem-helpers.c b/src/mm-modem-helpers.c
index e5776fc..7ff7484 100644
--- a/src/mm-modem-helpers.c
+++ b/src/mm-modem-helpers.c
@@ -1289,9 +1289,9 @@
info->operator_code = mm_get_string_unquoted_from_match_info (match_info, 4);
/* The returned strings may be given in e.g. UCS2 */
- mm_3gpp_normalize_operator (&info->operator_long, cur_charset);
- mm_3gpp_normalize_operator (&info->operator_short, cur_charset);
- mm_3gpp_normalize_operator (&info->operator_code, cur_charset);
+ mm_3gpp_normalize_operator (&info->operator_long, cur_charset, log_object);
+ mm_3gpp_normalize_operator (&info->operator_short, cur_charset, log_object);
+ mm_3gpp_normalize_operator (&info->operator_code, cur_charset, log_object);
/* Only try for access technology with UMTS-format matches.
* If none give, assume GSM */
@@ -4053,8 +4053,11 @@
void
mm_3gpp_normalize_operator (gchar **operator,
- MMModemCharset cur_charset)
+ MMModemCharset cur_charset,
+ gpointer log_object)
{
+ g_autofree gchar *normalized = NULL;
+
g_assert (operator);
if (*operator == NULL)
@@ -4062,31 +4065,38 @@
/* Despite +CSCS? may claim supporting UCS2, Some modems (e.g. Huawei)
* always report the operator name in ASCII in a +COPS response. */
- if (cur_charset == MM_MODEM_CHARSET_UCS2) {
- gchar *tmp;
+ if (cur_charset != MM_MODEM_CHARSET_UNKNOWN) {
+ g_autoptr(GError) error = NULL;
- tmp = g_strdup (*operator);
- /* In this case we're already checking UTF-8 validity */
- tmp = mm_charset_take_and_convert_to_utf8 (tmp, cur_charset);
- if (tmp) {
- g_clear_pointer (operator, g_free);
- *operator = tmp;
+ normalized = mm_modem_charset_str_to_utf8 (*operator, -1, cur_charset, TRUE, &error);
+ if (normalized)
goto out;
- }
+
+ mm_obj_dbg (log_object, "couldn't convert operator string '%s' from charset '%s': %s",
+ *operator,
+ mm_modem_charset_to_string (cur_charset),
+ error->message);
}
/* Charset is unknown or there was an error in conversion; try to see
* if the contents we got are valid UTF-8 already. */
- if (!g_utf8_validate (*operator, -1, NULL))
- g_clear_pointer (operator, g_free);
+ if (g_utf8_validate (*operator, -1, NULL))
+ normalized = g_strdup (*operator);
out:
/* Some modems (Novatel LTE) return the operator name as "Unknown" when
* it fails to obtain the operator name. Return NULL in such case.
*/
- if (*operator && g_ascii_strcasecmp (*operator, "unknown") == 0)
+ if (!normalized || g_ascii_strcasecmp (normalized, "unknown") == 0) {
+ /* If normalization failed, just cleanup the string */
g_clear_pointer (operator, g_free);
+ return;
+ }
+
+ mm_obj_dbg (log_object, "operator normalized '%s'->'%s'", *operator, normalized);
+ g_clear_pointer (operator, g_free);
+ *operator = g_steal_pointer (&normalized);
}
/*************************************************************************/
@@ -4326,10 +4336,9 @@
return NULL;
}
- bin = (guint8 *) mm_utils_hexstr2bin (raw, &binlen);
+ bin = mm_utils_hexstr2bin (raw, -1, &binlen, error);
if (!bin) {
- g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_INVALID_ARGS,
- "invalid raw emergency numbers list contents: %s", raw);
+ g_prefix_error (error, "invalid raw emergency numbers list contents: ");
return NULL;
}
@@ -5152,3 +5161,87 @@
g_strfreev (split);
return valid;
}
+
+/*****************************************************************************/
+
+gboolean
+mm_sim_parse_cpol_query_response (const gchar *response,
+ gchar **out_operator_code,
+ gboolean *out_gsm_act,
+ gboolean *out_gsm_compact_act,
+ gboolean *out_utran_act,
+ gboolean *out_eutran_act,
+ gboolean *out_ngran_act,
+ GError **error)
+{
+ g_autoptr(GMatchInfo) match_info = NULL;
+ g_autoptr(GRegex) r = NULL;
+ g_autofree gchar *operator_code = NULL;
+ guint format = 0;
+ guint act = 0;
+ guint match_count;
+
+ r = g_regex_new ("\\+CPOL:\\s*\\d+,\\s*(\\d+),\\s*\"(\\d+)\""
+ "(?:,\\s*(\\d+))?" /* GSM_AcTn */
+ "(?:,\\s*(\\d+))?" /* GSM_Compact_AcTn */
+ "(?:,\\s*(\\d+))?" /* UTRAN_AcTn */
+ "(?:,\\s*(\\d+))?" /* E-UTRAN_AcTn */
+ "(?:,\\s*(\\d+))?", /* NG-RAN_AcTn */
+ G_REGEX_RAW, 0, NULL);
+ g_regex_match (r, response, 0, &match_info);
+
+ if (!g_match_info_matches (match_info)) {
+ g_set_error (error,
+ MM_CORE_ERROR,
+ MM_CORE_ERROR_FAILED,
+ "Couldn't parse +CPOL reply: %s", response);
+ return FALSE;
+ }
+
+ match_count = g_match_info_get_match_count (match_info);
+ /* Remember that g_match_info_get_match_count() includes match #0 */
+ g_assert (match_count >= 3);
+
+ if (!mm_get_uint_from_match_info (match_info, 1, &format) ||
+ !(operator_code = mm_get_string_unquoted_from_match_info (match_info, 2))) {
+ g_set_error (error,
+ MM_CORE_ERROR,
+ MM_CORE_ERROR_FAILED,
+ "Couldn't parse +CPOL reply parameters: %s", response);
+ return FALSE;
+ }
+
+ if (format != 2) {
+ g_set_error (error,
+ MM_CORE_ERROR,
+ MM_CORE_ERROR_FAILED,
+ "+CPOL reply not using numeric operator code: %s", response);
+ return FALSE;
+ }
+
+ if (out_operator_code) {
+ *out_operator_code = g_steal_pointer (&operator_code);
+ }
+ if (out_gsm_act)
+ *out_gsm_act = match_count >= 4 &&
+ mm_get_uint_from_match_info (match_info, 3, &act) &&
+ act != 0;
+ if (out_gsm_compact_act)
+ *out_gsm_compact_act = match_count >= 5 &&
+ mm_get_uint_from_match_info (match_info, 4, &act) &&
+ act != 0;
+ if (out_utran_act)
+ *out_utran_act = match_count >= 6 &&
+ mm_get_uint_from_match_info (match_info, 5, &act) &&
+ act != 0;
+ if (out_eutran_act)
+ *out_eutran_act = match_count >= 7 &&
+ mm_get_uint_from_match_info (match_info, 6, &act) &&
+ act != 0;
+ if (out_ngran_act)
+ *out_ngran_act = match_count >= 8 &&
+ mm_get_uint_from_match_info (match_info, 7, &act) &&
+ act != 0;
+
+ return TRUE;
+}
diff --git a/src/mm-modem-helpers.h b/src/mm-modem-helpers.h
index b3b5138..785fd53 100644
--- a/src/mm-modem-helpers.h
+++ b/src/mm-modem-helpers.h
@@ -439,7 +439,8 @@
MMModemAccessTechnology mm_string_to_access_tech (const gchar *string);
void mm_3gpp_normalize_operator (gchar **operator,
- MMModemCharset cur_charset);
+ MMModemCharset cur_charset,
+ gpointer log_object);
gboolean mm_3gpp_parse_operator_id (const gchar *operator_id,
guint16 *mcc,
@@ -530,6 +531,20 @@
GError **error);
/*****************************************************************************/
+/* SIM specific helpers and utilities */
+/*****************************************************************************/
+
+/* +CPOL? response parser (for a single entry) - accepts only numeric operator format*/
+gboolean mm_sim_parse_cpol_query_response (const gchar *response,
+ gchar **out_operator_code,
+ gboolean *out_gsm_act,
+ gboolean *out_gsm_compact_act,
+ gboolean *out_utran_act,
+ gboolean *out_eutran_act,
+ gboolean *out_ngran_act,
+ GError **error);
+
+/*****************************************************************************/
/* Useful when clamp-ing an unsigned integer with implicit low limit set to 0,
* and in order to avoid -Wtype-limits warnings. */
diff --git a/src/mm-plugin-manager.c b/src/mm-plugin-manager.c
index 6c9bdf1..4e9f300 100644
--- a/src/mm-plugin-manager.c
+++ b/src/mm-plugin-manager.c
@@ -28,6 +28,7 @@
#include "mm-plugin-manager.h"
#include "mm-plugin.h"
#include "mm-shared.h"
+#include "mm-utils.h"
#include "mm-log-object.h"
#define SHARED_PREFIX "libmm-shared"
diff --git a/src/mm-shared-qmi.c b/src/mm-shared-qmi.c
index d3ff0c2..cd969c3 100644
--- a/src/mm-shared-qmi.c
+++ b/src/mm-shared-qmi.c
@@ -212,7 +212,7 @@
g_signal_handler_disconnect (ctx->client, ctx->serving_system_indication_id);
ctx->serving_system_indication_id = 0;
- g_assert (g_task_return_error_if_cancelled (task));
+ g_task_return_error_if_cancelled (task);
g_object_unref (task);
}
@@ -3188,11 +3188,45 @@
guint current_slot_number;
guint active_slot_number;
guint active_logical_id;
+ GArray *initial_slot_status;
+ GArray *final_slot_status;
+ gulong final_slot_status_timeout_id;
+ gulong load_sim_slots_indication_id;
} LoadSimSlotsContext;
static void
+clear_load_sim_slot_callbacks (GTask *task)
+{
+ MMIfaceModem *self;
+ LoadSimSlotsContext *ctx;
+ Private *priv;
+
+ self = g_task_get_source_object (task);
+ ctx = g_task_get_task_data (task);
+ priv = get_private (MM_SHARED_QMI (self));
+
+ if (ctx->client_uim && ctx->load_sim_slots_indication_id) {
+ g_signal_handler_disconnect(ctx->client_uim, ctx->load_sim_slots_indication_id);
+ ctx->load_sim_slots_indication_id = 0;
+ }
+ if (ctx->final_slot_status_timeout_id) {
+ g_source_remove (ctx->final_slot_status_timeout_id);
+ ctx->final_slot_status_timeout_id = 0;
+ }
+ /* Restore hot swap detection because we aren't loading slots anymore */
+ if (priv->uim_slot_status_indication_id)
+ g_signal_handler_unblock (ctx->client_uim, priv->uim_slot_status_indication_id);
+}
+
+static void
load_sim_slots_context_free (LoadSimSlotsContext *ctx)
{
+ if (ctx->initial_slot_status)
+ g_array_unref (ctx->initial_slot_status);
+ if (ctx->final_slot_status)
+ g_array_unref (ctx->final_slot_status);
+ g_assert (ctx->load_sim_slots_indication_id == 0);
+ g_assert (ctx->final_slot_status_timeout_id == 0);
g_clear_object (&ctx->current_sim);
g_list_free_full (ctx->sorted_sims, (GDestroyNotify)g_object_unref);
g_clear_pointer (&ctx->sim_slots, g_ptr_array_unref);
@@ -3220,6 +3254,7 @@
return FALSE;
ctx = g_task_get_task_data (G_TASK (res));
+
if (sim_slots)
*sim_slots = g_steal_pointer (&ctx->sim_slots);
if (primary_sim_slot)
@@ -3227,6 +3262,93 @@
return TRUE;
}
+/* Compares arrays of QmiPhysicalSlotStatusSlot */
+static gboolean
+compare_slot_status (GArray *slot_status1,
+ GArray *slot_status2)
+{
+ guint i;
+ guint j;
+
+ if (!slot_status1 && !slot_status2)
+ return TRUE;
+ if (!slot_status1 || !slot_status2 || slot_status1->len != slot_status2->len)
+ return FALSE;
+ for (i = 0; i < slot_status1->len; i++) {
+ /* Compare slot at index i from slot_status1 and slot_status2 */
+ QmiPhysicalSlotStatusSlot *slot_a;
+ QmiPhysicalSlotStatusSlot *slot_b;
+
+ slot_a = &g_array_index (slot_status1, QmiPhysicalSlotStatusSlot, i);
+ slot_b = &g_array_index (slot_status2, QmiPhysicalSlotStatusSlot, i);
+ if (slot_a->physical_card_status != slot_b->physical_card_status)
+ return FALSE;
+ if (slot_a->physical_slot_status != slot_b->physical_slot_status)
+ return FALSE;
+ if (slot_a->iccid->len != slot_b->iccid->len)
+ return FALSE;
+
+ for (j = 0; j < slot_a->iccid->len; j++) {
+ if (g_array_index (slot_a->iccid, guint8, j) != g_array_index (slot_b->iccid, guint8, j))
+ return FALSE;
+ }
+ }
+ return TRUE;
+}
+
+static gboolean
+hotswap_while_loading_slots (GTask *task)
+{
+ MMIfaceModem *self;
+
+ self = g_task_get_source_object (task);
+
+ mm_obj_dbg (self,
+ "Slot status before loading sim slots is different from slot status after loading sim slots, "
+ "assuming hotswap");
+ clear_load_sim_slot_callbacks (task);
+ g_task_return_new_error (task,
+ MM_CORE_ERROR,
+ MM_CORE_ERROR_ABORTED,
+ "Timed out waiting for final slot status to match initial slot status");
+ g_object_unref (task);
+
+ mm_base_modem_process_sim_event (MM_BASE_MODEM (self));
+ return G_SOURCE_REMOVE;
+}
+
+#define FINAL_SLOT_STATUS_TIMEOUT 3
+
+static void
+check_final_slot_status (GTask *task)
+{
+ LoadSimSlotsContext *ctx;
+ MMIfaceModem *self;
+
+ self = g_task_get_source_object (task);
+ ctx = g_task_get_task_data (task);
+
+ /* We may never receive a slot status indication, which means that
+ * no slot switch was performed while loading sim slots */
+ if (!ctx->final_slot_status || compare_slot_status (ctx->initial_slot_status, ctx->final_slot_status)) {
+ mm_obj_dbg (self, "Final slot status matches initial slot status");
+ clear_load_sim_slot_callbacks (task);
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
+ return;
+ }
+
+ if (!ctx->final_slot_status_timeout_id) {
+ /* Setup a timeout before which final and initial slot status should match */
+ mm_obj_dbg (self,
+ "Final slot status does not match initial slot status. "
+ "Waiting for final slot status indication...");
+ ctx->final_slot_status_timeout_id = g_timeout_add_seconds (FINAL_SLOT_STATUS_TIMEOUT,
+ (GSourceFunc) hotswap_while_loading_slots,
+ self);
+ }
+}
+
static void
active_slot_switch_ready (QmiClientUim *client,
GAsyncResult *res,
@@ -3244,10 +3366,13 @@
if ((!output || !qmi_message_uim_switch_slot_output_get_result (output, &error)) &&
!g_error_matches (error, QMI_PROTOCOL_ERROR, QMI_PROTOCOL_ERROR_NO_EFFECT)) {
mm_obj_err (self, "couldn't switch to original slot %u", ctx->active_slot_number);
+ clear_load_sim_slot_callbacks (task);
g_task_return_error (task, g_steal_pointer (&error));
- } else
- g_task_return_boolean (task, TRUE);
- g_object_unref (task);
+ g_object_unref (task);
+ return;
+ }
+
+ check_final_slot_status (task);
}
static void
@@ -3262,8 +3387,10 @@
/* If we're already in the original active SIM slot, nothing else to do */
if (ctx->current_slot_number == ctx->active_slot_number) {
- g_task_return_boolean (task, TRUE);
- g_object_unref (task);
+ mm_obj_dbg (self,
+ "Already on the original active SIM at slot %u. "
+ "No more slot switches necessary", ctx->active_slot_number);
+ check_final_slot_status (task);
return;
}
@@ -3379,6 +3506,35 @@
}
static void
+load_sim_slots_indication_cb (QmiClientUim *client,
+ QmiIndicationUimSlotStatusOutput *output,
+ GTask *task)
+{
+ g_autoptr(GError) error = NULL;
+ LoadSimSlotsContext *ctx;
+ MMIfaceModem *self;
+ GArray *physical_slots = NULL;
+
+ self = g_task_get_source_object (task);
+ ctx = g_task_get_task_data (task);
+
+ mm_obj_dbg (self, "received slot status indication while loading slots");
+ if (!qmi_indication_uim_slot_status_output_get_physical_slot_status (output,
+ &physical_slots,
+ &error)) {
+ mm_obj_warn (self, "could not process slot status indication: %s", error->message);
+ return;
+ }
+
+ g_clear_object (&ctx->final_slot_status);
+ ctx->final_slot_status = g_array_ref (physical_slots);
+
+ /* We are awaiting the final slot status before we finish the load_sim_slots task */
+ if (ctx->final_slot_status_timeout_id)
+ check_final_slot_status (task);
+}
+
+static void
uim_get_slot_status_ready (QmiClientUim *client,
GAsyncResult *res,
GTask *task)
@@ -3386,6 +3542,7 @@
g_autoptr(QmiMessageUimGetSlotStatusOutput) output = NULL;
LoadSimSlotsContext *ctx;
MMIfaceModem *self;
+ Private *priv;
GError *error = NULL;
GArray *physical_slots = NULL;
GArray *ext_information = NULL;
@@ -3394,6 +3551,7 @@
self = g_task_get_source_object (task);
ctx = g_task_get_task_data (task);
+ priv = get_private (MM_SHARED_QMI (self));
output = qmi_client_uim_get_slot_status_finish (client, res, &error);
if (!output ||
@@ -3404,6 +3562,11 @@
return;
}
+ /* Store the slot status before loading all sim slots.
+ * We know we are done loading sim slots when the final slot status
+ * indication is the same as initial slot status */
+ ctx->initial_slot_status = g_array_ref (physical_slots);
+
/* It's fine if we don't have EID information, but it should be well-formed if present. If it's malformed,
* there is probably a modem firmware bug. */
if (qmi_message_uim_get_slot_status_output_get_physical_slot_information (output, &ext_information, NULL) &&
@@ -3491,6 +3654,17 @@
}
g_assert_cmpuint (ctx->sim_slots->len, ==, physical_slots->len);
+ /* Block hotswap on slot_status_indications since
+ * we will be switching slots while loading them. */
+ if (priv->uim_slot_status_indication_id)
+ g_signal_handler_block (client, priv->uim_slot_status_indication_id);
+
+ /* Monitor slot status indications while we are loading slots. Once we are done loading slots,
+ * check that no hotswap occurred. */
+ ctx->load_sim_slots_indication_id = g_signal_connect (priv->uim_client,
+ "slot-status",
+ G_CALLBACK (load_sim_slots_indication_cb),
+ task);
/* Now, iterate over all the SIMs, we'll attempt to load info from them by
* quickly switching over to them, leaving the active SIM to the end */
load_next_sim_info (task);
@@ -3780,7 +3954,6 @@
MMSharedQmi *self)
{
GArray *physical_slots = NULL;
- guint i;
g_autoptr(GError) error = NULL;
mm_obj_dbg (self, "received slot status indication");
@@ -3792,27 +3965,13 @@
return;
}
- for (i = 0; i < physical_slots->len; i++) {
- QmiPhysicalSlotStatusSlot *slot_status;
-
- slot_status = &g_array_index (physical_slots, QmiPhysicalSlotStatusSlot, i);
-
- /* We only care about active slot changes */
- if (slot_status->physical_slot_status == QMI_UIM_SLOT_STATE_ACTIVE) {
- g_autofree gchar *iccid = NULL;
-
- if (slot_status->iccid && slot_status->iccid->len > 0) {
- iccid = mm_bcd_to_string ((const guint8 *) slot_status->iccid->data, slot_status->iccid->len,
- TRUE /* low_nybble_first */);
- }
-
- mm_iface_modem_check_for_sim_swap (MM_IFACE_MODEM (self),
- i + 1, /* Slot index */
- iccid,
- NULL,
- NULL);
- }
- }
+ /* A slot status indication means that
+ * 1) The physical slot to logical slot mapping has changed as a
+ * result of switching the slot. or,
+ * 2) A card has been removed from, or inserted to, the physical slot. or,
+ * 3) A physical slot is powered up or down. or,
+ * In all these cases, we must reprobe the modem to keep SIM objects updated */
+ mm_base_modem_process_sim_event (MM_BASE_MODEM (self));
}
static void
diff --git a/src/mm-sms-part-3gpp.c b/src/mm-sms-part-3gpp.c
index c18aaa7..fbf8e11 100644
--- a/src/mm-sms-part-3gpp.c
+++ b/src/mm-sms-part-3gpp.c
@@ -120,23 +120,26 @@
}
/* len is in semi-octets */
-static char *
-sms_decode_address (const guint8 *address, int len)
+static gchar *
+sms_decode_address (const guint8 *address,
+ gint len,
+ GError **error)
{
guint8 addrtype, addrplan;
- char *utf8;
+ gchar *utf8;
addrtype = address[0] & SMS_NUMBER_TYPE_MASK;
addrplan = address[0] & SMS_NUMBER_PLAN_MASK;
address++;
if (addrtype == SMS_NUMBER_TYPE_ALPHA) {
- guint8 *unpacked;
- guint32 unpacked_len;
+ g_autoptr(GByteArray) unpacked_array = NULL;
+ guint8 *unpacked = NULL;
+ guint32 unpacked_len;
+
unpacked = mm_charset_gsm_unpack (address, (len * 4) / 7, 0, &unpacked_len);
- utf8 = (char *)mm_charset_gsm_unpacked_to_utf8 (unpacked,
- unpacked_len);
- g_free (unpacked);
+ unpacked_array = g_byte_array_new_take (unpacked, unpacked_len);
+ utf8 = mm_modem_charset_bytearray_to_utf8 (unpacked_array, MM_MODEM_CHARSET_GSM, FALSE, error);
} else if (addrtype == SMS_NUMBER_TYPE_INTL &&
addrplan == SMS_NUMBER_PLAN_TELEPHONE) {
/* International telphone number, format as "+1234567890" */
@@ -239,41 +242,44 @@
return scheme;
}
-static char *
-sms_decode_text (const guint8 *text,
- int len,
- MMSmsEncoding encoding,
- int bit_offset,
- gpointer log_object)
+static gchar *
+sms_decode_text (const guint8 *text,
+ int len,
+ MMSmsEncoding encoding,
+ int bit_offset,
+ gpointer log_object,
+ GError **error)
{
- gchar *utf8;
-
if (encoding == MM_SMS_ENCODING_GSM7) {
- g_autofree guint8 *unpacked = NULL;
- guint32 unpacked_len;
+ g_autoptr(GByteArray) unpacked_array = NULL;
+ guint8 *unpacked = NULL;
+ guint32 unpacked_len;
+ gchar *utf8;
- mm_obj_dbg (log_object, "converting SMS part text from GSM-7 to UTF-8...");
unpacked = mm_charset_gsm_unpack ((const guint8 *) text, len, bit_offset, &unpacked_len);
- utf8 = (char *) mm_charset_gsm_unpacked_to_utf8 (unpacked, unpacked_len);
- mm_obj_dbg (log_object, " got UTF-8 text: '%s'", utf8);
- } else if (encoding == MM_SMS_ENCODING_UCS2) {
- g_autoptr(GByteArray) bytearray = NULL;
-
- mm_obj_dbg (log_object, "converting SMS part text from UTF-16BE to UTF-8...");
- bytearray = g_byte_array_append (g_byte_array_sized_new (len), (const guint8 *)text, len);
- /* Always assume UTF-16 instead of UCS-2! */
- utf8 = mm_modem_charset_byte_array_to_utf8 (bytearray, MM_MODEM_CHARSET_UTF16);
- if (!utf8) {
- mm_obj_warn (log_object, "couldn't convert SMS part contents from UTF-16BE to UTF-8: not decoding any text");
- utf8 = g_strdup ("");
- } else
- mm_obj_dbg (log_object, " got UTF-8 text: '%s'", utf8);
- } else {
- mm_obj_warn (log_object, "unexpected encoding: %s; not decoding any text", mm_sms_encoding_get_string (encoding));
- utf8 = g_strdup ("");
+ unpacked_array = g_byte_array_new_take (unpacked, unpacked_len);
+ utf8 = mm_modem_charset_bytearray_to_utf8 (unpacked_array, MM_MODEM_CHARSET_GSM, FALSE, error);
+ if (utf8)
+ mm_obj_dbg (log_object, "converted SMS part text from GSM-7 to UTF-8: %s", utf8);
+ return utf8;
}
- return utf8;
+ /* Always assume UTF-16 instead of UCS-2! */
+ if (encoding == MM_SMS_ENCODING_UCS2) {
+ g_autoptr(GByteArray) bytearray = NULL;
+ gchar *utf8;
+
+ bytearray = g_byte_array_append (g_byte_array_sized_new (len), (const guint8 *)text, len);
+ utf8 = mm_modem_charset_bytearray_to_utf8 (bytearray, MM_MODEM_CHARSET_UTF16, FALSE, error);
+ if (utf8)
+ mm_obj_dbg (log_object, "converted SMS part text from UTF-16BE to UTF-8: %s", utf8);
+ return utf8;
+ }
+
+ g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED,
+ "Couldn't convert SMS part contents from %s to UTF-8",
+ mm_sms_encoding_get_string (encoding));
+ return NULL;
}
static guint
@@ -339,24 +345,17 @@
gpointer log_object,
GError **error)
{
- gsize pdu_len;
- guint8 *pdu;
- MMSmsPart *part;
+ g_autofree guint8 *pdu = NULL;
+ gsize pdu_len;
/* Convert PDU from hex to binary */
- pdu = (guint8 *) mm_utils_hexstr2bin (hexpdu, &pdu_len);
+ pdu = mm_utils_hexstr2bin (hexpdu, -1, &pdu_len, error);
if (!pdu) {
- g_set_error_literal (error,
- MM_CORE_ERROR,
- MM_CORE_ERROR_FAILED,
- "Couldn't convert 3GPP PDU from hex to binary");
+ g_prefix_error (error, "Couldn't convert 3GPP PDU from hex to binary: ");
return NULL;
}
- part = mm_sms_part_3gpp_new_from_binary_pdu (index, pdu, pdu_len, log_object, error);
- g_free (pdu);
-
- return part;
+ return mm_sms_part_3gpp_new_from_binary_pdu (index, pdu, pdu_len, log_object, error);
}
MMSmsPart *
@@ -380,6 +379,7 @@
guint tp_dcs_offset = 0;
guint tp_user_data_len_offset = 0;
MMSmsEncoding user_data_encoding = MM_SMS_ENCODING_UNKNOWN;
+ gchar *address;
/* Create the new MMSmsPart */
sms_part = mm_sms_part_new (index, MM_SMS_PDU_TYPE_UNKNOWN);
@@ -412,8 +412,13 @@
if (smsc_addr_size_bytes > 0) {
PDU_SIZE_CHECK (offset + smsc_addr_size_bytes, "cannot read SMSC address");
/* SMSC may not be given in DELIVER PDUs */
- mm_sms_part_take_smsc (sms_part,
- sms_decode_address (&pdu[1], 2 * (smsc_addr_size_bytes - 1)));
+ address = sms_decode_address (&pdu[1], 2 * (smsc_addr_size_bytes - 1), error);
+ if (!address) {
+ g_prefix_error (error, "Couldn't read SMSC address: ");
+ mm_sms_part_free (sms_part);
+ return NULL;
+ }
+ mm_sms_part_take_smsc (sms_part, g_steal_pointer (&address));
mm_obj_dbg (log_object, " SMSC address parsed: '%s'", mm_sms_part_get_smsc (sms_part));
offset += smsc_addr_size_bytes;
} else
@@ -485,9 +490,13 @@
tp_addr_size_bytes = (tp_addr_size_digits + 1) >> 1;
PDU_SIZE_CHECK (offset + tp_addr_size_bytes, "cannot read number");
- mm_sms_part_take_number (sms_part,
- sms_decode_address (&pdu[offset],
- tp_addr_size_digits));
+ address = sms_decode_address (&pdu[offset], tp_addr_size_digits, error);
+ if (!address) {
+ g_prefix_error (error, "Couldn't read address: ");
+ mm_sms_part_free (sms_part);
+ return NULL;
+ }
+ mm_sms_part_take_number (sms_part, g_steal_pointer (&address));
mm_obj_dbg (log_object, " number parsed: %s", mm_sms_part_get_number (sms_part));
offset += (1 + tp_addr_size_bytes); /* +1 due to the Type of Address byte */
@@ -716,17 +725,24 @@
switch (user_data_encoding) {
case MM_SMS_ENCODING_GSM7:
case MM_SMS_ENCODING_UCS2:
- /* Otherwise if it's 7-bit or UCS2 we can decode it */
- mm_obj_dbg (log_object, "decoding SMS text with %u elements", tp_user_data_size_elements);
- mm_sms_part_take_text (sms_part,
- sms_decode_text (&pdu[tp_user_data_offset],
- tp_user_data_size_elements,
- user_data_encoding,
- bit_offset,
- log_object));
- g_warn_if_fail (mm_sms_part_get_text (sms_part) != NULL);
- break;
+ {
+ gchar *text;
+ /* Otherwise if it's 7-bit or UCS2 we can decode it */
+ mm_obj_dbg (log_object, "decoding SMS text with %u elements", tp_user_data_size_elements);
+ text = sms_decode_text (&pdu[tp_user_data_offset],
+ tp_user_data_size_elements,
+ user_data_encoding,
+ bit_offset,
+ log_object,
+ error);
+ if (!text) {
+ mm_sms_part_free (sms_part);
+ return NULL;
+ }
+ mm_sms_part_take_text (sms_part, text);
+ break;
+ }
case MM_SMS_ENCODING_8BIT:
case MM_SMS_ENCODING_UNKNOWN:
default:
@@ -969,12 +985,15 @@
}
if (encoding == MM_SMS_ENCODING_GSM7) {
- guint8 *unpacked, *packed;
- guint32 unlen = 0, packlen = 0;
+ g_autoptr(GByteArray) unpacked = NULL;
+ g_autofree guint8 *packed = NULL;
+ guint32 packlen = 0;
- unpacked = mm_charset_utf8_to_unpacked_gsm (mm_sms_part_get_text (part), &unlen);
- if (!unpacked || unlen == 0) {
- g_free (unpacked);
+ unpacked = mm_modem_charset_bytearray_from_utf8 (mm_sms_part_get_text (part), MM_MODEM_CHARSET_GSM, FALSE, error);
+ if (!unpacked)
+ goto error;
+
+ if (unpacked->len == 0) {
g_set_error_literal (error,
MM_MESSAGE_ERROR,
MM_MESSAGE_ERROR_INVALID_PDU_PARAMETER,
@@ -985,15 +1004,13 @@
/* Set real data length, in septets
* If we had UDH, add 7 septets
*/
- *udl_ptr = mm_sms_part_get_concat_sequence (part) ? (7 + unlen) : unlen;
+ *udl_ptr = mm_sms_part_get_concat_sequence (part) ? (7 + unpacked->len) : unpacked->len;
mm_obj_dbg (log_object, " user data length is %u septets (%s UDH)",
*udl_ptr,
mm_sms_part_get_concat_sequence (part) ? "with" : "without");
- packed = mm_charset_gsm_pack (unpacked, unlen, shift, &packlen);
- g_free (unpacked);
+ packed = mm_charset_gsm_pack (unpacked->data, unpacked->len, shift, &packlen);
if (!packed || packlen == 0) {
- g_free (packed);
g_set_error_literal (error,
MM_MESSAGE_ERROR,
MM_MESSAGE_ERROR_INVALID_PDU_PARAMETER,
@@ -1002,16 +1019,14 @@
}
memcpy (&pdu[offset], packed, packlen);
- g_free (packed);
offset += packlen;
} else if (encoding == MM_SMS_ENCODING_UCS2) {
g_autoptr(GByteArray) array = NULL;
g_autoptr(GError) inner_error = NULL;
- /* Try to guess a good value for the array */
- array = g_byte_array_sized_new (strlen (mm_sms_part_get_text (part)) * 2);
/* Always assume UTF-16 instead of UCS-2! */
- if (!mm_modem_charset_byte_array_append (array, mm_sms_part_get_text (part), FALSE, MM_MODEM_CHARSET_UTF16, &inner_error)) {
+ array = mm_modem_charset_bytearray_from_utf8 (mm_sms_part_get_text (part), MM_MODEM_CHARSET_UTF16, FALSE, &inner_error);
+ if (!array) {
g_set_error (error,
MM_MESSAGE_ERROR,
MM_MESSAGE_ERROR_INVALID_PDU_PARAMETER,
diff --git a/src/mm-sms-part-cdma.c b/src/mm-sms-part-cdma.c
index e08193c..d79a0f9 100644
--- a/src/mm-sms-part-cdma.c
+++ b/src/mm-sms-part-cdma.c
@@ -317,24 +317,17 @@
gpointer log_object,
GError **error)
{
- gsize pdu_len;
- guint8 *pdu;
- MMSmsPart *part;
+ g_autofree guint8 *pdu = NULL;
+ gsize pdu_len;
/* Convert PDU from hex to binary */
- pdu = (guint8 *) mm_utils_hexstr2bin (hexpdu, &pdu_len);
+ pdu = mm_utils_hexstr2bin (hexpdu, -1, &pdu_len, error);
if (!pdu) {
- g_set_error_literal (error,
- MM_CORE_ERROR,
- MM_CORE_ERROR_FAILED,
- "Couldn't convert CDMA PDU from hex to binary");
+ g_prefix_error (error, "Couldn't convert CDMA PDU from hex to binary: ");
return NULL;
}
- part = mm_sms_part_cdma_new_from_binary_pdu (index, pdu, pdu_len, log_object, error);
- g_free (pdu);
-
- return part;
+ return mm_sms_part_cdma_new_from_binary_pdu (index, pdu, pdu_len, log_object, error);
}
struct Parameter {
@@ -1375,65 +1368,49 @@
return TRUE;
}
-static void
+static GByteArray *
decide_best_encoding (const gchar *text,
gpointer log_object,
- GByteArray **out,
guint *num_fields,
guint *num_bits_per_field,
- Encoding *encoding)
+ Encoding *encoding,
+ GError **error)
{
- guint ascii_unsupported = 0;
- guint i;
- guint len;
- g_autoptr(GError) error = NULL;
+ g_autoptr(GByteArray) barray = NULL;
+ MMModemCharset target_charset = MM_MODEM_CHARSET_UNKNOWN;
+ guint len;
len = strlen (text);
- /* Check if we can do ASCII-7 */
- for (i = 0; i < len; i++) {
- if (text[i] & 0x80) {
- ascii_unsupported++;
- break;
- }
+ if (mm_charset_can_convert_to (text, MM_MODEM_CHARSET_IRA))
+ target_charset = MM_MODEM_CHARSET_IRA;
+ else if (mm_charset_can_convert_to (text, MM_MODEM_CHARSET_8859_1))
+ target_charset = MM_MODEM_CHARSET_8859_1;
+ else
+ target_charset = MM_MODEM_CHARSET_UCS2;
+
+ barray = mm_modem_charset_bytearray_from_utf8 (text, target_charset, FALSE, error);
+ if (!barray) {
+ g_prefix_error (error, "Couldn't decide best encoding: ");
+ return NULL;
}
- /* If ASCII-7 already supported, done we are */
- if (!ascii_unsupported) {
- *out = g_byte_array_sized_new (len);
- g_byte_array_append (*out, (const guint8 *)text, len);
+ if (target_charset == MM_MODEM_CHARSET_IRA) {
*num_fields = len;
*num_bits_per_field = 7;
*encoding = ENCODING_ASCII_7BIT;
- return;
- }
-
- /* Check if we can do Latin encoding */
- if (mm_charset_can_convert_to (text, MM_MODEM_CHARSET_8859_1)) {
- *out = g_byte_array_sized_new (len);
- if (!mm_modem_charset_byte_array_append (*out,
- text,
- FALSE,
- MM_MODEM_CHARSET_8859_1,
- &error))
- mm_obj_warn (log_object, "failed to convert to latin encoding: %s", error->message);
- *num_fields = (*out)->len;
+ } else if (target_charset == MM_MODEM_CHARSET_8859_1) {
+ *num_fields = barray->len;
*num_bits_per_field = 8;
*encoding = ENCODING_LATIN;
- return;
- }
+ } else if (target_charset == MM_MODEM_CHARSET_UCS2) {
+ *num_fields = barray->len / 2;
+ *num_bits_per_field = 16;
+ *encoding = ENCODING_UNICODE;
+ } else
+ g_assert_not_reached ();
- /* If no Latin and no ASCII, default to UTF-16 */
- *out = g_byte_array_sized_new (len * 2);
- if (!mm_modem_charset_byte_array_append (*out,
- text,
- FALSE,
- MM_MODEM_CHARSET_UCS2,
- &error))
- mm_obj_warn (log_object, "failed to convert to UTF-16 encoding: %s", error->message);
- *num_fields = (*out)->len / 2;
- *num_bits_per_field = 16;
- *encoding = ENCODING_UNICODE;
+ return g_steal_pointer (&barray);
}
static gboolean
@@ -1477,12 +1454,14 @@
/* Text or Data */
if (text) {
- decide_best_encoding (text,
- log_object,
- &converted,
- &num_fields,
- &num_bits_per_field,
- &encoding);
+ converted = decide_best_encoding (text,
+ log_object,
+ &num_fields,
+ &num_bits_per_field,
+ &encoding,
+ error);
+ if (!converted)
+ return FALSE;
aux = (const GByteArray *)converted;
} else {
aux = data;
diff --git a/src/mm-utils.c b/src/mm-utils.c
new file mode 100644
index 0000000..95fbb55
--- /dev/null
+++ b/src/mm-utils.c
@@ -0,0 +1,47 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This program 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 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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:
+ *
+ * Singleton support imported from NetworkManager.
+ * (C) Copyright 2014 Red Hat, Inc.
+ *
+ * GPtrArray lookup with GEqualFunc imported from GLib 2.48
+ */
+
+#include "mm-utils.h"
+
+#if !GLIB_CHECK_VERSION(2,54,0)
+
+gboolean
+mm_ptr_array_find_with_equal_func (GPtrArray *haystack,
+ gconstpointer needle,
+ GEqualFunc equal_func,
+ guint *index_)
+{
+ guint i;
+
+ g_return_val_if_fail (haystack != NULL, FALSE);
+
+ if (equal_func == NULL)
+ equal_func = g_direct_equal;
+
+ for (i = 0; i < haystack->len; i++) {
+ if (equal_func (g_ptr_array_index (haystack, i), needle)) {
+ if (index_ != NULL)
+ *index_ = i;
+ return TRUE;
+ }
+ }
+
+ return FALSE;
+}
+
+#endif
diff --git a/src/mm-utils.h b/src/mm-utils.h
index cdb123c..613205a 100644
--- a/src/mm-utils.h
+++ b/src/mm-utils.h
@@ -12,6 +12,8 @@
*
* Singleton support imported from NetworkManager.
* (C) Copyright 2014 Red Hat, Inc.
+ *
+ * GPtrArray lookup with GEqualFunc imported from GLib 2.48
*/
#ifndef MM_UTILS_H
@@ -78,4 +80,16 @@
} \
MM_DEFINE_SINGLETON_DESTRUCTOR(TYPE)
+
+#if !GLIB_CHECK_VERSION(2,54,0)
+
+/* Pointer Array lookup with a GEqualFunc, imported from GLib 2.54 */
+#define g_ptr_array_find_with_equal_func mm_ptr_array_find_with_equal_func
+gboolean mm_ptr_array_find_with_equal_func (GPtrArray *haystack,
+ gconstpointer needle,
+ GEqualFunc equal_func,
+ guint *index_);
+
+#endif
+
#endif /* MM_UTILS_H */
diff --git a/src/tests/test-charsets.c b/src/tests/test-charsets.c
index a15e033..8735fd2 100644
--- a/src/tests/test-charsets.c
+++ b/src/tests/test-charsets.c
@@ -23,23 +23,24 @@
static void
common_test_gsm7 (const gchar *in_utf8)
{
- guint32 unpacked_gsm_len = 0;
guint32 packed_gsm_len = 0;
guint32 unpacked_gsm_len_2 = 0;
- g_autofree guint8 *unpacked_gsm = NULL;
+ g_autoptr(GByteArray) unpacked_gsm = NULL;
g_autofree guint8 *packed_gsm = NULL;
- g_autofree guint8 *unpacked_gsm_2 = NULL;
+ guint8 *unpacked_gsm_2 = NULL;
+ g_autoptr(GByteArray) unpacked_gsm_2_array = NULL;
g_autofree gchar *built_utf8 = NULL;
+ g_autoptr(GError) error = NULL;
/* Convert to GSM */
- unpacked_gsm = mm_charset_utf8_to_unpacked_gsm (in_utf8, &unpacked_gsm_len);
+ unpacked_gsm = mm_modem_charset_bytearray_from_utf8 (in_utf8, MM_MODEM_CHARSET_GSM, FALSE, &error);
g_assert_nonnull (unpacked_gsm);
- g_assert_cmpuint (unpacked_gsm_len, >, 0);
+ g_assert_no_error (error);
/* Pack */
- packed_gsm = mm_charset_gsm_pack (unpacked_gsm, unpacked_gsm_len, 0, &packed_gsm_len);
+ packed_gsm = mm_charset_gsm_pack (unpacked_gsm->data, unpacked_gsm->len, 0, &packed_gsm_len);
g_assert_nonnull (packed_gsm);
- g_assert_cmpuint (packed_gsm_len, <=, unpacked_gsm_len);
+ g_assert_cmpuint (packed_gsm_len, <=, unpacked_gsm->len);
#if 0
{
@@ -56,10 +57,12 @@
/* Unpack */
unpacked_gsm_2 = mm_charset_gsm_unpack (packed_gsm, packed_gsm_len * 8 / 7, 0, &unpacked_gsm_len_2);
g_assert_nonnull (unpacked_gsm_2);
+ unpacked_gsm_2_array = g_byte_array_new_take (unpacked_gsm_2, unpacked_gsm_len_2);
/* And back to UTF-8 */
- built_utf8 = (gchar *) mm_charset_gsm_unpacked_to_utf8 (unpacked_gsm_2, unpacked_gsm_len_2);
+ built_utf8 = mm_modem_charset_bytearray_to_utf8 (unpacked_gsm_2_array, MM_MODEM_CHARSET_GSM, FALSE, &error);
g_assert_nonnull (built_utf8);
+ g_assert_no_error (error);
g_assert_cmpstr (built_utf8, ==, in_utf8);
}
@@ -314,53 +317,71 @@
}
static void
-test_take_convert_ucs2_hex_utf8 (void)
+test_str_ucs2_to_from_utf8 (void)
{
- gchar *src, *converted, *utf8;
+ const gchar *src = "0054002D004D006F00620069006C0065";
+ g_autofree gchar *utf8 = NULL;
+ g_autofree gchar *dst = NULL;
+ g_autoptr(GError) error = NULL;
- /* Ensure hex-encoded UCS-2 works */
- src = g_strdup ("0054002d004d006f00620069006c0065");
- converted = mm_charset_take_and_convert_to_utf8 (src, MM_MODEM_CHARSET_UCS2);
- g_assert_cmpstr (converted, ==, "T-Mobile");
- utf8 = mm_utf8_take_and_convert_to_charset (converted, MM_MODEM_CHARSET_UCS2);
- g_assert_cmpstr (utf8, ==, "0054002D004D006F00620069006C0065");
- g_free (utf8);
-}
-
-static void
-test_take_convert_ucs2_bad_ascii (void)
-{
- gchar *src, *converted;
-
- /* Test that something mostly ASCII returns most of the original string */
- src = g_strdup ("Orange\241");
- converted = mm_charset_take_and_convert_to_utf8 (src, MM_MODEM_CHARSET_UCS2);
- g_assert_cmpstr (converted, ==, "Orange");
- g_free (converted);
-}
-
-static void
-test_take_convert_ucs2_bad_ascii2 (void)
-{
- gchar *src, *converted;
-
- /* Ensure something completely screwed up doesn't crash */
- src = g_strdup ("\241\255\254\250\244\234");
- converted = mm_charset_take_and_convert_to_utf8 (src, MM_MODEM_CHARSET_UCS2);
- g_assert (converted == NULL);
-}
-
-static void
-test_take_convert_gsm_utf8 (void)
-{
- gchar *src, *converted, *utf8;
-
- src = g_strdup ("T-Mobile");
- converted = mm_charset_take_and_convert_to_utf8 (src, MM_MODEM_CHARSET_GSM);
- g_assert_cmpstr (converted, ==, "T-Mobile");
- utf8 = mm_utf8_take_and_convert_to_charset (converted, MM_MODEM_CHARSET_GSM);
+ utf8 = mm_modem_charset_str_to_utf8 (src, -1, MM_MODEM_CHARSET_UCS2, FALSE, &error);
+ g_assert_no_error (error);
g_assert_cmpstr (utf8, ==, "T-Mobile");
- g_free (utf8);
+
+ dst = mm_modem_charset_str_from_utf8 (utf8, MM_MODEM_CHARSET_UCS2, FALSE, &error);
+ g_assert_no_error (error);
+ g_assert_cmpstr (dst, ==, src);
+}
+
+static void
+test_str_gsm_to_from_utf8 (void)
+{
+ const gchar *src = "T-Mobile";
+ g_autofree gchar *utf8 = NULL;
+ g_autofree gchar *dst = NULL;
+ g_autoptr(GError) error = NULL;
+
+ /* Note: as long as the GSM string doesn't contain the '@' character, str_to_utf8()
+ * and str_from_utf8() can safely be used */
+
+ utf8 = mm_modem_charset_str_to_utf8 (src, -1, MM_MODEM_CHARSET_GSM, FALSE, &error);
+ g_assert_no_error (error);
+ g_assert_cmpstr (utf8, ==, src);
+
+ dst = mm_modem_charset_str_from_utf8 (utf8, MM_MODEM_CHARSET_GSM, FALSE, &error);
+ g_assert_no_error (error);
+ g_assert_cmpstr (dst, ==, src);
+}
+
+static void
+test_str_gsm_to_from_utf8_with_at (void)
+{
+ /* The NULs are '@' chars, except for the trailing one which is always taken as end-of-string */
+ const gchar src[] = { 'T', '-', 'M', 0x00, 'o', 'b', 'i', 0x00, 'l', 'e', 0x00 };
+ const gchar *utf8_expected = "T-M@obi@le";
+ const gchar *src_translit = "T-M?obi?le";
+ g_autofree gchar *utf8 = NULL;
+ g_autofree gchar *dst = NULL;
+ g_autoptr(GError) error = NULL;
+
+ /* Note: as long as the GSM string doesn't contain the '@' character, str_to_utf8()
+ * and str_from_utf8() can safely be used */
+
+ utf8 = mm_modem_charset_str_to_utf8 (src, G_N_ELEMENTS (src), MM_MODEM_CHARSET_GSM, FALSE, &error);
+ g_assert_no_error (error);
+ g_assert_cmpstr (utf8, ==, utf8_expected);
+
+ /* if charset conversion from UTF-8 contains '@' chars, running without transliteration
+ * will return an error */
+ dst = mm_modem_charset_str_from_utf8 (utf8, MM_MODEM_CHARSET_GSM, FALSE, &error);
+ g_assert_nonnull (error);
+ g_assert_null (dst);
+ g_clear_error (&error);
+
+ /* with transliteration, '@'->'?' */
+ dst = mm_modem_charset_str_from_utf8 (utf8, MM_MODEM_CHARSET_GSM, TRUE, &error);
+ g_assert_no_error (error);
+ g_assert_cmpstr (dst, ==, src_translit);
}
struct charset_can_convert_to_test_s {
@@ -444,10 +465,9 @@
g_test_add_func ("/MM/charsets/gsm7/pack/last-septet-alone", test_gsm7_pack_last_septet_alone);
g_test_add_func ("/MM/charsets/gsm7/pack/7-chars-offset", test_gsm7_pack_7_chars_offset);
- g_test_add_func ("/MM/charsets/take-convert/ucs2/hex", test_take_convert_ucs2_hex_utf8);
- g_test_add_func ("/MM/charsets/take-convert/ucs2/bad-ascii", test_take_convert_ucs2_bad_ascii);
- g_test_add_func ("/MM/charsets/take-convert/ucs2/bad-ascii-2", test_take_convert_ucs2_bad_ascii2);
- g_test_add_func ("/MM/charsets/take-convert/gsm", test_take_convert_gsm_utf8);
+ g_test_add_func ("/MM/charsets/str-from-to/ucs2", test_str_ucs2_to_from_utf8);
+ g_test_add_func ("/MM/charsets/str-from-to/gsm", test_str_gsm_to_from_utf8);
+ g_test_add_func ("/MM/charsets/str-from-to/gsm-with-at", test_str_gsm_to_from_utf8_with_at);
g_test_add_func ("/MM/charsets/can-convert-to", test_charset_can_covert_to);
diff --git a/src/tests/test-modem-helpers.c b/src/tests/test-modem-helpers.c
index a6bc72d..9624c52 100644
--- a/src/tests/test-modem-helpers.c
+++ b/src/tests/test-modem-helpers.c
@@ -626,9 +626,10 @@
static void
test_cops_response_mf627a (void *f, gpointer d)
{
+ /* The '@' in this string is ASCII 0x40, and 0x40 is a valid GSM-7 char: '¡' (which is 0xc2,0xa1 in UTF-8) */
const char *reply = "+COPS: (2,\"AT&T@\",\"AT&TD\",\"310410\",0),(3,\"Vstream Wireless\",\"VSTREAM\",\"31026\",0),";
static MM3gppNetworkInfo expected[] = {
- { MM_MODEM_3GPP_NETWORK_AVAILABILITY_CURRENT, (gchar *) "AT&T@", (gchar *) "AT&TD", (gchar *) "310410", MM_MODEM_ACCESS_TECHNOLOGY_GSM },
+ { MM_MODEM_3GPP_NETWORK_AVAILABILITY_CURRENT, (gchar *) "AT&T¡", (gchar *) "AT&TD", (gchar *) "310410", MM_MODEM_ACCESS_TECHNOLOGY_GSM },
{ MM_MODEM_3GPP_NETWORK_AVAILABILITY_FORBIDDEN, (gchar *) "Vstream Wireless", (gchar *) "VSTREAM", (gchar *) "31026", MM_MODEM_ACCESS_TECHNOLOGY_GSM },
};
@@ -638,9 +639,10 @@
static void
test_cops_response_mf627b (void *f, gpointer d)
{
+ /* The '@' in this string is ASCII 0x40, and 0x40 is a valid GSM-7 char: '¡' (which is 0xc2,0xa1 in UTF-8) */
const char *reply = "+COPS: (2,\"AT&Tp\",\"AT&T@\",\"310410\",0),(3,\"\",\"\",\"31026\",0),";
static MM3gppNetworkInfo expected[] = {
- { MM_MODEM_3GPP_NETWORK_AVAILABILITY_CURRENT, (gchar *) "AT&Tp", (gchar *) "AT&T@", (gchar *) "310410", MM_MODEM_ACCESS_TECHNOLOGY_GSM },
+ { MM_MODEM_3GPP_NETWORK_AVAILABILITY_CURRENT, (gchar *) "AT&Tp", (gchar *) "AT&T¡", (gchar *) "310410", MM_MODEM_ACCESS_TECHNOLOGY_GSM },
{ MM_MODEM_3GPP_NETWORK_AVAILABILITY_FORBIDDEN, NULL, NULL, (gchar *) "31026", MM_MODEM_ACCESS_TECHNOLOGY_GSM },
};
@@ -1041,7 +1043,7 @@
gchar *str;
str = g_strdup (t->input);
- mm_3gpp_normalize_operator (&str, t->charset);
+ mm_3gpp_normalize_operator (&str, t->charset, NULL);
if (!t->normalized)
g_assert (!str);
else