[Patch] Adding LTE support for Novatel Wireless devices to Modem Manager.
- From: Jason Glasgow <jglasgow google com>
- To: "networkmanager-list gnome org" <networkmanager-list gnome org>
- Subject: [Patch] Adding LTE support for Novatel Wireless devices to Modem Manager.
- Date: Wed, 14 Sep 2011 16:48:49 -0400
I have updated the patch:
- rebased against the master branch
- uses the new generated ModemManager.h header file
- deleted extraneous spaces
- tested with an E362
- included all the files as a git patch
Can you look at the patch again? Is there anything else that needs to get done before pushing?
-Jason
On Thu, Sep 1, 2011 at 8:09 PM, Vikram Kumar
<vkumar nvtl com> wrote:
Hi Dan,
I would like to follow up with you on the patch submitted by Novatel on July 20th. I still don't see it being incorporated to master or 0.5 version.
Please let me know if I should send a mail directly to you for follow ups, instead of sending to mailing list.
Regards,
Vikram
Hi Dan,
We have modified the code for master branch and attached is the patch for it. I have also attached the files for review.
Regards,
Vikram
-----Original Message-----
From: Dan Williams [mailto:
dcbw redhat com]
Sent: Wednesday, July 06, 2011 9:28 PM
To: Vikram Kumar
Cc:
networkmanager-list gnome org
Subject: Re: [Modem Manager] [Patch] Adding LTE devices support to Modem Manager.
On Sat, 2011-07-02 at 00:16 +0000, Vikram Kumar wrote:
> Dear Network Manager in Gnome.org,
>
> Please find patch for Modem Manager plugin version 0.4 to support LTE
> devices.
>
> List of attachments and explanation:
>
> 1) ModemManager.patch : patch file generated using diff.
>
> 2) ModemManager.tar: source file which has newly added files for
> plugin.
Thanks for sending; I'll review. As noted it probably doesn't apply to the MM_05 branch (or git master); we're also reworking the D-Bus API for git master anyway to be more compatible with multi-mode devices.
But since that rework isn't going to happen for MM_05 we probably want to have some short-term patch like this approach just to get the LTE support there.
Dan
From 1c572640dce2a9adec7ee13fb35866cdd6bda6b7 Mon Sep 17 00:00:00 2001
From: Vikram Kumar <vkumar nvtl com>
Date: Wed, 14 Sep 2011 10:23:26 -0400
Subject: [PATCH] novatel-lte: Add support for novatel LTE modems
Add Lte, Lte.Card and Lte.Network interfaces, update modem manager to
understand LTE modems, and add specific support for the Novatel E362
modem.
---
introspection/Makefile.am | 3 +
introspection/all.xml | 4 +
...org.freedesktop.ModemManager.Modem.Lte.Card.xml | 97 +
....freedesktop.ModemManager.Modem.Lte.Network.xml | 260 ++
.../org.freedesktop.ModemManager.Modem.Lte.xml | 71 +
.../org.freedesktop.ModemManager.Modem.xml | 5 +
org.freedesktop.ModemManager.conf.polkit | 40 +
plugins/Makefile.am | 2 +
plugins/mm-modem-novatel-lte.c | 930 +++++
plugins/mm-modem-novatel-lte.h | 46 +
plugins/mm-plugin-novatel.c | 16 +-
plugins/mm-plugin-novatel.h | 2 +
src/Makefile.am | 15 +
src/mm-charsets.c | 6 +
src/mm-charsets.h | 2 +
src/mm-generic-lte.c | 4412 ++++++++++++++++++++
src/mm-generic-lte.h | 207 +
src/mm-manager.c | 5 +
src/mm-modem-helpers.c | 89 +
src/mm-modem-helpers.h | 27 +
src/mm-modem-lte-card.c | 534 +++
src/mm-modem-lte-card.h | 122 +
src/mm-modem-lte-network.c | 564 +++
src/mm-modem-lte-network.h | 154 +
src/mm-modem-lte.h | 58 +
src/mm-modem.h | 1 +
src/mm-plugin-base.c | 6 +-
src/mm-plugin-base.h | 2 +
test/mm-test.py | 247 +-
29 files changed, 7910 insertions(+), 17 deletions(-)
create mode 100644 introspection/org.freedesktop.ModemManager.Modem.Lte.Card.xml
create mode 100644 introspection/org.freedesktop.ModemManager.Modem.Lte.Network.xml
create mode 100644 introspection/org.freedesktop.ModemManager.Modem.Lte.xml
create mode 100644 plugins/mm-modem-novatel-lte.c
create mode 100644 plugins/mm-modem-novatel-lte.h
mode change 100755 => 100644 plugins/mm-modem-samsung-gsm.c
mode change 100755 => 100644 plugins/mm-modem-samsung-gsm.h
mode change 100755 => 100644 plugins/mm-plugin-samsung.c
mode change 100755 => 100644 plugins/mm-plugin-samsung.h
create mode 100644 src/mm-generic-lte.c
create mode 100644 src/mm-generic-lte.h
create mode 100644 src/mm-modem-lte-card.c
create mode 100644 src/mm-modem-lte-card.h
create mode 100644 src/mm-modem-lte-network.c
create mode 100644 src/mm-modem-lte-network.h
create mode 100644 src/mm-modem-lte.h
diff --git a/introspection/Makefile.am b/introspection/Makefile.am
index 3c7a380..a2c0e79 100644
--- a/introspection/Makefile.am
+++ b/introspection/Makefile.am
@@ -7,10 +7,13 @@ EXTRA_DIST = \
mm-modem-connect-error.xml \
mm-modem-error.xml \
org.freedesktop.ModemManager.Modem.Gsm.xml \
+ org.freedesktop.ModemManager.Modem.Lte.xml \
org.freedesktop.ModemManager.Modem.Gsm.Card.xml \
+ org.freedesktop.ModemManager.Modem.Lte.Card.xml \
org.freedesktop.ModemManager.Modem.Gsm.Contacts.xml \
org.freedesktop.ModemManager.Modem.Gsm.Hso.xml \
org.freedesktop.ModemManager.Modem.Gsm.Network.xml \
+ org.freedesktop.ModemManager.Modem.Lte.Network.xml \
org.freedesktop.ModemManager.Modem.Gsm.SMS.xml \
org.freedesktop.ModemManager.Modem.Simple.xml \
mm-serial-error.xml \
diff --git a/introspection/all.xml b/introspection/all.xml
index c9c1dc5..aac4907 100644
--- a/introspection/all.xml
+++ b/introspection/all.xml
@@ -39,6 +39,10 @@
<xi:include href="org.freedesktop.ModemManager.Modem.Gsm.Ussd.xml"/>
<xi:include href="org.freedesktop.DBus.Properties.xml"/>
+ <xi:include href="org.freedesktop.ModemManager.Modem.Lte.xml"/>
+ <xi:include href="org.freedesktop.ModemManager.Modem.Lte.Card.xml"/>
+ <xi:include href="org.freedesktop.ModemManager.Modem.Lte.Network.xml"/>
+
<xi:include href="mm-serial-error.xml"/>
<xi:include href="mm-modem-error.xml"/>
<xi:include href="mm-modem-connect-error.xml"/>
diff --git a/introspection/org.freedesktop.ModemManager.Modem.Lte.Card.xml b/introspection/org.freedesktop.ModemManager.Modem.Lte.Card.xml
new file mode 100644
index 0000000..e24f2de
--- /dev/null
+++ b/introspection/org.freedesktop.ModemManager.Modem.Lte.Card.xml
@@ -0,0 +1,97 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+
+<node name="/" xmlns:tp="http://telepathy.freedesktop.org/wiki/DbusSpec#extensions-v0">
+ <interface name="org.freedesktop.ModemManager.Modem.Lte.Card">
+ <method name="GetImei">
+ <tp:docstring>
+ Get the IMEI of the card.
+ </tp:docstring>
+ <annotation name="org.freedesktop.DBus.GLib.Async" value=""/>
+ <annotation name="org.freedesktop.DBus.GLib.CSymbol" value="impl_lte_modem_get_imei"/>
+ <arg name="imei" type="s" direction="out">
+ <tp:docstring>
+ The IMEI.
+ </tp:docstring>
+ </arg>
+ </method>
+
+ <method name="GetImsi">
+ <tp:docstring>
+ Get the IMSI of the SIM card.
+ </tp:docstring>
+ <annotation name="org.freedesktop.DBus.GLib.Async" value=""/>
+ <annotation name="org.freedesktop.DBus.GLib.CSymbol" value="impl_lte_modem_get_imsi"/>
+ <arg name="imsi" type="s" direction="out">
+ <tp:docstring>
+ The IMSI.
+ </tp:docstring>
+ </arg>
+ </method>
+
+ <method name="SendPin">
+ <tp:docstring>
+ Send the PIN to unlock the SIM card.
+ </tp:docstring>
+ <annotation name="org.freedesktop.DBus.GLib.Async" value=""/>
+ <annotation name="org.freedesktop.DBus.GLib.CSymbol" value="impl_lte_modem_send_pin"/>
+ <arg name="pin" type="s" direction="in">
+ <tp:docstring>
+ The PIN code.
+ </tp:docstring>
+ </arg>
+ </method>
+
+ <method name="EnablePin">
+ <tp:docstring>
+ Enable or disable the PIN checking.
+ </tp:docstring>
+ <annotation name="org.freedesktop.DBus.GLib.Async" value=""/>
+ <annotation name="org.freedesktop.DBus.GLib.CSymbol" value="impl_lte_modem_enable_pin"/>
+ <arg name="pin" type="s" direction="in">
+ <tp:docstring>
+ The PIN code.
+ </tp:docstring>
+ </arg>
+ <arg name="enabled" type="b" direction="in">
+ <tp:docstring>
+ True to enable PIN checking.
+ </tp:docstring>
+ </arg>
+ </method>
+
+ <method name="ChangePin">
+ <tp:docstring>
+ Change the PIN code.
+ </tp:docstring>
+ <annotation name="org.freedesktop.DBus.GLib.Async" value=""/>
+ <annotation name="org.freedesktop.DBus.GLib.CSymbol" value="impl_lte_modem_change_pin"/>
+ <arg name="old_pin" type="s" direction="in">
+ <tp:docstring>
+ The current PIN code.
+ </tp:docstring>
+ </arg>
+ <arg name="new_pin" type="s" direction="in">
+ <tp:docstring>
+ The new PIN code.
+ </tp:docstring>
+ </arg>
+ </method>
+
+ <property name="SupportedBands" type="u" access="read" tp:type="MM_MODEM_LTE_BAND">
+ <tp:docstring>
+ Bands supported by the card. (Note for plugin writers:
+ returned value must not contain ANY)
+ </tp:docstring>
+ </property>
+
+<!--
+ <property name="SupportedModes" type="u" access="read" tp:type="MM_MODEM_LTE_MODE">
+ <tp:docstring>
+ Network selection modes supported by the card. (Note for plugin writers:
+ returned value must not contain ANY)
+ </tp:docstring>
+ </property>
+-->
+
+ </interface>
+</node>
diff --git a/introspection/org.freedesktop.ModemManager.Modem.Lte.Network.xml b/introspection/org.freedesktop.ModemManager.Modem.Lte.Network.xml
new file mode 100644
index 0000000..c8c641b
--- /dev/null
+++ b/introspection/org.freedesktop.ModemManager.Modem.Lte.Network.xml
@@ -0,0 +1,260 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+
+<node name="/" xmlns:tp="http://telepathy.freedesktop.org/wiki/DbusSpec#extensions-v0">
+ <interface name="org.freedesktop.ModemManager.Modem.Lte.Network">
+ <method name="Register">
+ <tp:docstring>
+ Register the device to network.
+ </tp:docstring>
+ <annotation name="org.freedesktop.DBus.GLib.Async" value=""/>
+ <annotation name="org.freedesktop.DBus.GLib.CSymbol" value="impl_lte_modem_register"/>
+ <arg name="network_id" type="s" direction="in">
+ <tp:docstring>
+ The network ID to register. An empty string can be used to register to the home network.
+ </tp:docstring>
+ </arg>
+ </method>
+
+ <method name="Scan">
+ <tp:docstring>
+ Scan for available networks.
+ </tp:docstring>
+ <annotation name="org.freedesktop.DBus.GLib.Async" value=""/>
+ <annotation name="org.freedesktop.DBus.GLib.CSymbol" value="impl_lte_modem_scan"/>
+ <arg name="results" type="aa{ss}" direction="out">
+ <tp:docstring>
+ <p>Found networks. It's an array of dictionaries (strings for both
+ keys and values) with each array element describing a mobile network
+ found in the scan. Each dict may include one or more of the following
+ keys:</p>
+ <ul>
+ <li>
+ "status": a number representing network availability status as
+ defined in 3GPP TS 27.007 section 7.3. e.g. "0" (unknown), "1"
+ (available), "2" (current), or "3" (forbidden). This key will
+ always be present.
+ </li>
+ <li>
+ "operator-long": long-format name of operator. If the name is
+ unknown, this field should not be present.
+ </li>
+ <li>
+ "operator-short": short-format name of operator. If the name is
+ unknown, this field should not be present.
+ </li>
+ <li>
+ "operator-num": mobile code of the operator. Returned in the
+ format "MCCMNC", where MCC is the three-digit ITU E.212 Mobile
+ Country Code and MNC is the two- or three-digit LTE Mobile
+ Network Code. e.g. "31026" or "310260".
+ </li>
+ <li>
+ "access-tech": a number representing the access technology used by
+ this mobile network as described in 3GPP TS 27.007 section 7.3.
+ e.g. "0" (LTE), "1" (LTE Compact), "2" (UTRAN/UMTS), "3" (EDGE),
+ etc.
+ </li>
+ </ul>
+ </tp:docstring>
+ </arg>
+ </method>
+
+ <method name="SetApn">
+ <tp:docstring>
+ Set the APN.
+ </tp:docstring>
+ <annotation name="org.freedesktop.DBus.GLib.Async" value=""/>
+ <annotation name="org.freedesktop.DBus.GLib.CSymbol" value="impl_lte_modem_set_apn"/>
+ <arg name="apn" type="s" direction="in">
+ <tp:docstring>
+ The APN.
+ </tp:docstring>
+ </arg>
+ </method>
+
+ <method name="GetSignalQuality">
+ <tp:docstring>
+ Get the current signal quality.
+ </tp:docstring>
+ <annotation name="org.freedesktop.DBus.GLib.Async" value=""/>
+ <annotation name="org.freedesktop.DBus.GLib.CSymbol" value="impl_lte_modem_get_signal_quality"/>
+ <arg name="quality" type="u" direction="out">
+ <tp:docstring>
+ Signal quality (percent).
+ </tp:docstring>
+ </arg>
+ </method>
+
+ <method name="SetBand">
+ <tp:docstring>
+ Sets the band the device is allowed to use when connecting to a mobile network.
+ </tp:docstring>
+ <annotation name="org.freedesktop.DBus.GLib.Async" value=""/>
+ <annotation name="org.freedesktop.DBus.GLib.CSymbol" value="impl_lte_modem_set_band"/>
+ <arg name="band" type="u" direction="in">
+ <tp:docstring>
+ The desired band. Only one band may be specified, and may not be UNKNOWN.
+ </tp:docstring>
+ </arg>
+ </method>
+
+ <method name="GetBand">
+ <tp:docstring>
+ Returns the current band the device is using. (Note for plugin writers: returned value must not be ANY)
+ </tp:docstring>
+ <annotation name="org.freedesktop.DBus.GLib.Async" value=""/>
+ <annotation name="org.freedesktop.DBus.GLib.CSymbol" value="impl_lte_modem_get_band"/>
+ <arg name="band" type="u" direction="out">
+ <tp:docstring>
+ The current band.
+ </tp:docstring>
+ </arg>
+ </method>
+
+ <method name="GetRegistrationInfo">
+ <tp:docstring>
+ Get the registration status and the current operator (if registered).
+ </tp:docstring>
+ <annotation name="org.freedesktop.DBus.GLib.Async" value=""/>
+ <annotation name="org.freedesktop.DBus.GLib.CSymbol" value="impl_lte_modem_get_reg_info"/>
+ <arg name="info" type="(uss)" direction="out">
+ <tp:docstring>
+ The returned information is composed of the following items in the
+ following order:
+ <ul>
+ <li>
+ Mobile registration status as defined in 3GPP TS 27.007 section
+ 10.1.19. See the MM_MODEM_LTE_NETWORK_REG_STATUS enumeration for
+ possible values.
+ </li>
+ <li>
+ Current operator code of the operator to which the mobile is
+ currently registered. Returned in the format "MCCMNC", where MCC
+ is the three-digit ITU E.212 Mobile Country Code and MNC is the
+ two- or three-digit LTE Mobile Network Code. If the MCC and MNC
+ are not known or the mobile is not registered to a mobile network,
+ this value should be a zero-length (blank) string. e.g. "31026"
+ or "310260".
+ </li>
+ <li>
+ Current operator name of the operator to which the mobile is
+ currently registered. If the operator name is not knowon or the
+ mobile is not registered to a mobile network, this value should
+ be a zero-length (blank) string.
+ </li>
+ </ul>
+ </tp:docstring>
+ </arg>
+ </method>
+
+ <method name="SetAllowedMode">
+ <tp:docstring>
+ Set the access technologies a device is allowed to use when connecting
+ to a mobile network.
+ </tp:docstring>
+ <annotation name="org.freedesktop.DBus.GLib.Async" value=""/>
+ <annotation name="org.freedesktop.DBus.GLib.CSymbol" value="impl_lte_modem_set_allowed_mode"/>
+ <arg name="mode" type="u" direction="in" tp:type="MM_MODEM_LTE_ALLOWED_MODE">
+ <tp:docstring>
+ The allowed mode. The device may not support all modes; see
+ the org.freedesktop.ModemManager.Lte.Card.SupportedModes property for
+ allowed modes for each device. All devices support the "ANY" flag.
+ </tp:docstring>
+ </arg>
+ </method>
+
+ <property name="AllowedMode" type="u" access="read" tp:type="MM_MODEM_LTE_ALLOWED_MODE">
+ <tp:docstring>
+ The allowed access technologies (eg GSM/WCDMA preference) the device is allowed
+ to use when connecting to a mobile network.
+ </tp:docstring>
+ </property>
+
+ <property name="AccessTechnology" type="u" access="read" tp:type="MM_MODEM_LTE_ACCESS_TECH">
+ <tp:docstring>
+ The current network access technology used by the device to communicate
+ with the base station. (Note to plugin writers: if the device's access
+ technology cannot be determined, use UNKNOWN)
+ </tp:docstring>
+ </property>
+
+ <signal name="SignalQuality">
+ <tp:docstring>
+ The signal quality changed.
+ </tp:docstring>
+ <arg name="quality" type="u">
+ <tp:docstring>
+ The new quality in percent, 0..100.
+ </tp:docstring>
+ </arg>
+ </signal>
+
+ <signal name="RegistrationInfo">
+ <tp:docstring>
+ The registration status changed.
+ </tp:docstring>
+ <arg name="status" type="u" tp:type="MM_MODEM_LTE_NETWORK_REG_STATUS">
+ <tp:docstring>
+ Mobile registration status as defined in 3GPP TS 27.007 section
+ 10.1.19.
+ </tp:docstring>
+ </arg>
+ <arg name="operator_code" type="s">
+ <tp:docstring>
+ Current operator code of the operator to which the mobile is
+ currently registered. Returned in the format "MCCMNC", where MCC
+ is the three-digit ITU E.212 Mobile Country Code and MNC is the
+ two- or three-digit LTE Mobile Network Code. If the MCC and MNC
+ are not known or the mobile is not registered to a mobile network,
+ this value should be a zero-length (blank) string. e.g. "31026" or
+ "310260".
+ </tp:docstring>
+ </arg>
+ <arg name="operator_name" type="s">
+ <tp:docstring>
+ Current operator name of the operator to which the mobile is
+ currently registered. If the operator name is not knowon or the
+ mobile is not registered to a mobile network, this value should
+ be a zero-length (blank) string.
+ </tp:docstring>
+ </arg>
+ </signal>
+
+ <tp:enum name="MM_MODEM_LTE_NETWORK_REG_STATUS" type="u">
+ <tp:docstring>
+ LTE registration code as defined in 3GPP TS 27.007 section 10.1.19.
+ </tp:docstring>
+ <tp:enumvalue suffix="IDLE" value="0">
+ <tp:docstring>
+ Not registered, not searching for new operator to register.
+ </tp:docstring>
+ </tp:enumvalue>
+ <tp:enumvalue suffix="HOME" value="1">
+ <tp:docstring>
+ Registered on home network.
+ </tp:docstring>
+ </tp:enumvalue>
+ <tp:enumvalue suffix="SEARCHING" value="2">
+ <tp:docstring>
+ Not registered, searching for new operator to register with.
+ </tp:docstring>
+ </tp:enumvalue>
+ <tp:enumvalue suffix="DENIED" value="3">
+ <tp:docstring>
+ Registration denied.
+ </tp:docstring>
+ </tp:enumvalue>
+ <tp:enumvalue suffix="UNKNOWN" value="4">
+ <tp:docstring>
+ Unknown registration status.
+ </tp:docstring>
+ </tp:enumvalue>
+ <tp:enumvalue suffix="ROAMING" value="5">
+ <tp:docstring>
+ Registered on a roaming network.
+ </tp:docstring>
+ </tp:enumvalue>
+ </tp:enum>
+
+ </interface>
+</node>
diff --git a/introspection/org.freedesktop.ModemManager.Modem.Lte.xml b/introspection/org.freedesktop.ModemManager.Modem.Lte.xml
new file mode 100644
index 0000000..c1670ee
--- /dev/null
+++ b/introspection/org.freedesktop.ModemManager.Modem.Lte.xml
@@ -0,0 +1,71 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+
+<node name="/" xmlns:tp="http://telepathy.freedesktop.org/wiki/DbusSpec#extensions-v0">
+ <interface name="org.freedesktop.ModemManager.Modem.Lte">
+
+ <tp:enum name="MM_MODEM_LTE_ALLOWED_MODE" type="u">
+ <tp:docstring>
+ Describes the device's current access mode preference; ie the specific
+ technology preferences the device is allowed to use when connecting to
+ a mobile network.
+ </tp:docstring>
+ <tp:enumvalue suffix="ANY" value="0">
+ <tp:docstring>Any mode can be used</tp:docstring>
+ </tp:enumvalue>
+ <tp:enumvalue suffix="GSM_PREFERRED" value="1">
+ <tp:docstring>Prefer GSM only</tp:docstring>
+ </tp:enumvalue>
+ <tp:enumvalue suffix="WCDMA_PREFERRED" value="2">
+ <tp:docstring>Prefer WCDMA only)</tp:docstring>
+ </tp:enumvalue>
+ </tp:enum>
+
+ <tp:enum name="MM_MODEM_LTE_ACCESS_TECH" type="u">
+ <tp:docstring>
+ Describes various access technologies that a device uses when connected
+ to a mobile network.
+ </tp:docstring>
+ <tp:enumvalue suffix="UNKNOWN" value="0">
+ <tp:docstring>The access technology used is unknown</tp:docstring>
+ </tp:enumvalue>
+ <tp:enumvalue suffix="GSM" value="1">
+ <tp:docstring>GSM</tp:docstring>
+ </tp:enumvalue>
+ <tp:enumvalue suffix="GPRS" value="2">
+ <tp:docstring>GPRS</tp:docstring>
+ </tp:enumvalue>
+ <tp:enumvalue suffix="EDGE" value="3">
+ <tp:docstring>EDGE</tp:docstring>
+ </tp:enumvalue>lte
+ <tp:enumvalue suffix="UMTS" value="4">
+ <tp:docstring>UMTS</tp:docstring>
+ </tp:enumvalue>
+ <tp:enumvalue suffix="HSDPA" value="5">
+ <tp:docstring>HSDPA</tp:docstring>
+ </tp:enumvalue>
+ <tp:enumvalue suffix="HSPA" value="6">
+ <tp:docstring>HSPA</tp:docstring>
+ </tp:enumvalue>
+ <tp:enumvalue suffix="LTE" value="7">
+ <tp:docstring>LTE</tp:docstring>
+ </tp:enumvalue>
+ <tp:enumvalue suffix="1xRTT" value="8">
+ <tp:docstring>1xRTT</tp:docstring>
+ </tp:enumvalue>
+ <tp:enumvalue suffix="EvDO" value="9">
+ <tp:docstring>1xRTT</tp:docstring>
+ </tp:enumvalue>
+ <tp:enumvalue suffix="EvDO_Rel0" value="10">
+ <tp:docstring>EvDO_Rel0</tp:docstring>
+ </tp:enumvalue>
+ <tp:enumvalue suffix="EvDOA" value="11">
+ <tp:docstring>EvDOA</tp:docstring>
+ </tp:enumvalue>
+ <tp:enumvalue suffix="HSUPA" value="12">
+ <tp:docstring>HSDPA</tp:docstring>
+ </tp:enumvalue>
+
+ </tp:enum>
+ </interface>
+</node>
+
diff --git a/introspection/org.freedesktop.ModemManager.Modem.xml b/introspection/org.freedesktop.ModemManager.Modem.xml
index a71a634..5ceba65 100644
--- a/introspection/org.freedesktop.ModemManager.Modem.xml
+++ b/introspection/org.freedesktop.ModemManager.Modem.xml
@@ -205,6 +205,11 @@
A CDMA device.
</tp:docstring>
</tp:enumvalue>
+ <tp:enumvalue suffix="LTE" value="3">
+ <tp:docstring>
+ A LTE device.
+ </tp:docstring>
+ </tp:enumvalue>
</tp:enum>
<tp:enum name="MM_MODEM_IP_METHOD" type="u">
diff --git a/org.freedesktop.ModemManager.conf.polkit b/org.freedesktop.ModemManager.conf.polkit
index 25490e3..93a4a4a 100644
--- a/org.freedesktop.ModemManager.conf.polkit
+++ b/org.freedesktop.ModemManager.conf.polkit
@@ -142,6 +142,46 @@
<allow send_destination="org.freedesktop.ModemManager"
send_interface="org.freedesktop.ModemManager.Modem.Gsm.SMS"
send_member="SetSmsc"/>
+
+ <allow send_destination="org.freedesktop.ModemManager"
+ send_interface="org.freedesktop.ModemManager.Modem.Lte.Network"
+ send_member="GetSignalQuality"/>
+
+ <allow send_destination="org.freedesktop.ModemManager"
+ send_interface="org.freedesktop.ModemManager.Modem.Lte.Network"
+ send_member="GetBand"/>
+
+ <allow send_destination="org.freedesktop.ModemManager"
+ send_interface="org.freedesktop.ModemManager.Modem.Lte.Network"
+ send_member="SetBand"/>
+
+ <allow send_destination="org.freedesktop.ModemManager"
+ send_interface="org.freedesktop.ModemManager.Modem.Lte.Network"
+ send_member="GetRegistrationInfo"/>
+
+ <allow send_destination="org.freedesktop.ModemManager"
+ send_interface="org.freedesktop.ModemManager.Modem.Lte.Network"
+ send_member="Scan"/>
+
+ <allow send_destination="org.freedesktop.ModemManager"
+ send_interface="org.freedesktop.ModemManager.Modem.Lte.Card"
+ send_member="GetImei"/>
+
+ <allow send_destination="org.freedesktop.ModemManager"
+ send_interface="org.freedesktop.ModemManager.Modem.Lte.Card"
+ send_member="GetImsi"/>
+
+ <allow send_destination="org.freedesktop.ModemManager"
+ send_interface="org.freedesktop.ModemManager.Modem.Lte.Card"
+ send_member="SendPin"/>
+
+ <allow send_destination="org.freedesktop.ModemManager"
+ send_interface="org.freedesktop.ModemManager.Modem.Lte.Card"
+ send_member="EnablePin"/>
+
+ <allow send_destination="org.freedesktop.ModemManager"
+ send_interface="org.freedesktop.ModemManager.Modem.Lte.Card"
+ send_member="ChangePin"/>
</policy>
<policy user="root">
diff --git a/plugins/Makefile.am b/plugins/Makefile.am
index 34e1c76..be84eb7 100644
--- a/plugins/Makefile.am
+++ b/plugins/Makefile.am
@@ -235,6 +235,8 @@ libmm_plugin_novatel_la_SOURCES = \
mm-plugin-novatel.h \
mm-modem-novatel-gsm.c \
mm-modem-novatel-gsm.h \
+ mm-modem-novatel-lte.c \
+ mm-modem-novatel-lte.h \
mm-modem-novatel-cdma.c \
mm-modem-novatel-cdma.h
diff --git a/plugins/mm-modem-novatel-lte.c b/plugins/mm-modem-novatel-lte.c
new file mode 100644
index 0000000..6238366
--- /dev/null
+++ b/plugins/mm-modem-novatel-lte.c
@@ -0,0 +1,930 @@
+/* -*- 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) 2008 - 2009 Novell, Inc.
+ * Copyright (C) 2009 - 2010 Red Hat, Inc.
+ * Copyright (C) 2011 Novatel Wireless, Inc.
+ */
+
+#include <config.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+#include "mm-modem-novatel-lte.h"
+#include "mm-modem-lte-network.h"
+#include "mm-modem-lte-card.h"
+#include "mm-modem-simple.h"
+#include "mm-errors.h"
+#include "mm-log.h"
+#include "mm-callback-info.h"
+#include "mm-modem-helpers.h"
+
+static void modem_init (MMModem *modem_class);
+static void modem_simple_init (MMModemSimple *class);
+static void modem_lte_network_init (MMModemLteNetwork *lte_network_class);
+static void modem_lte_card_init (MMModemLteCard *lte_card_class);
+static void do_connect (MMModem *modem,
+ const char *cmd,
+ MMModemFn callback,
+ gpointer user_data);
+
+
+G_DEFINE_TYPE_EXTENDED (MMModemNovatelLte, mm_modem_novatel_lte, MM_TYPE_GENERIC_LTE, 0,
+ G_IMPLEMENT_INTERFACE (MM_TYPE_MODEM, modem_init)
+ G_IMPLEMENT_INTERFACE (MM_TYPE_MODEM_SIMPLE, modem_simple_init)
+ G_IMPLEMENT_INTERFACE (MM_TYPE_MODEM_LTE_NETWORK, modem_lte_network_init)
+ G_IMPLEMENT_INTERFACE (MM_TYPE_MODEM_LTE_CARD, modem_lte_card_init))
+
+
+MMModem *
+mm_modem_novatel_lte_new (const char *device,
+ const char *driver,
+ const char *plugin,
+ guint32 vendor,
+ guint32 product)
+{
+ g_return_val_if_fail (device != NULL, NULL);
+ g_return_val_if_fail (driver != NULL, NULL);
+ g_return_val_if_fail (plugin != NULL, NULL);
+
+ return MM_MODEM (g_object_new (MM_TYPE_MODEM_NOVATEL_LTE,
+ MM_MODEM_MASTER_DEVICE, device,
+ MM_MODEM_DRIVER, driver,
+ MM_MODEM_PLUGIN, plugin,
+ MM_MODEM_HW_VID, vendor,
+ MM_MODEM_HW_PID, product,
+ NULL));
+}
+
+
+/*****************************************************************************/
+/* MMModemSimple interface */
+
+typedef enum {
+ SIMPLE_STATE_ENABLE = 0,
+ SIMPLE_STATE_SET_TECH_ALLOWED,
+ SIMPLE_STATE_SET_CALLPARM,
+ SIMPLE_STATE_SET_PDNS,
+ SIMPLE_STATE_SET_SDNS,
+ SIMPLE_STATE_SET_PNBNS,
+ SIMPLE_STATE_SET_SNBNS,
+ SIMPLE_STATE_SET_APN,
+ SIMPLE_STATE_SET_IP,
+ SIMPLE_STATE_SET_AUTH,
+ SIMPLE_STATE_SET_USER,
+ SIMPLE_STATE_SET_PWD,
+ SIMPLE_STATE_CONNECT,
+ SIMPLE_STATE_DONE
+} SimpleState;
+
+/* Looks a value up in the simple connect properties dictionary. If the
+ * requested key is not present in the dict, NULL is returned. If the
+ * requested key is present but is not a string, an error is returned.
+ */
+static gboolean
+simple_get_property (MMCallbackInfo *info,
+ const char *name,
+ GType expected_type,
+ const char **out_str,
+ guint32 *out_num,
+ gboolean *out_bool,
+ GError **error)
+{
+ GHashTable *properties = (GHashTable *) mm_callback_info_get_data (info, "simple-connect-properties");
+ GValue *value;
+ gint foo;
+
+ g_return_val_if_fail (properties != NULL, FALSE);
+ g_return_val_if_fail (name != NULL, FALSE);
+ if (out_str)
+ g_return_val_if_fail (*out_str == NULL, FALSE);
+
+ value = (GValue *) g_hash_table_lookup (properties, name);
+ if (!value)
+ return FALSE;
+
+ if ((expected_type == G_TYPE_STRING) && G_VALUE_HOLDS_STRING (value)) {
+ *out_str = g_value_get_string (value);
+ return TRUE;
+ } else if (expected_type == G_TYPE_UINT) {
+ if (G_VALUE_HOLDS_UINT (value)) {
+ *out_num = g_value_get_uint (value);
+ return TRUE;
+ } else if (G_VALUE_HOLDS_INT (value)) {
+ /* handle ints for convenience, but only if they are >= 0 */
+ foo = g_value_get_int (value);
+ if (foo >= 0) {
+ *out_num = (guint) foo;
+ return TRUE;
+ }
+ }
+ } else if (expected_type == G_TYPE_BOOLEAN && G_VALUE_HOLDS_BOOLEAN (value)) {
+ *out_bool = g_value_get_boolean (value);
+ return TRUE;
+ }
+
+ g_set_error (error, MM_MODEM_ERROR, MM_MODEM_ERROR_GENERAL,
+ "Invalid property type for '%s': %s (%s expected)",
+ name, G_VALUE_TYPE_NAME (value), g_type_name (expected_type));
+
+ return FALSE;
+}
+
+static const char *
+simple_get_string_property (MMCallbackInfo *info, const char *name, GError **error)
+{
+ const char *str = NULL;
+
+ simple_get_property (info, name, G_TYPE_STRING, &str, NULL, NULL, error);
+ return str;
+}
+
+static void
+connect_report_done (MMAtSerialPort *port,
+ GString *response,
+ GError *error,
+ gpointer user_data)
+{
+ MMCallbackInfo *info = (MMCallbackInfo *) user_data;
+ GError *real_error;
+
+ /* If the CEER command was successful, copy that error reason into the
+ * callback's error. If not, use the original error.
+ */
+
+ /* Have to do this little dance since mm_generic_lte_connect_complete()
+ * copies the provided error into the callback info.
+ */
+ real_error = info->error;
+ info->error = NULL;
+
+ if ( !error
+ && g_str_has_prefix (response->str, "+CEER: ")
+ && (strlen (response->str) > 7)) {
+ /* copy the connect failure reason into the error */
+ g_free (real_error->message);
+ real_error->message = g_strdup (response->str + 7); /* skip the "+CEER: " */
+ }
+
+ mm_generic_lte_connect_complete (MM_GENERIC_LTE (info->modem), real_error, info);
+ if(real_error)
+ g_error_free (real_error);
+}
+
+static void
+connect_status_done (MMAtSerialPort *port,
+ GString *response,
+ GError *error,
+ gpointer user_data)
+{
+ MMCallbackInfo *info = (MMCallbackInfo *) user_data;
+ GError *real_error;
+ real_error = info->error;
+ info->error = NULL;
+
+ if ( !error
+ && g_str_has_prefix (response->str, "$NWQMISTATUS: ")
+ && (strlen (response->str) > 14 )) {
+ if (g_strrstr(response->str, "CONNECTED") )
+ mm_generic_lte_connect_complete (MM_GENERIC_LTE (info->modem), NULL, info);
+ else {
+ /* copy the connect failure reason into the error */
+ g_free (real_error->message);
+ real_error->message = g_strdup (response->str + 14); /* skip the "$NWQMISTATUS: " */
+ mm_generic_lte_connect_complete (MM_GENERIC_LTE (info->modem), real_error, info);
+ }
+ }
+ if(real_error)
+ g_error_free (real_error);
+}
+
+static void
+connect_done (MMAtSerialPort *port,
+ GString *response,
+ GError *error,
+ gpointer user_data)
+{
+ MMCallbackInfo *info = (MMCallbackInfo *) user_data;
+
+ if (error) {
+ info->error = g_error_copy (error);
+ /* Try to get more information why it failed */
+ mm_at_serial_port_queue_command (port, "+CEER", 3, connect_report_done, info);
+ } else {
+ mm_at_serial_port_queue_command (port, "$NWQMISTATUS", 3, connect_status_done, info);
+ }
+}
+
+static void
+do_connect (MMModem *modem,
+ const char *cmd,
+ MMModemFn callback,
+ gpointer user_data)
+{
+ MMCallbackInfo *info;
+ MMAtSerialPort *port = mm_generic_lte_get_at_port ( MM_GENERIC_LTE(modem),
+ MM_PORT_TYPE_PRIMARY);
+
+ info = mm_callback_info_new (modem, callback, user_data);
+
+ mm_modem_set_state (modem, MM_MODEM_STATE_CONNECTING, MM_MODEM_STATE_REASON_NONE);
+ mm_at_serial_port_queue_command (port, cmd, 3, connect_done, info);
+}
+
+
+static char*
+addToConnectCmd(char* cmd, const char* str)
+{
+ if (str)
+ cmd = g_strdup_printf ("%s,%s", cmd, str);
+ else
+ cmd = g_strdup_printf ("%s,", cmd);
+ return cmd;
+}
+
+static void
+simple_state_machine (MMModem *modem, GError *error, gpointer user_data)
+{
+ MMCallbackInfo *info = (MMCallbackInfo *) user_data;
+ const char *str=NULL;
+ static char *cmd = NULL;
+ char *data_device = NULL;
+ SimpleState state = GPOINTER_TO_UINT (mm_callback_info_get_data (info, "simple-connect-state"));
+ SimpleState next_state = state;
+ gboolean done = FALSE;
+ GTimeVal tv;
+
+ if (mm_callback_info_check_modem_removed (info)) {
+ mm_callback_info_schedule (info);
+ return;
+ }
+
+ g_object_get (G_OBJECT (modem), MM_MODEM_DATA_DEVICE, &data_device, NULL);
+ g_get_current_time (&tv);
+ mm_dbg ("<%ld.%ld> (%s): simple connect state %d",
+ tv.tv_sec, tv.tv_usec, data_device, state);
+ g_free (data_device);
+
+ switch (state) {
+
+ case SIMPLE_STATE_ENABLE:
+ next_state = SIMPLE_STATE_SET_TECH_ALLOWED;
+ mm_modem_enable (modem, simple_state_machine, info);
+ break;
+ case SIMPLE_STATE_SET_TECH_ALLOWED:
+ next_state = SIMPLE_STATE_SET_CALLPARM;
+ str = simple_get_string_property (info, "tech", &info->error);
+ // Prepare QMI Connect command
+ if(str)
+ cmd = g_strdup_printf ("$NWQMICONNECT=%s", str);
+ else
+ cmd = g_strdup_printf ("$NWQMICONNECT=");
+
+ /* otherwise fall through as no allowed mode was sent */
+ case SIMPLE_STATE_SET_CALLPARM:
+ next_state = SIMPLE_STATE_SET_PDNS;
+ str = simple_get_string_property (info, "call_param", &info->error);
+ cmd = addToConnectCmd (cmd, str);
+
+ case SIMPLE_STATE_SET_PDNS:
+ next_state = SIMPLE_STATE_SET_SDNS;
+ str = simple_get_string_property (info, "pdns", &info->error);
+ cmd = addToConnectCmd (cmd, str);
+
+ case SIMPLE_STATE_SET_SDNS:
+ next_state = SIMPLE_STATE_SET_PNBNS;
+ str = simple_get_string_property (info, "sdns", &info->error);
+ cmd = addToConnectCmd (cmd, str);
+
+ case SIMPLE_STATE_SET_PNBNS:
+ next_state = SIMPLE_STATE_SET_SNBNS;
+ str = simple_get_string_property (info, "pnbns", &info->error);
+ cmd = addToConnectCmd (cmd, str);
+
+ case SIMPLE_STATE_SET_SNBNS:
+ next_state = SIMPLE_STATE_SET_APN;
+ str = simple_get_string_property (info, "snbns", &info->error);
+ cmd = addToConnectCmd (cmd, str);
+
+ case SIMPLE_STATE_SET_APN:
+ next_state = SIMPLE_STATE_SET_IP;
+ str = simple_get_string_property (info, "apn", &info->error);
+ cmd = addToConnectCmd (cmd, str);
+ if (str) {
+ mm_modem_lte_network_set_apn (MM_MODEM_LTE_NETWORK (modem), str, simple_state_machine, info);
+ break;
+ }
+
+ case SIMPLE_STATE_SET_IP:
+ next_state = SIMPLE_STATE_SET_AUTH;
+ str = simple_get_string_property (info, "ip", &info->error);
+ cmd = addToConnectCmd (cmd, str);
+
+ case SIMPLE_STATE_SET_AUTH:
+ next_state = SIMPLE_STATE_SET_USER;
+ str = simple_get_string_property (info, "auth", &info->error);
+ cmd = addToConnectCmd (cmd, str);
+
+ case SIMPLE_STATE_SET_USER:
+ next_state = SIMPLE_STATE_SET_PWD;
+ str = simple_get_string_property (info, "user", &info->error);
+ cmd = addToConnectCmd (cmd, str);
+
+ case SIMPLE_STATE_SET_PWD:
+ next_state = SIMPLE_STATE_CONNECT;
+ str = simple_get_string_property (info, "password", &info->error);
+ cmd = addToConnectCmd (cmd, str);
+
+ /* Fall through if no APN or no 'apn' property error */
+ case SIMPLE_STATE_CONNECT:
+ next_state = SIMPLE_STATE_DONE;
+ do_connect (modem, cmd, simple_state_machine, info);
+
+ break;
+ case SIMPLE_STATE_DONE:
+ done = TRUE;
+ break;
+ }
+
+ if (done) {
+ mm_callback_info_schedule (info);
+ g_free(cmd);
+ }
+ else
+ mm_callback_info_set_data (info, "simple-connect-state", GUINT_TO_POINTER (next_state), NULL);
+}
+
+static void printConnectProperties(MMModemSimple *simple, GHashTable *properties)
+{
+ GHashTableIter iter;
+ gpointer key, value;
+ GTimeVal tv;
+ char *data_device;
+
+ g_object_get (G_OBJECT (simple), MM_MODEM_DATA_DEVICE, &data_device, NULL);
+ g_get_current_time (&tv);
+
+ g_hash_table_iter_init (&iter, properties);
+ while (g_hash_table_iter_next (&iter, &key, &value)) {
+ char *val_str;
+
+ val_str = g_strdup_value_contents ((GValue *) value);
+ g_object_get (G_OBJECT (MM_MODEM (simple)), MM_MODEM_DATA_DEVICE, &data_device, NULL);
+ mm_dbg ("<%ld.%ld> (%s): %s => %s",
+ tv.tv_sec, tv.tv_usec,
+ data_device, (const char *) key, val_str);
+ g_free (val_str);
+ }
+ g_free (data_device);
+}
+
+static void
+simple_connect (MMModemSimple *simple,
+ GHashTable *properties,
+ MMModemFn callback,
+ gpointer user_data)
+{
+ MMCallbackInfo *info;
+
+ /* If debugging, print all the simple connect properties */
+ printConnectProperties(simple, properties);
+
+ info = mm_callback_info_new (MM_MODEM (simple), callback, user_data);
+ mm_callback_info_set_data (info, "simple-connect-properties",
+ g_hash_table_ref (properties),
+ (GDestroyNotify) g_hash_table_unref);
+
+ simple_state_machine (MM_MODEM (simple), NULL, info);
+}
+
+/*****************************************************************************/
+/* Modem class override functions */
+/*****************************************************************************/
+
+static void
+dmat_callback2 (MMAtSerialPort *port,
+ GString *response,
+ GError *error,
+ gpointer user_data)
+{
+ mm_serial_port_close (MM_SERIAL_PORT (port));
+}
+
+static void
+dmat_callback (MMAtSerialPort *port,
+ GString *response,
+ GError *error,
+ gpointer user_data)
+{
+ if (error) {
+ /* Try it again */
+ if (mm_serial_port_open (MM_SERIAL_PORT (port), NULL))
+ mm_at_serial_port_queue_command (port, "$NWDMAT=1", 2, dmat_callback2, NULL);
+ }
+
+ mm_serial_port_close (MM_SERIAL_PORT (port));
+}
+
+
+static gboolean
+grab_port (MMModem *modem,
+ const char *subsys,
+ const char *name,
+ MMPortType suggested_type,
+ gpointer user_data,
+ GError **error)
+{
+ MMGenericLte *lte = MM_GENERIC_LTE (modem);
+ MMPortType ptype = MM_PORT_TYPE_IGNORED;
+ MMPort *port = NULL;
+
+ if (suggested_type == MM_PORT_TYPE_UNKNOWN) {
+ if (!mm_generic_lte_get_at_port (lte, MM_PORT_TYPE_PRIMARY))
+ ptype = MM_PORT_TYPE_PRIMARY;
+ else if (!mm_generic_lte_get_at_port (lte, MM_PORT_TYPE_SECONDARY))
+ ptype = MM_PORT_TYPE_SECONDARY;
+ } else
+ ptype = suggested_type;
+
+ port = mm_generic_lte_grab_port (lte, subsys, name, ptype, error);
+ if (port && MM_IS_AT_SERIAL_PORT (port) && (ptype == MM_PORT_TYPE_PRIMARY)) {
+ /* Novatel secondary port needs to be flipped from DM to AT mode
+ * before it will answer our AT queries. So the primary port
+ * needs this string first or auto detection of ctrl port fails.
+ * Note: Early models/firmware were DM only
+ */
+
+ if (mm_serial_port_open (MM_SERIAL_PORT (port), NULL))
+ mm_at_serial_port_queue_command (MM_AT_SERIAL_PORT (port), "$NWDMAT=1", 2, dmat_callback, NULL);
+
+ }
+
+ return !!port;
+}
+
+static void disconnect_done (MMModem *modem,
+ GError *error,
+ gpointer user_data)
+{
+ MMCallbackInfo *info = (MMCallbackInfo *) user_data;
+ mm_callback_info_schedule (info);
+}
+
+static void
+do_disconnect_done (MMAtSerialPort *port,
+ GString *response,
+ GError *error,
+ gpointer user_data)
+{
+ MMCallbackInfo *info = (MMCallbackInfo *) user_data;
+ MMModemState prev_state;
+
+ if (mm_callback_info_check_modem_removed (info))
+ return;
+
+ if (error) {
+ if (info->modem) {
+ mm_dbg("error: Reset old state since the operation failed");
+ /* Reset old state since the operation failed */
+ prev_state = GPOINTER_TO_UINT (mm_callback_info_get_data (info,
+ MM_GENERIC_LTE_PREV_STATE_TAG));
+ mm_modem_set_state (MM_MODEM (info->modem),
+ prev_state,
+ MM_MODEM_STATE_REASON_NONE);
+ }
+ } else
+ mm_generic_lte_update_enabled_state (MM_GENERIC_LTE (info->modem),
+ FALSE,
+ MM_MODEM_STATE_REASON_NONE);
+
+ mm_callback_info_schedule (info);
+}
+
+
+static void
+disconnect_send_cmd (MMAtSerialPort *port,
+ gpointer user_data)
+{
+ MMCallbackInfo *info = (MMCallbackInfo *) user_data;
+ mm_at_serial_port_queue_command (MM_AT_SERIAL_PORT (port), "$NWQMIDISCONNECT", 2, do_disconnect_done, info);
+}
+
+static void
+disconnect_flash_done (MMSerialPort *port,
+ GError *error,
+ gpointer user_data)
+{
+ MMCallbackInfo *info = (MMCallbackInfo *) user_data;
+ if (mm_callback_info_check_modem_removed (info))
+ return;
+ if (error)
+ info->error = g_error_copy (error);
+
+ if (info->error) {
+ /* Ignore "NO CARRIER" response when modem disconnects and any flash
+ * failures we might encounter. Other errors are hard errors.
+ */
+ if ( !g_error_matches (info->error, MM_MODEM_CONNECT_ERROR,
+ MM_MODEM_CONNECT_ERROR_NO_CARRIER)
+ && !g_error_matches (info->error, MM_SERIAL_ERROR, MM_SERIAL_ERROR_FLASH_FAILED)) {
+ mm_callback_info_schedule (info);
+ return;
+ }
+ g_clear_error (&info->error);
+ }
+
+ mm_port_set_connected (MM_PORT (port), FALSE);
+ disconnect_send_cmd (MM_AT_SERIAL_PORT (port),
+ info);
+}
+
+static void
+do_disconnect (MMModem *modem,
+ MMModemFn callback,
+ gpointer user_data)
+{
+ MMCallbackInfo *info;
+ MMAtSerialPort *port = mm_generic_lte_get_at_port ( MM_GENERIC_LTE(modem),
+ MM_PORT_TYPE_PRIMARY);
+
+ info = mm_callback_info_new (modem, callback, user_data);
+
+ if (mm_serial_port_is_open (MM_SERIAL_PORT (port)))
+ mm_serial_port_flash (MM_SERIAL_PORT (port), 1000, TRUE, disconnect_flash_done, info);
+ else
+ mm_callback_info_schedule (info);
+}
+
+static void
+disconnect (MMModem *modem,
+ MMModemFn callback,
+ gpointer user_data)
+{
+ MMCallbackInfo *info;
+ MMModemState state;
+ info = mm_callback_info_new (modem, callback, user_data);
+
+ /* Cache the previous state so we can reset it if the operation fails */
+ state = mm_modem_get_state (modem);
+ mm_callback_info_set_data (info,
+ MM_GENERIC_LTE_PREV_STATE_TAG,
+ GUINT_TO_POINTER (state),
+ NULL);
+
+ mm_modem_set_state (modem, MM_MODEM_STATE_DISCONNECTING, MM_MODEM_STATE_REASON_NONE);
+ do_disconnect (modem, disconnect_done, info);
+}
+
+/*****************************************************************************/
+
+static void
+set_allowed_mode_done (MMAtSerialPort *port,
+ GString *response,
+ GError *error,
+ gpointer user_data)
+{
+ MMCallbackInfo *info = (MMCallbackInfo *) user_data;
+
+ if (error)
+ info->error = g_error_copy (error);
+
+ mm_callback_info_schedule (info);
+}
+
+static void
+set_allowed_mode (MMGenericLte *lte,
+ MMModemLteAllowedMode mode,
+ MMModemFn callback,
+ gpointer user_data)
+{
+ MMCallbackInfo *info;
+ MMAtSerialPort *port;
+ char *command;
+
+ info = mm_callback_info_new (MM_MODEM (lte), callback, user_data);
+
+ port = mm_generic_lte_get_best_at_port (lte, &info->error);
+ if (!port) {
+ mm_callback_info_schedule (info);
+ return;
+ }
+
+ command = g_strdup_printf ("$NWRAT=%d,2", mode);
+ mm_at_serial_port_queue_command (port, command, 3, set_allowed_mode_done, info);
+ g_free (command);
+}
+
+static gboolean
+parse_nwrat_response (GString *response,
+ MMModemLteAllowedMode *out_mode,
+ GError **error)
+{
+ GRegex *r;
+ GMatchInfo *match_info;
+ char *str;
+ gint mode = -1;
+ gboolean success = FALSE;
+
+ g_return_val_if_fail (response != NULL, FALSE);
+ g_return_val_if_fail (out_mode != NULL, FALSE);
+
+ r = g_regex_new ("\\$NWRAT:\\s*(\\d),(\\d),(\\d)", G_REGEX_UNGREEDY, 0, NULL);
+ if (!r) {
+ g_set_error_literal (error, MM_MODEM_ERROR, MM_MODEM_ERROR_GENERAL,
+ "Internal error parsing mode/tech response");
+ return FALSE;
+ }
+
+ if (!g_regex_match_full (r, response->str, response->len, 0, 0, &match_info, NULL)) {
+ g_set_error_literal (error, MM_MODEM_ERROR, MM_MODEM_ERROR_GENERAL,
+ "Failed to parse mode/tech response");
+ goto out;
+ }
+
+ str = g_match_info_fetch (match_info, 1);
+ mode = atoi (str);
+ g_free (str);
+
+ g_match_info_free (match_info);
+
+ if (mode < 0 || mode > 2) {
+ g_set_error_literal (error, MM_MODEM_ERROR, MM_MODEM_ERROR_GENERAL,
+ "Failed to parse mode/tech response");
+ goto out;
+ }
+
+ if (out_mode) {
+ if (mode == 0)
+ *out_mode = MM_MODEM_LTE_ALLOWED_MODE_ANY;
+ else if (mode == 1)
+ *out_mode = MM_MODEM_LTE_ALLOWED_MODE_GSM_PREFERRED;
+ else if (mode == 2)
+ *out_mode = MM_MODEM_LTE_ALLOWED_MODE_WCDMA_PREFERRED;
+ else
+ *out_mode = MM_MODEM_LTE_ALLOWED_MODE_ANY;
+ }
+ success = TRUE;
+
+out:
+ g_regex_unref (r);
+ return success;
+}
+
+static gboolean
+parse_syscfg (const char *reply,
+ guint32 *out_band)
+{
+ if (reply == NULL || strncmp (reply, "$NWBAND:", 8))
+ return FALSE;
+
+ if (sscanf (reply + 8, "%x", out_band))
+ return TRUE;
+ return FALSE;
+}
+
+
+static void
+get_allowed_mode_done (MMAtSerialPort *port,
+ GString *response,
+ GError *error,
+ gpointer user_data)
+{
+ MMCallbackInfo *info = (MMCallbackInfo *) user_data;
+ MMModemLteAllowedMode mode = MM_MODEM_LTE_ALLOWED_MODE_ANY;
+
+ if (mm_callback_info_check_modem_removed (info))
+ return;
+ if (error)
+ info->error = g_error_copy (error);
+
+ if (!info->error) {
+ parse_nwrat_response (response, &mode, &info->error);
+ mm_callback_info_set_result (info, GUINT_TO_POINTER (mode), NULL);
+ }
+
+ mm_callback_info_schedule (info);
+}
+
+static void
+get_allowed_mode (MMGenericLte *lte,
+ MMModemUIntFn callback,
+ gpointer user_data)
+{
+ MMCallbackInfo *info;
+ MMAtSerialPort *port;
+
+ info = mm_callback_info_uint_new (MM_MODEM (lte), callback, user_data);
+
+ port = mm_generic_lte_get_best_at_port (lte, &info->error);
+ if (!port) {
+ mm_callback_info_schedule (info);
+ return;
+ }
+
+ mm_at_serial_port_queue_command (port, "$NWRAT?", 3, get_allowed_mode_done, info);
+}
+
+static void
+set_band_done (MMAtSerialPort *port,
+ GString *response,
+ GError *error,
+ gpointer user_data)
+{
+ MMCallbackInfo *info = (MMCallbackInfo *) user_data;
+
+ if (error)
+ info->error = g_error_copy (error);
+
+ mm_callback_info_schedule (info);
+}
+
+static void
+set_band (MMModemLteNetwork *modem,
+ guint32 band,
+ MMModemFn callback,
+ gpointer user_data)
+{
+ MMCallbackInfo *info;
+ MMAtSerialPort *port;
+ char *command;
+
+ info = mm_callback_info_new (MM_MODEM (modem), callback, user_data);
+
+ port = mm_generic_lte_get_best_at_port (MM_GENERIC_LTE (modem), &info->error);
+ if (!port) {
+ mm_callback_info_schedule (info);
+ return;
+ }
+
+ mm_callback_info_set_data (info, "band", GUINT_TO_POINTER (band), NULL);
+ command = g_strdup_printf ("AT$NWBAND=%X", band);
+ mm_at_serial_port_queue_command (port, command, 3, set_band_done, info);
+ g_free (command);
+}
+
+static void
+get_band_done (MMAtSerialPort *port,
+ GString *response,
+ GError *error,
+ gpointer user_data)
+{
+ MMCallbackInfo *info = (MMCallbackInfo *) user_data;
+ guint32 band;
+
+ if (error)
+ info->error = g_error_copy (error);
+ else if (parse_syscfg (response->str, &band))
+ mm_callback_info_set_result (info, GUINT_TO_POINTER (band), NULL);
+
+ mm_callback_info_schedule (info);
+}
+
+static void
+get_band (MMModemLteNetwork *modem,
+ MMModemUIntFn callback,
+ gpointer user_data)
+{
+ MMAtSerialPort *port;
+ MMCallbackInfo *info;
+
+ info = mm_callback_info_uint_new (MM_MODEM (modem), callback, user_data);
+
+ /* Prefer cached band from unsolicited messages if we have it */
+
+ /* ask the modem */
+ port = mm_generic_lte_get_best_at_port (MM_GENERIC_LTE (modem), &info->error);
+ if (!port) {
+ mm_callback_info_schedule (info);
+ return;
+ }
+
+ mm_at_serial_port_queue_command (port, "AT$NWBAND?", 3, get_band_done, info);
+}
+
+
+
+static void
+get_act_request_done (MMAtSerialPort *port,
+ GString *response,
+ GError *error,
+ gpointer user_data)
+{
+ MMCallbackInfo *info = user_data;
+ MMModemLteAccessTech act = MM_MODEM_LTE_ACCESS_TECH_UNKNOWN;
+ const char *p;
+
+ if (error)
+ info->error = g_error_copy (error);
+ else {
+ p = mm_strip_tag (response->str, "$CNTI:");
+ p = strchr (p, ',');
+ if (p)
+ act = mm_lte_string_to_access_tech (p + 1);
+ }
+
+ mm_callback_info_set_result (info, GUINT_TO_POINTER (act), NULL);
+ mm_callback_info_schedule (info);
+}
+
+static void
+get_access_technology (MMGenericLte *modem,
+ MMModemUIntFn callback,
+ gpointer user_data)
+{
+ MMAtSerialPort *port;
+ MMCallbackInfo *info;
+
+ info = mm_callback_info_uint_new (MM_MODEM (modem), callback, user_data);
+ port = mm_generic_lte_get_best_at_port (modem, &info->error);
+ if (!port) {
+ mm_callback_info_schedule (info);
+ return;
+ }
+
+ mm_at_serial_port_queue_command (port, "$CNTI=0", 3, get_act_request_done, info);
+}
+
+static void
+get_string_done (MMAtSerialPort *port,
+ GString *response,
+ GError *error,
+ gpointer user_data)
+{
+ MMCallbackInfo *info = (MMCallbackInfo *) user_data;
+
+ if (error)
+ info->error = g_error_copy (error);
+ else
+ mm_callback_info_set_result (info, g_strdup (response->str), g_free);
+
+ mm_callback_info_schedule (info);
+}
+
+static void
+get_imei (MMModemLteCard *modem,
+ MMModemStringFn callback,
+ gpointer user_data)
+{
+ MMAtSerialPort *port;
+ MMCallbackInfo *info;
+
+ info = mm_callback_info_string_new (MM_MODEM (modem), callback, user_data);
+ port = mm_generic_lte_get_best_at_port (MM_GENERIC_LTE (modem), &info->error);
+ mm_at_serial_port_queue_command_cached (port, "+GSN", 3, get_string_done, info);
+}
+
+/*****************************************************************************/
+
+static void
+modem_init (MMModem *modem_class)
+{
+ modem_class->grab_port = grab_port;
+ modem_class->disconnect = disconnect;
+}
+
+static void
+modem_simple_init (MMModemSimple *class)
+{
+ class->connect = simple_connect;
+}
+
+static void
+modem_lte_card_init (MMModemLteCard *class)
+{
+ class->get_imei = get_imei;
+}
+
+static void
+modem_lte_network_init (MMModemLteNetwork *class)
+{
+ class->set_band = set_band;
+ class->get_band = get_band;
+}
+
+static void
+mm_modem_novatel_lte_init (MMModemNovatelLte *self)
+{
+}
+
+static void
+mm_modem_novatel_lte_class_init (MMModemNovatelLteClass *klass)
+{
+ MMGenericLteClass *lte_class = MM_GENERIC_LTE_CLASS (klass);
+
+ mm_modem_novatel_lte_parent_class = g_type_class_peek_parent (klass);
+
+ lte_class->set_allowed_mode = set_allowed_mode;
+ lte_class->get_allowed_mode = get_allowed_mode;
+ lte_class->get_access_technology = get_access_technology;
+}
+
diff --git a/plugins/mm-modem-novatel-lte.h b/plugins/mm-modem-novatel-lte.h
new file mode 100644
index 0000000..4929f5f
--- /dev/null
+++ b/plugins/mm-modem-novatel-lte.h
@@ -0,0 +1,46 @@
+/* -*- 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) 2008 - 2009 Novell, Inc.
+ * Copyright (C) 2009 Red Hat, Inc.
+ * Copyright (C) 2011 Novatel Wireless, Inc.
+ */
+
+#ifndef MM_MODEM_NOVATEL_LTE_H
+#define MM_MODEM_NOVATEL_LTE_H
+
+#include "mm-generic-lte.h"
+
+#define MM_TYPE_MODEM_NOVATEL_LTE (mm_modem_novatel_lte_get_type ())
+#define MM_MODEM_NOVATEL_LTE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MM_TYPE_MODEM_NOVATEL_LTE, MMModemNovatelLte))
+#define MM_MODEM_NOVATEL_LTE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), MM_TYPE_MODEM_NOVATEL_LTE, MMModemNovatelLteClass))
+#define MM_IS_MODEM_NOVATEL_LTE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MM_TYPE_MODEM_NOVATEL_LTE))
+#define MM_IS_MODEM_NOVATEL_LTE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), MM_TYPE_MODEM_NOVATEL_LTE))
+#define MM_MODEM_NOVATEL_LTE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), MM_TYPE_MODEM_NOVATEL_LTE, MMModemNovatelLteClass))
+
+typedef struct {
+ MMGenericLte parent;
+} MMModemNovatelLte;
+
+typedef struct {
+ MMGenericLteClass parent;
+} MMModemNovatelLteClass;
+
+GType mm_modem_novatel_lte_get_type (void);
+
+MMModem *mm_modem_novatel_lte_new (const char *device,
+ const char *driver,
+ const char *plugin_name,
+ guint32 vendor,
+ guint32 product);
+
+#endif /* MM_MODEM_NOVATEL_LTE_H */
diff --git a/plugins/mm-modem-samsung-gsm.c b/plugins/mm-modem-samsung-gsm.c
old mode 100755
new mode 100644
diff --git a/plugins/mm-modem-samsung-gsm.h b/plugins/mm-modem-samsung-gsm.h
old mode 100755
new mode 100644
diff --git a/plugins/mm-plugin-novatel.c b/plugins/mm-plugin-novatel.c
index 3b2b780..0a5b974 100644
--- a/plugins/mm-plugin-novatel.c
+++ b/plugins/mm-plugin-novatel.c
@@ -12,6 +12,7 @@
*
* Copyright (C) 2008 - 2009 Novell, Inc.
* Copyright (C) 2009 Red Hat, Inc.
+ * Copyright (C) 2011 Novatel Wireless, Inc.
*/
#include <string.h>
@@ -19,6 +20,7 @@
#include "mm-plugin-novatel.h"
#include "mm-modem-novatel-gsm.h"
#include "mm-modem-novatel-cdma.h"
+#include "mm-modem-novatel-lte.h"
G_DEFINE_TYPE (MMPluginNovatel, mm_plugin_novatel, MM_TYPE_PLUGIN_BASE)
@@ -49,6 +51,8 @@ get_level_for_capabilities (guint32 capabilities)
return 10;
if (capabilities & MM_PLUGIN_BASE_PORT_CAP_QCDM)
return 10;
+ if (capabilities & MM_PLUGIN_BASE_PORT_CAP_LTE)
+ return 10;
return 0;
}
@@ -140,7 +144,17 @@ grab_port (MMPluginBase *base,
caps = mm_plugin_base_supports_task_get_probed_capabilities (task);
sysfs_path = mm_plugin_base_supports_task_get_physdev_path (task);
if (!existing) {
- if (caps & MM_PLUGIN_BASE_PORT_CAP_GSM) {
+
+ if (caps & MM_PLUGIN_BASE_PORT_CAP_LTE) {
+ g_debug ("creating mm_modem_novatel_lte");
+ modem = mm_modem_novatel_lte_new (sysfs_path,
+ mm_plugin_base_supports_task_get_driver (task),
+ mm_plugin_get_name (MM_PLUGIN (base)),
+ vendor,
+ product);
+ }
+
+ else if (caps & MM_PLUGIN_BASE_PORT_CAP_GSM) {
modem = mm_modem_novatel_gsm_new (sysfs_path,
mm_plugin_base_supports_task_get_driver (task),
mm_plugin_get_name (MM_PLUGIN (base)),
diff --git a/plugins/mm-plugin-novatel.h b/plugins/mm-plugin-novatel.h
index 450bbdd..b66ee86 100644
--- a/plugins/mm-plugin-novatel.h
+++ b/plugins/mm-plugin-novatel.h
@@ -12,6 +12,7 @@
*
* Copyright (C) 2008 - 2009 Novell, Inc.
* Copyright (C) 2009 Red Hat, Inc.
+ * Copyright (C) 2011 Novatel Wireless, Inc.
*/
#ifndef MM_PLUGIN_NOVATEL_H
@@ -19,6 +20,7 @@
#include "mm-plugin-base.h"
#include "mm-generic-gsm.h"
+#include "mm-generic-lte.h"
#define MM_TYPE_PLUGIN_NOVATEL (mm_plugin_novatel_get_type ())
#define MM_PLUGIN_NOVATEL(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MM_TYPE_PLUGIN_NOVATEL, MMPluginNovatel))
diff --git a/plugins/mm-plugin-samsung.c b/plugins/mm-plugin-samsung.c
old mode 100755
new mode 100644
diff --git a/plugins/mm-plugin-samsung.h b/plugins/mm-plugin-samsung.h
old mode 100755
new mode 100644
diff --git a/src/Makefile.am b/src/Makefile.am
index d010805..7b3527b 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -113,6 +113,13 @@ modem_manager_SOURCES = \
mm-modem-gsm-sms.h \
mm-modem-gsm-ussd.c \
mm-modem-gsm-ussd.h \
+ mm-generic-lte.c \
+ mm-generic-lte.h \
+ mm-modem-lte-card.c \
+ mm-modem-lte-card.h \
+ mm-modem-lte-network.c \
+ mm-modem-lte-network.h \
+ mm-modem-lte.h \
mm-modem-simple.c \
mm-modem-simple.h \
mm-plugin.c \
@@ -150,6 +157,12 @@ mm-modem-gsm-sms-glue.h: $(top_srcdir)/introspection/org.freedesktop.ModemManage
mm-modem-gsm-ussd-glue.h: $(top_srcdir)/introspection/org.freedesktop.ModemManager.Modem.Gsm.Ussd.xml
$(AM_V_GEN) dbus-binding-tool --prefix=mm_modem_gsm_ussd --mode=glib-server --output=$@ $<
+mm-modem-lte-card-glue.h: $(top_srcdir)/introspection/org.freedesktop.ModemManager.Modem.Lte.Card.xml
+ dbus-binding-tool --prefix=mm_modem_lte_card --mode=glib-server --output=$@ $<
+
+mm-modem-lte-network-glue.h: $(top_srcdir)/introspection/org.freedesktop.ModemManager.Modem.Lte.Network.xml
+ dbus-binding-tool --prefix=mm_modem_lte_network --mode=glib-server --output=$@ $<
+
mm-properties-changed-glue.h: $(top_srcdir)/introspection/org.freedesktop.DBus.Properties.xml
$(AM_V_GEN) dbus-binding-tool --prefix=mm_properties_changed --mode=glib-server --output=$@ $<
@@ -162,6 +175,8 @@ BUILT_SOURCES = \
mm-modem-gsm-network-glue.h \
mm-modem-gsm-sms-glue.h \
mm-modem-gsm-ussd-glue.h \
+ mm-modem-lte-card-glue.h \
+ mm-modem-lte-network-glue.h \
mm-properties-changed-glue.h
mm-modem-location-glue.h: $(top_srcdir)/introspection/org.freedesktop.ModemManager.Modem.Location.xml
diff --git a/src/mm-charsets.c b/src/mm-charsets.c
index 708dd3e..5d429ff 100644
--- a/src/mm-charsets.c
+++ b/src/mm-charsets.c
@@ -414,6 +414,12 @@ mm_charset_gsm_unpacked_to_utf8 (const guint8 *gsm, guint32 len)
}
guint8 *
+mm_charset_lte_unpacked_to_utf8 (const guint8 *lte, guint32 len)
+{
+ return mm_charset_gsm_unpacked_to_utf8 (lte, len);
+}
+
+guint8 *
mm_charset_utf8_to_unpacked_gsm (const char *utf8, guint32 *out_len)
{
GByteArray *gsm;
diff --git a/src/mm-charsets.h b/src/mm-charsets.h
index d620b15..2d26298 100644
--- a/src/mm-charsets.h
+++ b/src/mm-charsets.h
@@ -57,6 +57,8 @@ guint8 *mm_charset_utf8_to_unpacked_gsm (const char *utf8, guint32 *out_len);
guint8 *mm_charset_gsm_unpacked_to_utf8 (const guint8 *gsm, guint32 len);
+guint8 *mm_charset_lte_unpacked_to_utf8 (const guint8 *lte, guint32 len);
+
guint8 *gsm_unpack (const guint8 *gsm,
guint32 num_septets,
guint8 start_offset, /* in bits */
diff --git a/src/mm-generic-lte.c b/src/mm-generic-lte.c
new file mode 100644
index 0000000..711683f
--- /dev/null
+++ b/src/mm-generic-lte.c
@@ -0,0 +1,4412 @@
+/* -*- 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) 2008 - 2009 Novell, Inc.
+ * Copyright (C) 2009 - 2010 Red Hat, Inc.
+ * Copyright (C) 2009 - 2010 Ericsson
+ * Copyright (C) 2011 Novatel Wireless, Inc.
+ */
+
+#include <config.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <ctype.h>
+#include "mm-generic-lte.h"
+#include "mm-modem-lte-card.h"
+#include "mm-modem-lte-network.h"
+#include "mm-modem-simple.h"
+#include "mm-errors.h"
+#include "mm-callback-info.h"
+#include "mm-at-serial-port.h"
+#include "mm-qcdm-serial-port.h"
+#include "mm-serial-parsers.h"
+#include "mm-modem-helpers.h"
+#include "mm-log.h"
+#include "mm-properties-changed-signal.h"
+#include "mm-utils.h"
+
+static void modem_init (MMModem *modem_class);
+static void modem_lte_card_init (MMModemLteCard *lte_card_class);
+static void modem_lte_network_init (MMModemLteNetwork *lte_network_class);
+static void modem_simple_init (MMModemSimple *class);
+
+G_DEFINE_TYPE_EXTENDED (MMGenericLte, mm_generic_lte, MM_TYPE_MODEM_BASE, 0,
+ G_IMPLEMENT_INTERFACE (MM_TYPE_MODEM, modem_init)
+ G_IMPLEMENT_INTERFACE (MM_TYPE_MODEM_LTE_CARD, modem_lte_card_init)
+ G_IMPLEMENT_INTERFACE (MM_TYPE_MODEM_LTE_NETWORK, modem_lte_network_init)
+ G_IMPLEMENT_INTERFACE (MM_TYPE_MODEM_SIMPLE, modem_simple_init))
+
+#define MM_GENERIC_LTE_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), MM_TYPE_GENERIC_LTE, MMGenericLtePrivate))
+
+typedef struct {
+ char *driver;
+ char *plugin;
+ char *device;
+
+ gboolean valid;
+ gboolean pin_checked;
+ guint32 pin_check_tries;
+ guint pin_check_timeout;
+ char *simid;
+ gboolean simid_checked;
+ guint32 simid_tries;
+
+ MMModemLteAllowedMode allowed_mode;
+
+ gboolean roam_allowed;
+
+ char *oper_code;
+ char *oper_name;
+ guint32 ip_method;
+
+ GPtrArray *reg_regex;
+
+ guint poll_id;
+
+ /* CREG and CGREG info */
+ gboolean creg_poll;
+ gboolean cgreg_poll;
+ /* Index 0 for CREG, index 1 for CGREG */
+ gulong lac[2];
+ gulong cell_id[2];
+ MMModemLteAccessTech act;
+
+ /* Index 0 for CREG, index 1 for CGREG */
+ MMModemLteNetworkRegStatus reg_status[2];
+ guint pending_reg_id;
+ MMCallbackInfo *pending_reg_info;
+ gboolean manual_reg;
+
+ guint signal_quality_id;
+ time_t signal_emit_timestamp;
+ time_t signal_update_timestamp;
+ guint32 signal_quality;
+ gint cid;
+
+ guint32 charsets;
+ guint32 cur_charset;
+
+ MMAtSerialPort *primary;
+ MMAtSerialPort *secondary;
+ MMQcdmSerialPort *qcdm;
+ MMPort *data;
+} MMGenericLtePrivate;
+
+static void get_registration_status (MMAtSerialPort *port, MMCallbackInfo *info);
+static void read_operator_code_done (MMAtSerialPort *port,
+ GString *response,
+ GError *error,
+ gpointer user_data);
+
+static void read_operator_name_done (MMAtSerialPort *port,
+ GString *response,
+ GError *error,
+ gpointer user_data);
+
+static void reg_state_changed (MMAtSerialPort *port,
+ GMatchInfo *match_info,
+ gpointer user_data);
+
+static void get_reg_status_done (MMAtSerialPort *port,
+ GString *response,
+ GError *error,
+ gpointer user_data);
+
+static gboolean handle_reg_status_response (MMGenericLte *self,
+ GString *response,
+ GError **error);
+
+static MMModemLteAccessTech etsi_act_to_mm_act (gint act);
+
+static void _internal_update_access_technology (MMGenericLte *modem,
+ MMModemLteAccessTech act);
+
+static void reg_info_updated (MMGenericLte *self,
+ gboolean update_rs,
+ MMGenericLteRegType rs_type,
+ MMModemLteNetworkRegStatus status,
+ gboolean update_code,
+ const char *oper_code,
+ gboolean update_name,
+ const char *oper_name);
+
+MMModem *
+mm_generic_lte_new (const char *device,
+ const char *driver,
+ const char *plugin,
+ guint vendor,
+ guint product)
+{
+ g_return_val_if_fail (device != NULL, NULL);
+ g_return_val_if_fail (driver != NULL, NULL);
+ g_return_val_if_fail (plugin != NULL, NULL);
+
+ return MM_MODEM (g_object_new (MM_TYPE_GENERIC_LTE,
+ MM_MODEM_MASTER_DEVICE, device,
+ MM_MODEM_DRIVER, driver,
+ MM_MODEM_PLUGIN, plugin,
+ MM_MODEM_HW_VID, vendor,
+ MM_MODEM_HW_PID, product,
+ NULL));
+}
+
+gint
+mm_generic_lte_get_cid (MMGenericLte *modem)
+{
+ g_return_val_if_fail (MM_IS_GENERIC_LTE (modem), 0);
+
+ return MM_GENERIC_LTE_GET_PRIVATE (modem)->cid;
+}
+
+typedef struct {
+ const char *result;
+ const char *normalized;
+ guint code;
+} CPinResult;
+
+static CPinResult unlock_results[] = {
+ /* Longer entries first so we catch the correct one with strcmp() */
+ { "PH-NETSUB PIN", "ph-netsub-pin", MM_MOBILE_ERROR_NETWORK_SUBSET_PIN },
+ { "PH-NETSUB PUK", "ph-netsub-puk", MM_MOBILE_ERROR_NETWORK_SUBSET_PUK },
+ { "PH-FSIM PIN", "ph-fsim-pin", MM_MOBILE_ERROR_PH_FSIM_PIN },
+ { "PH-FSIM PUK", "ph-fsim-puk", MM_MOBILE_ERROR_PH_FSIM_PUK },
+ { "PH-CORP PIN", "ph-corp-pin", MM_MOBILE_ERROR_CORP_PIN },
+ { "PH-CORP PUK", "ph-corp-puk", MM_MOBILE_ERROR_CORP_PUK },
+ { "PH-SIM PIN", "ph-sim-pin", MM_MOBILE_ERROR_PH_SIM_PIN },
+ { "PH-NET PIN", "ph-net-pin", MM_MOBILE_ERROR_NETWORK_PIN },
+ { "PH-NET PUK", "ph-net-puk", MM_MOBILE_ERROR_NETWORK_PUK },
+ { "PH-SP PIN", "ph-sp-pin", MM_MOBILE_ERROR_SERVICE_PIN },
+ { "PH-SP PUK", "ph-sp-puk", MM_MOBILE_ERROR_SERVICE_PUK },
+ { "SIM PIN2", "sim-pin2", MM_MOBILE_ERROR_SIM_PIN2 },
+ { "SIM PUK2", "sim-puk2", MM_MOBILE_ERROR_SIM_PUK2 },
+ { "SIM PIN", "sim-pin", MM_MOBILE_ERROR_SIM_PIN },
+ { "SIM PUK", "sim-puk", MM_MOBILE_ERROR_SIM_PUK },
+ { NULL, NULL, MM_MOBILE_ERROR_PHONE_FAILURE },
+};
+
+static GError *
+error_for_unlock_required (const char *unlock)
+{
+ CPinResult *iter = &unlock_results[0];
+
+ if (!unlock || !strlen (unlock))
+ return NULL;
+
+ /* Translate the error */
+ while (iter->result) {
+ if (!strcmp (iter->normalized, unlock))
+ return mm_mobile_error_for_code (iter->code);
+ iter++;
+ }
+
+ return g_error_new (MM_MOBILE_ERROR,
+ MM_MOBILE_ERROR_UNKNOWN,
+ "Unknown unlock request '%s'", unlock);
+}
+
+static void
+get_unlock_retries_cb (MMModem *modem,
+ guint32 result,
+ GError *error,
+ gpointer user_data)
+{
+ if (!error)
+ mm_modem_base_set_unlock_retries (MM_MODEM_BASE (modem), result);
+ else
+ mm_modem_base_set_unlock_retries (MM_MODEM_BASE (modem), MM_MODEM_LTE_CARD_UNLOCK_RETRIES_NOT_SUPPORTED);
+}
+
+static void
+pin_check_done (MMAtSerialPort *port,
+ GString *response,
+ GError *error,
+ gpointer user_data)
+{
+ MMCallbackInfo *info = (MMCallbackInfo *) user_data;
+ gboolean parsed = FALSE;
+
+ /* If the modem has already been removed, return without
+ * scheduling callback */
+ if (mm_callback_info_check_modem_removed (info))
+ return;
+
+ if (error)
+ info->error = g_error_copy (error);
+ else if (response && strstr (response->str, "+CPIN: ")) {
+ const char *str = strstr (response->str, "+CPIN: ") + 7;
+
+ /* Some phones (Motorola EZX models) seem to quote the response */
+ if (str[0] == '"')
+ str++;
+
+ if (g_str_has_prefix (str, "READY")) {
+ mm_modem_base_set_unlock_required (MM_MODEM_BASE (info->modem), NULL);
+ if (MM_MODEM_LTE_CARD_GET_INTERFACE (info->modem)->get_unlock_retries)
+ mm_modem_base_set_unlock_retries (MM_MODEM_BASE (info->modem), 0);
+ else
+ mm_modem_base_set_unlock_retries (MM_MODEM_BASE (info->modem),
+ MM_MODEM_LTE_CARD_UNLOCK_RETRIES_NOT_SUPPORTED);
+ parsed = TRUE;
+ } else {
+ CPinResult *iter = &unlock_results[0];
+
+ /* Translate the error */
+ while (iter->result) {
+ if (g_str_has_prefix (str, iter->result)) {
+ info->error = mm_mobile_error_for_code (iter->code);
+ mm_modem_base_set_unlock_required (MM_MODEM_BASE (info->modem), iter->normalized);
+ mm_modem_lte_card_get_unlock_retries (MM_MODEM_LTE_CARD (info->modem),
+ iter->normalized,
+ get_unlock_retries_cb,
+ NULL);
+ parsed = TRUE;
+ break;
+ }
+ iter++;
+ }
+ }
+ }
+
+ if (!parsed) {
+ /* Assume unlocked if we don't recognize the pin request result */
+ mm_modem_base_set_unlock_required (MM_MODEM_BASE (info->modem), NULL);
+ mm_modem_base_set_unlock_retries (MM_MODEM_BASE (info->modem), 0);
+
+ if (!info->error) {
+ info->error = g_error_new (MM_MODEM_ERROR,
+ MM_MODEM_ERROR_GENERAL,
+ "Could not parse PIN request response '%s'",
+ response->str);
+ }
+ }
+
+ mm_callback_info_schedule (info);
+}
+
+static void
+check_pin (MMGenericLte *modem,
+ MMModemFn callback,
+ gpointer user_data)
+{
+ MMGenericLtePrivate *priv;
+ MMCallbackInfo *info;
+
+ g_return_if_fail (MM_IS_GENERIC_LTE (modem));
+
+ priv = MM_GENERIC_LTE_GET_PRIVATE (modem);
+ info = mm_callback_info_new (MM_MODEM (modem), callback, user_data);
+ mm_at_serial_port_queue_command (priv->primary, "+CPIN?", 3, pin_check_done, info);
+}
+
+static void
+get_imei_cb (MMModem *modem,
+ const char *result,
+ GError *error,
+ gpointer user_data)
+{
+ if (modem) {
+ mm_modem_base_set_equipment_identifier (MM_MODEM_BASE (modem), error ? "" : result);
+ mm_serial_port_close (MM_SERIAL_PORT (MM_GENERIC_LTE_GET_PRIVATE (modem)->primary));
+ }
+}
+
+static void
+get_info_cb (MMModem *modem,
+ const char *manufacturer,
+ const char *model,
+ const char *version,
+ GError *error,
+ gpointer user_data)
+{
+ /* Base class handles saving the info for us */
+ if (modem)
+ mm_serial_port_close (MM_SERIAL_PORT (MM_GENERIC_LTE_GET_PRIVATE (modem)->primary));
+}
+
+/*****************************************************************************/
+
+static MMModemLteNetworkRegStatus
+lte_reg_status (MMGenericLte *self, guint32 *out_idx)
+{
+ MMGenericLtePrivate *priv = MM_GENERIC_LTE_GET_PRIVATE (self);
+ guint32 idx = 1;
+
+ /* Some devices (Blackberries for example) will respond to +CGREG, but
+ * return ERROR for +CREG, probably because their firmware is just stupid.
+ * So here we prefer the +CREG response, but if we never got a successful
+ * +CREG response, we'll take +CGREG instead.
+ */
+
+ if ( priv->reg_status[0] == MM_MODEM_LTE_NETWORK_REG_STATUS_HOME
+ || priv->reg_status[0] == MM_MODEM_LTE_NETWORK_REG_STATUS_ROAMING) {
+ idx = 0;
+ goto out;
+ }
+
+ if ( priv->reg_status[1] == MM_MODEM_LTE_NETWORK_REG_STATUS_HOME
+ || priv->reg_status[1] == MM_MODEM_LTE_NETWORK_REG_STATUS_ROAMING) {
+ idx = 1;
+ goto out;
+ }
+
+ if (priv->reg_status[0] == MM_MODEM_LTE_NETWORK_REG_STATUS_SEARCHING) {
+ idx = 0;
+ goto out;
+ }
+
+ if (priv->reg_status[1] == MM_MODEM_LTE_NETWORK_REG_STATUS_SEARCHING) {
+ idx = 1;
+ goto out;
+ }
+
+ if (priv->reg_status[0] != MM_MODEM_LTE_NETWORK_REG_STATUS_UNKNOWN) {
+ idx = 0;
+ goto out;
+ }
+
+out:
+ if (out_idx)
+ *out_idx = idx;
+ return priv->reg_status[idx];
+}
+
+void
+mm_generic_lte_update_enabled_state (MMGenericLte *self,
+ gboolean stay_connected,
+ MMModemStateReason reason)
+{
+ /* While connected we don't want registration status changes to change
+ * the modem's state away from CONNECTED.
+ */
+ if (stay_connected && (mm_modem_get_state (MM_MODEM (self)) >= MM_MODEM_STATE_DISCONNECTING))
+ return;
+
+ switch (lte_reg_status (self, NULL)) {
+ case MM_MODEM_LTE_NETWORK_REG_STATUS_HOME:
+ case MM_MODEM_LTE_NETWORK_REG_STATUS_ROAMING:
+ mm_modem_set_state (MM_MODEM (self), MM_MODEM_STATE_REGISTERED, reason);
+ break;
+ case MM_MODEM_LTE_NETWORK_REG_STATUS_SEARCHING:
+ mm_modem_set_state (MM_MODEM (self), MM_MODEM_STATE_SEARCHING, reason);
+ break;
+ case MM_MODEM_LTE_NETWORK_REG_STATUS_IDLE:
+ case MM_MODEM_LTE_NETWORK_REG_STATUS_DENIED:
+ case MM_MODEM_LTE_NETWORK_REG_STATUS_UNKNOWN:
+ default:
+ mm_modem_set_state (MM_MODEM (self), MM_MODEM_STATE_ENABLED, reason);
+ break;
+ }
+}
+
+static void
+check_valid (MMGenericLte *self)
+{
+ MMGenericLtePrivate *priv = MM_GENERIC_LTE_GET_PRIVATE (self);
+ gboolean new_valid = FALSE;
+ if (priv->primary && priv->data && priv->pin_checked && priv->simid_checked)
+ new_valid = TRUE;
+
+ mm_modem_base_set_valid (MM_MODEM_BASE (self), new_valid);
+}
+
+
+static void
+get_iccid_done (MMModem *modem,
+ const char *response,
+ GError *error,
+ gpointer user_data)
+{
+ MMGenericLtePrivate *priv;
+ const char *p = response;
+ GChecksum *sum = NULL;
+
+ if (error || !response || !strlen (response))
+ goto done;
+
+ sum = g_checksum_new (G_CHECKSUM_SHA1);
+
+ /* Make sure it looks like an ICCID */
+ while (*p) {
+ if (!isdigit (*p)) {
+ g_warning ("%s: invalid ICCID format (not a digit)", __func__);
+ goto done;
+ }
+ g_checksum_update (sum, (const guchar *) p++, 1);
+ }
+
+ priv = MM_GENERIC_LTE_GET_PRIVATE (modem);
+ g_free (priv->simid);
+ priv->simid = g_strdup (g_checksum_get_string (sum));
+
+ g_object_notify (G_OBJECT (modem), MM_MODEM_LTE_CARD_SIM_IDENTIFIER);
+
+done:
+ if (sum)
+ g_checksum_free (sum);
+
+ if (modem) {
+ MM_GENERIC_LTE_GET_PRIVATE (modem)->simid_checked = TRUE;
+ check_valid (MM_GENERIC_LTE (modem));
+ }
+}
+
+#define ICCID_CMD "+CRSM=176,12258,0,0,10"
+
+static void
+real_get_iccid_done (MMAtSerialPort *port,
+ GString *response,
+ GError *error,
+ gpointer user_data)
+{
+ MMCallbackInfo *info = (MMCallbackInfo *) user_data;
+ const char *str;
+ int sw1, sw2;
+ gboolean success = FALSE;
+ char buf[21], swapped[21];
+
+ /* If the modem has already been removed, return without
+ * scheduling callback */
+ if (mm_callback_info_check_modem_removed (info))
+ return;
+
+ if (error) {
+ info->error = g_error_copy (error);
+ goto done;
+ }
+
+ memset (buf, 0, sizeof (buf));
+ str = mm_strip_tag (response->str, "+CRSM:");
+ if (sscanf (str, "%d,%d,\"%20c\"", &sw1, &sw2, (char *) &buf) == 3)
+ success = TRUE;
+ else {
+ /* May not include quotes... */
+ if (sscanf (str, "%d,%d,%20c", &sw1, &sw2, (char *) &buf) == 3)
+ success = TRUE;
+ }
+
+ if (!success) {
+ info->error = g_error_new_literal (MM_MODEM_ERROR,
+ MM_MODEM_ERROR_GENERAL,
+ "Could not parse the CRSM response");
+ goto done;
+ }
+
+ if ((sw1 == 0x90 && sw2 == 0x00) || (sw1 == 0x91) || (sw1 == 0x92) || (sw1 == 0x9f)) {
+ gsize len = 0;
+ int f_pos = -1, i;
+
+ /* Make sure the buffer is only digits or 'F' */
+ for (len = 0; len < sizeof (buf) && buf[len]; len++) {
+ if (isdigit (buf[len]))
+ continue;
+ if (buf[len] == 'F' || buf[len] == 'f') {
+ buf[len] = 'F'; /* canonicalize the F */
+ f_pos = len;
+ continue;
+ }
+ if (buf[len] == '\"') {
+ buf[len] = 0;
+ break;
+ }
+
+ /* Invalid character */
+ info->error = g_error_new (MM_MODEM_ERROR,
+ MM_MODEM_ERROR_GENERAL,
+ "CRSM ICCID response contained invalid character '%c'",
+ buf[len]);
+ goto done;
+ }
+
+ /* BCD encoded ICCIDs are 20 digits long */
+ if (len != 20) {
+ info->error = g_error_new (MM_MODEM_ERROR,
+ MM_MODEM_ERROR_GENERAL,
+ "Invalid +CRSM ICCID response size (was %zd, expected 20)",
+ len);
+ goto done;
+ }
+
+ /* Ensure if there's an 'F' that it's second-to-last */
+ if ((f_pos >= 0) && (f_pos != len - 2)) {
+ info->error = g_error_new_literal (MM_MODEM_ERROR,
+ MM_MODEM_ERROR_GENERAL,
+ "Invalid +CRSM ICCID length (unexpected F)");
+ goto done;
+ }
+
+ /* Swap digits in the EFiccid response to get the actual ICCID, each
+ * group of 2 digits is reversed in the +CRSM response. i.e.:
+ *
+ * 21436587 -> 12345678
+ */
+ memset (swapped, 0, sizeof (swapped));
+ for (i = 0; i < 10; i++) {
+ swapped[i * 2] = buf[(i * 2) + 1];
+ swapped[(i * 2) + 1] = buf[i * 2];
+ }
+
+ /* Zero out the F for 19 digit ICCIDs */
+ if (swapped[len - 1] == 'F')
+ swapped[len - 1] = 0;
+
+ mm_callback_info_set_result (info, g_strdup (swapped), g_free);
+ } else {
+ MMGenericLtePrivate *priv = MM_GENERIC_LTE_GET_PRIVATE (info->modem);
+
+ if (priv->simid_tries++ < 2) {
+ /* Try one more time... Gobi 1K cards may reply to the first
+ * request with '+CRSM: 106,134,""' which is bogus because
+ * subsequent requests work fine.
+ */
+ mm_at_serial_port_queue_command (port, ICCID_CMD, 20, real_get_iccid_done, info);
+ return;
+ } else {
+ info->error = g_error_new (MM_MODEM_ERROR,
+ MM_MODEM_ERROR_GENERAL,
+ "SIM failed to handle CRSM request (sw1 %d sw2 %d)",
+ sw1, sw2);
+ }
+ }
+
+done:
+ /* Balance open from real_get_sim_iccid() */
+ mm_serial_port_close (MM_SERIAL_PORT (port));
+
+ mm_callback_info_schedule (info);
+}
+
+static void
+real_get_sim_iccid (MMGenericLte *self,
+ MMModemStringFn callback,
+ gpointer callback_data)
+{
+ MMCallbackInfo *info;
+ MMAtSerialPort *port;
+ GError *error = NULL;
+
+ port = mm_generic_lte_get_best_at_port (self, &error);
+ if (!port) {
+ callback (MM_MODEM (self), NULL, error, callback_data);
+ g_clear_error (&error);
+ return;
+ }
+
+ if (!mm_serial_port_open (MM_SERIAL_PORT (port), &error)) {
+ callback (MM_MODEM (self), NULL, error, callback_data);
+ g_clear_error (&error);
+ return;
+ }
+
+ info = mm_callback_info_string_new (MM_MODEM (self), callback, callback_data);
+
+ /* READ BINARY of EFiccid (ICC Identification) ETSI TS 102.221 section 13.2 */
+ mm_at_serial_port_queue_command (port, ICCID_CMD, 20, real_get_iccid_done, info);
+}
+
+static void
+initial_iccid_check (MMGenericLte *self)
+{
+ g_assert (MM_GENERIC_LTE_GET_CLASS (self)->get_sim_iccid);
+ MM_GENERIC_LTE_GET_CLASS (self)->get_sim_iccid (self, get_iccid_done, NULL);
+}
+
+static void initial_pin_check_done (MMModem *modem, GError *error, gpointer user_data);
+
+static gboolean
+pin_check_again (gpointer user_data)
+{
+ MMGenericLte *self = MM_GENERIC_LTE (user_data);
+ MMGenericLtePrivate *priv = MM_GENERIC_LTE_GET_PRIVATE (self);
+
+ priv->pin_check_timeout = 0;
+ check_pin (self, initial_pin_check_done, NULL);
+ return FALSE;
+}
+
+static void
+initial_pin_check_done (MMModem *modem, GError *error, gpointer user_data)
+{
+ MMGenericLtePrivate *priv;
+
+ /* modem could have been removed before we get here, in which case
+ * 'modem' will be NULL.
+ */
+ if (!modem)
+ return;
+
+ g_return_if_fail (MM_IS_GENERIC_LTE (modem));
+ priv = MM_GENERIC_LTE_GET_PRIVATE (modem);
+
+ if ( error
+ && priv->pin_check_tries++ < 3
+ && !mm_modem_base_get_unlock_required (MM_MODEM_BASE (modem))) {
+ /* Try it again a few times */
+ if (priv->pin_check_timeout)
+ g_source_remove (priv->pin_check_timeout);
+ priv->pin_check_timeout = g_timeout_add_seconds (2, pin_check_again, modem);
+ } else {
+ /* Try to get the SIM ICCID after we've checked PIN status and the SIM
+ * is ready.
+ */
+ initial_iccid_check (MM_GENERIC_LTE (modem));
+
+ priv->pin_checked = TRUE;
+ mm_serial_port_close (MM_SERIAL_PORT (priv->primary));
+ }
+}
+
+static void
+initial_pin_check (MMGenericLte *self)
+{
+ GError *error = NULL;
+ MMGenericLtePrivate *priv;
+
+ g_return_if_fail (MM_IS_GENERIC_LTE (self));
+ priv = MM_GENERIC_LTE_GET_PRIVATE (self);
+
+ g_return_if_fail (priv->primary != NULL);
+
+ if (mm_serial_port_open (MM_SERIAL_PORT (priv->primary), &error)) {
+ mm_at_serial_port_queue_command (priv->primary, "+CMEE=1", 2, NULL, NULL);
+ check_pin (self, initial_pin_check_done, NULL);
+ } else {
+ g_warning ("%s: failed to open serial port: (%d) %s",
+ __func__,
+ error ? error->code : -1,
+ error && error->message ? error->message : "(unknown)");
+ g_clear_error (&error);
+
+ /* Ensure the modem is still somewhat usable if opening the serial
+ * port fails for some reason.
+ */
+ initial_pin_check_done (MM_MODEM (self), NULL, NULL);
+ }
+}
+
+static void
+initial_imei_check (MMGenericLte *self)
+{
+ GError *error = NULL;
+ MMGenericLtePrivate *priv;
+
+ g_return_if_fail (MM_IS_GENERIC_LTE (self));
+ priv = MM_GENERIC_LTE_GET_PRIVATE (self);
+
+ g_return_if_fail (priv->primary != NULL);
+
+ if (mm_serial_port_open (MM_SERIAL_PORT (priv->primary), &error)) {
+ /* Make sure echoing is off */
+ mm_at_serial_port_queue_command (priv->primary, "E0", 3, NULL, NULL);
+
+ /* Get modem's imei number */
+ mm_modem_lte_card_get_imei (MM_MODEM_LTE_CARD (self),
+ get_imei_cb,
+ NULL);
+ } else {
+ g_warning ("%s: failed to open serial port: (%d) %s",
+ __func__,
+ error ? error->code : -1,
+ error && error->message ? error->message : "(unknown)");
+ g_clear_error (&error);
+ }
+}
+
+static void
+initial_info_check (MMGenericLte *self)
+{
+ GError *error = NULL;
+ MMGenericLtePrivate *priv;
+
+ g_return_if_fail (MM_IS_GENERIC_LTE (self));
+ priv = MM_GENERIC_LTE_GET_PRIVATE (self);
+
+ g_return_if_fail (priv->primary != NULL);
+
+ if (mm_serial_port_open (MM_SERIAL_PORT (priv->primary), &error)) {
+ /* Make sure echoing is off */
+ mm_at_serial_port_queue_command (priv->primary, "E0", 3, NULL, NULL);
+ mm_modem_base_get_card_info (MM_MODEM_BASE (self),
+ priv->primary,
+ NULL,
+ get_info_cb,
+ NULL);
+ } else {
+ g_warning ("%s: failed to open serial port: (%d) %s",
+ __func__,
+ error ? error->code : -1,
+ error && error->message ? error->message : "(unknown)");
+ g_clear_error (&error);
+ }
+}
+
+static gboolean
+owns_port (MMModem *modem, const char *subsys, const char *name)
+{
+ return !!mm_modem_base_get_port (MM_MODEM_BASE (modem), subsys, name);
+}
+
+MMPort *
+mm_generic_lte_grab_port (MMGenericLte *self,
+ const char *subsys,
+ const char *name,
+ MMPortType ptype,
+ GError **error)
+{
+ MMGenericLtePrivate *priv = MM_GENERIC_LTE_GET_PRIVATE (self);
+
+ MMPort *port = NULL;
+ GRegex *regex;
+
+ g_return_val_if_fail (!strcmp (subsys, "net") || !strcmp (subsys, "tty"), FALSE);
+
+ port = mm_modem_base_add_port (MM_MODEM_BASE (self), subsys, name, ptype);
+ if (!port) {
+ g_warn_if_fail (port != NULL);
+ return NULL;
+ }
+
+ if (MM_IS_AT_SERIAL_PORT (port)) {
+ GPtrArray *array;
+ int i;
+
+ mm_at_serial_port_set_response_parser (MM_AT_SERIAL_PORT (port),
+ mm_serial_parser_v1_parse,
+ mm_serial_parser_v1_new (),
+ mm_serial_parser_v1_destroy);
+
+ /* Set up CREG unsolicited message handlers */
+ array = mm_lte_creg_regex_get (FALSE);
+ for (i = 0; i < array->len; i++) {
+ regex = g_ptr_array_index (array, i);
+
+ mm_at_serial_port_add_unsolicited_msg_handler (MM_AT_SERIAL_PORT (port), regex, reg_state_changed, self, NULL);
+ }
+ mm_lte_creg_regex_destroy (array);
+
+ if (ptype == MM_PORT_TYPE_PRIMARY) {
+ priv->primary = MM_AT_SERIAL_PORT (port);
+ if (!priv->data) {
+ priv->data = port;
+ g_object_notify (G_OBJECT (self), MM_MODEM_DATA_DEVICE);
+ }
+
+ /* Get the modem's general info */
+ initial_info_check (self);
+
+ /* Get modem's IMEI */
+ initial_imei_check (self);
+
+ /* Get modem's initial lock/unlock state; this also ensures the
+ * SIM is ready by waiting if necessary for the SIM to initalize.
+ */
+ initial_pin_check (self);
+
+ } else if (ptype == MM_PORT_TYPE_SECONDARY)
+ priv->secondary = MM_AT_SERIAL_PORT (port);
+ } else if (MM_IS_QCDM_SERIAL_PORT (port)) {
+ if (!priv->qcdm)
+ priv->qcdm = MM_QCDM_SERIAL_PORT (port);
+ } else if (!strcmp (subsys, "net")) {
+ /* Net device (if any) is the preferred data port */
+ if (!priv->data || MM_IS_AT_SERIAL_PORT (priv->data)) {
+ priv->data = port;
+ g_object_notify (G_OBJECT (self), MM_MODEM_DATA_DEVICE);
+ check_valid (self);
+ }
+ }
+
+ return port;
+}
+
+static gboolean
+grab_port (MMModem *modem,
+ const char *subsys,
+ const char *name,
+ MMPortType suggested_type,
+ gpointer user_data,
+ GError **error)
+{
+ MMGenericLte *self = MM_GENERIC_LTE (modem);
+ MMGenericLtePrivate *priv = MM_GENERIC_LTE_GET_PRIVATE (self);
+ MMPortType ptype = MM_PORT_TYPE_IGNORED;
+
+ if (priv->primary)
+ g_return_val_if_fail (suggested_type != MM_PORT_TYPE_PRIMARY, FALSE);
+
+ if (!strcmp (subsys, "tty")) {
+ if (suggested_type != MM_PORT_TYPE_UNKNOWN)
+ ptype = suggested_type;
+ else {
+ if (!priv->primary)
+ ptype = MM_PORT_TYPE_PRIMARY;
+ else if (!priv->secondary)
+ ptype = MM_PORT_TYPE_SECONDARY;
+ }
+ }
+
+ return !!mm_generic_lte_grab_port (self, subsys, name, ptype, error);
+}
+
+static void
+release_port (MMModem *modem, const char *subsys, const char *name)
+{
+ MMGenericLtePrivate *priv = MM_GENERIC_LTE_GET_PRIVATE (modem);
+ MMPort *port;
+
+ if (strcmp (subsys, "tty") && strcmp (subsys, "net"))
+ return;
+
+ port = mm_modem_base_get_port (MM_MODEM_BASE (modem), subsys, name);
+ if (!port)
+ return;
+
+ if (port == (MMPort *) priv->primary) {
+ mm_modem_base_remove_port (MM_MODEM_BASE (modem), port);
+ priv->primary = NULL;
+ }
+
+ if (port == priv->data) {
+ priv->data = NULL;
+ g_object_notify (G_OBJECT (modem), MM_MODEM_DATA_DEVICE);
+ }
+
+ if (port == (MMPort *) priv->secondary) {
+ mm_modem_base_remove_port (MM_MODEM_BASE (modem), port);
+ priv->secondary = NULL;
+ }
+
+ if (port == (MMPort *) priv->qcdm) {
+ mm_modem_base_remove_port (MM_MODEM_BASE (modem), port);
+ priv->qcdm = NULL;
+ }
+
+ check_valid (MM_GENERIC_LTE (modem));
+}
+
+static void
+reg_poll_response (MMAtSerialPort *port,
+ GString *response,
+ GError *error,
+ gpointer user_data)
+{
+ MMGenericLte *self = MM_GENERIC_LTE (user_data);
+
+ if (!error)
+ handle_reg_status_response (self, response, NULL);
+}
+
+static void
+periodic_signal_quality_cb (MMModem *modem,
+ guint32 result,
+ GError *error,
+ gpointer user_data)
+{
+ /* Cached signal quality already updated */
+}
+
+static void
+periodic_access_tech_cb (MMModem *modem,
+ guint32 act,
+ GError *error,
+ gpointer user_data)
+{
+ if (modem && !error && act)
+ mm_generic_lte_update_access_technology (MM_GENERIC_LTE (modem), act);
+}
+
+static gboolean
+periodic_poll_cb (gpointer user_data)
+{
+ MMGenericLte *self = MM_GENERIC_LTE (user_data);
+ MMGenericLtePrivate *priv = MM_GENERIC_LTE_GET_PRIVATE (self);
+ MMAtSerialPort *port;
+
+ port = mm_generic_lte_get_best_at_port (self, NULL);
+ if (!port)
+ return TRUE; /* oh well, try later */
+
+ if (priv->creg_poll)
+ mm_at_serial_port_queue_command (port, "+CREG?", 10, reg_poll_response, self);
+ if (priv->cgreg_poll)
+ mm_at_serial_port_queue_command (port, "+CGREG?", 10, reg_poll_response, self);
+
+ /* Don't poll signal quality if we got a notification in the past 10 seconds */
+ if (time (NULL) - priv->signal_update_timestamp > 10) {
+ mm_modem_lte_network_get_signal_quality (MM_MODEM_LTE_NETWORK (self),
+ periodic_signal_quality_cb,
+ NULL);
+ }
+
+ if (MM_GENERIC_LTE_GET_CLASS (self)->get_access_technology)
+ MM_GENERIC_LTE_GET_CLASS (self)->get_access_technology (self, periodic_access_tech_cb, NULL);
+
+ return TRUE; /* continue running */
+}
+
+#define CREG_NUM_TAG "creg-num"
+#define CGREG_NUM_TAG "cgreg-num"
+
+static void
+initial_unsolicited_reg_check_done (MMCallbackInfo *info)
+{
+ MMGenericLtePrivate *priv;
+ guint creg_num, cgreg_num;
+
+ if (!info->modem || info->error)
+ goto done;
+
+ priv = MM_GENERIC_LTE_GET_PRIVATE (info->modem);
+ if (!priv->secondary)
+ goto done;
+
+ /* Enable unsolicited registration responses on secondary ports too,
+ * to ensure that we get the response even if the modem is connected
+ * on the primary port. We enable responses on both ports because we
+ * cannot trust modems to reliably send the responses on the port we
+ * enable them on.
+ */
+
+ creg_num = GPOINTER_TO_UINT (mm_callback_info_get_data (info, CREG_NUM_TAG));
+ switch (creg_num) {
+ case 1:
+ mm_at_serial_port_queue_command (priv->secondary, "+CREG=1", 3, NULL, NULL);
+ break;
+ case 2:
+ mm_at_serial_port_queue_command (priv->secondary, "+CREG=2", 3, NULL, NULL);
+ break;
+ default:
+ break;
+ }
+
+ cgreg_num = GPOINTER_TO_UINT (mm_callback_info_get_data (info, CGREG_NUM_TAG));
+ switch (cgreg_num) {
+ case 1:
+ mm_at_serial_port_queue_command (priv->secondary, "+CGREG=1", 3, NULL, NULL);
+ break;
+ case 2:
+ mm_at_serial_port_queue_command (priv->secondary, "+CGREG=2", 3, NULL, NULL);
+ break;
+ default:
+ break;
+ }
+
+done:
+ mm_callback_info_schedule (info);
+}
+
+static void
+cgreg1_done (MMAtSerialPort *port,
+ GString *response,
+ GError *error,
+ gpointer user_data)
+{
+ MMCallbackInfo *info = user_data;
+
+ /* If the modem has already been removed, return without
+ * scheduling callback */
+ if (mm_callback_info_check_modem_removed (info))
+ return;
+
+ if (error) {
+ MMGenericLtePrivate *priv = MM_GENERIC_LTE_GET_PRIVATE (info->modem);
+
+ /* The modem doesn't like unsolicited CGREG, so we'll need to poll */
+ priv->cgreg_poll = TRUE;
+ } else
+ mm_callback_info_set_data (info, CGREG_NUM_TAG, GUINT_TO_POINTER (1), NULL);
+
+ /* Success; get initial state */
+ mm_at_serial_port_queue_command (port, "+CGREG?", 10, reg_poll_response, info->modem);
+
+ initial_unsolicited_reg_check_done (info);
+}
+
+static void
+cgreg2_done (MMAtSerialPort *port,
+ GString *response,
+ GError *error,
+ gpointer user_data)
+{
+ MMCallbackInfo *info = user_data;
+
+ /* If the modem has already been removed, return without
+ * scheduling callback */
+ if (mm_callback_info_check_modem_removed (info))
+ return;
+
+ /* Ignore errors */
+ if (error) {
+ /* Try CGREG=1 instead */
+ mm_at_serial_port_queue_command (port, "+CGREG=1", 3, cgreg1_done, info);
+ } else {
+ mm_callback_info_set_data (info, CGREG_NUM_TAG, GUINT_TO_POINTER (2), NULL);
+
+ /* Success; get initial state */
+ mm_at_serial_port_queue_command (port, "+CGREG?", 10, reg_poll_response, info->modem);
+
+ /* All done */
+ initial_unsolicited_reg_check_done (info);
+ }
+}
+
+static void
+creg1_done (MMAtSerialPort *port,
+ GString *response,
+ GError *error,
+ gpointer user_data)
+{
+ MMCallbackInfo *info = user_data;
+ MMGenericLtePrivate *priv;
+
+ /* If the modem has already been removed, return without
+ * scheduling callback */
+ if (mm_callback_info_check_modem_removed (info))
+ return;
+
+ priv = MM_GENERIC_LTE_GET_PRIVATE (info->modem);
+
+ if (error) {
+ /* The modem doesn't like unsolicited CREG, so we'll need to poll */
+ priv->creg_poll = TRUE;
+ } else
+ mm_callback_info_set_data (info, CREG_NUM_TAG, GUINT_TO_POINTER (1), NULL);
+
+ /* Success; get initial state */
+ mm_at_serial_port_queue_command (port, "+CREG?", 10, reg_poll_response, info->modem);
+
+ /* Now try to set up CGREG messages */
+ mm_at_serial_port_queue_command (port, "+CGREG=2", 3, cgreg2_done, info);
+}
+
+static void
+creg2_done (MMAtSerialPort *port,
+ GString *response,
+ GError *error,
+ gpointer user_data)
+{
+ MMCallbackInfo *info = user_data;
+
+ /* If the modem has already been removed, return without
+ * scheduling callback */
+ if (mm_callback_info_check_modem_removed (info))
+ return;
+
+ /* Ignore errors */
+ if (error)
+ mm_at_serial_port_queue_command (port, "+CREG=1", 3, creg1_done, info);
+ else {
+ mm_callback_info_set_data (info, CREG_NUM_TAG, GUINT_TO_POINTER (2), NULL);
+
+ /* Success; get initial state */
+ mm_at_serial_port_queue_command (port, "+CREG?", 10, reg_poll_response, info->modem);
+
+ /* Now try to set up CGREG messages */
+ mm_at_serial_port_queue_command (port, "+CGREG=2", 3, cgreg2_done, info);
+ }
+}
+
+static void
+enable_failed (MMModem *modem, GError *error, MMCallbackInfo *info)
+{
+ MMGenericLtePrivate *priv;
+
+ /* If modem already removed, do nothing */
+ if (!modem || mm_callback_info_check_modem_removed (info))
+ return;
+
+ if (error)
+ info->error = g_error_copy (error);
+
+ mm_modem_set_state (modem,
+ MM_MODEM_STATE_DISABLED,
+ MM_MODEM_STATE_REASON_NONE);
+
+ priv = MM_GENERIC_LTE_GET_PRIVATE (modem);
+
+ if (priv->primary && mm_serial_port_is_open (MM_SERIAL_PORT (priv->primary)))
+ mm_serial_port_close_force (MM_SERIAL_PORT (priv->primary));
+ if (priv->secondary && mm_serial_port_is_open (MM_SERIAL_PORT (priv->secondary)))
+ mm_serial_port_close_force (MM_SERIAL_PORT (priv->secondary));
+
+ mm_callback_info_schedule (info);
+}
+
+static guint32 best_charsets[] = {
+ MM_MODEM_CHARSET_UTF8,
+ MM_MODEM_CHARSET_UCS2,
+ MM_MODEM_CHARSET_8859_1,
+ MM_MODEM_CHARSET_IRA,
+ MM_MODEM_CHARSET_GSM,
+ MM_MODEM_CHARSET_UNKNOWN
+};
+
+static void
+enabled_set_charset_done (MMModem *modem,
+ GError *error,
+ gpointer user_data)
+{
+ MMCallbackInfo *info = (MMCallbackInfo *) user_data;
+ guint idx;
+
+ /* only modem removals are really a hard error */
+ if (error) {
+ if (!modem) {
+ enable_failed (modem, error, info);
+ return;
+ }
+
+ /* Try the next best charset */
+ idx = GPOINTER_TO_UINT (mm_callback_info_get_data (info, "best-charset")) + 1;
+ if (best_charsets[idx] == MM_MODEM_CHARSET_UNKNOWN) {
+ GError *tmp_error;
+
+ /* No more character sets we can use */
+ tmp_error = g_error_new_literal (MM_MODEM_ERROR,
+ MM_MODEM_ERROR_UNSUPPORTED_CHARSET,
+ "Failed to find a usable modem character set");
+ enable_failed (modem, tmp_error, info);
+ g_error_free (tmp_error);
+ } else {
+ /* Send the new charset */
+ mm_callback_info_set_data (info, "best-charset", GUINT_TO_POINTER (idx), NULL);
+ mm_modem_set_charset (modem, best_charsets[idx], enabled_set_charset_done, info);
+ }
+ } else {
+ /* Modem is now enabled; update the state */
+ mm_generic_lte_update_enabled_state (MM_GENERIC_LTE (modem), FALSE, MM_MODEM_STATE_REASON_NONE);
+
+ /* Set up unsolicited registration notifications */
+ mm_at_serial_port_queue_command (MM_GENERIC_LTE_GET_PRIVATE (modem)->primary,
+ "+CREG=2", 3, creg2_done, info);
+ }
+}
+
+static void
+supported_charsets_done (MMModem *modem,
+ guint32 charsets,
+ GError *error,
+ gpointer user_data)
+{
+ MMCallbackInfo *info = (MMCallbackInfo *) user_data;
+
+ if (!modem) {
+ enable_failed (modem, error, info);
+ return;
+ }
+
+ /* Switch the device's charset; we prefer UTF-8, but UCS2 will do too */
+ mm_modem_set_charset (modem, best_charsets[0], enabled_set_charset_done, info);
+}
+
+static void
+get_allowed_mode_done (MMModem *modem,
+ MMModemLteAllowedMode mode,
+ GError *error,
+ gpointer user_data)
+{
+ if (modem) {
+ mm_generic_lte_update_allowed_mode (MM_GENERIC_LTE (modem),
+ error ? MM_MODEM_LTE_ALLOWED_MODE_ANY : mode);
+ }
+}
+
+void
+mm_generic_lte_enable_complete (MMGenericLte *self,
+ GError *error,
+ MMCallbackInfo *info)
+{
+ MMGenericLtePrivate *priv;
+
+ g_return_if_fail (self != NULL);
+ g_return_if_fail (MM_IS_GENERIC_LTE (self));
+ g_return_if_fail (info != NULL);
+
+ priv = MM_GENERIC_LTE_GET_PRIVATE (self);
+
+ if (error) {
+ enable_failed ((MMModem *) self, error, info);
+ return;
+ }
+
+ /* Open the second port here if the modem has one. We'll use it for
+ * signal strength and registration updates when the device is connected,
+ * but also many devices will send unsolicited registration or other
+ * messages to the secondary port but not the primary.
+ */
+ if (priv->secondary) {
+ if (!mm_serial_port_open (MM_SERIAL_PORT (priv->secondary), &error)) {
+ mm_dbg ("error opening secondary port: (%d) %s",
+ error ? error->code : -1,
+ error && error->message ? error->message : "(unknown)");
+ }
+ }
+
+ /* Try one more time to get the SIM ID */
+ if (!priv->simid)
+ MM_GENERIC_LTE_GET_CLASS (self)->get_sim_iccid (self, get_iccid_done, NULL);
+
+ /* Get allowed mode */
+ if (MM_GENERIC_LTE_GET_CLASS (self)->get_allowed_mode)
+ MM_GENERIC_LTE_GET_CLASS (self)->get_allowed_mode (self, get_allowed_mode_done, NULL);
+
+ /* And supported character sets */
+ mm_modem_get_supported_charsets (MM_MODEM (self), supported_charsets_done, info);
+}
+
+static void
+real_do_enable_power_up_done (MMGenericLte *self,
+ GString *response,
+ GError *error,
+ MMCallbackInfo *info)
+{
+ /* Ignore power-up errors as not all devices actually support CFUN=1 */
+ mm_generic_lte_enable_complete (self, error, info);
+}
+
+
+static void
+enable_done (MMAtSerialPort *port,
+ GString *response,
+ GError *error,
+ gpointer user_data)
+{
+ MMCallbackInfo *info = (MMCallbackInfo *) user_data;
+
+ /* If the modem has already been removed, return without
+ * scheduling callback */
+ if (mm_callback_info_check_modem_removed (info))
+ return;
+
+ real_do_enable_power_up_done (MM_GENERIC_LTE (info->modem), response, error, info);
+}
+
+static void
+init_done (MMAtSerialPort *port,
+ GString *response,
+ GError *error,
+ gpointer user_data)
+{
+ MMCallbackInfo *info = (MMCallbackInfo *) user_data;
+ char *cmd = NULL;
+
+ /* If the modem has already been removed, return without
+ * scheduling callback */
+ if (mm_callback_info_check_modem_removed (info))
+ return;
+
+ if (error) {
+ mm_generic_lte_enable_complete (MM_GENERIC_LTE (info->modem), error, info);
+ return;
+ }
+
+ /* Ensure echo is off after the init command; some modems ignore the
+ * E0 when it's in the same line as ATZ (Option GIO322).
+ */
+ mm_at_serial_port_queue_command (port, "E0", 2, NULL, NULL);
+
+ /* Some phones (like Blackberries) don't support +CMEE=1, so make it
+ * optional. It completely violates 3GPP TS 27.007 (9.1) but what can we do...
+ */
+ mm_at_serial_port_queue_command (port, "+CMEE=1", 2, NULL, NULL);
+
+ g_object_get (G_OBJECT (info->modem), MM_GENERIC_LTE_INIT_CMD_OPTIONAL, &cmd, NULL);
+ mm_at_serial_port_queue_command (port, cmd, 2, NULL, NULL);
+ g_free (cmd);
+
+ g_object_get (G_OBJECT (info->modem), MM_GENERIC_LTE_POWER_UP_CMD, &cmd, NULL);
+ if (cmd && strlen (cmd))
+ mm_at_serial_port_queue_command (port, cmd, 5, enable_done, user_data);
+ else
+ enable_done (port, NULL, NULL, user_data);
+ g_free (cmd);
+}
+
+static void
+enable_flash_done (MMSerialPort *port, GError *error, gpointer user_data)
+{
+ MMCallbackInfo *info = user_data;
+ char *cmd = NULL;
+
+ if (error) {
+ mm_generic_lte_enable_complete (MM_GENERIC_LTE (info->modem), error, info);
+ return;
+ }
+
+ g_object_get (G_OBJECT (info->modem), MM_GENERIC_LTE_INIT_CMD, &cmd, NULL);
+ mm_at_serial_port_queue_command (MM_AT_SERIAL_PORT (port), cmd, 3, init_done, user_data);
+ g_free (cmd);
+}
+
+static void
+real_do_enable (MMGenericLte *self, MMModemFn callback, gpointer user_data)
+{
+ MMGenericLtePrivate *priv = MM_GENERIC_LTE_GET_PRIVATE (self);
+ MMCallbackInfo *info;
+
+ info = mm_callback_info_new (MM_MODEM (self), callback, user_data);
+ mm_serial_port_flash (MM_SERIAL_PORT (priv->primary), 100, FALSE, enable_flash_done, info);
+}
+
+static void
+enable (MMModem *modem,
+ MMModemFn callback,
+ gpointer user_data)
+{
+ MMGenericLtePrivate *priv = MM_GENERIC_LTE_GET_PRIVATE (modem);
+ GError *error = NULL;
+ const char *unlock;
+
+ /* If the device needs a PIN, deal with that now, but we don't care
+ * about SIM-PIN2/SIM-PUK2 since the device is operational without it.
+ */
+ unlock = mm_modem_base_get_unlock_required (MM_MODEM_BASE (modem));
+ if (unlock && strcmp (unlock, "sim-puk2") && strcmp (unlock, "sim-pin2")) {
+ MMCallbackInfo *info;
+
+ info = mm_callback_info_new (modem, callback, user_data);
+ info->error = error_for_unlock_required (unlock);
+ mm_callback_info_schedule (info);
+ return;
+ }
+
+ /* First, reset the previously used CID */
+ priv->cid = -1;
+
+ if (!mm_serial_port_open (MM_SERIAL_PORT (priv->primary), &error)) {
+ MMCallbackInfo *info;
+
+ g_assert (error);
+ info = mm_callback_info_new (modem, callback, user_data);
+ info->error = error;
+ mm_callback_info_schedule (info);
+ return;
+ }
+
+ mm_modem_set_state (modem, MM_MODEM_STATE_ENABLING, MM_MODEM_STATE_REASON_NONE);
+
+ g_assert (MM_GENERIC_LTE_GET_CLASS (modem)->do_enable);
+ MM_GENERIC_LTE_GET_CLASS (modem)->do_enable (MM_GENERIC_LTE (modem), callback, user_data);
+}
+
+static void
+disable_done (MMAtSerialPort *port,
+ GString *response,
+ GError *error,
+ gpointer user_data)
+{
+ MMCallbackInfo *info = user_data;
+
+ /* If the modem has already been removed, return without
+ * scheduling callback */
+ if (mm_callback_info_check_modem_removed (info))
+ return;
+
+ if (error)
+ info->error = g_error_copy (error);
+ else {
+ MMGenericLte *self = MM_GENERIC_LTE (info->modem);
+
+ mm_serial_port_close_force (MM_SERIAL_PORT (port));
+ mm_modem_set_state (MM_MODEM (info->modem),
+ MM_MODEM_STATE_DISABLED,
+ MM_MODEM_STATE_REASON_NONE);
+
+ /* Clear out circuit-switched registration info... */
+ reg_info_updated (self,
+ TRUE, MM_GENERIC_LTE_REG_TYPE_CS, MM_MODEM_LTE_NETWORK_REG_STATUS_UNKNOWN,
+ TRUE, NULL,
+ TRUE, NULL);
+ /* ...and packet-switched registration info */
+ reg_info_updated (self,
+ TRUE, MM_GENERIC_LTE_REG_TYPE_PS, MM_MODEM_LTE_NETWORK_REG_STATUS_UNKNOWN,
+ TRUE, NULL,
+ TRUE, NULL);
+ }
+ mm_callback_info_schedule (info);
+}
+
+static void
+disable_flash_done (MMSerialPort *port,
+ GError *error,
+ gpointer user_data)
+{
+ MMGenericLtePrivate *priv;
+ MMCallbackInfo *info = user_data;
+ MMModemState prev_state;
+ char *cmd = NULL;
+
+ /* If the modem has already been removed, return without
+ * scheduling callback */
+ if (mm_callback_info_check_modem_removed (info))
+ return;
+
+ if (error) {
+ info->error = g_error_copy (error);
+
+ /* Reset old state since the operation failed */
+ prev_state = GPOINTER_TO_UINT (mm_callback_info_get_data (info, MM_GENERIC_LTE_PREV_STATE_TAG));
+ mm_modem_set_state (MM_MODEM (info->modem),
+ prev_state,
+ MM_MODEM_STATE_REASON_NONE);
+
+ mm_callback_info_schedule (info);
+ return;
+ }
+
+ priv = MM_GENERIC_LTE_GET_PRIVATE (info->modem);
+
+ /* Disable unsolicited messages */
+ mm_at_serial_port_queue_command (MM_AT_SERIAL_PORT (port), "AT+CREG=0", 3, NULL, NULL);
+ mm_at_serial_port_queue_command (MM_AT_SERIAL_PORT (port), "AT+CGREG=0", 3, NULL, NULL);
+
+ g_object_get (G_OBJECT (info->modem), MM_GENERIC_LTE_POWER_DOWN_CMD, &cmd, NULL);
+ if (cmd && strlen (cmd))
+ mm_at_serial_port_queue_command (MM_AT_SERIAL_PORT (port), cmd, 5, disable_done, user_data);
+ else
+ disable_done (MM_AT_SERIAL_PORT (port), NULL, NULL, user_data);
+ g_free (cmd);
+}
+
+static void
+secondary_unsolicited_done (MMAtSerialPort *port,
+ GString *response,
+ GError *error,
+ gpointer user_data)
+{
+ mm_serial_port_close_force (MM_SERIAL_PORT (port));
+}
+
+static void
+disable (MMModem *modem,
+ MMModemFn callback,
+ gpointer user_data)
+{
+ MMGenericLte *self = MM_GENERIC_LTE (modem);
+ MMGenericLtePrivate *priv = MM_GENERIC_LTE_GET_PRIVATE (self);
+ MMCallbackInfo *info;
+ MMModemState state;
+
+
+ /* First, reset the previously used CID and clean up registration */
+ g_warn_if_fail (priv->cid == -1);
+ priv->cid = -1;
+
+ mm_generic_lte_pending_registration_stop (MM_GENERIC_LTE (modem));
+
+ if (priv->poll_id) {
+ g_source_remove (priv->poll_id);
+ priv->poll_id = 0;
+ }
+
+ if (priv->signal_quality_id) {
+ g_source_remove (priv->signal_quality_id);
+ priv->signal_quality_id = 0;
+ }
+
+ if (priv->pin_check_timeout) {
+ g_source_remove (priv->pin_check_timeout);
+ priv->pin_check_timeout = 0;
+ }
+
+ //update_lac_ci (self, 0, 0, 0);
+ //update_lac_ci (self, 0, 0, 1);
+ _internal_update_access_technology (self, MM_MODEM_LTE_ACCESS_TECH_UNKNOWN);
+
+ /* Clean up the secondary port if it's open */
+ if (priv->secondary && mm_serial_port_is_open (MM_SERIAL_PORT (priv->secondary))) {
+ mm_at_serial_port_queue_command (priv->secondary, "+CREG=0", 3, NULL, NULL);
+ mm_at_serial_port_queue_command (priv->secondary, "+CGREG=0", 3, NULL, NULL);
+ mm_at_serial_port_queue_command (priv->secondary, "+CMER=0", 3, secondary_unsolicited_done, NULL);
+ }
+
+ info = mm_callback_info_new (modem, callback, user_data);
+
+ /* Cache the previous state so we can reset it if the operation fails */
+ state = mm_modem_get_state (modem);
+ mm_callback_info_set_data (info,
+ MM_GENERIC_LTE_PREV_STATE_TAG,
+ GUINT_TO_POINTER (state),
+ NULL);
+
+ mm_modem_set_state (MM_MODEM (info->modem),
+ MM_MODEM_STATE_DISABLING,
+ MM_MODEM_STATE_REASON_NONE);
+
+ if (mm_port_get_connected (MM_PORT (priv->primary)))
+ mm_serial_port_flash (MM_SERIAL_PORT (priv->primary), 1000, TRUE, disable_flash_done, info);
+ else
+ disable_flash_done (MM_SERIAL_PORT (priv->primary), NULL, info);
+}
+
+static void
+get_string_done (MMAtSerialPort *port,
+ GString *response,
+ GError *error,
+ gpointer user_data)
+{
+ MMCallbackInfo *info = (MMCallbackInfo *) user_data;
+
+ /* If the modem has already been removed, return without
+ * scheduling callback */
+ if (mm_callback_info_check_modem_removed (info))
+ return;
+
+ if (error)
+ info->error = g_error_copy (error);
+ else
+ mm_callback_info_set_result (info, g_strdup (response->str), g_free);
+
+ mm_callback_info_schedule (info);
+}
+
+static void
+get_mnc_length_done (MMAtSerialPort *port,
+ GString *response,
+ GError *error,
+ gpointer user_data)
+{
+ MMCallbackInfo *info = (MMCallbackInfo *) user_data;
+ int sw1, sw2;
+ const char *imsi;
+ gboolean success = FALSE;
+ char hex[51];
+ char *bin;
+
+ /* If the modem has already been removed, return without
+ * scheduling callback */
+ if (mm_callback_info_check_modem_removed (info))
+ return;
+
+ if (error) {
+ info->error = g_error_copy (error);
+ goto done;
+ }
+
+ memset (hex, 0, sizeof (hex));
+ if (sscanf (response->str, "+CRSM:%d,%d,\"%50c\"", &sw1, &sw2, (char *) &hex) == 3)
+ success = TRUE;
+ else {
+ /* May not include quotes... */
+ if (sscanf (response->str, "+CRSM:%d,%d,%50c", &sw1, &sw2, (char *) &hex) == 3)
+ success = TRUE;
+ }
+
+ if (!success) {
+ info->error = g_error_new_literal (MM_MODEM_ERROR,
+ MM_MODEM_ERROR_GENERAL,
+ "Could not parse the CRSM response");
+ goto done;
+ }
+
+ if ((sw1 == 0x90 && sw2 == 0x00) || (sw1 == 0x91) || (sw1 == 0x92) || (sw1 == 0x9f)) {
+ gsize buflen = 0;
+ guint32 mnc_len;
+
+ /* Make sure the buffer is only hex characters */
+ while (buflen < sizeof (hex) && hex[buflen]) {
+ if (!isxdigit (hex[buflen])) {
+ hex[buflen] = 0x0;
+ break;
+ }
+ buflen++;
+ }
+
+ /* Convert hex string to binary */
+ bin = utils_hexstr2bin (hex, &buflen);
+ if (!bin || buflen < 4) {
+ info->error = g_error_new (MM_MODEM_ERROR,
+ MM_MODEM_ERROR_GENERAL,
+ "SIM returned malformed response '%s'",
+ hex);
+ g_free (bin);
+ goto done;
+ }
+
+ /* MNC length is byte 4 of this SIM file */
+ mnc_len = bin[3] & 0xFF;
+ if (mnc_len == 2 || mnc_len == 3) {
+ imsi = mm_callback_info_get_data (info, "imsi");
+ mm_callback_info_set_result (info, g_strndup (imsi, 3 + mnc_len), g_free);
+ } else {
+ info->error = g_error_new (MM_MODEM_ERROR,
+ MM_MODEM_ERROR_GENERAL,
+ "SIM returned invalid MNC length %d (should be either 2 or 3)",
+ mnc_len);
+ }
+ g_free (bin);
+ } else {
+ info->error = g_error_new (MM_MODEM_ERROR,
+ MM_MODEM_ERROR_GENERAL,
+ "SIM failed to handle CRSM request (sw1 %d sw2 %d)",
+ sw1, sw2);
+ }
+
+done:
+ mm_callback_info_schedule (info);
+}
+
+static void
+get_operator_id_imsi_done (MMModem *modem,
+ const char *result,
+ GError *error,
+ gpointer user_data)
+{
+ MMGenericLtePrivate *priv = MM_GENERIC_LTE_GET_PRIVATE (modem);
+ MMCallbackInfo *info = (MMCallbackInfo *) user_data;
+
+ if (error) {
+ info->error = g_error_copy (error);
+ mm_callback_info_schedule (info);
+ return;
+ }
+
+ mm_callback_info_set_data (info, "imsi", g_strdup (result), g_free);
+
+ /* READ BINARY of EFad (Administrative Data) ETSI 51.011 section 10.3.18 */
+ mm_at_serial_port_queue_command_cached (priv->primary,
+ "+CRSM=176,28589,0,0,4",
+ 3,
+ get_mnc_length_done,
+ info);
+}
+
+static void
+get_spn_done (MMAtSerialPort *port,
+ GString *response,
+ GError *error,
+ gpointer user_data)
+{
+ MMCallbackInfo *info = (MMCallbackInfo *) user_data;
+ int sw1, sw2;
+ gboolean success = FALSE;
+ char hex[51];
+ char *bin, *utf8;
+
+ if (error) {
+ info->error = g_error_copy (error);
+ goto done;
+ }
+
+ memset (hex, 0, sizeof (hex));
+ if (sscanf (response->str, "+CRSM:%d,%d,\"%50c\"", &sw1, &sw2, (char *) &hex) == 3)
+ success = TRUE;
+ else {
+ /* May not include quotes... */
+ if (sscanf (response->str, "+CRSM:%d,%d,%50c", &sw1, &sw2, (char *) &hex) == 3)
+ success = TRUE;
+ }
+
+ if (!success) {
+ info->error = g_error_new_literal (MM_MODEM_ERROR,
+ MM_MODEM_ERROR_GENERAL,
+ "Could not parse the CRSM response");
+ goto done;
+ }
+
+ if ((sw1 == 0x90 && sw2 == 0x00) || (sw1 == 0x91) || (sw1 == 0x92) || (sw1 == 0x9f)) {
+ gsize buflen = 0;
+
+ /* Make sure the buffer is only hex characters */
+ while (buflen < sizeof (hex) && hex[buflen]) {
+ if (!isxdigit (hex[buflen])) {
+ hex[buflen] = 0x0;
+ break;
+ }
+ buflen++;
+ }
+
+ /* Convert hex string to binary */
+ bin = utils_hexstr2bin (hex, &buflen);
+ if (!bin) {
+ info->error = g_error_new (MM_MODEM_ERROR,
+ MM_MODEM_ERROR_GENERAL,
+ "SIM returned malformed response '%s'",
+ hex);
+ goto done;
+ }
+
+ /* Remove the FF filler at the end */
+ while (bin[buflen - 1] == (char)0xff)
+ buflen--;
+
+ /* First byte is metadata; remainder is GSM-7 unpacked into octets; convert to UTF8 */
+ utf8 = (char *)mm_charset_lte_unpacked_to_utf8 ((guint8 *)bin + 1, buflen - 1);
+ g_free(bin);
+ mm_callback_info_set_result(info, utf8, g_free);
+ } else {
+ info->error = g_error_new (MM_MODEM_ERROR,
+ MM_MODEM_ERROR_GENERAL,
+ "SIM failed to handle CRSM request (sw1 %d sw2 %d)",
+ sw1, sw2);
+ }
+
+done:
+ mm_callback_info_schedule (info);
+}
+
+
+static void
+get_imei (MMModemLteCard *modem,
+ MMModemStringFn callback,
+ gpointer user_data)
+{
+ MMGenericLtePrivate *priv = MM_GENERIC_LTE_GET_PRIVATE (modem);
+ MMCallbackInfo *info;
+
+ info = mm_callback_info_string_new (MM_MODEM (modem), callback, user_data);
+ mm_at_serial_port_queue_command_cached (priv->primary, "+CGSN", 3, get_string_done, info);
+}
+
+static void
+get_imsi (MMModemLteCard *modem,
+ MMModemStringFn callback,
+ gpointer user_data)
+{
+ MMGenericLtePrivate *priv = MM_GENERIC_LTE_GET_PRIVATE (modem);
+ MMCallbackInfo *info;
+
+ info = mm_callback_info_string_new (MM_MODEM (modem), callback, user_data);
+ mm_at_serial_port_queue_command_cached (priv->primary, "+CIMI", 3, get_string_done, info);
+}
+
+static void
+get_operator_id (MMModemLteCard *modem,
+ MMModemStringFn callback,
+ gpointer user_data)
+{
+ MMCallbackInfo *info;
+
+ info = mm_callback_info_string_new (MM_MODEM (modem), callback, user_data);
+ mm_modem_lte_card_get_imsi (MM_MODEM_LTE_CARD (modem),
+ get_operator_id_imsi_done,
+ info);
+}
+
+static void
+get_spn (MMModemLteCard *modem,
+ MMModemStringFn callback,
+ gpointer user_data)
+{
+ MMGenericLtePrivate *priv = MM_GENERIC_LTE_GET_PRIVATE (modem);
+ MMCallbackInfo *info;
+
+ info = mm_callback_info_string_new (MM_MODEM (modem), callback, user_data);
+
+ /* READ BINARY of EFspn (Service Provider Name) ETSI 51.011 section 10.3.11 */
+ mm_at_serial_port_queue_command_cached (priv->primary,
+ "+CRSM=176,28486,0,0,17",
+ 3,
+ get_spn_done,
+ info);
+}
+
+static void
+get_card_info (MMModem *modem,
+ MMModemInfoFn callback,
+ gpointer user_data)
+{
+ MMAtSerialPort *port;
+ GError *error = NULL;
+
+ port = mm_generic_lte_get_best_at_port (MM_GENERIC_LTE (modem), &error);
+ mm_modem_base_get_card_info (MM_MODEM_BASE (modem), port, error, callback, user_data);
+ g_clear_error (&error);
+}
+
+#define PIN_PORT_TAG "pin-port"
+#define SAVED_ERROR_TAG "error"
+
+static void
+pin_recheck_done (MMModem *modem, GError *error, gpointer user_data);
+
+static gboolean
+pin_recheck_again (gpointer user_data)
+{
+ MMCallbackInfo *info = (MMCallbackInfo *) user_data;
+
+ MM_GENERIC_LTE_GET_PRIVATE (info->modem)->pin_check_timeout = 0;
+ check_pin (MM_GENERIC_LTE (info->modem), pin_recheck_done, info);
+ return FALSE;
+}
+
+static void
+pin_recheck_done (MMModem *modem, GError *error, gpointer user_data)
+{
+ MMCallbackInfo *info = (MMCallbackInfo *) user_data;
+ MMGenericLtePrivate *priv;
+ MMSerialPort *port;
+ GError *saved_error;
+
+ /* Do nothing if modem removed */
+ if (!modem || mm_callback_info_check_modem_removed (info))
+ return;
+
+ priv = MM_GENERIC_LTE_GET_PRIVATE (info->modem);
+
+ /* Clear the pin check timeout to ensure that it won't ever get a
+ * stale MMCallbackInfo if the modem got removed. We'll reschedule it here
+ * anyway if needed.
+ */
+ if (priv->pin_check_timeout)
+ g_source_remove (priv->pin_check_timeout);
+ priv->pin_check_timeout = 0;
+
+ /* Propagate the error to the info */
+ if (error)
+ info->error = g_error_copy (error);
+
+ /* If the modem wasn't removed, and the modem isn't ready yet, ask it for
+ * the current PIN status a few times since some devices take a bit to fully
+ * enable themselves after a SIM PIN/PUK unlock.
+ */
+ if (info->error && !g_error_matches (info->error, MM_MODEM_ERROR, MM_MODEM_ERROR_REMOVED)) {
+ if (priv->pin_check_tries < 4) {
+ g_clear_error (&info->error);
+ priv->pin_check_tries++;
+ priv->pin_check_timeout = g_timeout_add_seconds (2, pin_recheck_again, info);
+ return;
+ }
+ }
+
+ /* Otherwise, clean up and return the PIN check result */
+ port = mm_callback_info_get_data (info, PIN_PORT_TAG);
+ if (port)
+ mm_serial_port_close (port);
+
+ /* If we have a saved error from sending PIN, return that to callers */
+ saved_error = mm_callback_info_get_data (info, SAVED_ERROR_TAG);
+ if (saved_error) {
+ if (!mm_modem_base_get_unlock_required (MM_MODEM_BASE (info->modem))) {
+ /* Original unlock failed but the modem is actually unlocked, so
+ * return success. Sometimes happens if the modem doesn't allow
+ * CPIN="xxxx" when it's already unlocked and returns an error.
+ * Do nothing.
+ */
+ } else {
+ /* Unlock failed after recheck, return original error */
+ g_clear_error (&info->error);
+ info->error = g_error_copy (saved_error);
+ }
+ }
+
+ mm_callback_info_schedule (info);
+}
+
+static void
+send_pin_done (MMAtSerialPort *port,
+ GString *response,
+ GError *error,
+ gpointer user_data)
+{
+ MMCallbackInfo *info = (MMCallbackInfo *) user_data;
+
+ /* If the modem has already been removed, return without
+ * scheduling callback */
+ if (mm_callback_info_check_modem_removed (info))
+ return;
+
+ if (error) {
+ if (error->domain != MM_MOBILE_ERROR) {
+ info->error = g_error_copy (error);
+ mm_callback_info_schedule (info);
+ mm_serial_port_close (MM_SERIAL_PORT (port));
+ return;
+ } else {
+ /* Keep the real error around so we can send it back
+ * when we're done rechecking CPIN status.
+ */
+ mm_callback_info_set_data (info, SAVED_ERROR_TAG,
+ g_error_copy (error),
+ (GDestroyNotify) g_error_free);
+ }
+ }
+
+ /* Get latest PIN status */
+ MM_GENERIC_LTE_GET_PRIVATE (info->modem)->pin_check_tries = 0;
+ check_pin (MM_GENERIC_LTE (info->modem), pin_recheck_done, info);
+}
+
+static void
+send_pin (MMModemLteCard *modem,
+ const char *pin,
+ MMModemFn callback,
+ gpointer user_data)
+{
+ MMCallbackInfo *info;
+ char *command;
+ MMAtSerialPort *port;
+
+ info = mm_callback_info_new (MM_MODEM (modem), callback, user_data);
+
+ /* Ensure we have a usable port to use for the unlock */
+ port = mm_generic_lte_get_best_at_port (MM_GENERIC_LTE (modem), &info->error);
+ if (!port) {
+ mm_callback_info_schedule (info);
+ return;
+ }
+
+ /* Modem may not be enabled yet, which sometimes can't be done until
+ * the device has been unlocked. In this case we have to open the port
+ * ourselves.
+ */
+ if (!mm_serial_port_open (MM_SERIAL_PORT (port), &info->error)) {
+ mm_callback_info_schedule (info);
+ return;
+ }
+ mm_callback_info_set_data (info, PIN_PORT_TAG, port, NULL);
+
+ command = g_strdup_printf ("+CPIN=\"%s\"", pin);
+ mm_at_serial_port_queue_command (port, command, 3, send_pin_done, info);
+ g_free (command);
+}
+
+
+static void
+enable_pin_done (MMAtSerialPort *port,
+ GString *response,
+ GError *error,
+ gpointer user_data)
+{
+ MMCallbackInfo *info = (MMCallbackInfo *) user_data;
+
+ /* If the modem has already been removed, return without
+ * scheduling callback */
+ if (mm_callback_info_check_modem_removed (info))
+ return;
+
+ if (error)
+ info->error = g_error_copy (error);
+ mm_callback_info_schedule (info);
+}
+
+static void
+enable_pin (MMModemLteCard *modem,
+ const char *pin,
+ gboolean enabled,
+ MMModemFn callback,
+ gpointer user_data)
+{
+ MMGenericLtePrivate *priv = MM_GENERIC_LTE_GET_PRIVATE (modem);
+ MMCallbackInfo *info;
+ char *command;
+
+ info = mm_callback_info_new (MM_MODEM (modem), callback, user_data);
+ command = g_strdup_printf ("+CLCK=\"SC\",%d,\"%s\"", enabled ? 1 : 0, pin);
+ mm_at_serial_port_queue_command (priv->primary, command, 3, enable_pin_done, info);
+ g_free (command);
+}
+
+static void
+change_pin_done (MMAtSerialPort *port,
+ GString *response,
+ GError *error,
+ gpointer user_data)
+{
+ MMCallbackInfo *info = (MMCallbackInfo *) user_data;
+
+ /* If the modem has already been removed, return without
+ * scheduling callback */
+ if (mm_callback_info_check_modem_removed (info))
+ return;
+
+ if (error)
+ info->error = g_error_copy (error);
+ mm_callback_info_schedule (info);
+}
+
+static void
+change_pin (MMModemLteCard *modem,
+ const char *old_pin,
+ const char *new_pin,
+ MMModemFn callback,
+ gpointer user_data)
+{
+ MMGenericLtePrivate *priv = MM_GENERIC_LTE_GET_PRIVATE (modem);
+ MMCallbackInfo *info;
+ char *command;
+
+ info = mm_callback_info_new (MM_MODEM (modem), callback, user_data);
+ command = g_strdup_printf ("+CPWD=\"SC\",\"%s\",\"%s\"", old_pin, new_pin);
+ mm_at_serial_port_queue_command (priv->primary, command, 3, change_pin_done, info);
+ g_free (command);
+}
+
+static void
+get_unlock_retries (MMModemLteCard *modem,
+ const char *pin_type,
+ MMModemUIntFn callback,
+ gpointer user_data)
+{
+ MMCallbackInfo *info = mm_callback_info_uint_new (MM_MODEM (modem), callback, user_data);
+
+ mm_callback_info_set_result (info,
+ GUINT_TO_POINTER (MM_MODEM_LTE_CARD_UNLOCK_RETRIES_NOT_SUPPORTED),
+ NULL);
+
+ mm_callback_info_schedule (info);
+}
+
+static void
+reg_info_updated (MMGenericLte *self,
+ gboolean update_rs,
+ MMGenericLteRegType rs_type,
+ MMModemLteNetworkRegStatus status,
+ gboolean update_code,
+ const char *oper_code,
+ gboolean update_name,
+ const char *oper_name)
+{
+ MMGenericLtePrivate *priv = MM_GENERIC_LTE_GET_PRIVATE (self);
+ MMModemLteNetworkRegStatus old_status;
+ gboolean changed = FALSE;
+
+ if (update_rs) {
+ g_return_if_fail ( rs_type == MM_GENERIC_LTE_REG_TYPE_CS
+ || rs_type == MM_GENERIC_LTE_REG_TYPE_PS);
+
+ old_status = lte_reg_status (self, NULL);
+ priv->reg_status[rs_type - 1] = status;
+ if (lte_reg_status (self, NULL) != old_status)
+ changed = TRUE;
+ }
+
+ if (update_code) {
+ if (g_strcmp0 (oper_code, priv->oper_code) != 0) {
+ g_free (priv->oper_code);
+ priv->oper_code = g_strdup (oper_code);
+ changed = TRUE;
+ }
+ }
+
+ if (update_name) {
+ if (g_strcmp0 (oper_name, priv->oper_name) != 0) {
+ g_free (priv->oper_name);
+ priv->oper_name = g_strdup (oper_name);
+ changed = TRUE;
+ }
+ }
+
+ if (changed) {
+ mm_modem_lte_network_registration_info (MM_MODEM_LTE_NETWORK (self),
+ lte_reg_status (self, NULL),
+ priv->oper_code,
+ priv->oper_name);
+ }
+}
+
+static void
+convert_operator_from_ucs2 (char **operator)
+{
+ const char *p;
+ char *converted;
+ size_t len;
+
+ g_return_if_fail (operator != NULL);
+ g_return_if_fail (*operator != NULL);
+
+ p = *operator;
+ len = strlen (p);
+
+ /* Len needs to be a multiple of 4 for UCS2 */
+ if ((len < 4) || ((len % 4) != 0))
+ return;
+
+ while (*p) {
+ if (!isxdigit (*p++))
+ return;
+ }
+
+ converted = mm_modem_charset_hex_to_utf8 (*operator, MM_MODEM_CHARSET_UCS2);
+ if (converted) {
+ g_free (*operator);
+ *operator = converted;
+ }
+}
+
+static char *
+parse_operator (const char *reply, MMModemCharset cur_charset)
+{
+ char *operator = NULL;
+
+ if (reply && !strncmp (reply, "+COPS: ", 7)) {
+ /* Got valid reply */
+ GRegex *r;
+ GMatchInfo *match_info;
+
+ reply += 7;
+ r = g_regex_new ("(\\d),(\\d),\"(.+)\"", G_REGEX_UNGREEDY, 0, NULL);
+ if (!r)
+ return NULL;
+
+ g_regex_match (r, reply, 0, &match_info);
+ if (g_match_info_matches (match_info))
+ operator = g_match_info_fetch (match_info, 3);
+
+ g_match_info_free (match_info);
+ g_regex_unref (r);
+ }
+
+ if (operator) {
+ /* Some modems (Option & HSO) return the operator name as a hexadecimal
+ * string of the bytes of the operator name as encoded by the current
+ * character set.
+ */
+ if (cur_charset == MM_MODEM_CHARSET_UCS2)
+ convert_operator_from_ucs2 (&operator);
+
+ /* Ensure the operator name is valid UTF-8 so that we can send it
+ * through D-Bus and such.
+ */
+ if (!g_utf8_validate (operator, -1, NULL)) {
+ g_free (operator);
+ operator = NULL;
+ }
+ }
+
+ return operator;
+}
+
+static void
+read_operator_code_done (MMAtSerialPort *port,
+ GString *response,
+ GError *error,
+ gpointer user_data)
+{
+ MMGenericLte *self = MM_GENERIC_LTE (user_data);
+ char *oper;
+
+ if (!error) {
+ oper = parse_operator (response->str, MM_MODEM_CHARSET_UNKNOWN);
+
+ if (oper) {
+ reg_info_updated (self, FALSE, MM_GENERIC_LTE_REG_TYPE_UNKNOWN, 0,
+ TRUE, oper,
+ FALSE, NULL);
+ }
+ }
+}
+
+static void
+read_operator_name_done (MMAtSerialPort *port,
+ GString *response,
+ GError *error,
+ gpointer user_data)
+{
+ MMGenericLte *self = MM_GENERIC_LTE (user_data);
+ MMGenericLtePrivate *priv = MM_GENERIC_LTE_GET_PRIVATE (self);
+ char *oper;
+
+ if (!error) {
+ oper = parse_operator (response->str, priv->cur_charset);
+
+ if (oper) {
+ reg_info_updated (self, FALSE, MM_GENERIC_LTE_REG_TYPE_UNKNOWN, 0,
+ FALSE, NULL,
+ TRUE, oper);
+ }
+ }
+}
+
+/* Registration */
+#define REG_STATUS_AGAIN_TAG "reg-status-again"
+
+void
+mm_generic_lte_pending_registration_stop (MMGenericLte *modem)
+{
+ MMGenericLtePrivate *priv = MM_GENERIC_LTE_GET_PRIVATE (modem);
+
+ if (priv->pending_reg_id) {
+ /* Clear the registration timeout handler */
+ g_source_remove (priv->pending_reg_id);
+ priv->pending_reg_id = 0;
+ }
+
+ if (priv->pending_reg_info) {
+ /* Clear any ongoing registration status callback */
+ mm_callback_info_set_data (priv->pending_reg_info, REG_STATUS_AGAIN_TAG, NULL, NULL);
+
+ /* And schedule the callback */
+ mm_callback_info_schedule (priv->pending_reg_info);
+ priv->pending_reg_info = NULL;
+ }
+}
+
+static void
+got_signal_quality (MMModem *modem,
+ guint32 quality,
+ GError *error,
+ gpointer user_data)
+{
+ mm_generic_lte_update_signal_quality (MM_GENERIC_LTE (modem), quality);
+}
+
+static void
+roam_disconnect_done (MMModem *modem,
+ GError *error,
+ gpointer user_data)
+{
+ mm_info ("Disconnected because roaming is not allowed");
+}
+
+static void
+get_reg_act_done (MMModem *modem,
+ guint32 act,
+ GError *error,
+ gpointer user_data)
+{
+ if (modem && !error && act)
+ mm_generic_lte_update_access_technology (MM_GENERIC_LTE (modem), act);
+}
+
+void
+mm_generic_lte_set_reg_status (MMGenericLte *self,
+ MMGenericLteRegType rs_type,
+ MMModemLteNetworkRegStatus status)
+{
+ MMGenericLtePrivate *priv;
+ MMAtSerialPort *port;
+
+ g_return_if_fail (MM_IS_GENERIC_LTE (self));
+
+ g_return_if_fail ( rs_type == MM_GENERIC_LTE_REG_TYPE_CS
+ || rs_type == MM_GENERIC_LTE_REG_TYPE_PS);
+
+ priv = MM_GENERIC_LTE_GET_PRIVATE (self);
+
+ if (priv->reg_status[rs_type - 1] == status)
+ return;
+
+ mm_dbg ("%s registration state changed: %d",
+ (rs_type == MM_GENERIC_LTE_REG_TYPE_CS) ? "CS" : "PS",
+ status);
+ priv->reg_status[rs_type - 1] = status;
+
+ port = mm_generic_lte_get_best_at_port (self, NULL);
+
+ if (status == MM_MODEM_LTE_NETWORK_REG_STATUS_HOME ||
+ status == MM_MODEM_LTE_NETWORK_REG_STATUS_ROAMING) {
+
+ /* If we're connected and we're not supposed to roam, but the device
+ * just roamed, disconnect the connection to avoid charging the user
+ * loads of money.
+ */
+ if ( (status == MM_MODEM_LTE_NETWORK_REG_STATUS_ROAMING)
+ && (mm_modem_get_state (MM_MODEM (self)) == MM_MODEM_STATE_CONNECTED)
+ && (priv->roam_allowed == FALSE)) {
+ mm_modem_disconnect (MM_MODEM (self), roam_disconnect_done, NULL);
+ } else {
+ /* Grab the new operator name and MCC/MNC */
+ if (port) {
+ mm_at_serial_port_queue_command (port, "+COPS=3,2;+COPS?", 3, read_operator_code_done, self);
+ mm_at_serial_port_queue_command (port, "+COPS=3,0;+COPS?", 3, read_operator_name_done, self);
+ }
+
+ /* And update signal quality and access technology */
+ mm_modem_lte_network_get_signal_quality (MM_MODEM_LTE_NETWORK (self), got_signal_quality, NULL);
+ if (MM_GENERIC_LTE_GET_CLASS (self)->get_access_technology)
+ MM_GENERIC_LTE_GET_CLASS (self)->get_access_technology (self, get_reg_act_done, NULL);
+ }
+ } else
+ reg_info_updated (self, FALSE, rs_type, 0, TRUE, NULL, TRUE, NULL);
+
+ mm_generic_lte_update_enabled_state (self, TRUE, MM_MODEM_STATE_REASON_NONE);
+}
+
+/* Returns TRUE if the modem is "done", ie has registered or been denied */
+static gboolean
+reg_status_updated (MMGenericLte *self,
+ MMGenericLteRegType rs_type,
+ int new_value,
+ GError **error)
+{
+ MMModemLteNetworkRegStatus status;
+ gboolean status_done = FALSE;
+
+ switch (new_value) {
+ case 0:
+ status = MM_MODEM_LTE_NETWORK_REG_STATUS_IDLE;
+ break;
+ case 1:
+ status = MM_MODEM_LTE_NETWORK_REG_STATUS_HOME;
+ break;
+ case 2:
+ status = MM_MODEM_LTE_NETWORK_REG_STATUS_SEARCHING;
+ break;
+ case 3:
+ status = MM_MODEM_LTE_NETWORK_REG_STATUS_DENIED;
+ break;
+ case 4:
+ status = MM_MODEM_LTE_NETWORK_REG_STATUS_UNKNOWN;
+ break;
+ case 5:
+ status = MM_MODEM_LTE_NETWORK_REG_STATUS_ROAMING;
+ break;
+ default:
+ status = MM_MODEM_LTE_NETWORK_REG_STATUS_UNKNOWN;
+ break;
+ }
+
+ mm_generic_lte_set_reg_status (self, rs_type, status);
+
+ /* Registration has either completed successfully or completely failed */
+ switch (status) {
+ case MM_MODEM_LTE_NETWORK_REG_STATUS_HOME:
+ case MM_MODEM_LTE_NETWORK_REG_STATUS_ROAMING:
+ /* Successfully registered - stop registration */
+ status_done = TRUE;
+ break;
+ case MM_MODEM_LTE_NETWORK_REG_STATUS_DENIED:
+ /* registration failed - stop registration */
+ if (error)
+ *error = mm_mobile_error_for_code (MM_MOBILE_ERROR_NETWORK_NOT_ALLOWED);
+ status_done = TRUE;
+ break;
+ case MM_MODEM_LTE_NETWORK_REG_STATUS_SEARCHING:
+ if (error)
+ *error = mm_mobile_error_for_code (MM_MOBILE_ERROR_NETWORK_TIMEOUT);
+ break;
+ case MM_MODEM_LTE_NETWORK_REG_STATUS_IDLE:
+ if (error)
+ *error = mm_mobile_error_for_code (MM_MOBILE_ERROR_NO_NETWORK);
+ break;
+ default:
+ if (error)
+ *error = mm_mobile_error_for_code (MM_MOBILE_ERROR_UNKNOWN);
+ break;
+ }
+ return status_done;
+}
+
+static MMGenericLteRegType
+cgreg_to_reg_type (gboolean cgreg)
+{
+ return (cgreg ? MM_GENERIC_LTE_REG_TYPE_PS : MM_GENERIC_LTE_REG_TYPE_CS);
+}
+
+static void
+reg_state_changed (MMAtSerialPort *port,
+ GMatchInfo *match_info,
+ gpointer user_data)
+{
+ MMGenericLte *self = MM_GENERIC_LTE (user_data);
+ MMGenericLtePrivate *priv = MM_GENERIC_LTE_GET_PRIVATE (self);
+ guint32 state = 0;
+ gulong lac = 0, cell_id = 0;
+ gint act = -1;
+ gboolean cgreg = FALSE;
+ GError *error = NULL;
+
+ if (!mm_lte_parse_creg_response (match_info, &state, &lac, &cell_id, &act, &cgreg, &error)) {
+ mm_warn ("error parsing unsolicited registration: %s",
+ error && error->message ? error->message : "(unknown)");
+ return;
+ }
+
+ if (reg_status_updated (self, cgreg_to_reg_type (cgreg), state, NULL)) {
+ /* If registration is finished (either registered or failed) but the
+ * registration query hasn't completed yet, just remove the timeout and
+ * let the registration query complete.
+ */
+ if (priv->pending_reg_id) {
+ g_source_remove (priv->pending_reg_id);
+ priv->pending_reg_id = 0;
+ }
+ }
+
+ //update_lac_ci (self, lac, cell_id, cgreg ? 1 : 0);
+
+ /* Only update access technology if it appeared in the CREG/CGREG response */
+ if (act != -1)
+ mm_generic_lte_update_access_technology (self, etsi_act_to_mm_act (act));
+}
+
+static gboolean
+reg_status_again (gpointer data)
+{
+ MMCallbackInfo *info = (MMCallbackInfo *) data;
+ MMGenericLtePrivate *priv = MM_GENERIC_LTE_GET_PRIVATE (info->modem);
+
+ g_warn_if_fail (info == priv->pending_reg_info);
+
+ if (priv->pending_reg_info)
+ get_registration_status (priv->primary, info);
+
+ return FALSE;
+}
+
+static void
+reg_status_again_remove (gpointer data)
+{
+ guint id = GPOINTER_TO_UINT (data);
+
+ /* Technically the GSource ID can be 0, but in practice it won't be */
+ if (id > 0)
+ g_source_remove (id);
+}
+
+static gboolean
+handle_reg_status_response (MMGenericLte *self,
+ GString *response,
+ GError **error)
+{
+ MMGenericLtePrivate *priv = MM_GENERIC_LTE_GET_PRIVATE (self);
+ GMatchInfo *match_info;
+ guint32 status = 0;
+ gulong lac = 0, ci = 0;
+ gint act = -1;
+ gboolean cgreg = FALSE;
+ guint i;
+
+ /* Try to match the response */
+ for (i = 0; i < priv->reg_regex->len; i++) {
+ GRegex *r = g_ptr_array_index (priv->reg_regex, i);
+
+ if (g_regex_match (r, response->str, 0, &match_info))
+ break;
+ g_match_info_free (match_info);
+ match_info = NULL;
+ }
+
+ if (!match_info) {
+ g_set_error_literal (error, MM_MODEM_ERROR, MM_MODEM_ERROR_GENERAL,
+ "Unknown registration status response");
+ return FALSE;
+ }
+
+ /* And parse it */
+ if (!mm_lte_parse_creg_response (match_info, &status, &lac, &ci, &act, &cgreg, error)) {
+ g_match_info_free (match_info);
+ return FALSE;
+ }
+
+ /* Success; update cached location information */
+ //update_lac_ci (self, lac, ci, cgreg ? 1 : 0);
+
+ /* Only update access technology if it appeared in the CREG/CGREG response */
+ if (act != -1)
+ mm_generic_lte_update_access_technology (self, etsi_act_to_mm_act (act));
+
+ if (status >= 0) {
+ /* Update cached registration status */
+ reg_status_updated (self, cgreg_to_reg_type (cgreg), status, NULL);
+ }
+
+ return TRUE;
+}
+
+#define CGREG_TRIED_TAG "cgreg-tried"
+
+static void
+get_reg_status_done (MMAtSerialPort *port,
+ GString *response,
+ GError *error,
+ gpointer user_data)
+{
+ MMCallbackInfo *info = (MMCallbackInfo *) user_data;
+ MMGenericLte *self;
+ MMGenericLtePrivate *priv;
+ guint id;
+ MMModemLteNetworkRegStatus status;
+
+ /* If the modem has already been removed, return without
+ * scheduling callback */
+ if (mm_callback_info_check_modem_removed (info))
+ return;
+
+ self = MM_GENERIC_LTE (info->modem);
+ priv = MM_GENERIC_LTE_GET_PRIVATE (self);
+
+ /* This function should only get called during the connect sequence when
+ * polling for registration state, since explicit registration requests
+ * from D-Bus clients are filled from the cached registration state.
+ */
+ g_return_if_fail (info == priv->pending_reg_info);
+
+ if (error) {
+ gboolean cgreg_tried = !!mm_callback_info_get_data (info, CGREG_TRIED_TAG);
+
+ /* If this was a +CREG error, try +CGREG. Some devices (blackberries)
+ * respond to +CREG with an error but return a valid +CGREG response.
+ * So try both. If we get an error from both +CREG and +CGREG, that's
+ * obviously a hard fail.
+ */
+ if (cgreg_tried == FALSE) {
+ mm_callback_info_set_data (info, CGREG_TRIED_TAG, GUINT_TO_POINTER (TRUE), NULL);
+ mm_at_serial_port_queue_command (port, "+CGREG?", 10, get_reg_status_done, info);
+ return;
+ } else {
+ info->error = g_error_copy (error);
+ goto reg_done;
+ }
+ }
+
+ /* The unsolicited registration state handlers will intercept the CREG
+ * response and update the cached registration state for us, so we usually
+ * just need to check the cached state here.
+ */
+
+ if (strlen (response->str)) {
+ /* But just in case the unsolicited handlers doesn't do it... */
+ if (!handle_reg_status_response (self, response, &info->error))
+ goto reg_done;
+ }
+
+ status = lte_reg_status (self, NULL);
+ if ( status != MM_MODEM_LTE_NETWORK_REG_STATUS_HOME
+ && status != MM_MODEM_LTE_NETWORK_REG_STATUS_ROAMING
+ && status != MM_MODEM_LTE_NETWORK_REG_STATUS_DENIED) {
+ /* If we're still waiting for automatic registration to complete or
+ * fail, check again in a few seconds.
+ */
+ id = g_timeout_add_seconds (1, reg_status_again, info);
+ mm_callback_info_set_data (info, REG_STATUS_AGAIN_TAG,
+ GUINT_TO_POINTER (id),
+ reg_status_again_remove);
+ return;
+ }
+
+reg_done:
+ /* This will schedule the pending registration's the callback for us */
+ mm_generic_lte_pending_registration_stop (self);
+}
+
+static void
+get_registration_status (MMAtSerialPort *port, MMCallbackInfo *info)
+{
+ mm_at_serial_port_queue_command (port, "+CREG?", 10, get_reg_status_done, info);
+}
+
+static void
+register_done (MMAtSerialPort *port,
+ GString *response,
+ GError *error,
+ gpointer user_data)
+{
+ MMCallbackInfo *info = user_data;
+ MMGenericLtePrivate *priv;
+
+ mm_callback_info_unref (info);
+
+ /* If the modem has already been removed, return without
+ * scheduling callback */
+ if (mm_callback_info_check_modem_removed (info))
+ return;
+
+ priv = MM_GENERIC_LTE_GET_PRIVATE (info->modem);
+
+ /* If the registration timed out (and thus pending_reg_info will be NULL)
+ * and the modem eventually got around to sending the response for the
+ * registration request then just ignore the response since the callback is
+ * already called.
+ */
+
+ if (priv->pending_reg_info) {
+ g_warn_if_fail (info == priv->pending_reg_info);
+
+ /* Don't use cached registration state here since it could be up to
+ * 30 seconds old. Get fresh registration state.
+ */
+ get_registration_status (port, info);
+ }
+}
+
+static gboolean
+registration_timed_out (gpointer data)
+{
+ MMCallbackInfo *info = (MMCallbackInfo *) data;
+ MMGenericLtePrivate *priv = MM_GENERIC_LTE_GET_PRIVATE (info->modem);
+
+ g_warn_if_fail (info == priv->pending_reg_info);
+
+ /* Clear out circuit-switched registration info... */
+ reg_info_updated (MM_GENERIC_LTE (info->modem),
+ TRUE, MM_GENERIC_LTE_REG_TYPE_CS, MM_MODEM_LTE_NETWORK_REG_STATUS_IDLE,
+ TRUE, NULL,
+ TRUE, NULL);
+ /* ... and packet-switched registration info */
+ reg_info_updated (MM_GENERIC_LTE (info->modem),
+ TRUE, MM_GENERIC_LTE_REG_TYPE_PS, MM_MODEM_LTE_NETWORK_REG_STATUS_IDLE,
+ TRUE, NULL,
+ TRUE, NULL);
+
+ info->error = mm_mobile_error_for_code (MM_MOBILE_ERROR_NETWORK_TIMEOUT);
+ mm_generic_lte_pending_registration_stop (MM_GENERIC_LTE (info->modem));
+
+ return FALSE;
+}
+
+static gboolean
+reg_is_idle (MMModemLteNetworkRegStatus status)
+{
+ if ( status == MM_MODEM_LTE_NETWORK_REG_STATUS_HOME
+ || status == MM_MODEM_LTE_NETWORK_REG_STATUS_ROAMING
+ || status == MM_MODEM_LTE_NETWORK_REG_STATUS_SEARCHING)
+ return FALSE;
+ return TRUE;
+}
+
+static void
+do_register (MMModemLteNetwork *modem,
+ const char *network_id,
+ MMModemFn callback,
+ gpointer user_data)
+{
+ MMGenericLte *self = MM_GENERIC_LTE (modem);
+ MMGenericLtePrivate *priv = MM_GENERIC_LTE_GET_PRIVATE (self);
+ MMCallbackInfo *info;
+ char *command = NULL;
+
+ /* Clear any previous registration */
+ mm_generic_lte_pending_registration_stop (self);
+
+ info = mm_callback_info_new (MM_MODEM (modem), callback, user_data);
+
+ priv->pending_reg_id = g_timeout_add_seconds (60, registration_timed_out, info);
+ priv->pending_reg_info = info;
+
+ /* If the user sent a specific network to use, lock it in. If no specific
+ * network was given, and the modem is not registered and not searching,
+ * kick it to search for a network. Also do auto registration if the modem
+ * had been set to manual registration last time but now is not.
+ */
+ if (network_id) {
+ command = g_strdup_printf ("+COPS=1,2,\"%s\"", network_id);
+ priv->manual_reg = TRUE;
+ } else if (reg_is_idle (lte_reg_status (self, NULL)) || priv->manual_reg) {
+ command = g_strdup ("+COPS=0,,");
+ priv->manual_reg = FALSE;
+ }
+
+ /* Ref the callback info to ensure it stays alive for register_done() even
+ * if the timeout triggers and ends registration (which calls the callback
+ * and unrefs the callback info). Some devices (hso) will delay the
+ * registration response until the registration is done (and thus
+ * unsolicited registration responses will arrive before the +COPS is
+ * complete). Most other devices will return the +COPS response immediately
+ * and the unsolicited response (if any) at a later time.
+ *
+ * To handle both these cases, unsolicited registration responses will just
+ * remove the pending registration timeout but we let the +COPS command
+ * complete. For those devices that delay the +COPS response (hso) the
+ * callback will be called from register_done(). For those devices that
+ * return the +COPS response immediately, we'll poll the registration state
+ * and call the callback from get_reg_status_done() in response to the
+ * polled response. The registration timeout will only be triggered when
+ * the +COPS response is never received.
+ */
+ mm_callback_info_ref (info);
+
+ if (command) {
+ mm_at_serial_port_queue_command (priv->primary, command, 120, register_done, info);
+ g_free (command);
+ } else
+ register_done (priv->primary, NULL, NULL, info);
+}
+
+static void
+lte_network_reg_info_invoke (MMCallbackInfo *info)
+{
+ MMGenericLtePrivate *priv = MM_GENERIC_LTE_GET_PRIVATE (info->modem);
+ MMModemLteNetworkRegInfoFn callback = (MMModemLteNetworkRegInfoFn) info->callback;
+
+ callback (MM_MODEM_LTE_NETWORK (info->modem),
+ lte_reg_status (MM_GENERIC_LTE (info->modem), NULL),
+ priv->oper_code,
+ priv->oper_name,
+ info->error,
+ info->user_data);
+}
+
+static void
+get_registration_info (MMModemLteNetwork *self,
+ MMModemLteNetworkRegInfoFn callback,
+ gpointer user_data)
+{
+ MMCallbackInfo *info;
+
+ info = mm_callback_info_new_full (MM_MODEM (self),
+ lte_network_reg_info_invoke,
+ G_CALLBACK (callback),
+ user_data);
+ /* Registration info updates are handled internally either by unsolicited
+ * updates or by polling. Thus just return the cached registration state.
+ */
+ mm_callback_info_schedule (info);
+}
+
+void
+mm_generic_lte_connect_complete (MMGenericLte *modem,
+ GError *error,
+ MMCallbackInfo *info)
+{
+ MMGenericLtePrivate *priv;
+
+ g_return_if_fail (modem != NULL);
+ g_return_if_fail (MM_IS_GENERIC_LTE (modem));
+ g_return_if_fail (info != NULL);
+
+ priv = MM_GENERIC_LTE_GET_PRIVATE (modem);
+
+ if (error) {
+ mm_generic_lte_update_enabled_state (modem, FALSE, MM_MODEM_STATE_REASON_NONE);
+ info->error = g_error_copy (error);
+ } else {
+ /* Modem is connected; update the state */
+ mm_port_set_connected (priv->data, TRUE);
+ mm_modem_set_state (MM_MODEM (modem),
+ MM_MODEM_STATE_CONNECTED,
+ MM_MODEM_STATE_REASON_NONE);
+ }
+
+ mm_callback_info_schedule (info);
+}
+
+static void
+disconnect_done (MMModem *modem,
+ GError *error,
+ gpointer user_data)
+{
+ MMCallbackInfo *info = (MMCallbackInfo *) user_data;
+ MMModemState prev_state;
+
+ /* Do nothing if modem removed */
+ if (!modem || mm_callback_info_check_modem_removed (info))
+ return;
+
+ if (error) {
+ info->error = g_error_copy (error);
+ /* Reset old state since the operation failed */
+ prev_state = GPOINTER_TO_UINT (mm_callback_info_get_data (info, MM_GENERIC_LTE_PREV_STATE_TAG));
+ mm_modem_set_state (MM_MODEM (info->modem),
+ prev_state,
+ MM_MODEM_STATE_REASON_NONE);
+ } else {
+ MMGenericLte *self = MM_GENERIC_LTE (modem);
+ MMGenericLtePrivate *priv = MM_GENERIC_LTE_GET_PRIVATE (self);
+
+ mm_port_set_connected (priv->data, FALSE);
+ priv->cid = -1;
+ mm_generic_lte_update_enabled_state (self, FALSE, MM_MODEM_STATE_REASON_NONE);
+ }
+
+ mm_callback_info_schedule (info);
+}
+
+static void
+disconnect_all_done (MMAtSerialPort *port,
+ GString *response,
+ GError *error,
+ gpointer user_data)
+{
+ MMCallbackInfo *info = (MMCallbackInfo *)user_data;
+
+ /* If the modem has already been removed, return without
+ * scheduling callback */
+ if (mm_callback_info_check_modem_removed (info))
+ return;
+
+ mm_callback_info_schedule (info);
+}
+
+static void
+disconnect_send_cgact (MMAtSerialPort *port,
+ gint cid,
+ MMAtSerialResponseFn callback,
+ gpointer user_data)
+{
+ char *command;
+
+ if (cid >= 0)
+ command = g_strdup_printf ("+CGACT=0,%d", cid);
+ else {
+ /* Disable all PDP contexts */
+ command = g_strdup_printf ("+CGACT=0");
+ }
+
+ mm_at_serial_port_queue_command (port, command, 3, callback, user_data);
+ g_free (command);
+}
+
+#define DISCONNECT_CGACT_DONE_TAG "disconnect-cgact-done"
+
+static void
+disconnect_flash_done (MMSerialPort *port,
+ GError *error,
+ gpointer user_data)
+{
+ MMCallbackInfo *info = (MMCallbackInfo *) user_data;
+ MMGenericLtePrivate *priv;
+
+ /* If the modem has already been removed, return without
+ * scheduling callback */
+ if (mm_callback_info_check_modem_removed (info))
+ return;
+
+ if (error) {
+ /* Ignore "NO CARRIER" response when modem disconnects and any flash
+ * failures we might encounter. Other errors are hard errors.
+ */
+ if ( !g_error_matches (error, MM_MODEM_CONNECT_ERROR, MM_MODEM_CONNECT_ERROR_NO_CARRIER)
+ && !g_error_matches (error, MM_SERIAL_ERROR, MM_SERIAL_ERROR_FLASH_FAILED)) {
+ info->error = g_error_copy (error);
+ mm_callback_info_schedule (info);
+ return;
+ }
+ }
+
+ priv = MM_GENERIC_LTE_GET_PRIVATE (info->modem);
+ mm_port_set_connected (priv->data, FALSE);
+
+ /* Don't bother doing the CGACT again if it was done on a secondary port */
+ if (mm_callback_info_get_data (info, DISCONNECT_CGACT_DONE_TAG))
+ disconnect_all_done (MM_AT_SERIAL_PORT (priv->primary), NULL, NULL, info);
+ else {
+ disconnect_send_cgact (MM_AT_SERIAL_PORT (priv->primary),
+ priv->cid,
+ disconnect_all_done,
+ info);
+ }
+}
+
+static void
+disconnect_secondary_cgact_done (MMAtSerialPort *port,
+ GString *response,
+ GError *error,
+ gpointer user_data)
+{
+ MMCallbackInfo *info = user_data;
+ MMGenericLte *self;
+ MMGenericLtePrivate *priv;
+
+ /* If the modem has already been removed, return without
+ * scheduling callback */
+ if (mm_callback_info_check_modem_removed (info))
+ return;
+
+ self = MM_GENERIC_LTE (info->modem);
+ priv = MM_GENERIC_LTE_GET_PRIVATE (self);
+
+ /* Now that we've tried deactivating the PDP context on the secondary
+ * port, continue with flashing the primary port.
+ */
+ if (!error)
+ mm_callback_info_set_data (info, DISCONNECT_CGACT_DONE_TAG, GUINT_TO_POINTER (TRUE), NULL);
+
+ mm_serial_port_flash (MM_SERIAL_PORT (priv->primary), 1000, TRUE, disconnect_flash_done, info);
+}
+
+static void
+real_do_disconnect (MMGenericLte *self,
+ gint cid,
+ MMModemFn callback,
+ gpointer user_data)
+{
+ MMGenericLtePrivate *priv = MM_GENERIC_LTE_GET_PRIVATE (self);
+ MMCallbackInfo *info;
+
+ info = mm_callback_info_new (MM_MODEM (self), callback, user_data);
+
+ /* If the primary port is connected (with PPP) then try sending the PDP
+ * context deactivation on the secondary port because not all modems will
+ * respond to flashing (since either the modem or the kernel's serial
+ * driver doesn't support it).
+ */
+ if ( mm_port_get_connected (MM_PORT (priv->primary))
+ && priv->secondary
+ && mm_serial_port_is_open (MM_SERIAL_PORT (priv->secondary))) {
+ disconnect_send_cgact (MM_AT_SERIAL_PORT (priv->secondary),
+ priv->cid,
+ disconnect_secondary_cgact_done,
+ info);
+ } else {
+ /* Just flash the primary port */
+ mm_serial_port_flash (MM_SERIAL_PORT (priv->primary), 1000, TRUE, disconnect_flash_done, info);
+ }
+}
+
+static void
+disconnect (MMModem *modem,
+ MMModemFn callback,
+ gpointer user_data)
+{
+ MMGenericLte *self = MM_GENERIC_LTE (modem);
+ MMGenericLtePrivate *priv = MM_GENERIC_LTE_GET_PRIVATE (self);
+ MMCallbackInfo *info;
+ MMModemState state;
+
+ priv->roam_allowed = TRUE;
+
+ info = mm_callback_info_new (modem, callback, user_data);
+
+ /* Cache the previous state so we can reset it if the operation fails */
+ state = mm_modem_get_state (modem);
+ mm_callback_info_set_data (info,
+ MM_GENERIC_LTE_PREV_STATE_TAG,
+ GUINT_TO_POINTER (state),
+ NULL);
+
+ mm_modem_set_state (modem, MM_MODEM_STATE_DISCONNECTING, MM_MODEM_STATE_REASON_NONE);
+
+ g_assert (MM_GENERIC_LTE_GET_CLASS (self)->do_disconnect);
+ MM_GENERIC_LTE_GET_CLASS (self)->do_disconnect (self, priv->cid, disconnect_done, info);
+}
+
+static void
+lte_network_scan_invoke (MMCallbackInfo *info)
+{
+ MMModemLteNetworkScanFn callback = (MMModemLteNetworkScanFn) info->callback;
+
+ callback (MM_MODEM_LTE_NETWORK (info->modem),
+ (GPtrArray *) mm_callback_info_get_data (info, "scan-results"),
+ info->error,
+ info->user_data);
+}
+
+static void
+scan_done (MMAtSerialPort *port,
+ GString *response,
+ GError *error,
+ gpointer user_data)
+{
+ MMCallbackInfo *info = (MMCallbackInfo *) user_data;
+ GPtrArray *results;
+
+ /* If the modem has already been removed, return without
+ * scheduling callback */
+ if (mm_callback_info_check_modem_removed (info))
+ return;
+
+ if (error)
+ info->error = g_error_copy (error);
+ else {
+ results = mm_lte_parse_scan_response (response->str, &info->error);
+ if (results)
+ mm_callback_info_set_data (info, "scan-results", results, mm_lte_destroy_scan_data);
+ }
+
+ mm_callback_info_schedule (info);
+}
+
+static void
+scan (MMModemLteNetwork *modem,
+ MMModemLteNetworkScanFn callback,
+ gpointer user_data)
+{
+ MMGenericLtePrivate *priv = MM_GENERIC_LTE_GET_PRIVATE (modem);
+ MMCallbackInfo *info;
+
+ info = mm_callback_info_new_full (MM_MODEM (modem),
+ lte_network_scan_invoke,
+ G_CALLBACK (callback),
+ user_data);
+
+ mm_at_serial_port_queue_command (priv->primary, "+COPS=?", 120, scan_done, info);
+}
+
+/* SetApn */
+
+#define APN_CID_TAG "generic-lte-cid"
+
+static void
+set_apn_done (MMAtSerialPort *port,
+ GString *response,
+ GError *error,
+ gpointer user_data)
+{
+ MMCallbackInfo *info = (MMCallbackInfo *) user_data;
+
+ /* If the modem has already been removed, return without
+ * scheduling callback */
+ if (mm_callback_info_check_modem_removed (info))
+ return;
+
+ if (error)
+ info->error = g_error_copy (error);
+ else {
+ MMGenericLtePrivate *priv = MM_GENERIC_LTE_GET_PRIVATE (info->modem);
+
+ priv->cid = GPOINTER_TO_INT (mm_callback_info_get_data (info, APN_CID_TAG));
+ }
+
+ mm_callback_info_schedule (info);
+}
+
+static void
+cid_range_read (MMAtSerialPort *port,
+ GString *response,
+ GError *error,
+ gpointer user_data)
+{
+ MMCallbackInfo *info = (MMCallbackInfo *) user_data;
+ guint32 cid = 0;
+
+ /* If the modem has already been removed, return without
+ * scheduling callback */
+ if (mm_callback_info_check_modem_removed (info))
+ return;
+
+ if (error)
+ info->error = g_error_copy (error);
+ else if (g_str_has_prefix (response->str, "+CGDCONT:")) {
+ GRegex *r;
+ GMatchInfo *match_info;
+
+ r = g_regex_new ("\\+CGDCONT:\\s*\\((\\d+)-(\\d+)\\),\\(?\"(\\S+)\"",
+ G_REGEX_DOLLAR_ENDONLY | G_REGEX_RAW,
+ 0, &info->error);
+ if (r) {
+ g_regex_match_full (r, response->str, response->len, 0, 0, &match_info, &info->error);
+ while (cid == 0 && g_match_info_matches (match_info)) {
+ char *tmp;
+
+ tmp = g_match_info_fetch (match_info, 3);
+ if (!strcmp (tmp, "IP")) {
+ int max_cid;
+ int highest_cid = GPOINTER_TO_INT (mm_callback_info_get_data (info, "highest-cid"));
+
+ g_free (tmp);
+
+ tmp = g_match_info_fetch (match_info, 2);
+ max_cid = atoi (tmp);
+
+ if (highest_cid < max_cid)
+ cid = highest_cid + 1;
+ else
+ cid = highest_cid;
+ }
+
+ g_free (tmp);
+ g_match_info_next (match_info, NULL);
+ }
+
+ if (cid == 0)
+ /* Choose something */
+ cid = 1;
+ }
+ } else
+ info->error = g_error_new_literal (MM_MODEM_ERROR,
+ MM_MODEM_ERROR_GENERAL,
+ "Could not parse the response");
+
+ if (info->error)
+ mm_callback_info_schedule (info);
+ else {
+ const char *apn = (const char *) mm_callback_info_get_data (info, "apn");
+ char *command;
+
+ mm_callback_info_set_data (info, APN_CID_TAG, GINT_TO_POINTER (cid), NULL);
+
+ command = g_strdup_printf ("+CGDCONT=%d,\"IP\",\"%s\"", cid, apn);
+ mm_at_serial_port_queue_command (port, command, 3, set_apn_done, info);
+ g_free (command);
+ }
+}
+
+static void
+existing_apns_read (MMAtSerialPort *port,
+ GString *response,
+ GError *error,
+ gpointer user_data)
+{
+ MMCallbackInfo *info = (MMCallbackInfo *) user_data;
+ gboolean found = FALSE;
+
+ /* If the modem has already been removed, return without
+ * scheduling callback */
+ if (mm_callback_info_check_modem_removed (info))
+ return;
+
+ if (error) {
+ info->error = g_error_copy (error);
+ } else if (g_str_has_prefix (response->str, "+CGDCONT:")) {
+ GRegex *r;
+ GMatchInfo *match_info;
+
+ r = g_regex_new ("\\+CGDCONT:\\s*(\\d+)\\s*,\"(\\S+)\",\"(\\S+)\",\"(\\S*)\"",
+ G_REGEX_DOLLAR_ENDONLY | G_REGEX_RAW,
+ 0, &info->error);
+ if (r) {
+ const char *new_apn = (const char *) mm_callback_info_get_data (info, "apn");
+
+ g_regex_match_full (r, response->str, response->len, 0, 0, &match_info, &info->error);
+ while (!found && g_match_info_matches (match_info)) {
+ char *cid;
+ char *pdp_type;
+ char *apn;
+ int num_cid;
+
+ cid = g_match_info_fetch (match_info, 1);
+ num_cid = atoi (cid);
+ pdp_type = g_match_info_fetch (match_info, 2);
+ apn = g_match_info_fetch (match_info, 3);
+
+ if (!strcmp (apn, new_apn)) {
+ MM_GENERIC_LTE_GET_PRIVATE (info->modem)->cid = num_cid;
+ found = TRUE;
+ }
+
+ if (!found && !strcmp (pdp_type, "IP")) {
+ int highest_cid;
+
+ highest_cid = GPOINTER_TO_INT (mm_callback_info_get_data (info, "highest-cid"));
+ if (num_cid > highest_cid)
+ mm_callback_info_set_data (info, "highest-cid", GINT_TO_POINTER (num_cid), NULL);
+ }
+
+ g_free (cid);
+ g_free (pdp_type);
+ g_free (apn);
+ g_match_info_next (match_info, NULL);
+ }
+
+ g_match_info_free (match_info);
+ g_regex_unref (r);
+ }
+ } else if (strlen (response->str) == 0) {
+ /* No APNs configured, just don't set error */
+ } else
+ info->error = g_error_new_literal (MM_MODEM_ERROR,
+ MM_MODEM_ERROR_GENERAL,
+ "Could not parse the response");
+
+ if (found || info->error)
+ mm_callback_info_schedule (info);
+ else {
+ /* APN not configured on the card. Get the allowed CID range */
+ mm_at_serial_port_queue_command_cached (port, "+CGDCONT=?", 3, cid_range_read, info);
+ }
+}
+
+static void
+set_apn (MMModemLteNetwork *modem,
+ const char *apn,
+ MMModemFn callback,
+ gpointer user_data)
+{
+ MMGenericLtePrivate *priv = MM_GENERIC_LTE_GET_PRIVATE (modem);
+ MMCallbackInfo *info;
+
+ info = mm_callback_info_new (MM_MODEM (modem), callback, user_data);
+ mm_callback_info_set_data (info, "apn", g_strdup (apn), g_free);
+
+ /* Start by searching if the APN is already in card */
+ mm_at_serial_port_queue_command (priv->primary, "+CGDCONT?", 3, existing_apns_read, info);
+}
+
+/* GetSignalQuality */
+
+static gboolean
+emit_signal_quality_change (gpointer user_data)
+{
+ MMGenericLte *self = MM_GENERIC_LTE (user_data);
+ MMGenericLtePrivate *priv = MM_GENERIC_LTE_GET_PRIVATE (self);
+
+ priv->signal_quality_id = 0;
+ priv->signal_emit_timestamp = time (NULL);
+ mm_modem_lte_network_signal_quality (MM_MODEM_LTE_NETWORK (self), priv->signal_quality);
+ return FALSE;
+}
+
+void
+mm_generic_lte_update_signal_quality (MMGenericLte *self, guint32 quality)
+{
+ MMGenericLtePrivate *priv;
+ guint delay = 0;
+
+ g_return_if_fail (self != NULL);
+ g_return_if_fail (MM_IS_GENERIC_LTE (self));
+ g_return_if_fail (quality <= 100);
+
+ priv = MM_GENERIC_LTE_GET_PRIVATE (self);
+
+ priv->signal_update_timestamp = time (NULL);
+
+ if (priv->signal_quality == quality)
+ return;
+
+ priv->signal_quality = quality;
+
+ /* Some modems will send unsolcited signal quality changes quite often,
+ * so rate-limit them to every few seconds. Track the last time we
+ * emitted signal quality so that we send the signal immediately if there
+ * haven't been any updates in a while.
+ */
+ if (!priv->signal_quality_id) {
+ if (priv->signal_emit_timestamp > 0) {
+ time_t curtime;
+ long int diff;
+
+ curtime = time (NULL);
+ diff = curtime - priv->signal_emit_timestamp;
+ if (diff == 0) {
+ /* If the device is sending more than one update per second,
+ * make sure we don't spam clients with signals.
+ */
+ delay = 3;
+ } else if ((diff > 0) && (diff <= 3)) {
+ /* Emitted an update less than 3 seconds ago; schedule an update
+ * 3 seconds after the previous one.
+ */
+ delay = (guint) diff;
+ } else {
+ /* Otherwise, we haven't emitted an update in the last 3 seconds,
+ * or the user turned their clock back, or something like that.
+ */
+ delay = 0;
+ }
+ }
+
+ priv->signal_quality_id = g_timeout_add_seconds (delay,
+ emit_signal_quality_change,
+ self);
+ }
+}
+
+static void
+get_csq_done (MMAtSerialPort *port,
+ GString *response,
+ GError *error,
+ gpointer user_data)
+{
+ MMCallbackInfo *info = (MMCallbackInfo *) user_data;
+ char *reply = response->str;
+ gboolean parsed = FALSE;
+
+ /* If the modem has already been removed, return without
+ * scheduling callback */
+ if (mm_callback_info_check_modem_removed (info))
+ return;
+
+ if (error) {
+ info->error = g_error_copy (error);
+ goto done;
+ }
+
+ if (!strncmp (reply, "+CSQ: ", 6)) {
+ /* Got valid reply */
+ int quality;
+ int ber;
+
+ if (sscanf (reply + 6, "%d, %d", &quality, &ber)) {
+ /* 99 means unknown */
+ if (quality == 99) {
+ info->error = g_error_new_literal (MM_MOBILE_ERROR,
+ MM_MOBILE_ERROR_NO_NETWORK,
+ "No service");
+ } else {
+ /* Normalize the quality */
+ quality = CLAMP (quality, 0, 31) * 100 / 31;
+
+ mm_generic_lte_update_signal_quality (MM_GENERIC_LTE (info->modem), quality);
+ mm_callback_info_set_result (info, GUINT_TO_POINTER (quality), NULL);
+ }
+ parsed = TRUE;
+ }
+ }
+
+ if (!parsed && !info->error) {
+ info->error = g_error_new_literal (MM_MODEM_ERROR, MM_MODEM_ERROR_GENERAL,
+ "Could not parse signal quality results");
+ }
+
+done:
+ mm_callback_info_schedule (info);
+}
+
+static void
+get_signal_quality (MMModemLteNetwork *modem,
+ MMModemUIntFn callback,
+ gpointer user_data)
+{
+ MMGenericLtePrivate *priv = MM_GENERIC_LTE_GET_PRIVATE (modem);
+ MMCallbackInfo *info;
+ MMAtSerialPort *port;
+
+ info = mm_callback_info_uint_new (MM_MODEM (modem), callback, user_data);
+
+ port = mm_generic_lte_get_best_at_port (MM_GENERIC_LTE (modem), NULL);
+ if (port) {
+ mm_at_serial_port_queue_command (port, "+CSQ", 3, get_csq_done, info);
+ } else {
+ /* Use cached signal quality */
+ mm_callback_info_set_result (info, GUINT_TO_POINTER (priv->signal_quality), NULL);
+ mm_callback_info_schedule (info);
+ }
+}
+
+/*****************************************************************************/
+
+typedef struct {
+ MMModemLteAccessTech mm_act;
+ gint etsi_act;
+} ModeEtsi;
+
+
+static ModeEtsi modes_table[] = {
+ { MM_MODEM_LTE_ACCESS_TECH_LTE, 0 },
+ { MM_MODEM_LTE_ACCESS_TECH_GPRS, 1 },
+ { MM_MODEM_LTE_ACCESS_TECH_EDGE, 2 },
+ { MM_MODEM_LTE_ACCESS_TECH_UMTS, 3 },
+ { MM_MODEM_LTE_ACCESS_TECH_HSDPA, 4 },
+ { MM_MODEM_LTE_ACCESS_TECH_HSPA, 5 },
+ { MM_MODEM_LTE_ACCESS_TECH_GSM, 6 },
+ { MM_MODEM_LTE_ACCESS_TECH_1xRTT, 7 }, /* E-UTRAN/LTE => HSPA for now */
+ { MM_MODEM_LTE_ACCESS_TECH_EvDO, 8 },
+ { MM_MODEM_LTE_ACCESS_TECH_EvDO_Rel0, 9 },
+ { MM_MODEM_LTE_ACCESS_TECH_EvDOA, 10 },
+ { MM_MODEM_LTE_ACCESS_TECH_HSUPA, 11 },
+ { MM_MODEM_LTE_ACCESS_TECH_UNKNOWN, -1 }
+};
+
+static MMModemLteAccessTech
+etsi_act_to_mm_act (gint act)
+{
+ ModeEtsi *iter = &modes_table[0];
+
+ while (iter->mm_act != MM_MODEM_LTE_ACCESS_TECH_UNKNOWN) {
+ if (iter->etsi_act == act)
+ return iter->mm_act;
+ iter++;
+ }
+ return MM_MODEM_LTE_ACCESS_TECH_UNKNOWN;
+}
+
+static void
+_internal_update_access_technology (MMGenericLte *modem,
+ MMModemLteAccessTech act)
+{
+ MMGenericLtePrivate *priv;
+
+ g_return_if_fail (modem != NULL);
+ g_return_if_fail (MM_IS_GENERIC_LTE (modem));
+ g_return_if_fail (act >= MM_MODEM_LTE_ACCESS_TECH_UNKNOWN && act <= MM_MODEM_LTE_ACCESS_TECH_HSUPA);
+
+ priv = MM_GENERIC_LTE_GET_PRIVATE (modem);
+
+ if (act != priv->act) {
+ priv->act = act;
+ g_object_notify (G_OBJECT (modem), MM_MODEM_LTE_NETWORK_ACCESS_TECHNOLOGY);
+ }
+}
+
+void
+mm_generic_lte_update_access_technology (MMGenericLte *self,
+ MMModemLteAccessTech act)
+{
+ g_return_if_fail (self != NULL);
+ g_return_if_fail (MM_IS_GENERIC_LTE (self));
+
+ /* For plugins, don't update the access tech when the modem isn't enabled */
+ if (mm_modem_get_state (MM_MODEM (self)) >= MM_MODEM_STATE_ENABLED)
+ _internal_update_access_technology (self, act);
+}
+
+void
+mm_generic_lte_update_allowed_mode (MMGenericLte *self,
+ MMModemLteAllowedMode mode)
+{
+ MMGenericLtePrivate *priv;
+
+ g_return_if_fail (self != NULL);
+ g_return_if_fail (MM_IS_GENERIC_LTE (self));
+
+ priv = MM_GENERIC_LTE_GET_PRIVATE (self);
+
+ if (mode != priv->allowed_mode) {
+ priv->allowed_mode = mode;
+ g_object_notify (G_OBJECT (self), MM_MODEM_LTE_NETWORK_ALLOWED_MODE);
+ }
+}
+
+static void
+set_allowed_mode_done (MMModem *modem, GError *error, gpointer user_data)
+{
+ MMCallbackInfo *info = user_data;
+
+ /* Do nothing if modem removed */
+ if (!modem || mm_callback_info_check_modem_removed (info))
+ return;
+
+ if (error)
+ info->error = g_error_copy (error);
+ else {
+ MMModemLteAllowedMode mode = GPOINTER_TO_UINT (mm_callback_info_get_data (info, "mode"));
+
+ mm_generic_lte_update_allowed_mode (MM_GENERIC_LTE (info->modem), mode);
+ }
+
+ mm_callback_info_schedule (info);
+}
+
+static void
+set_allowed_mode (MMModemLteNetwork *net,
+ MMModemLteAllowedMode mode,
+ MMModemFn callback,
+ gpointer user_data)
+{
+ MMGenericLte *self = MM_GENERIC_LTE (net);
+ MMCallbackInfo *info;
+
+ info = mm_callback_info_new (MM_MODEM (self), callback, user_data);
+
+ switch (mode) {
+ case MM_MODEM_LTE_ALLOWED_MODE_ANY:
+ case MM_MODEM_LTE_ALLOWED_MODE_GSM_PREFERRED:
+ case MM_MODEM_LTE_ALLOWED_MODE_WCDMA_PREFERRED:
+
+ if (!MM_GENERIC_LTE_GET_CLASS (self)->set_allowed_mode) {
+ info->error = g_error_new_literal (MM_MODEM_ERROR, MM_MODEM_ERROR_OPERATION_NOT_SUPPORTED,
+ "Operation not supported");
+ } else {
+ mm_callback_info_set_data (info, "mode", GUINT_TO_POINTER (mode), NULL);
+ MM_GENERIC_LTE_GET_CLASS (self)->set_allowed_mode (self, mode, set_allowed_mode_done, info);
+ }
+ break;
+ default:
+ info->error = g_error_new_literal (MM_MODEM_ERROR, MM_MODEM_ERROR_GENERAL, "Invalid mode.");
+ break;
+ }
+
+ if (info->error)
+ mm_callback_info_schedule (info);
+}
+
+/*****************************************************************************/
+/* Charset stuff */
+
+static void
+get_charsets_done (MMAtSerialPort *port,
+ GString *response,
+ GError *error,
+ gpointer user_data)
+{
+ MMCallbackInfo *info = (MMCallbackInfo *) user_data;
+ MMGenericLtePrivate *priv;
+
+ /* If the modem has already been removed, return without
+ * scheduling callback */
+ if (mm_callback_info_check_modem_removed (info))
+ return;
+
+ if (error) {
+ info->error = g_error_copy (error);
+ mm_callback_info_schedule (info);
+ return;
+ }
+
+ priv = MM_GENERIC_LTE_GET_PRIVATE (info->modem);
+
+ priv->charsets = MM_MODEM_CHARSET_UNKNOWN;
+ if (!mm_lte_parse_cscs_support_response (response->str, &priv->charsets)) {
+ info->error = g_error_new_literal (MM_MODEM_ERROR,
+ MM_MODEM_ERROR_GENERAL,
+ "Failed to parse the supported character sets response");
+ } else
+ mm_callback_info_set_result (info, GUINT_TO_POINTER (priv->charsets), NULL);
+
+ mm_callback_info_schedule (info);
+}
+
+static void
+get_supported_charsets (MMModem *modem,
+ MMModemUIntFn callback,
+ gpointer user_data)
+{
+ MMGenericLte *self = MM_GENERIC_LTE (modem);
+ MMGenericLtePrivate *priv = MM_GENERIC_LTE_GET_PRIVATE (self);
+ MMCallbackInfo *info;
+ MMAtSerialPort *port;
+
+ info = mm_callback_info_uint_new (MM_MODEM (self), callback, user_data);
+
+ /* Use cached value if we have one */
+ if (priv->charsets) {
+ mm_callback_info_set_result (info, GUINT_TO_POINTER (priv->charsets), NULL);
+ mm_callback_info_schedule (info);
+ return;
+ }
+
+ /* Otherwise hit up the modem */
+ port = mm_generic_lte_get_best_at_port (self, &info->error);
+ if (!port) {
+ mm_callback_info_schedule (info);
+ return;
+ }
+
+ mm_at_serial_port_queue_command (port, "+CSCS=?", 3, get_charsets_done, info);
+}
+
+static void
+set_get_charset_done (MMAtSerialPort *port,
+ GString *response,
+ GError *error,
+ gpointer user_data)
+{
+ MMCallbackInfo *info = (MMCallbackInfo *) user_data;
+ MMGenericLtePrivate *priv;
+ MMModemCharset tried_charset;
+ const char *p;
+
+ /* If the modem has already been removed, return without
+ * scheduling callback */
+ if (mm_callback_info_check_modem_removed (info))
+ return;
+
+ if (error) {
+ info->error = g_error_copy (error);
+ mm_callback_info_schedule (info);
+ return;
+ }
+
+ p = response->str;
+ if (g_str_has_prefix (p, "+CSCS:"))
+ p += 6;
+ while (*p == ' ')
+ p++;
+
+ priv = MM_GENERIC_LTE_GET_PRIVATE (info->modem);
+ priv->cur_charset = mm_modem_charset_from_string (p);
+
+ tried_charset = GPOINTER_TO_UINT (mm_callback_info_get_data (info, "charset"));
+
+ if (tried_charset != priv->cur_charset) {
+ info->error = g_error_new (MM_MODEM_ERROR,
+ MM_MODEM_ERROR_UNSUPPORTED_CHARSET,
+ "Modem failed to change character set to %s",
+ mm_modem_charset_to_string (tried_charset));
+ }
+
+ mm_callback_info_schedule (info);
+}
+
+#define TRIED_NO_QUOTES_TAG "tried-no-quotes"
+
+static void
+set_charset_done (MMAtSerialPort *port,
+ GString *response,
+ GError *error,
+ gpointer user_data)
+{
+ MMCallbackInfo *info = (MMCallbackInfo *) user_data;
+
+ /* If the modem has already been removed, return without
+ * scheduling callback */
+ if (mm_callback_info_check_modem_removed (info))
+ return;
+
+ if (error) {
+ gboolean tried_no_quotes = !!mm_callback_info_get_data (info, TRIED_NO_QUOTES_TAG);
+ MMModemCharset charset = GPOINTER_TO_UINT (mm_callback_info_get_data (info, "charset"));
+ char *command;
+
+ if (tried_no_quotes) {
+ info->error = g_error_copy (error);
+ mm_callback_info_schedule (info);
+ return;
+ }
+
+ /* Some modems puke if you include the quotes around the character
+ * set name, so lets try it again without them.
+ */
+ mm_callback_info_set_data (info, TRIED_NO_QUOTES_TAG, GUINT_TO_POINTER (TRUE), NULL);
+ command = g_strdup_printf ("+CSCS=%s", mm_modem_charset_to_string (charset));
+ mm_at_serial_port_queue_command (port, command, 3, set_charset_done, info);
+ g_free (command);
+ } else
+ mm_at_serial_port_queue_command (port, "+CSCS?", 3, set_get_charset_done, info);
+}
+
+static gboolean
+check_for_single_value (guint32 value)
+{
+ gboolean found = FALSE;
+ guint32 i;
+
+ for (i = 1; i <= 32; i++) {
+ if (value & 0x1) {
+ if (found)
+ return FALSE; /* More than one bit set */
+ found = TRUE;
+ }
+ value >>= 1;
+ }
+
+ return TRUE;
+}
+
+static void
+set_charset (MMModem *modem,
+ MMModemCharset charset,
+ MMModemFn callback,
+ gpointer user_data)
+{
+ MMGenericLtePrivate *priv = MM_GENERIC_LTE_GET_PRIVATE (modem);
+ MMCallbackInfo *info;
+ const char *str;
+ char *command;
+ MMAtSerialPort *port;
+
+ info = mm_callback_info_new (modem, callback, user_data);
+
+ if (!(priv->charsets & charset) || !check_for_single_value (charset)) {
+ info->error = g_error_new (MM_MODEM_ERROR,
+ MM_MODEM_ERROR_UNSUPPORTED_CHARSET,
+ "Character set 0x%X not supported",
+ charset);
+ mm_callback_info_schedule (info);
+ return;
+ }
+
+ str = mm_modem_charset_to_string (charset);
+ if (!str) {
+ info->error = g_error_new (MM_MODEM_ERROR,
+ MM_MODEM_ERROR_UNSUPPORTED_CHARSET,
+ "Unhandled character set 0x%X",
+ charset);
+ mm_callback_info_schedule (info);
+ return;
+ }
+
+ port = mm_generic_lte_get_best_at_port (MM_GENERIC_LTE (modem), &info->error);
+ if (!port) {
+ mm_callback_info_schedule (info);
+ return;
+ }
+
+ mm_callback_info_set_data (info, "charset", GUINT_TO_POINTER (charset), NULL);
+
+ command = g_strdup_printf ("+CSCS=\"%s\"", str);
+ mm_at_serial_port_queue_command (port, command, 3, set_charset_done, info);
+ g_free (command);
+}
+
+MMModemCharset
+mm_generic_lte_get_charset (MMGenericLte *self)
+{
+ g_return_val_if_fail (self != NULL, MM_MODEM_CHARSET_UNKNOWN);
+ g_return_val_if_fail (MM_IS_GENERIC_LTE (self), MM_MODEM_CHARSET_UNKNOWN);
+
+ return MM_GENERIC_LTE_GET_PRIVATE (self)->cur_charset;
+}
+
+MMAtSerialPort *
+mm_generic_lte_get_at_port (MMGenericLte *modem,
+ MMPortType ptype)
+{
+ g_return_val_if_fail (MM_IS_GENERIC_LTE (modem), NULL);
+ g_return_val_if_fail (ptype != MM_PORT_TYPE_UNKNOWN, NULL);
+
+ if (ptype == MM_PORT_TYPE_PRIMARY)
+ return MM_GENERIC_LTE_GET_PRIVATE (modem)->primary;
+ else if (ptype == MM_PORT_TYPE_SECONDARY)
+ return MM_GENERIC_LTE_GET_PRIVATE (modem)->secondary;
+
+ return NULL;
+}
+
+MMAtSerialPort *
+mm_generic_lte_get_best_at_port (MMGenericLte *self, GError **error)
+{
+ MMGenericLtePrivate *priv;
+
+ g_return_val_if_fail (self != NULL, NULL);
+ g_return_val_if_fail (MM_IS_GENERIC_LTE (self), NULL);
+
+ priv = MM_GENERIC_LTE_GET_PRIVATE (self);
+ return priv->primary;
+}
+
+/*****************************************************************************/
+/* MMModemSimple interface */
+
+typedef enum {
+ SIMPLE_STATE_CHECK_PIN = 0,
+ SIMPLE_STATE_ENABLE,
+ SIMPLE_STATE_ALLOWED_MODE,
+ SIMPLE_STATE_REGISTER,
+ SIMPLE_STATE_SET_APN,
+ SIMPLE_STATE_CONNECT,
+ SIMPLE_STATE_DONE
+} SimpleState;
+
+/* Looks a value up in the simple connect properties dictionary. If the
+ * requested key is not present in the dict, NULL is returned. If the
+ * requested key is present but is not a string, an error is returned.
+ */
+static gboolean
+simple_get_property (MMCallbackInfo *info,
+ const char *name,
+ GType expected_type,
+ const char **out_str,
+ guint32 *out_num,
+ gboolean *out_bool,
+ GError **error)
+{
+ GHashTable *properties = (GHashTable *) mm_callback_info_get_data (info, "simple-connect-properties");
+ GValue *value;
+ gint foo;
+
+ g_return_val_if_fail (properties != NULL, FALSE);
+ g_return_val_if_fail (name != NULL, FALSE);
+ if (out_str)
+ g_return_val_if_fail (*out_str == NULL, FALSE);
+
+ value = (GValue *) g_hash_table_lookup (properties, name);
+ if (!value)
+ return FALSE;
+
+ if ((expected_type == G_TYPE_STRING) && G_VALUE_HOLDS_STRING (value)) {
+ *out_str = g_value_get_string (value);
+ return TRUE;
+ } else if (expected_type == G_TYPE_UINT) {
+ if (G_VALUE_HOLDS_UINT (value)) {
+ *out_num = g_value_get_uint (value);
+ return TRUE;
+ } else if (G_VALUE_HOLDS_INT (value)) {
+ /* handle ints for convenience, but only if they are >= 0 */
+ foo = g_value_get_int (value);
+ if (foo >= 0) {
+ *out_num = (guint) foo;
+ return TRUE;
+ }
+ }
+ } else if (expected_type == G_TYPE_BOOLEAN && G_VALUE_HOLDS_BOOLEAN (value)) {
+ *out_bool = g_value_get_boolean (value);
+ return TRUE;
+ }
+
+ g_set_error (error, MM_MODEM_ERROR, MM_MODEM_ERROR_GENERAL,
+ "Invalid property type for '%s': %s (%s expected)",
+ name, G_VALUE_TYPE_NAME (value), g_type_name (expected_type));
+
+ return FALSE;
+}
+
+static const char *
+simple_get_string_property (MMCallbackInfo *info, const char *name, GError **error)
+{
+ const char *str = NULL;
+
+ simple_get_property (info, name, G_TYPE_STRING, &str, NULL, NULL, error);
+ return str;
+}
+
+static gboolean
+simple_get_uint_property (MMCallbackInfo *info, const char *name, guint32 *out_val, GError **error)
+{
+ return simple_get_property (info, name, G_TYPE_UINT, NULL, out_val, NULL, error);
+}
+
+static gboolean
+simple_get_bool_property (MMCallbackInfo *info, const char *name, gboolean *out_val, GError **error)
+{
+ return simple_get_property (info, name, G_TYPE_BOOLEAN, NULL, NULL, out_val, error);
+}
+
+static gboolean
+simple_get_allowed_mode (MMCallbackInfo *info,
+ MMModemLteAllowedMode *out_mode,
+ GError **error)
+{
+ MMModemLteAllowedMode allowed_mode = MM_MODEM_LTE_ALLOWED_MODE_ANY;
+ GError *tmp_error = NULL;
+
+ /* check for new allowed mode first */
+ if (simple_get_uint_property (info, "allowed_mode", &allowed_mode, &tmp_error)) {
+ if (allowed_mode > MM_MODEM_LTE_ALLOWED_MODE_WCDMA_PREFERRED) {
+ g_set_error (&tmp_error, MM_MODEM_ERROR, MM_MODEM_ERROR_GENERAL,
+ "Invalid allowed mode");
+ } else {
+ *out_mode = allowed_mode;
+ return TRUE;
+ }
+ }
+
+ if (error)
+ *error = tmp_error;
+ return FALSE;
+}
+
+static void
+simple_state_machine (MMModem *modem, GError *error, gpointer user_data)
+{
+ MMCallbackInfo *info = (MMCallbackInfo *) user_data;
+ MMGenericLtePrivate *priv;
+ const char *str, *unlock = NULL;
+ SimpleState state = GPOINTER_TO_UINT (mm_callback_info_get_data (info, "simple-connect-state"));
+ SimpleState next_state = state;
+ gboolean done = FALSE;
+ MMModemLteAllowedMode allowed_mode;
+ gboolean home_only = FALSE;
+ char *data_device;
+
+ /* Do nothing if modem removed */
+ if (!modem || mm_callback_info_check_modem_removed (info))
+ return;
+
+ if (error) {
+ info->error = g_error_copy (error);
+ goto out;
+ }
+
+ priv = MM_GENERIC_LTE_GET_PRIVATE (modem);
+
+ g_object_get (G_OBJECT (modem), MM_MODEM_DATA_DEVICE, &data_device, NULL);
+ mm_dbg ("(%s): simple connect state %d", data_device, state);
+ g_free (data_device);
+
+ switch (state) {
+ case SIMPLE_STATE_CHECK_PIN:
+ next_state = SIMPLE_STATE_ENABLE;
+
+ /* If we need a PIN, send it now, but we don't care about SIM-PIN2/SIM-PUK2
+ * since the device is operational without it.
+ */
+ unlock = mm_modem_base_get_unlock_required (MM_MODEM_BASE (modem));
+ if (unlock && strcmp (unlock, "sim-puk2") && strcmp (unlock, "sim-pin2")) {
+ gboolean success = FALSE;
+
+ if (!strcmp (unlock, "sim-pin")) {
+ str = simple_get_string_property (info, "pin", &info->error);
+ if (str) {
+ mm_modem_lte_card_send_pin (MM_MODEM_LTE_CARD (modem), str, simple_state_machine, info);
+ success = TRUE;
+ }
+ }
+ if (!success && !info->error)
+ info->error = error_for_unlock_required (unlock);
+ break;
+ }
+ /* Fall through if no PIN required */
+ case SIMPLE_STATE_ENABLE:
+ next_state = SIMPLE_STATE_ALLOWED_MODE;
+ mm_modem_enable (modem, simple_state_machine, info);
+ break;
+ case SIMPLE_STATE_ALLOWED_MODE:
+ next_state = SIMPLE_STATE_REGISTER;
+ if ( simple_get_allowed_mode (info, &allowed_mode, &info->error)
+ && (allowed_mode != priv->allowed_mode)) {
+ mm_modem_lte_network_set_allowed_mode (MM_MODEM_LTE_NETWORK (modem),
+ allowed_mode,
+ simple_state_machine,
+ info);
+ break;
+ } else if (info->error)
+ break;
+ /* otherwise fall through as no allowed mode was sent */
+ case SIMPLE_STATE_REGISTER:
+ next_state = SIMPLE_STATE_SET_APN;
+ str = simple_get_string_property (info, "network_id", &info->error);
+ if (info->error)
+ str = NULL;
+ mm_modem_lte_network_register (MM_MODEM_LTE_NETWORK (modem), str, simple_state_machine, info);
+ break;
+ case SIMPLE_STATE_SET_APN:
+ next_state = SIMPLE_STATE_CONNECT;
+ str = simple_get_string_property (info, "apn", &info->error);
+ if (str || info->error) {
+ if (str)
+ mm_modem_lte_network_set_apn (MM_MODEM_LTE_NETWORK (modem), str, simple_state_machine, info);
+ break;
+ }
+ /* Fall through if no APN or no 'apn' property error */
+ case SIMPLE_STATE_CONNECT:
+ next_state = SIMPLE_STATE_DONE;
+ str = simple_get_string_property (info, "number", &info->error);
+ if (!info->error) {
+ if (simple_get_bool_property (info, "home_only", &home_only, &info->error)) {
+ MMModemLteNetworkRegStatus status;
+
+ priv->roam_allowed = !home_only;
+
+ /* Don't connect if we're not supposed to be roaming */
+ status = lte_reg_status (MM_GENERIC_LTE (modem), NULL);
+ if (home_only && (status == MM_MODEM_LTE_NETWORK_REG_STATUS_ROAMING)) {
+ info->error = g_error_new_literal (MM_MOBILE_ERROR,
+ MM_MOBILE_ERROR_GPRS_ROAMING_NOT_ALLOWED,
+ "Roaming is not allowed.");
+ break;
+ }
+ } else if (info->error)
+ break;
+
+ mm_modem_connect (modem, str, simple_state_machine, info);
+ }
+ break;
+ case SIMPLE_STATE_DONE:
+ done = TRUE;
+ break;
+ }
+
+ out:
+ if (info->error || done)
+ mm_callback_info_schedule (info);
+ else
+ mm_callback_info_set_data (info, "simple-connect-state", GUINT_TO_POINTER (next_state), NULL);
+}
+
+static void
+simple_connect (MMModemSimple *simple,
+ GHashTable *properties,
+ MMModemFn callback,
+ gpointer user_data)
+{
+ MMCallbackInfo *info;
+ GHashTableIter iter;
+ gpointer key, value;
+ char *data_device;
+
+
+ /* List simple connect properties when debugging */
+ g_object_get (G_OBJECT (simple), MM_MODEM_DATA_DEVICE, &data_device, NULL);
+ g_hash_table_iter_init (&iter, properties);
+ while (g_hash_table_iter_next (&iter, &key, &value)) {
+ char *val_str;
+
+ val_str = g_strdup_value_contents ((GValue *) value);
+ mm_dbg ("(%s): %s => %s", data_device, (const char *) key, val_str);
+ g_free (val_str);
+ }
+ g_free (data_device);
+
+ info = mm_callback_info_new (MM_MODEM (simple), callback, user_data);
+ mm_callback_info_set_data (info, "simple-connect-properties",
+ g_hash_table_ref (properties),
+ (GDestroyNotify) g_hash_table_unref);
+
+ simple_state_machine (MM_MODEM (simple), NULL, info);
+}
+
+static void
+simple_free_gvalue (gpointer data)
+{
+ g_value_unset ((GValue *) data);
+ g_slice_free (GValue, data);
+}
+
+static GValue *
+simple_uint_value (guint32 i)
+{
+ GValue *val;
+
+ val = g_slice_new0 (GValue);
+ g_value_init (val, G_TYPE_UINT);
+ g_value_set_uint (val, i);
+
+ return val;
+}
+
+static GValue *
+simple_string_value (const char *str)
+{
+ GValue *val;
+
+ val = g_slice_new0 (GValue);
+ g_value_init (val, G_TYPE_STRING);
+ g_value_set_string (val, str);
+
+ return val;
+}
+
+#define SS_HASH_TAG "simple-get-status"
+
+static void
+simple_status_got_signal_quality (MMModem *modem,
+ guint32 result,
+ GError *error,
+ gpointer user_data)
+{
+ MMCallbackInfo *info = (MMCallbackInfo *) user_data;
+ GHashTable *properties;
+
+ if (!error) {
+ properties = (GHashTable *) mm_callback_info_get_data (info, SS_HASH_TAG);
+ g_hash_table_insert (properties, "signal_quality", simple_uint_value (result));
+ }
+ mm_callback_info_chain_complete_one (info);
+}
+
+static void
+simple_status_got_band (MMModem *modem,
+ guint32 result,
+ GError *error,
+ gpointer user_data)
+{
+ MMCallbackInfo *info = (MMCallbackInfo *) user_data;
+ GHashTable *properties;
+
+ if (!error) {
+ properties = (GHashTable *) mm_callback_info_get_data (info, SS_HASH_TAG);
+ g_hash_table_insert (properties, "band", simple_uint_value (result));
+ }
+ mm_callback_info_chain_complete_one (info);
+}
+
+static void
+get_modem_state(MMModem *modem,
+ MMCallbackInfo *info)
+{
+ GHashTable *properties;
+ MMModemState state = mm_modem_get_state (modem);
+ properties = (GHashTable *) mm_callback_info_get_data (info, SS_HASH_TAG);
+ g_hash_table_insert (properties, "state", simple_uint_value (state));
+}
+
+static void
+simple_status_got_reg_info (MMModemLteNetwork *modem,
+ MMModemLteNetworkRegStatus status,
+ const char *oper_code,
+ const char *oper_name,
+ GError *error,
+ gpointer user_data)
+{
+ MMCallbackInfo *info = (MMCallbackInfo *) user_data;
+ GHashTable *properties;
+
+ /* Do nothing if modem removed */
+ if (!modem || mm_callback_info_check_modem_removed (info))
+ return;
+
+ if (error)
+ info->error = g_error_copy (error);
+ else {
+ properties = (GHashTable *) mm_callback_info_get_data (info, SS_HASH_TAG);
+
+ g_hash_table_insert (properties, "registration_status", simple_uint_value (status));
+ g_hash_table_insert (properties, "operator_code", simple_string_value (oper_code));
+ g_hash_table_insert (properties, "operator_name", simple_string_value (oper_name));
+ }
+ mm_callback_info_chain_complete_one (info);
+}
+
+static void
+simple_get_status_invoke (MMCallbackInfo *info)
+{
+ MMModemSimpleGetStatusFn callback = (MMModemSimpleGetStatusFn) info->callback;
+
+ callback (MM_MODEM_SIMPLE (info->modem),
+ (GHashTable *) mm_callback_info_get_data (info, SS_HASH_TAG),
+ info->error, info->user_data);
+}
+
+static void
+simple_get_status (MMModemSimple *simple,
+ MMModemSimpleGetStatusFn callback,
+ gpointer user_data)
+{
+ MMModemLteNetwork *lte = MM_MODEM_LTE_NETWORK (simple);
+ MMGenericLtePrivate *priv = MM_GENERIC_LTE_GET_PRIVATE (simple);
+ GHashTable *properties;
+ MMCallbackInfo *info;
+ MMGenericLte *self = MM_GENERIC_LTE(simple);
+
+ info = mm_callback_info_new_full (MM_MODEM (simple),
+ simple_get_status_invoke,
+ G_CALLBACK (callback),
+ user_data);
+
+ properties = g_hash_table_new_full (g_str_hash, g_str_equal, NULL, simple_free_gvalue);
+ mm_callback_info_set_data (info, SS_HASH_TAG, properties, (GDestroyNotify) g_hash_table_unref);
+
+ mm_callback_info_chain_start (info, 3);
+ mm_modem_lte_network_get_signal_quality (lte, simple_status_got_signal_quality, info);
+ mm_modem_lte_network_get_band (lte, simple_status_got_band, info);
+ mm_modem_lte_network_get_registration_info (lte, simple_status_got_reg_info, info);
+ get_modem_state( (MMModem* ) lte, info);
+
+ if (priv->act < 0) {
+ if (MM_GENERIC_LTE_GET_CLASS (self)->get_access_technology) {
+ MM_GENERIC_LTE_GET_CLASS (self)->get_access_technology (self, get_reg_act_done, NULL);
+ }
+ }
+ /* New key */
+ g_hash_table_insert (properties, "access_technology", simple_uint_value (priv->act));
+}
+
+/*****************************************************************************/
+
+static void
+modem_state_changed (MMGenericLte *self, GParamSpec *pspec, gpointer user_data)
+{
+ MMGenericLtePrivate *priv = MM_GENERIC_LTE_GET_PRIVATE (self);
+ MMModemState state;
+
+ /* Start polling registration status and signal quality when enabled */
+
+ state = mm_modem_get_state (MM_MODEM (self));
+ if (state >= MM_MODEM_STATE_ENABLED) {
+ if (!priv->poll_id)
+ priv->poll_id = g_timeout_add_seconds (30, periodic_poll_cb, self);
+ } else {
+ if (priv->poll_id)
+ g_source_remove (priv->poll_id);
+ priv->poll_id = 0;
+ }
+}
+
+/*****************************************************************************/
+
+static void
+modem_init (MMModem *modem_class)
+{
+ modem_class->owns_port = owns_port;
+ modem_class->grab_port = grab_port;
+ modem_class->release_port = release_port;
+ modem_class->enable = enable;
+ modem_class->disable = disable;
+ //modem_class->connect = connect;
+ modem_class->disconnect = disconnect;
+ modem_class->get_info = get_card_info;
+ modem_class->get_supported_charsets = get_supported_charsets;
+ modem_class->set_charset = set_charset;
+}
+
+static void
+modem_lte_card_init (MMModemLteCard *class)
+{
+ class->get_imei = get_imei;
+ class->get_imsi = get_imsi;
+ class->get_operator_id = get_operator_id;
+ class->get_spn = get_spn;
+ class->send_pin = send_pin;
+ //class->send_puk = send_puk;
+ class->enable_pin = enable_pin;
+ class->change_pin = change_pin;
+ class->get_unlock_retries = get_unlock_retries;
+}
+
+static void
+modem_lte_network_init (MMModemLteNetwork *class)
+{
+ class->do_register = do_register;
+ class->get_registration_info = get_registration_info;
+ class->set_allowed_mode = set_allowed_mode;
+ class->set_apn = set_apn;
+ class->scan = scan;
+ class->get_signal_quality = get_signal_quality;
+}
+
+static void
+modem_simple_init (MMModemSimple *class)
+{
+ class->connect = simple_connect;
+ class->get_status = simple_get_status;
+}
+
+static void
+mm_generic_lte_init (MMGenericLte *self)
+{
+ MMGenericLtePrivate *priv = MM_GENERIC_LTE_GET_PRIVATE (self);
+
+ priv->act = MM_MODEM_LTE_ACCESS_TECH_UNKNOWN;
+ priv->reg_regex = mm_lte_creg_regex_get (TRUE);
+ priv->roam_allowed = TRUE;
+
+ mm_properties_changed_signal_register_property (G_OBJECT (self),
+ MM_MODEM_LTE_NETWORK_ALLOWED_MODE,
+ NULL,
+ MM_MODEM_LTE_NETWORK_DBUS_INTERFACE);
+
+ mm_properties_changed_signal_register_property (G_OBJECT (self),
+ MM_MODEM_LTE_NETWORK_ACCESS_TECHNOLOGY,
+ NULL,
+ MM_MODEM_LTE_NETWORK_DBUS_INTERFACE);
+
+ g_signal_connect (self, "notify::" MM_MODEM_STATE,
+ G_CALLBACK (modem_state_changed), NULL);
+}
+
+static void
+set_property (GObject *object, guint prop_id,
+ const GValue *value, GParamSpec *pspec)
+{
+ switch (prop_id) {
+ case MM_MODEM_PROP_TYPE:
+ case MM_GENERIC_LTE_PROP_POWER_UP_CMD:
+ case MM_GENERIC_LTE_PROP_POWER_DOWN_CMD:
+ case MM_GENERIC_LTE_PROP_INIT_CMD:
+ case MM_GENERIC_LTE_PROP_INIT_CMD_OPTIONAL:
+ case MM_GENERIC_LTE_PROP_SUPPORTED_BANDS:
+ case MM_GENERIC_LTE_PROP_ALLOWED_MODE:
+ case MM_GENERIC_LTE_PROP_ACCESS_TECHNOLOGY:
+ case MM_GENERIC_LTE_PROP_SIM_IDENTIFIER:
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+get_property (GObject *object, guint prop_id,
+ GValue *value, GParamSpec *pspec)
+{
+ MMGenericLtePrivate *priv = MM_GENERIC_LTE_GET_PRIVATE (object);
+
+ switch (prop_id) {
+ case MM_MODEM_PROP_DATA_DEVICE:
+ if (priv->data)
+ g_value_set_string (value, mm_port_get_device (priv->data));
+ else
+ g_value_set_string (value, NULL);
+ break;
+ case MM_MODEM_PROP_TYPE:
+ g_value_set_uint (value, MM_MODEM_TYPE_LTE);
+ break;
+ case MM_GENERIC_LTE_PROP_POWER_UP_CMD:
+ g_value_set_string (value, "+CFUN=1");
+ break;
+ case MM_GENERIC_LTE_PROP_POWER_DOWN_CMD:
+ /* CFUN=0 is dangerous and often will shoot devices in the head (that's
+ * what it's supposed to do). So don't use CFUN=0 by default, but let
+ * specific plugins use it when they know it's safe to do so. For
+ * example, CFUN=0 will often make phones turn themselves off, but some
+ * dedicated devices (ex Sierra WWAN cards) will just turn off their
+ * radio but otherwise still work.
+ */
+ g_value_set_string (value, "");
+ break;
+ case MM_GENERIC_LTE_PROP_INIT_CMD:
+ g_value_set_string (value, "Z E0 V1");
+ break;
+ case MM_GENERIC_LTE_PROP_INIT_CMD_OPTIONAL:
+ g_value_set_string (value, "X4 &C1");
+ break;
+ case MM_GENERIC_LTE_PROP_SUPPORTED_BANDS:
+ g_value_set_uint (value, 0);
+ break;
+ case MM_GENERIC_LTE_PROP_ALLOWED_MODE:
+ g_value_set_uint (value, priv->allowed_mode);
+ break;
+ case MM_GENERIC_LTE_PROP_ACCESS_TECHNOLOGY:
+ if (mm_modem_get_state (MM_MODEM (object)) >= MM_MODEM_STATE_ENABLED)
+ g_value_set_uint (value, priv->act);
+ else
+ g_value_set_uint (value, MM_MODEM_LTE_ACCESS_TECH_UNKNOWN);
+ break;
+ case MM_GENERIC_LTE_PROP_SIM_IDENTIFIER:
+ g_value_set_string (value, priv->simid);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+finalize (GObject *object)
+{
+ MMGenericLtePrivate *priv = MM_GENERIC_LTE_GET_PRIVATE (object);
+
+ mm_generic_lte_pending_registration_stop (MM_GENERIC_LTE (object));
+
+ if (priv->pin_check_timeout) {
+ g_source_remove (priv->pin_check_timeout);
+ priv->pin_check_timeout = 0;
+ }
+
+ if (priv->poll_id) {
+ g_source_remove (priv->poll_id);
+ priv->poll_id = 0;
+ }
+
+ if (priv->signal_quality_id) {
+ g_source_remove (priv->signal_quality_id);
+ priv->signal_quality_id = 0;
+ }
+
+ mm_lte_creg_regex_destroy (priv->reg_regex);
+
+ g_free (priv->oper_code);
+ g_free (priv->oper_name);
+ g_free (priv->simid);
+
+ G_OBJECT_CLASS (mm_generic_lte_parent_class)->finalize (object);
+}
+
+static void
+mm_generic_lte_class_init (MMGenericLteClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+ mm_generic_lte_parent_class = g_type_class_peek_parent (klass);
+ g_type_class_add_private (object_class, sizeof (MMGenericLtePrivate));
+
+ /* Virtual methods */
+ object_class->set_property = set_property;
+ object_class->get_property = get_property;
+ object_class->finalize = finalize;
+
+ klass->do_enable = real_do_enable;
+ klass->do_enable_power_up_done = real_do_enable_power_up_done;
+ klass->do_disconnect = real_do_disconnect;
+ klass->get_sim_iccid = real_get_sim_iccid;
+
+ /* Properties */
+ g_object_class_override_property (object_class,
+ MM_MODEM_PROP_DATA_DEVICE,
+ MM_MODEM_DATA_DEVICE);
+
+ g_object_class_override_property (object_class,
+ MM_MODEM_PROP_TYPE,
+ MM_MODEM_TYPE);
+
+ g_object_class_override_property (object_class,
+ MM_GENERIC_LTE_PROP_SUPPORTED_BANDS,
+ MM_MODEM_LTE_CARD_SUPPORTED_BANDS);
+
+ g_object_class_override_property (object_class,
+ MM_GENERIC_LTE_PROP_ALLOWED_MODE,
+ MM_MODEM_LTE_NETWORK_ALLOWED_MODE);
+
+ g_object_class_override_property (object_class,
+ MM_GENERIC_LTE_PROP_ACCESS_TECHNOLOGY,
+ MM_MODEM_LTE_NETWORK_ACCESS_TECHNOLOGY);
+
+ g_object_class_override_property (object_class,
+ MM_GENERIC_LTE_PROP_SIM_IDENTIFIER,
+ MM_MODEM_LTE_CARD_SIM_IDENTIFIER);
+
+ g_object_class_install_property
+ (object_class, MM_GENERIC_LTE_PROP_POWER_UP_CMD,
+ g_param_spec_string (MM_GENERIC_LTE_POWER_UP_CMD,
+ "PowerUpCommand",
+ "Power up command",
+ "+CFUN=1",
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
+
+ g_object_class_install_property
+ (object_class, MM_GENERIC_LTE_PROP_POWER_DOWN_CMD,
+ g_param_spec_string (MM_GENERIC_LTE_POWER_DOWN_CMD,
+ "PowerDownCommand",
+ "Power down command",
+ "+CFUN=0",
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
+
+ g_object_class_install_property
+ (object_class, MM_GENERIC_LTE_PROP_INIT_CMD,
+ g_param_spec_string (MM_GENERIC_LTE_INIT_CMD,
+ "InitCommand",
+ "Initialization command",
+ NULL,
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
+
+ g_object_class_install_property
+ (object_class, MM_GENERIC_LTE_PROP_INIT_CMD_OPTIONAL,
+ g_param_spec_string (MM_GENERIC_LTE_INIT_CMD_OPTIONAL,
+ "InitCommandOptional",
+ "Optional initialization command (errors ignored)",
+ NULL,
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
+}
+
diff --git a/src/mm-generic-lte.h b/src/mm-generic-lte.h
new file mode 100644
index 0000000..f06c157
--- /dev/null
+++ b/src/mm-generic-lte.h
@@ -0,0 +1,207 @@
+/* -*- 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) 2008 - 2009 Novell, Inc.
+ * Copyright (C) 2009 - 2010 Red Hat, Inc.
+ * Copyright (C) 2011 Novatel Wireless, Inc.
+ */
+
+#ifndef MM_GENERIC_LTE_H
+#define MM_GENERIC_LTE_H
+
+#include "mm-modem-lte.h"
+#include "mm-modem-lte-network.h"
+#include "mm-modem-base.h"
+#include "mm-at-serial-port.h"
+#include "mm-callback-info.h"
+#include "mm-charsets.h"
+
+#define MM_TYPE_GENERIC_LTE (mm_generic_lte_get_type ())
+#define MM_GENERIC_LTE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MM_TYPE_GENERIC_LTE, MMGenericLte))
+#define MM_GENERIC_LTE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), MM_TYPE_GENERIC_LTE, MMGenericLteClass))
+#define MM_IS_GENERIC_LTE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MM_TYPE_GENERIC_LTE))
+#define MM_IS_GENERIC_LTE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), MM_TYPE_GENERIC_LTE))
+#define MM_GENERIC_LTE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), MM_TYPE_GENERIC_LTE, MMGenericLteClass))
+
+#define MM_GENERIC_LTE_POWER_UP_CMD "power-up-cmd"
+#define MM_GENERIC_LTE_POWER_DOWN_CMD "power-down-cmd"
+#define MM_GENERIC_LTE_INIT_CMD "init-cmd"
+#define MM_GENERIC_LTE_INIT_CMD_OPTIONAL "init-cmd-optional"
+
+typedef enum {
+ MM_GENERIC_LTE_PROP_FIRST = 0x2000,
+
+ MM_GENERIC_LTE_PROP_POWER_UP_CMD,
+ MM_GENERIC_LTE_PROP_POWER_DOWN_CMD,
+ MM_GENERIC_LTE_PROP_INIT_CMD,
+ MM_GENERIC_LTE_PROP_SUPPORTED_BANDS,
+ MM_GENERIC_LTE_PROP_INIT_CMD_OPTIONAL,
+ MM_GENERIC_LTE_PROP_ALLOWED_MODE,
+ MM_GENERIC_LTE_PROP_ACCESS_TECHNOLOGY,
+ MM_GENERIC_LTE_PROP_SIM_IDENTIFIER,
+} MMGenericLteProp;
+
+typedef enum {
+ MM_GENERIC_LTE_REG_TYPE_UNKNOWN = 0,
+ MM_GENERIC_LTE_REG_TYPE_CS = 1,
+ MM_GENERIC_LTE_REG_TYPE_PS = 2
+} MMGenericLteRegType;
+
+typedef struct {
+ MMModemBase parent;
+} MMGenericLte;
+
+typedef struct {
+ MMModemBaseClass parent;
+
+ /* Called after opening the primary serial port and updating the modem's
+ * state to ENABLING, but before sending any commands to the device. Modems
+ * that need to perform custom initialization sequences or other setup should
+ * generally override this method instead of the MMModem interface's enable()
+ * method, unless the customization must happen *after* the generic init
+ * sequence has completed. When the subclass' enable attempt is complete
+ * the subclass should call mm_generic_lte_enable_complete() with any error
+ * encountered during the process and the MMCallbackInfo created from the
+ * callback and user_data passed in here.
+ */
+ void (*do_enable) (MMGenericLte *self, MMModemFn callback, gpointer user_data);
+
+ /* Called after the generic class has attempted to power up the modem.
+ * Subclasses can handle errors here if they know the device supports their
+ * power up command. Will only be called if the device does *not* override
+ * the MMModem enable() command or allows the generic class' do_enable()
+ * handler to execute.
+ */
+ void (*do_enable_power_up_done) (MMGenericLte *self,
+ GString *response,
+ GError *error,
+ MMCallbackInfo *info);
+
+
+ /* Called to terminate the active data call and deactivate the given PDP
+ * context.
+ */
+ void (*do_disconnect) (MMGenericLte *self,
+ gint cid,
+ MMModemFn callback,
+ gpointer user_data);
+
+ /* Called by the generic class to set the allowed operating mode of the device */
+ void (*set_allowed_mode) (MMGenericLte *self,
+ MMModemLteAllowedMode mode,
+ MMModemFn callback,
+ gpointer user_data);
+
+ /* Called by the generic class to get the allowed operating mode of the device */
+ void (*get_allowed_mode) (MMGenericLte *self,
+ MMModemUIntFn callback,
+ gpointer user_data);
+
+ /* Called by the generic class to the current radio access technology the
+ * device is using while communicating with the base station.
+ */
+ void (*get_access_technology) (MMGenericLte *self,
+ MMModemUIntFn callback,
+ gpointer user_data);
+
+ /* Called by the generic class to retrieve the SIM's ICCID */
+ void (*get_sim_iccid) (MMGenericLte *self,
+ MMModemStringFn callback,
+ gpointer user_data);
+
+} MMGenericLteClass;
+
+GType mm_generic_lte_get_type (void);
+
+MMModem *mm_generic_lte_new (const char *device,
+ const char *driver,
+ const char *plugin,
+ guint vendor,
+ guint product);
+
+/* Private, for subclasses */
+
+#define MM_GENERIC_LTE_PREV_STATE_TAG "prev-state"
+
+void mm_generic_lte_pending_registration_stop (MMGenericLte *modem);
+
+gint mm_generic_lte_get_cid (MMGenericLte *modem);
+
+void mm_generic_lte_set_reg_status (MMGenericLte *modem,
+ MMGenericLteRegType reg_type,
+ MMModemLteNetworkRegStatus status);
+
+MMModemCharset mm_generic_lte_get_charset (MMGenericLte *modem);
+
+/* Called to asynchronously update the current allowed operating mode that the
+ * device is allowed to use when connecting to a network. This isn't the
+ * specific access technology the device is currently using (see
+ * mm_generic_lte_set_access_technology() for that) but the mode the device is
+ * allowed to choose from when connecting.
+ */
+void mm_generic_lte_update_allowed_mode (MMGenericLte *modem,
+ MMModemLteAllowedMode mode);
+
+/* Called to asynchronously update the current access technology of the device;
+ * this is NOT the 2G/3G mode preference, but the current radio access
+ * technology being used to communicate with the base station.
+ */
+void mm_generic_lte_update_access_technology (MMGenericLte *modem,
+ MMModemLteAccessTech act);
+
+/* Called to asynchronously update the current signal quality of the device;
+ * 'quality' is a 0 - 100% quality.
+ */
+void mm_generic_lte_update_signal_quality (MMGenericLte *modem, guint32 quality);
+
+MMAtSerialPort *mm_generic_lte_get_at_port (MMGenericLte *modem,
+ MMPortType ptype);
+
+MMAtSerialPort *mm_generic_lte_get_best_at_port (MMGenericLte *modem,
+ GError **error);
+
+MMPort *mm_generic_lte_grab_port (MMGenericLte *modem,
+ const char *subsys,
+ const char *name,
+ MMPortType ptype,
+ GError **error);
+
+/* stay_connected should be TRUE for unsolicited registration updates, otherwise
+ * the registration update will clear connected/connecting/disconnecting state
+ * which we don't want. stay_connected should be FALSE for other cases like
+ * updating the state after disconnecting, or after a connect error occurs.
+ */
+void mm_generic_lte_update_enabled_state (MMGenericLte *modem,
+ gboolean stay_connected,
+ MMModemStateReason reason);
+
+/* Called to complete the enable operation for custom enable() handling; if an
+ * error is passed in, it copies the error to the callback info. This function
+ * always schedules the callback info. It will also update the modem with the
+ * correct state for both failure and success of the enable operation.
+ */
+void mm_generic_lte_enable_complete (MMGenericLte *modem,
+ GError *error,
+ MMCallbackInfo *info);
+
+/* Called to complete the enable operation for custom connect() handling; if an
+ * error is passed in, it copies the error to the callback info. This function
+ * always schedules the callback info. It will also update the modem with the
+ * correct state for both failure and success of the connect operation.
+ */
+void mm_generic_lte_connect_complete (MMGenericLte *modem,
+ GError *error,
+ MMCallbackInfo *info);
+
+
+#endif /* MM_GENERIC_LTE_H */
+
diff --git a/src/mm-manager.c b/src/mm-manager.c
index 3bf042e..db3d662 100644
--- a/src/mm-manager.c
+++ b/src/mm-manager.c
@@ -12,6 +12,7 @@
*
* Copyright (C) 2008 - 2009 Novell, Inc.
* Copyright (C) 2009 - 2010 Red Hat, Inc.
+ * Copyright (C) 2011 Novatel Wireless, Inc.
*/
#include <string.h>
@@ -594,6 +595,8 @@ do_grab_port (gpointer user_data)
type_name = "GSM";
else if (modem_type == MM_MODEM_TYPE_CDMA)
type_name = "CDMA";
+ else if (modem_type == MM_MODEM_TYPE_LTE)
+ type_name = "LTE";
device = mm_modem_get_device (modem);
mm_info ("(%s): %s modem %s claimed port %s",
@@ -1191,5 +1194,7 @@ mm_manager_class_init (MMManagerClass *manager_class)
dbus_g_error_domain_register (MM_MODEM_ERROR, "org.freedesktop.ModemManager.Modem", MM_TYPE_MODEM_ERROR);
dbus_g_error_domain_register (MM_MODEM_CONNECT_ERROR, "org.freedesktop.ModemManager.Modem", MM_TYPE_MODEM_CONNECT_ERROR);
dbus_g_error_domain_register (MM_MOBILE_ERROR, "org.freedesktop.ModemManager.Modem.Gsm", MM_TYPE_MOBILE_ERROR);
+ dbus_g_error_domain_register (MM_MOBILE_ERROR, "org.freedesktop.ModemManager.Modem.Lte", MM_TYPE_MOBILE_ERROR);
+
}
diff --git a/src/mm-modem-helpers.c b/src/mm-modem-helpers.c
index f6a0ffa..b63e076 100644
--- a/src/mm-modem-helpers.c
+++ b/src/mm-modem-helpers.c
@@ -12,6 +12,7 @@
*
* Copyright (C) 2008 - 2009 Novell, Inc.
* Copyright (C) 2009 - 2010 Red Hat, Inc.
+ * Copyright (C) 2011 Novatel Wireless, Inc.
*/
#include <config.h>
@@ -214,6 +215,12 @@ mm_gsm_parse_scan_response (const char *reply, GError **error)
return results;
}
+GPtrArray *
+mm_lte_parse_scan_response (const char *reply, GError **error)
+{
+ return mm_gsm_parse_scan_response (reply, error);
+}
+
void
mm_gsm_destroy_scan_data (gpointer data)
{
@@ -223,6 +230,15 @@ mm_gsm_destroy_scan_data (gpointer data)
g_ptr_array_free (results, TRUE);
}
+void
+mm_lte_destroy_scan_data (gpointer data)
+{
+ GPtrArray *results = (GPtrArray *) data;
+
+ g_ptr_array_foreach (results, (GFunc) g_hash_table_destroy, NULL);
+ g_ptr_array_free (results, TRUE);
+}
+
/*************************************************************************/
/* +CREG: <stat> (GSM 07.07 CREG=1 unsolicited) */
@@ -312,6 +328,12 @@ mm_gsm_creg_regex_get (gboolean solicited)
return array;
}
+GPtrArray *
+mm_lte_creg_regex_get (gboolean solicited)
+{
+ return mm_gsm_creg_regex_get (solicited);
+}
+
void
mm_gsm_creg_regex_destroy (GPtrArray *array)
{
@@ -319,6 +341,13 @@ mm_gsm_creg_regex_destroy (GPtrArray *array)
g_ptr_array_free (array, TRUE);
}
+void
+mm_lte_creg_regex_destroy (GPtrArray *array)
+{
+ g_ptr_array_foreach (array, (GFunc) g_regex_unref, NULL);
+ g_ptr_array_free (array, TRUE);
+}
+
/*************************************************************************/
static gulong
@@ -463,6 +492,24 @@ mm_gsm_parse_creg_response (GMatchInfo *info,
return TRUE;
}
+gboolean
+mm_lte_parse_creg_response (GMatchInfo *info,
+ guint32 *out_reg_state,
+ gulong *out_lac,
+ gulong *out_ci,
+ gint *out_act,
+ gboolean *out_cgreg,
+ GError **error)
+{
+ return mm_gsm_parse_creg_response (info,
+ out_reg_state,
+ out_lac,
+ out_ci,
+ out_act,
+ out_cgreg,
+ error);
+}
+
/*************************************************************************/
gboolean
@@ -788,6 +835,14 @@ mm_gsm_parse_cscs_support_response (const char *reply,
return success;
}
+gboolean
+mm_lte_parse_cscs_support_response (const char *reply,
+ MMModemCharset *out_charsets)
+{
+ return mm_gsm_parse_cscs_support_response (reply,
+ out_charsets);
+}
+
/*************************************************************************/
MMModemGsmAccessTech
@@ -820,6 +875,40 @@ mm_gsm_string_to_access_tech (const char *string)
return MM_MODEM_GSM_ACCESS_TECH_UNKNOWN;
}
+MMModemLteAccessTech
+mm_lte_string_to_access_tech (const char *string)
+{
+ g_return_val_if_fail (string != NULL, MM_MODEM_LTE_ACCESS_TECH_UNKNOWN);
+
+ /* Better technologies are listed first since modems sometimes say
+ * stuff like "GPRS/EDGE" and that should be handled as EDGE.
+ */
+ if (strcasestr (string, "GSM"))
+ return MM_MODEM_LTE_ACCESS_TECH_GSM;
+ else if (strcasestr (string, "GPRS"))
+ return MM_MODEM_LTE_ACCESS_TECH_GPRS;
+ else if (strcasestr (string, "EDGE"))
+ return MM_MODEM_LTE_ACCESS_TECH_EDGE;
+ else if (strcasestr (string, "UMTS"))
+ return MM_MODEM_LTE_ACCESS_TECH_UMTS;
+ else if (strcasestr (string, "HSDPA"))
+ return MM_MODEM_LTE_ACCESS_TECH_HSDPA;
+ else if (strcasestr (string, "HSPA"))
+ return MM_MODEM_LTE_ACCESS_TECH_HSPA;
+ else if (strcasestr (string, "LTE"))
+ return MM_MODEM_LTE_ACCESS_TECH_LTE;
+ else if (strcasestr (string, "1xRTT"))
+ return MM_MODEM_LTE_ACCESS_TECH_1xRTT;
+ else if (strcasestr (string, "EVDO"))
+ return MM_MODEM_LTE_ACCESS_TECH_EvDO;
+ else if (strcasestr (string, "EVDO_REL0"))
+ return MM_MODEM_LTE_ACCESS_TECH_EvDO_Rel0;
+ else if (strcasestr (string, "EVDOA"))
+ return MM_MODEM_LTE_ACCESS_TECH_EvDOA;
+
+ return MM_MODEM_LTE_ACCESS_TECH_UNKNOWN;
+}
+
/*************************************************************************/
char *
diff --git a/src/mm-modem-helpers.h b/src/mm-modem-helpers.h
index a47f469..4e81675 100644
--- a/src/mm-modem-helpers.h
+++ b/src/mm-modem-helpers.h
@@ -12,6 +12,7 @@
*
* Copyright (C) 2008 - 2009 Novell, Inc.
* Copyright (C) 2009 - 2010 Red Hat, Inc.
+ * Copyright (C) 2011 Novatel Wireless, Inc.
*/
#ifndef MM_MODEM_HELPERS_H
@@ -20,6 +21,7 @@
#include <ModemManager.h>
#include "mm-modem-cdma.h"
+/* #include "mm-modem-lte.h" */
#include "mm-charsets.h"
#define MM_SCAN_TAG_STATUS "status"
@@ -34,6 +36,8 @@ void mm_gsm_destroy_scan_data (gpointer data);
GPtrArray *mm_gsm_creg_regex_get (gboolean solicited);
+GPtrArray *mm_lte_creg_regex_get (gboolean solicited);
+
void mm_gsm_creg_regex_destroy (GPtrArray *array);
gboolean mm_gsm_parse_creg_response (GMatchInfo *info,
@@ -44,6 +48,22 @@ gboolean mm_gsm_parse_creg_response (GMatchInfo *info,
gboolean *out_cgreg,
GError **error);
+GPtrArray *mm_lte_parse_scan_response (const char *reply, GError **error);
+
+void mm_lte_destroy_scan_data (gpointer data);
+
+GPtrArray *mm_ltem_creg_regex_get (gboolean solicited);
+
+void mm_lte_creg_regex_destroy (GPtrArray *array);
+
+gboolean mm_lte_parse_creg_response (GMatchInfo *info,
+ guint32 *out_reg_state,
+ gulong *out_lac,
+ gulong *out_ci,
+ gint *out_act,
+ gboolean *out_cgreg,
+ GError **error);
+
const char *mm_strip_tag (const char *str, const char *cmd);
gboolean mm_cdma_parse_spservice_response (const char *reply,
@@ -58,8 +78,15 @@ gboolean mm_cdma_parse_eri (const char *reply,
gboolean mm_gsm_parse_cscs_support_response (const char *reply,
MMModemCharset *out_charsets);
+gboolean mm_lte_parse_cscs_support_response (const char *reply,
+ MMModemCharset *out_charsets);
+
+
MMModemGsmAccessTech mm_gsm_string_to_access_tech (const char *string);
+MMModemLteAccessTech mm_lte_string_to_access_tech (const char *string);
+
+
char *mm_create_device_identifier (guint vid,
guint pid,
const char *ati,
diff --git a/src/mm-modem-lte-card.c b/src/mm-modem-lte-card.c
new file mode 100644
index 0000000..40df1b4
--- /dev/null
+++ b/src/mm-modem-lte-card.c
@@ -0,0 +1,534 @@
+/* -*- 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) 2008 Novell, Inc.
+ * Copyright (C) 2009 Red Hat, Inc.
+ * Copyright (C) 2011 Novatel Wireless, Inc.
+ */
+
+#include <dbus/dbus-glib.h>
+#include <string.h>
+
+#include "mm-modem-lte-card.h"
+#include "mm-errors.h"
+#include "mm-callback-info.h"
+#include "mm-modem-lte.h"
+
+static void impl_lte_modem_get_imei (MMModemLteCard *modem,
+ DBusGMethodInvocation *context);
+
+static void impl_lte_modem_get_imsi (MMModemLteCard *modem,
+ DBusGMethodInvocation *context);
+
+static void impl_lte_modem_send_pin (MMModemLteCard *modem,
+ const char *pin,
+ DBusGMethodInvocation *context);
+
+static void impl_lte_modem_enable_pin (MMModemLteCard *modem,
+ const char *pin,
+ gboolean enabled,
+ DBusGMethodInvocation *context);
+
+static void impl_lte_modem_change_pin (MMModemLteCard *modem,
+ const char *old_pin,
+ const char *new_pin,
+ DBusGMethodInvocation *context);
+
+#include "mm-modem-lte-card-glue.h"
+
+/*****************************************************************************/
+
+static void
+str_call_done (MMModem *modem, const char *result, GError *error, gpointer user_data)
+{
+ DBusGMethodInvocation *context = (DBusGMethodInvocation *) user_data;
+
+ if (error)
+ dbus_g_method_return_error (context, error);
+ else
+ dbus_g_method_return (context, result);
+}
+
+static void
+str_call_not_supported (MMModemLteCard *self,
+ MMModemStringFn callback,
+ gpointer user_data)
+{
+ MMCallbackInfo *info;
+
+ info = mm_callback_info_string_new (MM_MODEM (self), callback, user_data);
+ info->error = g_error_new_literal (MM_MODEM_ERROR, MM_MODEM_ERROR_OPERATION_NOT_SUPPORTED,
+ "Operation not supported");
+
+ mm_callback_info_schedule (info);
+}
+
+static void
+uint_call_not_supported (MMModemLteCard *self,
+ MMModemUIntFn callback,
+ gpointer user_data)
+{
+ MMCallbackInfo *info;
+
+ info = mm_callback_info_uint_new (MM_MODEM (self), callback, user_data);
+ info->error = g_error_new_literal (MM_MODEM_ERROR, MM_MODEM_ERROR_OPERATION_NOT_SUPPORTED,
+ "Operation not supported");
+ mm_callback_info_schedule (info);
+}
+
+static void
+async_call_done (MMModem *modem, GError *error, gpointer user_data)
+{
+ DBusGMethodInvocation *context = (DBusGMethodInvocation *) user_data;
+
+ if (error)
+ dbus_g_method_return_error (context, error);
+ else
+ dbus_g_method_return (context);
+}
+
+static void
+async_call_not_supported (MMModemLteCard *self,
+ MMModemFn callback,
+ gpointer user_data)
+{
+ MMCallbackInfo *info;
+
+ info = mm_callback_info_new (MM_MODEM (self), callback, user_data);
+ info->error = g_error_new_literal (MM_MODEM_ERROR, MM_MODEM_ERROR_OPERATION_NOT_SUPPORTED,
+ "Operation not supported");
+ mm_callback_info_schedule (info);
+}
+
+/*****************************************************************************/
+
+void
+mm_modem_lte_card_get_imei (MMModemLteCard *self,
+ MMModemStringFn callback,
+ gpointer user_data)
+{
+ g_return_if_fail (MM_IS_MODEM_LTE_CARD (self));
+ g_return_if_fail (callback != NULL);
+
+ if (MM_MODEM_LTE_CARD_GET_INTERFACE (self)->get_imei)
+ MM_MODEM_LTE_CARD_GET_INTERFACE (self)->get_imei (self, callback, user_data);
+ else
+ str_call_not_supported (self, callback, user_data);
+}
+
+void
+mm_modem_lte_card_get_imsi (MMModemLteCard *self,
+ MMModemStringFn callback,
+ gpointer user_data)
+{
+ g_return_if_fail (MM_IS_MODEM_LTE_CARD (self));
+ g_return_if_fail (callback != NULL);
+
+ if (MM_MODEM_LTE_CARD_GET_INTERFACE (self)->get_imsi)
+ MM_MODEM_LTE_CARD_GET_INTERFACE (self)->get_imsi (self, callback, user_data);
+ else
+ str_call_not_supported (self, callback, user_data);
+}
+
+void mm_modem_lte_card_get_unlock_retries (MMModemLteCard *self,
+ const char *pin_type,
+ MMModemUIntFn callback,
+ gpointer user_data)
+{
+ g_return_if_fail (MM_IS_MODEM_LTE_CARD (self));
+ g_return_if_fail (pin_type != NULL);
+ g_return_if_fail (callback != NULL);
+
+ if (MM_MODEM_LTE_CARD_GET_INTERFACE (self)->get_unlock_retries)
+ MM_MODEM_LTE_CARD_GET_INTERFACE (self)->get_unlock_retries (self, pin_type, callback, user_data);
+ else
+ uint_call_not_supported (self, callback, user_data);
+}
+
+void
+mm_modem_lte_card_get_operator_id (MMModemLteCard *self,
+ MMModemStringFn callback,
+ gpointer user_data)
+{
+ g_return_if_fail (MM_IS_MODEM_LTE_CARD (self));
+ g_return_if_fail (callback != NULL);
+
+ if (MM_MODEM_LTE_CARD_GET_INTERFACE (self)->get_operator_id)
+ MM_MODEM_LTE_CARD_GET_INTERFACE (self)->get_operator_id (self, callback, user_data);
+ else
+ str_call_not_supported (self, callback, user_data);
+}
+
+void
+mm_modem_lte_card_get_spn (MMModemLteCard *self,
+ MMModemStringFn callback,
+ gpointer user_data)
+{
+ g_return_if_fail (MM_IS_MODEM_LTE_CARD (self));
+ g_return_if_fail (callback != NULL);
+
+ if (MM_MODEM_LTE_CARD_GET_INTERFACE (self)->get_spn)
+ MM_MODEM_LTE_CARD_GET_INTERFACE (self)->get_spn (self, callback, user_data);
+ else
+ str_call_not_supported (self, callback, user_data);
+}
+
+void
+mm_modem_lte_card_send_pin (MMModemLteCard *self,
+ const char *pin,
+ MMModemFn callback,
+ gpointer user_data)
+{
+ g_return_if_fail (MM_IS_MODEM_LTE_CARD (self));
+ g_return_if_fail (pin != NULL);
+ g_return_if_fail (callback != NULL);
+
+ if (MM_MODEM_LTE_CARD_GET_INTERFACE (self)->send_pin)
+ MM_MODEM_LTE_CARD_GET_INTERFACE (self)->send_pin (self, pin, callback, user_data);
+ else
+ async_call_not_supported (self, callback, user_data);
+}
+
+void
+mm_modem_lte_card_enable_pin (MMModemLteCard *self,
+ const char *pin,
+ gboolean enabled,
+ MMModemFn callback,
+ gpointer user_data)
+{
+ g_return_if_fail (MM_IS_MODEM_LTE_CARD (self));
+ g_return_if_fail (pin != NULL);
+ g_return_if_fail (callback != NULL);
+
+ if (MM_MODEM_LTE_CARD_GET_INTERFACE (self)->enable_pin)
+ MM_MODEM_LTE_CARD_GET_INTERFACE (self)->enable_pin (self, pin, enabled, callback, user_data);
+ else
+ async_call_not_supported (self, callback, user_data);
+}
+
+void
+mm_modem_lte_card_change_pin (MMModemLteCard *self,
+ const char *old_pin,
+ const char *new_pin,
+ MMModemFn callback,
+ gpointer user_data)
+{
+ g_return_if_fail (MM_IS_MODEM_LTE_CARD (self));
+ g_return_if_fail (old_pin != NULL);
+ g_return_if_fail (new_pin != NULL);
+ g_return_if_fail (callback != NULL);
+
+ if (MM_MODEM_LTE_CARD_GET_INTERFACE (self)->change_pin)
+ MM_MODEM_LTE_CARD_GET_INTERFACE (self)->change_pin (self, old_pin, new_pin, callback, user_data);
+ else
+ async_call_not_supported (self, callback, user_data);
+}
+
+/*****************************************************************************/
+
+static void
+imei_auth_cb (MMAuthRequest *req,
+ GObject *owner,
+ DBusGMethodInvocation *context,
+ gpointer user_data)
+{
+ MMModemLteCard *self = MM_MODEM_LTE_CARD (owner);
+ GError *error = NULL;
+
+ /* Return any authorization error, otherwise get the IMEI */
+ if (!mm_modem_auth_finish (MM_MODEM (self), req, &error)) {
+ dbus_g_method_return_error (context, error);
+ g_error_free (error);
+ } else
+ mm_modem_lte_card_get_imei (self, str_call_done, context);
+}
+
+static void
+impl_lte_modem_get_imei (MMModemLteCard *modem, DBusGMethodInvocation *context)
+{
+ GError *error = NULL;
+
+ /* Make sure the caller is authorized to get the IMEI */
+ if (!mm_modem_auth_request (MM_MODEM (modem),
+ MM_AUTHORIZATION_DEVICE_INFO,
+ context,
+ imei_auth_cb,
+ NULL,
+ NULL,
+ &error)) {
+ dbus_g_method_return_error (context, error);
+ g_error_free (error);
+ }
+}
+
+/*****************************************************************************/
+
+static void
+imsi_auth_cb (MMAuthRequest *req,
+ GObject *owner,
+ DBusGMethodInvocation *context,
+ gpointer user_data)
+{
+ MMModemLteCard *self = MM_MODEM_LTE_CARD (owner);
+ GError *error = NULL;
+
+ /* Return any authorization error, otherwise get the IMSI */
+ if (!mm_modem_auth_finish (MM_MODEM (self), req, &error)) {
+ dbus_g_method_return_error (context, error);
+ g_error_free (error);
+ } else
+ mm_modem_lte_card_get_imsi (self, str_call_done, context);
+}
+
+static void
+impl_lte_modem_get_imsi (MMModemLteCard *modem, DBusGMethodInvocation *context)
+{
+ GError *error = NULL;
+
+ /* Make sure the caller is authorized to get the IMSI */
+ if (!mm_modem_auth_request (MM_MODEM (modem),
+ MM_AUTHORIZATION_DEVICE_INFO,
+ context,
+ imsi_auth_cb,
+ NULL,
+ NULL,
+ &error)) {
+ dbus_g_method_return_error (context, error);
+ g_error_free (error);
+ }
+}
+
+/*****************************************************************************/
+
+typedef struct {
+ char *pin;
+ char *pin2;
+ gboolean enabled;
+} SendPinInfo;
+
+static void
+send_pin_info_destroy (gpointer data)
+{
+ SendPinInfo *info = data;
+
+ g_free (info->pin);
+ g_free (info->pin2);
+ memset (info, 0, sizeof (SendPinInfo));
+ g_free (info);
+}
+
+static SendPinInfo *
+send_pin_info_new (const char *pin,
+ const char *pin2,
+ gboolean enabled)
+{
+ SendPinInfo *info;
+
+ info = g_malloc0 (sizeof (SendPinInfo));
+
+ info->pin = g_strdup (pin);
+ info->pin2 = g_strdup (pin2);
+ info->enabled = enabled;
+ return info;
+}
+
+/*****************************************************************************/
+
+static void
+send_pin_auth_cb (MMAuthRequest *req,
+ GObject *owner,
+ DBusGMethodInvocation *context,
+ gpointer user_data)
+{
+ MMModemLteCard *self = MM_MODEM_LTE_CARD (owner);
+ SendPinInfo *info = user_data;
+ GError *error = NULL;
+
+ /* Return any authorization error, otherwise unlock the modem */
+ if (!mm_modem_auth_finish (MM_MODEM (self), req, &error)) {
+ dbus_g_method_return_error (context, error);
+ g_error_free (error);
+ } else
+ mm_modem_lte_card_send_pin (self, info->pin, async_call_done, context);
+}
+
+static void
+impl_lte_modem_send_pin (MMModemLteCard *modem,
+ const char *pin,
+ DBusGMethodInvocation *context)
+{
+ GError *error = NULL;
+ SendPinInfo *info;
+
+ info = send_pin_info_new (pin, NULL, FALSE);
+
+ /* Make sure the caller is authorized to unlock the modem */
+ if (!mm_modem_auth_request (MM_MODEM (modem),
+ MM_AUTHORIZATION_DEVICE_CONTROL,
+ context,
+ send_pin_auth_cb,
+ info,
+ send_pin_info_destroy,
+ &error)) {
+ dbus_g_method_return_error (context, error);
+ g_error_free (error);
+ }
+}
+
+/*****************************************************************************/
+
+static void
+enable_pin_auth_cb (MMAuthRequest *req,
+ GObject *owner,
+ DBusGMethodInvocation *context,
+ gpointer user_data)
+{
+ MMModemLteCard *self = MM_MODEM_LTE_CARD (owner);
+ SendPinInfo *info = user_data;
+ GError *error = NULL;
+
+ /* Return any authorization error, otherwise enable the PIN */
+ if (!mm_modem_auth_finish (MM_MODEM (self), req, &error)) {
+ dbus_g_method_return_error (context, error);
+ g_error_free (error);
+ } else
+ mm_modem_lte_card_enable_pin (self, info->pin, info->enabled, async_call_done, context);
+}
+
+static void
+impl_lte_modem_enable_pin (MMModemLteCard *modem,
+ const char *pin,
+ gboolean enabled,
+ DBusGMethodInvocation *context)
+{
+ GError *error = NULL;
+ SendPinInfo *info;
+
+ info = send_pin_info_new (pin, NULL, enabled);
+
+ /* Make sure the caller is authorized to enable a PIN */
+ if (!mm_modem_auth_request (MM_MODEM (modem),
+ MM_AUTHORIZATION_DEVICE_CONTROL,
+ context,
+ enable_pin_auth_cb,
+ info,
+ send_pin_info_destroy,
+ &error)) {
+ dbus_g_method_return_error (context, error);
+ g_error_free (error);
+ }
+}
+
+/*****************************************************************************/
+
+static void
+change_pin_auth_cb (MMAuthRequest *req,
+ GObject *owner,
+ DBusGMethodInvocation *context,
+ gpointer user_data)
+{
+ MMModemLteCard *self = MM_MODEM_LTE_CARD (owner);
+ SendPinInfo *info = user_data;
+ GError *error = NULL;
+
+ /* Return any authorization error, otherwise change the PIN */
+ if (!mm_modem_auth_finish (MM_MODEM (self), req, &error)) {
+ dbus_g_method_return_error (context, error);
+ g_error_free (error);
+ } else
+ mm_modem_lte_card_change_pin (self, info->pin, info->pin2, async_call_done, context);
+}
+
+static void
+impl_lte_modem_change_pin (MMModemLteCard *modem,
+ const char *old_pin,
+ const char *new_pin,
+ DBusGMethodInvocation *context)
+{
+ GError *error = NULL;
+ SendPinInfo *info;
+
+ info = send_pin_info_new (old_pin, new_pin, FALSE);
+
+ /* Make sure the caller is authorized to change the PIN */
+ if (!mm_modem_auth_request (MM_MODEM (modem),
+ MM_AUTHORIZATION_DEVICE_CONTROL,
+ context,
+ change_pin_auth_cb,
+ info,
+ send_pin_info_destroy,
+ &error)) {
+ dbus_g_method_return_error (context, error);
+ g_error_free (error);
+ }
+}
+
+/*****************************************************************************/
+
+static void
+mm_modem_lte_card_init (gpointer g_iface)
+{
+ static gboolean initialized = FALSE;
+
+ if (G_LIKELY (initialized))
+ return;
+
+ initialized = TRUE;
+
+ g_object_interface_install_property
+ (g_iface,
+ g_param_spec_string (MM_MODEM_LTE_CARD_SIM_IDENTIFIER,
+ "SimIdentifier",
+ "An obfuscated identifier of the SIM",
+ NULL,
+ G_PARAM_READABLE));
+
+ g_object_interface_install_property
+ (g_iface,
+ g_param_spec_uint (MM_MODEM_LTE_CARD_SUPPORTED_BANDS,
+ "Supported Modes",
+ "Supported frequency bands of the card",
+ MM_MODEM_LTE_BAND_UNKNOWN,
+ G_MAXUINT32,
+ MM_MODEM_LTE_BAND_UNKNOWN,
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
+}
+
+GType
+mm_modem_lte_card_get_type (void)
+{
+ static GType card_type = 0;
+
+ if (G_UNLIKELY (!card_type)) {
+ const GTypeInfo card_info = {
+ sizeof (MMModemLteCard), /* class_size */
+ mm_modem_lte_card_init, /* base_init */
+ NULL, /* base_finalize */
+ NULL,
+ NULL, /* class_finalize */
+ NULL, /* class_data */
+ 0,
+ 0, /* n_preallocs */
+ NULL
+ };
+
+ card_type = g_type_register_static (G_TYPE_INTERFACE,
+ "MMModemLteCard",
+ &card_info, 0);
+
+ g_type_interface_add_prerequisite (card_type, G_TYPE_OBJECT);
+ g_type_interface_add_prerequisite (card_type, MM_TYPE_MODEM);
+ dbus_g_object_type_install_info (card_type, &dbus_glib_mm_modem_lte_card_object_info);
+ }
+
+ return card_type;
+}
diff --git a/src/mm-modem-lte-card.h b/src/mm-modem-lte-card.h
new file mode 100644
index 0000000..a462f6b
--- /dev/null
+++ b/src/mm-modem-lte-card.h
@@ -0,0 +1,122 @@
+/* -*- 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) 2008 Novell, Inc.
+ * Copyright (C) 2009 Red Hat, Inc.
+ * Copyright (C) 2011 Novatel Wireless, Inc.
+ */
+
+#ifndef MM_MODEM_LTE_CARD_H
+#define MM_MODEM_LTE_CARD_H
+
+#include <mm-modem.h>
+
+#define MM_TYPE_MODEM_LTE_CARD (mm_modem_lte_card_get_type ())
+#define MM_MODEM_LTE_CARD(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MM_TYPE_MODEM_LTE_CARD, MMModemLteCard))
+#define MM_IS_MODEM_LTE_CARD(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MM_TYPE_MODEM_LTE_CARD))
+#define MM_MODEM_LTE_CARD_GET_INTERFACE(obj) (G_TYPE_INSTANCE_GET_INTERFACE ((obj), MM_TYPE_MODEM_LTE_CARD, MMModemLteCard))
+
+#define MM_MODEM_LTE_CARD_SUPPORTED_BANDS "supported-bands"
+#define MM_MODEM_LTE_CARD_SUPPORTED_MODES "supported-modes"
+#define MM_MODEM_LTE_CARD_SIM_IDENTIFIER "sim-identifier"
+
+#define MM_MODEM_LTE_CARD_SIM_PIN "sim-pin"
+typedef struct _MMModemLteCard MMModemLteCard;
+
+#define MM_MODEM_LTE_CARD_UNLOCK_RETRIES_NOT_SUPPORTED 999
+
+typedef struct _MMModemLteCard MMModemGsmCard;
+
+struct _MMModemLteCard {
+ GTypeInterface g_iface;
+
+ /* Methods */
+ void (*get_imei) (MMModemLteCard *self,
+ MMModemStringFn callback,
+ gpointer user_data);
+
+ void (*get_imsi) (MMModemLteCard *self,
+ MMModemStringFn callback,
+ gpointer user_data);
+
+ void (*get_unlock_retries) (MMModemLteCard *self,
+ const char *pin_type,
+ MMModemUIntFn callback,
+ gpointer user_data);
+
+ void (*get_operator_id) (MMModemLteCard *self,
+ MMModemStringFn callback,
+ gpointer user_data);
+
+ void (*get_spn) (MMModemGsmCard *self,
+ MMModemStringFn callback,
+ gpointer user_data);
+
+ void (*send_pin) (MMModemLteCard *self,
+ const char *pin,
+ MMModemFn callback,
+ gpointer user_data);
+
+ void (*enable_pin) (MMModemLteCard *self,
+ const char *pin,
+ gboolean enabled,
+ MMModemFn callback,
+ gpointer user_data);
+
+ void (*change_pin) (MMModemLteCard *self,
+ const char *old_pin,
+ const char *new_pin,
+ MMModemFn callback,
+ gpointer user_data);
+};
+
+GType mm_modem_lte_card_get_type (void);
+
+void mm_modem_lte_card_get_imei (MMModemLteCard *self,
+ MMModemStringFn callback,
+ gpointer user_data);
+
+void mm_modem_lte_card_get_imsi (MMModemLteCard *self,
+ MMModemStringFn callback,
+ gpointer user_data);
+
+void mm_modem_lte_card_get_unlock_retries (MMModemGsmCard *self,
+ const char *pin_type,
+ MMModemUIntFn callback,
+ gpointer user_data);
+
+void mm_modem_lte_card_get_operator_id (MMModemGsmCard *self,
+ MMModemStringFn callback,
+ gpointer user_data);
+
+void mm_modem_lte_card_get_spn (MMModemGsmCard *self,
+ MMModemStringFn callback,
+ gpointer user_data);
+
+void mm_modem_lte_card_send_pin (MMModemGsmCard *self,
+ const char *pin,
+ MMModemFn callback,
+ gpointer user_data);
+
+void mm_modem_lte_card_enable_pin (MMModemGsmCard *self,
+ const char *pin,
+ gboolean enabled,
+ MMModemFn callback,
+ gpointer user_data);
+
+void mm_modem_lte_card_change_pin (MMModemLteCard *self,
+ const char *old_pin,
+ const char *new_pin,
+ MMModemFn callback,
+ gpointer user_data);
+
+#endif /* MM_MODEM_LTE_CARD_H */
diff --git a/src/mm-modem-lte-network.c b/src/mm-modem-lte-network.c
new file mode 100644
index 0000000..84bd57b
--- /dev/null
+++ b/src/mm-modem-lte-network.c
@@ -0,0 +1,564 @@
+/* -*- 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) 2008 Novell, Inc.
+ * Copyright (C) 2010 Red Hat, Inc.
+ * Copyright (C) 2011 Novatel Wireless, Inc.
+ */
+
+#include <string.h>
+#include <dbus/dbus-glib.h>
+
+#include "mm-modem-lte-network.h"
+#include "mm-errors.h"
+#include "mm-callback-info.h"
+#include "mm-marshal.h"
+#include "mm-utils.h"
+#include "mm-log.h"
+
+static void impl_lte_modem_register (MMModemLteNetwork *modem,
+ const char *network_id,
+ DBusGMethodInvocation *context);
+
+static void impl_lte_modem_scan (MMModemLteNetwork *modem,
+ DBusGMethodInvocation *context);
+
+static void impl_lte_modem_set_apn (MMModemLteNetwork *modem,
+ const char *apn,
+ DBusGMethodInvocation *context);
+
+static void impl_lte_modem_get_signal_quality (MMModemLteNetwork *modem,
+ DBusGMethodInvocation *context);
+
+static void impl_lte_modem_set_band (MMModemLteNetwork *modem,
+ guint32 band,
+ DBusGMethodInvocation *context);
+
+static void impl_lte_modem_get_band (MMModemLteNetwork *modem,
+ DBusGMethodInvocation *context);
+
+static void impl_lte_modem_set_allowed_mode (MMModemLteNetwork *modem,
+ MMModemLteAllowedMode mode,
+ DBusGMethodInvocation *context);
+
+static void impl_lte_modem_get_reg_info (MMModemLteNetwork *modem,
+ DBusGMethodInvocation *context);
+
+#include "mm-modem-lte-network-glue.h"
+
+/*****************************************************************************/
+
+enum {
+ SIGNAL_QUALITY,
+ REGISTRATION_INFO,
+ NETWORK_MODE,
+
+ LAST_SIGNAL
+};
+
+static guint signals[LAST_SIGNAL] = { 0 };
+
+/*****************************************************************************/
+
+static void
+async_call_done (MMModem *modem, GError *error, gpointer user_data)
+{
+ DBusGMethodInvocation *context = (DBusGMethodInvocation *) user_data;
+
+ if (error)
+ dbus_g_method_return_error (context, error);
+ else
+ dbus_g_method_return (context);
+}
+
+static void
+async_call_not_supported (MMModemLteNetwork *self,
+ MMModemFn callback,
+ gpointer user_data)
+{
+ MMCallbackInfo *info;
+
+ info = mm_callback_info_new (MM_MODEM (self), callback, user_data);
+ info->error = g_error_new_literal (MM_MODEM_ERROR, MM_MODEM_ERROR_OPERATION_NOT_SUPPORTED,
+ "Operation not supported");
+ mm_callback_info_schedule (info);
+}
+
+static void
+uint_call_done (MMModem *modem, guint32 result, GError *error, gpointer user_data)
+{
+ DBusGMethodInvocation *context = (DBusGMethodInvocation *) user_data;
+
+ if (error)
+ dbus_g_method_return_error (context, error);
+ else
+ dbus_g_method_return (context, result);
+}
+
+static void
+uint_call_not_supported (MMModemLteNetwork *self,
+ MMModemUIntFn callback,
+ gpointer user_data)
+{
+ MMCallbackInfo *info;
+
+ info = mm_callback_info_uint_new (MM_MODEM (self), callback, user_data);
+ info->error = g_error_new_literal (MM_MODEM_ERROR, MM_MODEM_ERROR_OPERATION_NOT_SUPPORTED,
+ "Operation not supported");
+ mm_callback_info_schedule (info);
+}
+
+static void
+reg_info_call_done (MMModemLteNetwork *self,
+ MMModemLteNetworkRegStatus status,
+ const char *oper_code,
+ const char *oper_name,
+ GError *error,
+ gpointer user_data)
+{
+ DBusGMethodInvocation *context = (DBusGMethodInvocation *) user_data;
+
+ if (error)
+ dbus_g_method_return_error (context, error);
+ else {
+ GValueArray *array;
+ GValue value = { 0, };
+
+ array = g_value_array_new (3);
+
+ /* Status */
+ g_value_init (&value, G_TYPE_UINT);
+ g_value_set_uint (&value, (guint32) status);
+ g_value_array_append (array, &value);
+ g_value_unset (&value);
+
+ /* Operator code */
+ g_value_init (&value, G_TYPE_STRING);
+ g_value_set_string (&value, oper_code);
+ g_value_array_append (array, &value);
+ g_value_unset (&value);
+
+ /* Operator name */
+ g_value_init (&value, G_TYPE_STRING);
+ g_value_set_string (&value, oper_name);
+ g_value_array_append (array, &value);
+ g_value_unset (&value);
+
+ dbus_g_method_return (context, array);
+ }
+}
+
+static void
+reg_info_invoke (MMCallbackInfo *info)
+{
+ MMModemLteNetworkRegInfoFn callback = (MMModemLteNetworkRegInfoFn) info->callback;
+
+ callback (MM_MODEM_LTE_NETWORK (info->modem), 0, NULL, NULL, info->error, info->user_data);
+}
+
+static void
+reg_info_call_not_supported (MMModemLteNetwork *self,
+ MMModemLteNetworkRegInfoFn callback,
+ gpointer user_data)
+{
+ MMCallbackInfo *info;
+
+ info = mm_callback_info_new_full (MM_MODEM (self), reg_info_invoke, G_CALLBACK (callback), user_data);
+ info->error = g_error_new_literal (MM_MODEM_ERROR, MM_MODEM_ERROR_OPERATION_NOT_SUPPORTED,
+ "Operation not supported");
+
+ mm_callback_info_schedule (info);
+}
+
+static void
+scan_call_done (MMModemLteNetwork *self,
+ GPtrArray *results,
+ GError *error,
+ gpointer user_data)
+{
+ DBusGMethodInvocation *context = (DBusGMethodInvocation *) user_data;
+
+ if (error)
+ dbus_g_method_return_error (context, error);
+ else
+ dbus_g_method_return (context, results);
+}
+
+static void
+lte_network_scan_invoke (MMCallbackInfo *info)
+{
+ MMModemLteNetworkScanFn callback = (MMModemLteNetworkScanFn) info->callback;
+
+ callback (MM_MODEM_LTE_NETWORK (info->modem), NULL, info->error, info->user_data);
+}
+
+static void
+scan_call_not_supported (MMModemLteNetwork *self,
+ MMModemLteNetworkScanFn callback,
+ gpointer user_data)
+{
+ MMCallbackInfo *info;
+
+ info = mm_callback_info_new_full (MM_MODEM (self), lte_network_scan_invoke, G_CALLBACK (callback), user_data);
+ info->error = g_error_new_literal (MM_MODEM_ERROR, MM_MODEM_ERROR_OPERATION_NOT_SUPPORTED,
+ "Operation not supported");
+
+ mm_callback_info_schedule (info);
+}
+
+/*****************************************************************************/
+
+void
+mm_modem_lte_network_register (MMModemLteNetwork *self,
+ const char *network_id,
+ MMModemFn callback,
+ gpointer user_data)
+{
+ g_return_if_fail (MM_IS_MODEM_LTE_NETWORK (self));
+ g_return_if_fail (callback != NULL);
+
+ if (MM_MODEM_LTE_NETWORK_GET_INTERFACE (self)->do_register)
+ MM_MODEM_LTE_NETWORK_GET_INTERFACE (self)->do_register (self, network_id, callback, user_data);
+ else
+ async_call_not_supported (self, callback, user_data);
+}
+
+void
+mm_modem_lte_network_scan (MMModemLteNetwork *self,
+ MMModemLteNetworkScanFn callback,
+ gpointer user_data)
+{
+ g_return_if_fail (MM_IS_MODEM_LTE_NETWORK (self));
+ g_return_if_fail (callback != NULL);
+
+ if (MM_MODEM_LTE_NETWORK_GET_INTERFACE (self)->scan)
+ MM_MODEM_LTE_NETWORK_GET_INTERFACE (self)->scan (self, callback, user_data);
+ else
+ scan_call_not_supported (self, callback, user_data);
+}
+
+void
+mm_modem_lte_network_set_apn (MMModemLteNetwork *self,
+ const char *apn,
+ MMModemFn callback,
+ gpointer user_data)
+{
+ g_return_if_fail (MM_IS_MODEM_LTE_NETWORK (self));
+ g_return_if_fail (apn != NULL);
+ g_return_if_fail (callback != NULL);
+
+ if (MM_MODEM_LTE_NETWORK_GET_INTERFACE (self)->set_apn)
+ MM_MODEM_LTE_NETWORK_GET_INTERFACE (self)->set_apn (self, apn, callback, user_data);
+ else
+ async_call_not_supported (self, callback, user_data);
+}
+
+void
+mm_modem_lte_network_get_signal_quality (MMModemLteNetwork *self,
+ MMModemUIntFn callback,
+ gpointer user_data)
+{
+ g_return_if_fail (MM_IS_MODEM_LTE_NETWORK (self));
+ g_return_if_fail (callback != NULL);
+
+ if (MM_MODEM_LTE_NETWORK_GET_INTERFACE (self)->get_signal_quality)
+ MM_MODEM_LTE_NETWORK_GET_INTERFACE (self)->get_signal_quality (self, callback, user_data);
+ else
+ uint_call_not_supported (self, callback, user_data);
+}
+
+void
+mm_modem_lte_network_set_band (MMModemLteNetwork *self,
+ guint32 band,
+ MMModemFn callback,
+ gpointer user_data)
+{
+ g_return_if_fail (MM_IS_MODEM_LTE_NETWORK (self));
+ g_return_if_fail (callback != NULL);
+
+ if (MM_MODEM_LTE_NETWORK_GET_INTERFACE (self)->set_band)
+ MM_MODEM_LTE_NETWORK_GET_INTERFACE (self)->set_band (self, band, callback, user_data);
+ else
+ async_call_not_supported (self, callback, user_data);
+}
+
+void
+mm_modem_lte_network_get_band (MMModemLteNetwork *self,
+ MMModemUIntFn callback,
+ gpointer user_data)
+{
+ g_return_if_fail (MM_IS_MODEM_LTE_NETWORK (self));
+ g_return_if_fail (callback != NULL);
+
+ if (MM_MODEM_LTE_NETWORK_GET_INTERFACE (self)->get_band)
+ MM_MODEM_LTE_NETWORK_GET_INTERFACE (self)->get_band (self, callback, user_data);
+ else
+ uint_call_not_supported (self, callback, user_data);
+}
+
+void
+mm_modem_lte_network_set_allowed_mode (MMModemLteNetwork *self,
+ MMModemLteAllowedMode mode,
+ MMModemFn callback,
+ gpointer user_data)
+{
+ g_return_if_fail (MM_IS_MODEM_LTE_NETWORK (self));
+ g_return_if_fail (callback != NULL);
+
+ if (MM_MODEM_LTE_NETWORK_GET_INTERFACE (self)->set_allowed_mode)
+ MM_MODEM_LTE_NETWORK_GET_INTERFACE (self)->set_allowed_mode (self, mode, callback, user_data);
+ else
+ async_call_not_supported (self, callback, user_data);
+}
+
+void
+mm_modem_lte_network_get_registration_info (MMModemLteNetwork *self,
+ MMModemLteNetworkRegInfoFn callback,
+ gpointer user_data)
+{
+ g_return_if_fail (MM_IS_MODEM_LTE_NETWORK (self));
+ g_return_if_fail (callback != NULL);
+
+ if (MM_MODEM_LTE_NETWORK_GET_INTERFACE (self)->get_registration_info)
+ MM_MODEM_LTE_NETWORK_GET_INTERFACE (self)->get_registration_info (self, callback, user_data);
+ else
+ reg_info_call_not_supported (self, callback, user_data);
+}
+
+void
+mm_modem_lte_network_signal_quality (MMModemLteNetwork *self,
+ guint32 quality)
+{
+ g_return_if_fail (MM_IS_MODEM_LTE_NETWORK (self));
+
+ g_signal_emit (self, signals[SIGNAL_QUALITY], 0, quality);
+}
+
+void
+mm_modem_lte_network_registration_info (MMModemLteNetwork *self,
+ MMModemLteNetworkRegStatus status,
+ const char *oper_code,
+ const char *oper_name)
+{
+ g_return_if_fail (MM_IS_MODEM_LTE_NETWORK (self));
+
+ g_signal_emit (self, signals[REGISTRATION_INFO], 0, status,
+ oper_code ? oper_code : "",
+ oper_name ? oper_name : "");
+}
+
+/*****************************************************************************/
+
+static void
+impl_lte_modem_register (MMModemLteNetwork *modem,
+ const char *network_id,
+ DBusGMethodInvocation *context)
+{
+ const char *id;
+
+ /* DBus does not support NULL strings, so the caller should pass an empty string
+ for manual registration. */
+ if (strlen (network_id) < 1)
+ id = NULL;
+ else
+ id = network_id;
+
+ mm_modem_lte_network_register (modem, id, async_call_done, context);
+}
+
+static void
+scan_auth_cb (MMAuthRequest *req,
+ GObject *owner,
+ DBusGMethodInvocation *context,
+ gpointer user_data)
+{
+ MMModemLteNetwork *self = MM_MODEM_LTE_NETWORK (owner);
+ GError *error = NULL;
+
+ /* Return any authorization error, otherwise get the IMEI */
+ if (!mm_modem_auth_finish (MM_MODEM (self), req, &error)) {
+ dbus_g_method_return_error (context, error);
+ g_error_free (error);
+ } else
+ mm_modem_lte_network_scan (self, scan_call_done, context);
+}
+
+static void
+impl_lte_modem_scan (MMModemLteNetwork *modem,
+ DBusGMethodInvocation *context)
+{
+ GError *error = NULL;
+
+ /* Make sure the caller is authorized to request a scan */
+ if (!mm_modem_auth_request (MM_MODEM (modem),
+ MM_AUTHORIZATION_DEVICE_CONTROL,
+ context,
+ scan_auth_cb,
+ NULL,
+ NULL,
+ &error)) {
+ dbus_g_method_return_error (context, error);
+ g_error_free (error);
+ }
+}
+
+static void
+impl_lte_modem_set_apn (MMModemLteNetwork *modem,
+ const char *apn,
+ DBusGMethodInvocation *context)
+{
+ mm_modem_lte_network_set_apn (modem, apn, async_call_done, context);
+}
+
+static void
+impl_lte_modem_get_signal_quality (MMModemLteNetwork *modem,
+ DBusGMethodInvocation *context)
+{
+ mm_modem_lte_network_get_signal_quality (modem, uint_call_done, context);
+}
+
+static void
+impl_lte_modem_set_band (MMModemLteNetwork *modem,
+ MMModemLteBand band,
+ DBusGMethodInvocation *context)
+{
+ mm_modem_lte_network_set_band (modem, band, async_call_done, context);
+}
+
+static void
+impl_lte_modem_get_band (MMModemLteNetwork *modem,
+ DBusGMethodInvocation *context)
+{
+ mm_modem_lte_network_get_band (modem, uint_call_done, context);
+}
+
+
+static void
+impl_lte_modem_set_allowed_mode (MMModemLteNetwork *modem,
+ MMModemLteAllowedMode mode,
+ DBusGMethodInvocation *context)
+{
+ if (mode > MM_MODEM_LTE_ALLOWED_MODE_WCDMA_PREFERRED) {
+ GError *error;
+
+ error = g_error_new (MM_MODEM_ERROR, MM_MODEM_ERROR_OPERATION_NOT_SUPPORTED,
+ "Unknown allowed mode %d", mode);
+ dbus_g_method_return_error (context, error);
+ g_error_free (error);
+ return;
+ }
+
+ mm_modem_lte_network_set_allowed_mode (modem, mode, async_call_done, context);
+}
+
+static void
+impl_lte_modem_get_reg_info (MMModemLteNetwork *modem,
+ DBusGMethodInvocation *context)
+{
+ mm_modem_lte_network_get_registration_info (modem, reg_info_call_done, context);
+}
+
+/*****************************************************************************/
+
+static void
+mm_modem_lte_network_init (gpointer g_iface)
+{
+ GType iface_type = G_TYPE_FROM_INTERFACE (g_iface);
+ static gboolean initialized = FALSE;
+
+ if (initialized)
+ return;
+
+ /* Properties */
+ g_object_interface_install_property
+ (g_iface,
+ g_param_spec_uint (MM_MODEM_LTE_NETWORK_ALLOWED_MODE,
+ "Allowed Mode",
+ "Allowed network access mode",
+ MM_MODEM_LTE_ALLOWED_MODE_ANY,
+ MM_MODEM_LTE_ALLOWED_MODE_WCDMA_PREFERRED,
+ MM_MODEM_LTE_ALLOWED_MODE_ANY,
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
+
+ g_object_interface_install_property
+ (g_iface,
+ g_param_spec_uint (MM_MODEM_LTE_NETWORK_ACCESS_TECHNOLOGY,
+ "Access Technology",
+ "Current access technology in use when connected to "
+ "a mobile network.",
+ MM_MODEM_LTE_ACCESS_TECH_UNKNOWN,
+ MM_MODEM_LTE_ACCESS_TECH_HSUPA,
+ MM_MODEM_LTE_ACCESS_TECH_UNKNOWN,
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
+
+ /* Signals */
+ signals[SIGNAL_QUALITY] =
+ g_signal_new ("signal-quality",
+ iface_type,
+ G_SIGNAL_RUN_FIRST,
+ G_STRUCT_OFFSET (MMModemLteNetwork, signal_quality),
+ NULL, NULL,
+ g_cclosure_marshal_VOID__UINT,
+ G_TYPE_NONE, 1,
+ G_TYPE_UINT);
+
+ signals[REGISTRATION_INFO] =
+ g_signal_new ("registration-info",
+ iface_type,
+ G_SIGNAL_RUN_FIRST,
+ G_STRUCT_OFFSET (MMModemLteNetwork, registration_info),
+ NULL, NULL,
+ mm_marshal_VOID__UINT_STRING_STRING,
+ G_TYPE_NONE, 3,
+ G_TYPE_UINT, G_TYPE_STRING, G_TYPE_STRING);
+
+ signals[NETWORK_MODE] =
+ g_signal_new ("network-mode",
+ iface_type,
+ G_SIGNAL_RUN_FIRST,
+ 0, NULL, NULL,
+ g_cclosure_marshal_VOID__UINT,
+ G_TYPE_NONE, 1,
+ G_TYPE_UINT);
+
+ initialized = TRUE;
+}
+
+GType
+mm_modem_lte_network_get_type (void)
+{
+ static GType network_type = 0;
+
+ if (!G_UNLIKELY (network_type)) {
+ const GTypeInfo network_info = {
+ sizeof (MMModemLteNetwork), /* class_size */
+ mm_modem_lte_network_init, /* base_init */
+ NULL, /* base_finalize */
+ NULL,
+ NULL, /* class_finalize */
+ NULL, /* class_data */
+ 0,
+ 0, /* n_preallocs */
+ NULL
+ };
+
+ network_type = g_type_register_static (G_TYPE_INTERFACE,
+ "MMModemLteNetwork",
+ &network_info, 0);
+
+ g_type_interface_add_prerequisite (network_type, G_TYPE_OBJECT);
+ g_type_interface_add_prerequisite (network_type, MM_TYPE_MODEM);
+ dbus_g_object_type_install_info (network_type, &dbus_glib_mm_modem_lte_network_object_info);
+ }
+
+ return network_type;
+}
diff --git a/src/mm-modem-lte-network.h b/src/mm-modem-lte-network.h
new file mode 100644
index 0000000..f8fc8a2
--- /dev/null
+++ b/src/mm-modem-lte-network.h
@@ -0,0 +1,154 @@
+/* -*- 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) 2008 Novell, Inc.
+ * Copyright (C) 2009 - 2010 Red Hat, Inc.
+ * Copyright (C) 2011 Novatel Wireless, Inc.
+ */
+
+#ifndef MM_MODEM_LTE_NETWORK_H
+#define MM_MODEM_LTE_NETWORK_H
+
+#include <mm-modem.h>
+#include <mm-modem-lte.h>
+
+#define MM_MODEM_LTE_NETWORK_DBUS_INTERFACE "org.freedesktop.ModemManager.Modem.Lte.Network"
+
+#define MM_TYPE_MODEM_LTE_NETWORK (mm_modem_lte_network_get_type ())
+#define MM_MODEM_LTE_NETWORK(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MM_TYPE_MODEM_LTE_NETWORK, MMModemLteNetwork))
+#define MM_IS_MODEM_LTE_NETWORK(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MM_TYPE_MODEM_LTE_NETWORK))
+#define MM_MODEM_LTE_NETWORK_GET_INTERFACE(obj) (G_TYPE_INSTANCE_GET_INTERFACE ((obj), MM_TYPE_MODEM_LTE_NETWORK, MMModemLteNetwork))
+
+#define MM_MODEM_LTE_NETWORK_ALLOWED_MODE "allowed-mode"
+#define MM_MODEM_LTE_NETWORK_ACCESS_TECHNOLOGY "access-technology"
+
+typedef enum {
+ MM_MODEM_LTE_NETWORK_PROP_FIRST = 0x1200,
+
+ MM_MODEM_LTE_NETWORK_PROP_ALLOWED_MODE = MM_MODEM_LTE_NETWORK_PROP_FIRST,
+ MM_MODEM_LTE_NETWORK_PROP_ACCESS_TECHNOLOGY,
+} MMModemLteNetworkProp;
+
+typedef struct _MMModemLteNetwork MMModemLteNetwork;
+
+typedef void (*MMModemLteNetworkScanFn) (MMModemLteNetwork *self,
+ GPtrArray *results,
+ GError *error,
+ gpointer user_data);
+
+typedef void (*MMModemLteNetworkRegInfoFn) (MMModemLteNetwork *self,
+ MMModemLteNetworkRegStatus status,
+ const char *oper_code,
+ const char *oper_name,
+ GError *error,
+ gpointer user_data);
+
+struct _MMModemLteNetwork {
+ GTypeInterface g_iface;
+
+ /* Methods */
+ /* 'register' is a reserved word */
+ void (*do_register) (MMModemLteNetwork *self,
+ const char *network_id,
+ MMModemFn callback,
+ gpointer user_data);
+
+ void (*scan) (MMModemLteNetwork *self,
+ MMModemLteNetworkScanFn callback,
+ gpointer user_data);
+
+ void (*set_apn) (MMModemLteNetwork *self,
+ const char *apn,
+ MMModemFn callback,
+ gpointer user_data);
+
+ void (*get_signal_quality) (MMModemLteNetwork *self,
+ MMModemUIntFn callback,
+ gpointer user_data);
+
+ void (*set_band) (MMModemLteNetwork *self,
+ MMModemLteBand band,
+ MMModemFn callback,
+ gpointer user_data);
+
+ void (*get_band) (MMModemLteNetwork *self,
+ MMModemUIntFn callback,
+ gpointer user_data);
+
+ void (*set_allowed_mode) (MMModemLteNetwork *self,
+ MMModemLteAllowedMode mode,
+ MMModemFn callback,
+ gpointer user_data);
+
+ void (*get_registration_info) (MMModemLteNetwork *self,
+ MMModemLteNetworkRegInfoFn callback,
+ gpointer user_data);
+
+ /* Signals */
+ void (*signal_quality) (MMModemLteNetwork *self,
+ guint32 quality);
+
+ void (*registration_info) (MMModemLteNetwork *self,
+ MMModemLteNetworkRegStatus status,
+ const char *open_code,
+ const char *oper_name);
+};
+
+GType mm_modem_lte_network_get_type (void);
+
+void mm_modem_lte_network_register (MMModemLteNetwork *self,
+ const char *network_id,
+ MMModemFn callback,
+ gpointer user_data);
+
+void mm_modem_lte_network_scan (MMModemLteNetwork *self,
+ MMModemLteNetworkScanFn callback,
+ gpointer user_data);
+
+void mm_modem_lte_network_set_apn (MMModemLteNetwork *self,
+ const char *apn,
+ MMModemFn callback,
+ gpointer user_data);
+
+void mm_modem_lte_network_get_signal_quality (MMModemLteNetwork *self,
+ MMModemUIntFn callback,
+ gpointer user_data);
+
+void mm_modem_lte_network_set_band (MMModemLteNetwork *self,
+ MMModemLteBand band,
+ MMModemFn callback,
+ gpointer user_data);
+
+void mm_modem_lte_network_get_band (MMModemLteNetwork *self,
+ MMModemUIntFn callback,
+ gpointer user_data);
+
+void mm_modem_lte_network_get_registration_info (MMModemLteNetwork *self,
+ MMModemLteNetworkRegInfoFn callback,
+ gpointer user_data);
+
+/* Protected */
+
+void mm_modem_lte_network_signal_quality (MMModemLteNetwork *self,
+ guint32 quality);
+
+void mm_modem_lte_network_set_allowed_mode (MMModemLteNetwork *self,
+ MMModemLteAllowedMode mode,
+ MMModemFn callback,
+ gpointer user_data);
+
+void mm_modem_lte_network_registration_info (MMModemLteNetwork *self,
+ MMModemLteNetworkRegStatus status,
+ const char *oper_code,
+ const char *oper_name);
+
+#endif /* MM_MODEM_LTE_NETWORK_H */
diff --git a/src/mm-modem-lte.h b/src/mm-modem-lte.h
new file mode 100644
index 0000000..938438b
--- /dev/null
+++ b/src/mm-modem-lte.h
@@ -0,0 +1,58 @@
+/* -*- 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) 2008 - 2009 Novell, Inc.
+ * Copyright (C) 2009 Red Hat, Inc.
+ * Copyright (C) 2011 Novatel Wireless, Inc.
+ */
+
+#ifndef MM_MODEM_LTE_H
+#define MM_MODEM_LTE_H
+
+typedef enum {
+ MM_MODEM_LTE_BAND_UNKNOWN = 0x00000000,
+ MM_MODEM_LTE_BAND_BC0_A = 0x00000001, //CDMA2000 Band Class 0, A-System
+ MM_MODEM_LTE_BAND_BC0_B = 0x00000002, //CDMA2000 Band Class 0, B-System
+ MM_MODEM_LTE_BAND_BC1 = 0x00000004, //CDMA2000 Band Class 1, all blocks
+ MM_MODEM_LTE_BAND_BC2 = 0x00000008, //CDMA2000 Band Class 2 place holder
+ MM_MODEM_LTE_BAND_BC3 = 0x00000010, //CDMA2000 Band Class 3, A-System
+ MM_MODEM_LTE_BAND_BC4 = 0x00000020, //CDMA2000 Band Class 4, all blocks
+ MM_MODEM_LTE_BAND_BC5 = 0x00000040, //CDMA2000 Band Class 5, all blocks
+ MM_MODEM_LTE_BAND_GSM_DCS_1800 = 0x00000080, //GSM DCS band
+ MM_MODEM_LTE_BAND_GSM_EGSM_900 = 0x00000100, //GSM Extended GSM (E-GSM) band
+ MM_MODEM_LTE_BAND_GSM_PGSM_900 = 0x00000200, //GSM Primary GSM (P-GSM) band
+ MM_MODEM_LTE_BAND_BC6 = 0x00000400, //CDMA2000 Band Class 6
+ MM_MODEM_LTE_BAND_BC7 = 0x00000800, //CDMA2000 Band Class 7
+ MM_MODEM_LTE_BAND_BC8 = 0x00001000, //CDMA2000 Band Class 8
+ MM_MODEM_LTE_BAND_BC9 = 0x00002000, //CDMA2000 Band Class 9
+ MM_MODEM_LTE_BAND_BC10 = 0x00004000, //CDMA2000 Band Class 10
+ MM_MODEM_LTE_BAND_BC11 = 0x00008000, //CDMA2000 Band Class 11
+ MM_MODEM_LTE_BAND_GSM_450 = 0x00010000, //GSM 450 band
+ MM_MODEM_LTE_BAND_GSM_480 = 0x00020000, //GSM 480 band
+ MM_MODEM_LTE_BAND_GSM_750 = 0x00040000, //GSM 750 band
+ MM_MODEM_LTE_BAND_GSM_850 = 0x00080000, //GSM 850 band
+ MM_MODEM_LTE_BAND_GSM_RGSM_900 = 0x00100000, //GSM Railways GSM Band
+ MM_MODEM_LTE_BAND_GSM_PCS_1900 = 0x00200000, //GSM PCS band
+ MM_MODEM_LTE_BAND_WCDMA_I_IMT_2000 = 0x00400000, //WCDMA I IMT 2000 band
+ MM_MODEM_LTE_BAND_WCDMA_II_PCS_1900 = 0x00800000, //WCDMA II PCS band
+ MM_MODEM_LTE_BAND_WCDMA_III_1700 = 0x01000000, //WCDMA III 1700 band
+ MM_MODEM_LTE_BAND_WCDMA_IV_1700 = 0x02000000, //WCDMA IV 1700 band
+ MM_MODEM_LTE_BAND_WCDMA_V_850 = 0x04000000, //WCDMA V US850 band
+ MM_MODEM_LTE_BAND_WCDMA_VI_800 = 0x08000000, //WCDMA VI JAPAN 800 band
+ MM_MODEM_LTE_BAND_RESERVED_1 = 0x10000000, //for BC12/BC14 Reserved
+ MM_MODEM_LTE_BAND_RESERVED_2 = 0x20000000, //for BC12/BC14 Reserved
+ MM_MODEM_LTE_BAND_RESERVED_3 = 0x40000000, //Reserved
+ MM_MODEM_LTE_BAND_RESERVED_4 = 0x80000000, //Reserved
+} MMModemLteBand;
+
+
+#endif /* MM_MODEM_LTE_H */
diff --git a/src/mm-modem.h b/src/mm-modem.h
index da39ff5..b580724 100644
--- a/src/mm-modem.h
+++ b/src/mm-modem.h
@@ -12,6 +12,7 @@
*
* Copyright (C) 2008 - 2009 Novell, Inc.
* Copyright (C) 2009 - 2010 Red Hat, Inc.
+ * Copyright (C) 2011 Novatel Wireless, Inc.
*/
#ifndef MM_MODEM_H
diff --git a/src/mm-plugin-base.c b/src/mm-plugin-base.c
index 01fe9c5..82614d0 100644
--- a/src/mm-plugin-base.c
+++ b/src/mm-plugin-base.c
@@ -12,6 +12,7 @@
*
* Copyright (C) 2008 - 2009 Novell, Inc.
* Copyright (C) 2009 - 2010 Red Hat, Inc.
+ * Copyright (C) 2011 Novatel Wireless, Inc.
*/
#define _GNU_SOURCE /* for strcasestr */
@@ -421,7 +422,7 @@ mm_plugin_base_supports_task_class_init (MMPluginBaseSupportsTaskClass *klass)
MM_PLUGIN_BASE_PORT_CAP_IS856 | \
MM_PLUGIN_BASE_PORT_CAP_IS856_A)
-#define CAP_GSM_OR_CDMA (MM_PLUGIN_BASE_PORT_CAP_CDMA | MM_PLUGIN_BASE_PORT_CAP_GSM)
+#define CAP_GSM_OR_CDMA_OR_LTE (MM_PLUGIN_BASE_PORT_CAP_CDMA | MM_PLUGIN_BASE_PORT_CAP_GSM | MM_PLUGIN_BASE_PORT_CAP_LTE)
struct modem_caps {
char *name;
@@ -429,6 +430,7 @@ struct modem_caps {
};
static struct modem_caps modem_caps[] = {
+ {"+CLTE", MM_PLUGIN_BASE_PORT_CAP_LTE},
{"+CGSM", MM_PLUGIN_BASE_PORT_CAP_GSM},
{"+CIS707-A", MM_PLUGIN_BASE_PORT_CAP_IS707_A},
{"+CIS707A", MM_PLUGIN_BASE_PORT_CAP_IS707_A}, /* Cmotech */
@@ -808,7 +810,7 @@ real_handle_probe_response (MMPluginBase *self,
if (task_priv->probe_state <= PROBE_STATE_CAPS_LAST) {
/* Probing capabilities */
- if (task_priv->probed_caps & CAP_GSM_OR_CDMA) {
+ if (task_priv->probed_caps & CAP_GSM_OR_CDMA_OR_LTE) {
/* Got capabilities probed, go on with vendor probing */
task_priv->probe_state = PROBE_STATE_VENDOR_FIRST;
} else if (task_priv->probe_state < PROBE_STATE_CAPS_LAST) {
diff --git a/src/mm-plugin-base.h b/src/mm-plugin-base.h
index 4b0932c..a9f3a1e 100644
--- a/src/mm-plugin-base.h
+++ b/src/mm-plugin-base.h
@@ -11,6 +11,7 @@
* GNU General Public License for more details:
*
* Copyright (C) 2009 Red Hat, Inc.
+ * Copyright (C) 2011 Novatel Wireless, Inc.
*/
#ifndef MM_PLUGIN_BASE_H
@@ -39,6 +40,7 @@
#define MM_PLUGIN_BASE_PORT_CAP_IS856_A 0x0200 /* CDMA 3G EVDO rev A */
#define MM_PLUGIN_BASE_PORT_CAP_QCDM 0x0400 /* QCDM-capable port */
#define MM_PLUGIN_BASE_PORT_CAP_AT 0x0800 /* Responds to AT commands */
+#define MM_PLUGIN_BASE_PORT_CAP_LTE 0x1000 /* LTE */
#define MM_TYPE_PLUGIN_BASE_SUPPORTS_TASK (mm_plugin_base_supports_task_get_type ())
#define MM_PLUGIN_BASE_SUPPORTS_TASK(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MM_TYPE_PLUGIN_BASE_SUPPORTS_TASK, MMPluginBaseSupportsTask))
diff --git a/test/mm-test.py b/test/mm-test.py
index 99a355f..a8593de 100755
--- a/test/mm-test.py
+++ b/test/mm-test.py
@@ -13,6 +13,7 @@
#
# Copyright (C) 2008 Novell, Inc.
# Copyright (C) 2009 Red Hat, Inc.
+# Copyright (C) 2011 Novatel Wireless, Inc.
#
import sys, dbus, time, os, string, subprocess, socket
@@ -26,6 +27,8 @@ MM_DBUS_INTERFACE_MODEM_CDMA='org.freedesktop.ModemManager.Modem.Cdma'
MM_DBUS_INTERFACE_MODEM_GSM_CARD='org.freedesktop.ModemManager.Modem.Gsm.Card'
MM_DBUS_INTERFACE_MODEM_GSM_NETWORK='org.freedesktop.ModemManager.Modem.Gsm.Network'
MM_DBUS_INTERFACE_MODEM_SIMPLE='org.freedesktop.ModemManager.Modem.Simple'
+MM_DBUS_INTERFACE_MODEM_LTE_CARD='org.freedesktop.ModemManager.Modem.Lte.Card'
+MM_DBUS_INTERFACE_MODEM_LTE_NETWORK='org.freedesktop.ModemManager.Modem.Lte.Network'
def get_cdma_band_class(band_class):
if band_class == 1:
@@ -249,6 +252,173 @@ def gsm_connect(proxy, apn, user, password):
print "Error connecting: %s" % e
return False
+def lte_print_status(results):
+
+ if results['state'] == 0:
+ print "state: MM_MODEM_STATE_UNKNOWN"
+ elif results['state'] == 10:
+ print "state: MM_MODEM_STATE_DISABLED"
+ elif results['state'] == 20:
+ print "state: MM_MODEM_STATE_DISABLING"
+ elif results['state'] == 30:
+ print "state: MM_MODEM_STATE_ENABLING"
+ elif results['state'] == 40:
+ print "state: MM_MODEM_STATE_ENABLED"
+ elif results['state'] == 50:
+ print "state: MM_MODEM_STATE_SEARCHING"
+ elif results['state'] == 60:
+ print "state: MM_MODEM_STATE_REGISTERED"
+ elif results['state'] == 70:
+ print "state: MM_MODEM_STATE_DISCONNECTING"
+ elif results['state'] == 80:
+ print "state: MM_MODEM_STATE_CONNECTING"
+ elif results['state'] == 90:
+ print "state: MM_MODEM_STATE_CONNECTED"
+ else:
+ print "state: (Unknown)"
+
+ global modem_state
+ modem_state = results['state']
+ if modem_state < modem_state_connected:
+ print "signal_quality: %d" % results['signal_quality']
+
+ if results['registration_status'] == 0:
+ print "registration_status: MM_MODEM_LTE_NETWORK_REG_STATUS_IDLE"
+ elif results['registration_status'] == 1:
+ print "registration_status: MM_MODEM_LTE_NETWORK_REG_STATUS_HOME"
+ elif results['registration_status'] == 2:
+ print "registration_status: MM_MODEM_LTE_NETWORK_REG_STATUS_SEARCHIN"
+ elif results['registration_status'] == 3:
+ print "registration_status: MM_MODEM_LTE_NETWORK_REG_STATUS_DENIED"
+ elif results['registration_status'] == 4:
+ print "registration_status: MM_MODEM_LTE_NETWORK_REG_STATUS_UNKNOWN"
+ elif results['registration_status'] == 5:
+ print "registration_status: MM_MODEM_LTE_NETWORK_REG_STATUS_ROAMING"
+
+ if results['access_technology'] == 0:
+ print "access_technology: MM_MODEM_LTE_ACCESS_TECH_ANY"
+ elif results['access_technology'] == 1:
+ print "access_technology: MM_MODEM_LTE_ACCESS_TECH_GSM"
+ elif results['access_technology'] == 2:
+ print "access_technology: MM_MODEM_LTE_ACCESS_TECH_GPRS"
+ elif results['access_technology'] == 3:
+ print "access_technology: MM_MODEM_LTE_ACCESS_TECH_EDGE"
+ elif results['access_technology'] == 4:
+ print "access_technology: MM_MODEM_LTE_ACCESS_TECH_UMTS"
+ elif results['access_technology'] == 5:
+ print "access_technology: MM_MODEM_LTE_ACCESS_TECH_HSDPA"
+ elif results['access_technology'] == 6:
+ print "access_technology: MM_MODEM_LTE_ACCESS_TECH_HSPA"
+ elif results['access_technology'] == 7:
+ print "access_technology: MM_MODEM_LTE_ACCESS_TECH_LTE"
+ elif results['access_technology'] == 8:
+ print "access_technology: MM_MODEM_LTE_ACCESS_TECH_1xRTT"
+ elif results['access_technology'] == 9:
+ print "access_technology: MM_MODEM_LTE_ACCESS_TECH_EvDO"
+ elif results['access_technology'] == 10:
+ print "access_technology: MM_MODEM_LTE_ACCESS_TECH_EvDO_Rel0"
+ elif results['access_technology'] == 11:
+ print "access_technology: MM_MODEM_LTE_ACCESS_TECH_EvDOA"
+
+
+def lte_get_status(proxy):
+ simple_modem = dbus.Interface(proxy, dbus_interface=MM_DBUS_INTERFACE_MODEM_SIMPLE)
+ try:
+ results = simple_modem.GetStatus()
+ lte_print_status(results)
+ return results['state']
+ except Exception, e:
+ print "Error simple.GetStatus(): %s" % e
+
+
+def lte_inspect(proxy, dump_private):
+ # Lte.Card interface
+ card = dbus.Interface(proxy, dbus_interface=MM_DBUS_INTERFACE_MODEM_LTE_CARD)
+
+ imei = "<private>"
+ lte_get_status(proxy)
+ if dump_private and modem_state < modem_state_connected:
+ try:
+ imei = card.GetImei()
+ print "IMEI: %s" % imei
+ except dbus.exceptions.DBusException, e:
+ print "Error GetImei: %s" % e
+ imei = "<unavailable>"
+
+ try:
+ imsi = card.GetImsi()
+ print "IMSI: %s" % imsi
+ except dbus.exceptions.DBusException, e:
+ print "Error GetImsi: %s" % e
+ imsi = "<unavailable>"
+
+ # Lte.Network interface
+ net = dbus.Interface(proxy, dbus_interface=MM_DBUS_INTERFACE_MODEM_LTE_NETWORK)
+ try:
+ quality = net.GetSignalQuality()
+ print "Signal quality: %d" % quality
+ except dbus.exceptions.DBusException, e:
+ print "Error reading signal quality: %s" % e
+
+ try:
+ band = net.GetBand()
+ print "Band: %x" % band
+ except dbus.exceptions.DBusException, e:
+ print "Error reading getband: %s" % e
+
+ return
+
+def lte_connect(proxy, apn, tech, call_param, pdns, sdns, pnbns, snbns, ip, auth, user, password):
+
+ # Modem.Simple interface
+ simple = dbus.Interface(proxy, dbus_interface=MM_DBUS_INTERFACE_MODEM_SIMPLE)
+ try:
+ print "Setting Opts"
+ opts = {}
+ if apn is not None:
+ opts['apn'] = apn
+ if tech is not None:
+ opts['tech'] = tech
+ if call_param is not None:
+ opts['call_param'] = call_param
+ if pdns is not None:
+ opts['pdns'] = pdns
+ if sdns is not None:
+ opts['sdns'] = sdns
+ if pnbns is not None:
+ opts['pnbns'] = pnbns
+ if snbns is not None:
+ opts['snbns'] = snbns
+ if ip is not None:
+ opts['ip'] = ip
+ if auth is not None:
+ opts['auth'] = auth
+ if user is not None:
+ opts['user'] = user
+ if password is not None:
+ opts['password'] = password
+
+ print "status before connect"
+ state_before = lte_get_status(proxy)
+
+ if state_before == 90:
+ return True
+
+ print "Calling simple.connect"
+ simple.Connect(opts, timeout=120)
+
+ print "status after connect"
+ state_after = lte_get_status(proxy)
+
+ if state_after == 90:
+ return True
+ else:
+ return False
+
+ except Exception, e:
+ print "Error connecting: %s" % e
+ return False
+
def pppd_find():
paths = ["/usr/local/sbin/pppd", "/usr/sbin/pppd", "/sbin/pppd"]
for p in paths:
@@ -390,27 +560,46 @@ def try_ping(iface):
dump_private = False
connect = False
+disconnect = False
+scan = False
apn = None
user = None
password = None
+tech = None
+call_param = None
+pdns = None
+sdns = None
+pnbns = None
+snbns = None
+ip = None
+auth = None
do_ip = False
do_scan = True
x = 1
+modem_state_connected = 90
+modem_state = 0 # MM_MODEM_STATE_UNKNOWN
+
while x < len(sys.argv):
if sys.argv[x] == "--private":
dump_private = True
elif sys.argv[x] == "--connect":
connect = True
+ elif sys.argv[x] == "--disconnect":
+ disconnect = True
+ elif sys.argv[x] == "--scan":
+ scan = True
elif (sys.argv[x] == "--user" or sys.argv[x] == "--username"):
x += 1
user = sys.argv[x]
- elif sys.argv[x] == "--apn":
+ elif sys.argv[x] == "--apn":
x += 1
apn = sys.argv[x]
- elif sys.argv[x] == "--password":
+ print "APN: %s" % apn
+
+ elif sys.argv[x] == "--password":
x += 1
password = sys.argv[x]
- elif sys.argv[x] == "--ip":
+ elif sys.argv[x] == "--ip":
do_ip = True
if os.geteuid() != 0:
print "You probably want to be root to use --ip"
@@ -444,6 +633,8 @@ for m in modems:
print "GSM modem"
elif type == 2:
print "CDMA modem"
+ elif type == 3:
+ print "LTE modem"
else:
print "Invalid modem type: %d" % type
@@ -453,18 +644,28 @@ for m in modems:
print "Data device: '%s'" % data_device
# Modem interface
- modem = dbus.Interface(proxy, dbus_interface=MM_DBUS_INTERFACE_MODEM)
+ try:
+ modem = dbus.Interface(proxy, dbus_interface=MM_DBUS_INTERFACE_MODEM)
+ print "Got dbus interface modem "
+ except dbus.exceptions.DBusException, e:
+ print "Error getting dbus interface modem: %s" % e
+ sys.exit(1)
try:
modem.Enable(True)
+ print "modem enabled"
except dbus.exceptions.DBusException, e:
print "Error enabling modem: %s" % e
sys.exit(1)
- info = modem.GetInfo()
- print "Vendor: %s" % info[0]
- print "Model: %s" % info[1]
- print "Version: %s" % info[2]
+ try:
+ info = modem.GetInfo()
+ print "Vendor: %s" % info[0]
+ print "Model: %s" % info[1]
+ print "Version: %s" % info[2]
+ except dbus.exceptions.DBusException, e:
+ print "Error modem.GetInfo: %s" % e
+ sys.exit(1)
if type == 1:
gsm_inspect(proxy, dump_private, do_scan)
@@ -474,9 +675,28 @@ for m in modems:
cdma_inspect(proxy, dump_private)
if connect == True:
connect_success = cdma_connect(proxy, user, password)
+ elif type == 3:
+ lte_inspect(proxy, dump_private)
+ if connect == True:
+ print "APN = %s" % apn
+ connect_success = lte_connect(proxy, apn, tech, call_param, pdns, sdns, pnbns, snbns, ip, auth, user, password)
+ print
+
+ if connect_success:
+ print "Connect Success! "
+
+ if disconnect == True:
+ if modem_state == modem_state_connected:
+ print "Disconnecting..."
+ modem.Disconnect()
+ if type == 3:
+ print "status after disconnect"
+ lte_get_status(proxy)
+ else:
+ print "Modem is already disconnected"
print
- if connect_success and do_ip:
+ if connect_success and do_ip and type != 3:
tmpfile = "/tmp/mm-test-%d.tmp" % os.getpid()
success = False
try:
@@ -520,8 +740,7 @@ for m in modems:
modem.Disconnect()
except Exception, e:
print "Error tearing down IP: %s" % e
-
- time.sleep(5)
-
- modem.Enable(False)
-
+ if type != 3:
+ time.sleep(5)
+ modem.Enable(False)
+
--
1.7.3.1
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]