Hi Jonas,
On 04/04/2013 03:20 AM, Jonas Bonn wrote:
The Telit HE910 turns out to be a rather quirky device and the
existing
'telit' driver doesn't really work as is. This patch is the result of
many hours of trial-and-error and direct consultation with Telit in
order to find an initialization sequence that works, doesn't lock up
the modem, and fits the Ofono paradigm.
Is the HE910 sufficiently different that it might be easier to simply
write a dedicated driver for it? One can even have multiple drivers in
the telit.c file.
E.g. something like:
static struct ofono_modem_driver telit_driver = {
.name = "telit",
...
};
static struct ofono_modem_driver telit_he910_driver = {
...
};
1) The SIM status unsolicited messages need to be used in order to know
when the SIM is sufficiently ready to allow certain ther commands to
be able to complete without error. Unfortunately, these messages are
not reliably issued in a way that allows them to always be caught. As
such, the modem needs to be prodded to re-issue these messages by:
i) Using SIMDET=0 to logically 'remove' the SIM before powering on
the modem
ii) Setting SIMDET=1 iff a SIM is physically inserted in order to have
the QSS states re-issued
iii) Finally, setting SIMDET=2 once the modem is powered up to re-enable
the normal QSS unsolicited messages
2) CFUN state 4 powers off the modem (airplane mode), but it also powers
off the SIM card. This doesn't fit well with the Ofono model as the
GPRS atom won't be created unless the SIM card is usable. Consultation
with Telit resulted in the following alternative:
It sounds like the concept of Online is not relevant. Maybe you should
simply not implement the .set_enable method. This way the modem
initialization logic in the core proceeds straight to post_online state...
i) Set CFUN=1 when enabling the modem and set COPS=2 to take the
modem
'offline'
ii) Set COPS=0 when putting the modem 'online'
iii) CFUN=4 may be used when disabling the modem. The telit driver
previously used CFUN=0, but this is actually not an 'offline' state
at all.
... which is better than trying to mess with COPS, since this has
potential side effects on the netreg atom.
3) The voicecall atom cannot be created until the SIM is ready.
Then move it to the post_sim or post_online state.
4) The CMER command will always fail
5) The CMER? command will always fail if ofono_sim_inserted_notify is
called before QSS reaches state 3.
The combination of using CFUN=1 when enabled works around another quirk
in the modem: if the SIM is hotplugged (SIMIN signal toggles) when CFUN=4,
the SIM logic actually wakes up and QSS messages may be issued. This is
out of sync with the expected behaviour and is difficult to reconcile
with the rest of the control flow in the ofono driver.
The Telit HE910 has a propensity to lock up when echo is enabled. We
have seen this primarily on the 'modem' port where the modem locks up
after echoing back the command when there is pending command on the
'aux' port. The changed initialization flow of this patch makes this
problem in general more difficult to hit, but as an additional measure
this patch disables echo on the modem port completely.
These changes have been tested with a Telit HE910 modem only. As the
documentation for earlier Telit modems is almost identical, I expect
this new control flow to work with those modems as well. That said, it
would be good if that could be tested with someone with such a modem.
---
plugins/telit.c | 138 ++++++++++++++++++++++++++++++++++++++++++++++----------
1 file changed, 114 insertions(+), 24 deletions(-)
diff --git a/plugins/telit.c b/plugins/telit.c
index f661ab9..2b625da 100644
--- a/plugins/telit.c
+++ b/plugins/telit.c
@@ -63,6 +63,7 @@
#endif
static const char *none_prefix[] = { NULL };
+static const char *simdet_prefix[]= { "#SIMDET:", NULL };
#ifdef NEED_BLUETOOTH
static const char *rsen_prefix[]= { "#RSEN:", NULL };
#endif
@@ -242,13 +243,32 @@ static void switch_sim_state_status(struct ofono_modem *modem, int
status)
}
break;
case 1: /* SIM inserted */
- case 2: /* SIM inserted and PIN unlocked */
+ /* If PIN is required, we need to inform ofono that sim is inserted. */
if (data->have_sim == FALSE) {
ofono_sim_inserted_notify(data->sim, TRUE);
data->have_sim = TRUE;
}
break;
+ case 2: /* SIM inserted and PIN unlocked */
+ /* The voicecall atom cannot be created until the SIM is
+ * unlocked and ready for use.
+ */
+ ofono_voicecall_create(modem, 0, "atmodem", data->chat);
+
You can simply move this part to post_sim or post_online
+ /*
+ * Tell the modem not to automatically initiate auto-attach
+ * proceedures on its own. Again, this command requires a
+ * functional SIM in order to succeed.
+ */
+ g_at_chat_send(data->chat, "AT#AUTOATT=0", none_prefix,
+ NULL, NULL, NULL);
+
+ break;
case 3: /* SIM inserted, SMS and phonebook ready */
+ if (data->have_sim == FALSE) {
+ ofono_sim_inserted_notify(data->sim, TRUE);
+ data->have_sim = TRUE;
+ }
if (data->sms_phonebook_added == FALSE) {
ofono_phonebook_create(modem, 0, "atmodem", data->chat);
ofono_sms_create(modem, 0, "atmodem", data->chat);
@@ -279,6 +299,46 @@ static void telit_qss_notify(GAtResult *result, gpointer user_data)
switch_sim_state_status(modem, status);
}
+static void simdet_query_cb(gboolean ok, GAtResult *result, gpointer user_data)
+{
+ struct ofono_modem *modem = user_data;
+ struct telit_data *data = ofono_modem_get_data(modem);
+ struct ofono_modem *m = data->sap_modem ? : modem;
+ GAtResultIter iter;
+ int mode;
+ int inserted;
+
+ DBG("%p", modem);
+
+ if (!ok)
+ return;
+
+ g_at_result_iter_init(&iter, result);
+
+ if (!g_at_result_iter_next(&iter, "#SIMDET:"))
+ return;
+
+ g_at_result_iter_next_number(&iter,&mode);
+ g_at_result_iter_next_number(&iter,&inserted);
+
+ g_at_chat_send(data->chat, "AT#QSS=2", none_prefix, NULL, NULL, NULL);
+
+ /* If the SIM is already physically present, we need to tell the
+ * modem so by setting SIMDET to 1 in order for it to regenerate
+ * the QSS messages corresponding to the current state
+ */
+ if (inserted) {
+ g_at_chat_send(data->chat, "AT#SIMDET=1", none_prefix,
+ NULL, NULL, NULL);
+ }
+
+ /* Restore SIM presence detection via SIMIN pin */
+ g_at_chat_send(data->chat, "AT#SIMDET=2", none_prefix,
+ NULL, NULL, NULL);
+
+ ofono_modem_set_powered(m, TRUE);
+}
+
static void cfun_enable_cb(gboolean ok, GAtResult *result, gpointer user_data)
{
struct ofono_modem *modem = user_data;
@@ -307,21 +367,14 @@ static void cfun_enable_cb(gboolean ok, GAtResult *result, gpointer
user_data)
data->have_sim = FALSE;
data->sms_phonebook_added = FALSE;
- ofono_modem_set_powered(m, TRUE);
+ /* Set modem 'offline' */
+ g_at_chat_send(data->chat, "AT+COPS=2", none_prefix, NULL, NULL, NULL);
- /*
- * Tell the modem not to automatically initiate auto-attach
- * proceedures on its own.
+ /* Re-enabling QSS reporting requires that we manually poke the
+ * modem with its current state... this requires querying SIMDET
*/
- g_at_chat_send(data->chat, "AT#AUTOATT=0", none_prefix,
- NULL, NULL, NULL);
-
- /* Follow sim state */
- g_at_chat_register(data->chat, "#QSS:", telit_qss_notify,
- FALSE, modem, NULL);
-
- /* Enable sim state notification */
- g_at_chat_send(data->chat, "AT#QSS=2", none_prefix, NULL, NULL, NULL);
+ g_at_chat_send(data->chat, "AT#SIMDET?", simdet_prefix,
+ simdet_query_cb, modem, NULL);
}
static int telit_enable(struct ofono_modem *modem)
@@ -349,15 +402,52 @@ static int telit_enable(struct ofono_modem *modem)
*/
g_at_chat_send(data->chat, "ATE0 +CMEE=1", none_prefix,
NULL, NULL, NULL);
+ /* The Telit modem loses command responses when ECHO is on...
+ * it is a bug and Telit has been informed of it, but the
+ * easiest way forward for now is to just disable ECHO on
+ * the modem port.
+ */
+ g_at_chat_send(data->modem, "ATE0", none_prefix,
+ NULL, NULL, NULL);
- /*
- * Disable sim state notification so that we sure get a notification
- * when we enable it again later and don't have to query it.
+ /* Telit is quirky, quirky, quirky; getting a reliable QSS
+ * indication at startup is tricky.
+ * - QSS transitions aren't necessarily sent when the SIM
+ * is first powered on
+ * - The QSS state, if 1, doesn't automatically transition
+ * to 2/3 when QSS mode=2 is set.
+ * In order to ensure that we get a reliable QSS indication,
+ * the SIM to needs to be logically (or physically) reinserted
+ * when the SIM core is powered up.
+ *
+ * The SIMIN pin will correctly trigger a QSS transition, but
+ * it's edge-triggered so that it won't trigger a QSS indication
+ * unless a SIM is physically reinserted; it's therefore necessary
+ * to do a "soft" reinsertion by way of the SIMDET command. The
+ * sequence that reliably works is:
+ * - set QSS=0
+ * - set SIMDET to 0 (SIM logically removed)
+ * - put modem into CFUN!=4 in order to power up the SIM
+ * - when modem is powered, set QSS=2
+ * - check SIMDET value to see if SIM is physically inserted
+ * or not; if yes, we need to manually trigger a QSS transition
+ * by doing a "soft insertion"; we do this by setting SIMDET=1
+ * (SIM logically inserted)
+ * - then we can set SIMDET=2 to enable SIM hotplug the rest
+ * of the way.
*/
+
+ /* Follow sim state */
g_at_chat_send(data->chat, "AT#QSS=0", none_prefix, NULL, NULL, NULL);
+ g_at_chat_send(data->chat, "AT#SIMDET=0", none_prefix,
+ NULL, NULL, NULL);
+ g_at_chat_register(data->chat, "#QSS:", telit_qss_notify,
+ FALSE, modem, NULL);
- /* Set phone functionality */
- g_at_chat_send(data->chat, "AT+CFUN=4", none_prefix,
+ /* Set modem to full functionality... this is required to have the
+ * SIM powered up which is pretty much a prerequisite for Ofono.
+ */
+ g_at_chat_send(data->chat, "AT+CFUN=1", none_prefix,
cfun_enable_cb, modem, NULL);
return -EINPROGRESS;
@@ -437,7 +527,7 @@ static int telit_disable(struct ofono_modem *modem)
g_at_chat_unregister_all(data->chat);
/* Power down modem */
- g_at_chat_send(data->chat, "AT+CFUN=0", none_prefix,
+ g_at_chat_send(data->chat, "AT+CFUN=4", none_prefix,
cfun_disable_cb, modem, NULL);
return -EINPROGRESS;
@@ -565,9 +655,9 @@ static void telit_pre_sim(struct ofono_modem *modem)
DBG("%p", modem);
ofono_devinfo_create(modem, 0, "atmodem", data->chat);
- data->sim = ofono_sim_create(modem, OFONO_VENDOR_TELIT, "atmodem",
- data->chat);
- ofono_voicecall_create(modem, 0, "atmodem", data->chat);
+ data->sim = ofono_sim_create(modem, OFONO_VENDOR_TELIT,
+ "atmodem", data->chat);
+
}
static void telit_post_sim(struct ofono_modem *modem)
@@ -604,7 +694,7 @@ static void telit_set_online(struct ofono_modem *modem, ofono_bool_t
online,
{
struct telit_data *data = ofono_modem_get_data(modem);
struct cb_data *cbd = cb_data_new(cb, user_data);
- char const *command = online ? "AT+CFUN=1,0" : "AT+CFUN=4,0";
+ char const *command = online ? "AT+COPS=0" : "AT+COPS=2";
As mentioned before, I really don't like this due to potential side
effects with the netreg atom.
DBG("modem %p %s", modem, online ? "online" :
"offline");
Regards,
-Denis