3 * oFono - Open Source Telephony
5 * Copyright (C) 2008-2011 Intel Corporation. All rights reserved.
6 * Copyright (C) 2010 ST-Ericsson AB.
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License version 2 as
10 * published by the Free Software Foundation.
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
34 #include <ofono/log.h>
35 #include <ofono/modem.h>
36 #include <ofono/netreg.h>
39 #include "gatresult.h"
45 static const char *none_prefix[] = { NULL };
46 static const char *creg_prefix[] = { "+CREG:", NULL };
47 static const char *cops_prefix[] = { "+COPS:", NULL };
48 static const char *csq_prefix[] = { "+CSQ:", NULL };
49 static const char *cind_prefix[] = { "+CIND:", NULL };
50 static const char *zpas_prefix[] = { "+ZPAS:", NULL };
51 static const char *option_tech_prefix[] = { "_OCTI:", "_OUWCTI:", NULL };
55 char mcc[OFONO_MAX_MCC_LENGTH + 1];
56 char mnc[OFONO_MAX_MNC_LENGTH + 1];
57 int signal_index; /* If strength is reported via CIND */
58 int signal_min; /* min strength reported via CIND */
59 int signal_max; /* max strength reported via CIND */
60 int signal_invalid; /* invalid strength reported via CIND */
62 struct ofono_network_time time;
71 struct ofono_netreg *netreg;
74 static void extract_mcc_mnc(const char *str, char *mcc, char *mnc)
76 /* Three digit country code */
77 strncpy(mcc, str, OFONO_MAX_MCC_LENGTH);
78 mcc[OFONO_MAX_MCC_LENGTH] = '\0';
80 /* Usually a 2 but sometimes 3 digit network code */
81 strncpy(mnc, str + OFONO_MAX_MCC_LENGTH, OFONO_MAX_MNC_LENGTH);
82 mnc[OFONO_MAX_MNC_LENGTH] = '\0';
85 static int zte_parse_tech(GAtResult *result)
88 const char *network, *domain;
91 g_at_result_iter_init(&iter, result);
93 if (!g_at_result_iter_next(&iter, "+ZPAS:"))
96 if (!g_at_result_iter_next_string(&iter, &network))
99 if (!g_at_result_iter_next_string(&iter, &domain))
102 if (g_str_equal(network, "GSM") == TRUE ||
103 g_str_equal(network, "GPRS") == TRUE)
104 tech = ACCESS_TECHNOLOGY_GSM;
105 else if (g_str_equal(network, "EDGE") == TRUE)
106 tech = ACCESS_TECHNOLOGY_GSM_EGPRS;
107 else if (g_str_equal(network, "UMTS") == TRUE)
108 tech = ACCESS_TECHNOLOGY_UTRAN;
109 else if (g_str_equal(network, "HSDPA") == TRUE)
110 tech = ACCESS_TECHNOLOGY_UTRAN_HSDPA;
114 DBG("network %s domain %s tech %d", network, domain, tech);
119 static int option_parse_tech(GAtResult *result)
125 g_at_result_iter_init(&iter, result);
127 if (!g_at_result_iter_next(&iter, "_OCTI:"))
130 if (!g_at_result_iter_next_number(&iter, &s))
133 if (!g_at_result_iter_next_number(&iter, &octi))
136 if (!g_at_result_iter_next(&iter, "_OUWCTI:"))
139 if (!g_at_result_iter_next_number(&iter, &s))
142 if (!g_at_result_iter_next_number(&iter, &ouwcti))
147 tech = ACCESS_TECHNOLOGY_GSM;
150 tech = ACCESS_TECHNOLOGY_GSM;
153 tech = ACCESS_TECHNOLOGY_GSM_EGPRS;
162 tech = ACCESS_TECHNOLOGY_UTRAN;
165 tech = ACCESS_TECHNOLOGY_UTRAN_HSDPA;
168 tech = ACCESS_TECHNOLOGY_UTRAN_HSUPA;
171 tech = ACCESS_TECHNOLOGY_UTRAN_HSDPA_HSUPA;
175 DBG("octi %d ouwcti %d tech %d", octi, ouwcti, tech);
180 static void at_creg_cb(gboolean ok, GAtResult *result, gpointer user_data)
182 struct cb_data *cbd = user_data;
183 ofono_netreg_status_cb_t cb = cbd->cb;
184 int status, lac, ci, tech;
185 struct ofono_error error;
186 struct netreg_data *nd = cbd->user;
188 decode_at_error(&error, g_at_result_final_response(result));
191 cb(&error, -1, -1, -1, -1, cbd->data);
195 if (at_util_parse_reg(result, "+CREG:", NULL, &status,
196 &lac, &ci, &tech, nd->vendor) == FALSE) {
197 CALLBACK_WITH_FAILURE(cb, -1, -1, -1, -1, cbd->data);
201 if ((status == 1 || status == 5) && (tech == -1))
204 cb(&error, status, lac, ci, tech, cbd->data);
207 static void zte_tech_cb(gboolean ok, GAtResult *result, gpointer user_data)
209 struct cb_data *cbd = user_data;
210 struct ofono_netreg *netreg = cbd->data;
211 struct netreg_data *nd = ofono_netreg_get_data(netreg);
214 nd->tech = zte_parse_tech(result);
219 static void option_tech_cb(gboolean ok, GAtResult *result, gpointer user_data)
221 struct cb_data *cbd = user_data;
222 struct ofono_netreg *netreg = cbd->data;
223 struct netreg_data *nd = ofono_netreg_get_data(netreg);
226 nd->tech = option_parse_tech(result);
231 static void at_registration_status(struct ofono_netreg *netreg,
232 ofono_netreg_status_cb_t cb,
235 struct netreg_data *nd = ofono_netreg_get_data(netreg);
236 struct cb_data *cbd = cb_data_new(cb, data);
240 switch (nd->vendor) {
241 case OFONO_VENDOR_MBM:
243 * Send *ERINFO to find out the current tech, it will be
244 * intercepted in mbm_erinfo_notify
246 g_at_chat_send(nd->chat, "AT*ERINFO?", none_prefix,
249 case OFONO_VENDOR_GOBI:
251 * Send *CNTI=0 to find out the current tech, it will be
252 * intercepted in gobi_cnti_notify
254 g_at_chat_send(nd->chat, "AT*CNTI=0", none_prefix,
257 case OFONO_VENDOR_NOVATEL:
259 * Send $CNTI=0 to find out the current tech, it will be
260 * intercepted in nw_cnti_notify
262 g_at_chat_send(nd->chat, "AT$CNTI=0", none_prefix,
265 case OFONO_VENDOR_ZTE:
267 * Send +ZPAS? to find out the current tech, zte_tech_cb
268 * will call, fire CREG? to do the rest.
270 if (g_at_chat_send(nd->chat, "AT+ZPAS?", zpas_prefix,
271 zte_tech_cb, cbd, NULL) == 0)
274 case OFONO_VENDOR_OPTION_HSO:
276 * Send AT_OCTI?;_OUWCTI? to find out the current tech,
277 * option_tech_cb will call, fire CREG? to do the rest.
279 if (g_at_chat_send(nd->chat, "AT_OCTI?;_OUWCTI?",
281 option_tech_cb, cbd, NULL) == 0)
286 if (g_at_chat_send(nd->chat, "AT+CREG?", creg_prefix,
287 at_creg_cb, cbd, g_free) > 0)
292 CALLBACK_WITH_FAILURE(cb, -1, -1, -1, -1, data);
295 static void cops_cb(gboolean ok, GAtResult *result, gpointer user_data)
297 struct cb_data *cbd = user_data;
298 struct netreg_data *nd = ofono_netreg_get_data(cbd->user);
299 ofono_netreg_operator_cb_t cb = cbd->cb;
300 struct ofono_network_operator op;
304 struct ofono_error error;
306 decode_at_error(&error, g_at_result_final_response(result));
311 g_at_result_iter_init(&iter, result);
313 if (!g_at_result_iter_next(&iter, "+COPS:"))
316 g_at_result_iter_skip_next(&iter);
318 ok = g_at_result_iter_next_number(&iter, &format);
320 if (ok == FALSE || format != 0)
323 if (g_at_result_iter_next_string(&iter, &name) == FALSE)
327 if (g_at_result_iter_next_number(&iter, &tech) == FALSE)
328 tech = ACCESS_TECHNOLOGY_GSM;
330 strncpy(op.name, name, OFONO_MAX_OPERATOR_NAME_LENGTH);
331 op.name[OFONO_MAX_OPERATOR_NAME_LENGTH] = '\0';
333 strncpy(op.mcc, nd->mcc, OFONO_MAX_MCC_LENGTH);
334 op.mcc[OFONO_MAX_MCC_LENGTH] = '\0';
336 strncpy(op.mnc, nd->mnc, OFONO_MAX_MNC_LENGTH);
337 op.mnc[OFONO_MAX_MNC_LENGTH] = '\0';
343 DBG("cops_cb: %s, %s %s %d", name, nd->mcc, nd->mnc, tech);
345 cb(&error, &op, cbd->data);
351 cb(&error, NULL, cbd->data);
356 static void cops_numeric_cb(gboolean ok, GAtResult *result, gpointer user_data)
358 struct cb_data *cbd = user_data;
359 struct netreg_data *nd = ofono_netreg_get_data(cbd->user);
360 ofono_netreg_operator_cb_t cb = cbd->cb;
365 struct ofono_error error;
367 decode_at_error(&error, g_at_result_final_response(result));
372 g_at_result_iter_init(&iter, result);
374 if (!g_at_result_iter_next(&iter, "+COPS:"))
377 g_at_result_iter_skip_next(&iter);
379 ok = g_at_result_iter_next_number(&iter, &format);
381 if (ok == FALSE || format != 2)
384 if (g_at_result_iter_next_string(&iter, &str) == FALSE)
387 len = strspn(str, "0123456789");
389 if (len != 5 && len != 6)
392 extract_mcc_mnc(str, nd->mcc, nd->mnc);
394 DBG("Cops numeric got mcc: %s, mnc: %s", nd->mcc, nd->mnc);
396 ok = g_at_chat_send(nd->chat, "AT+COPS=3,0", none_prefix,
400 ok = g_at_chat_send(nd->chat, "AT+COPS?", cops_prefix,
407 cb(&error, NULL, cbd->data);
411 static void at_current_operator(struct ofono_netreg *netreg,
412 ofono_netreg_operator_cb_t cb, void *data)
414 struct netreg_data *nd = ofono_netreg_get_data(netreg);
415 struct cb_data *cbd = cb_data_new(cb, data);
420 /* Nokia modems have a broken return value for the string
421 * returned for the numeric value. It misses a " at the end.
422 * Trying to read this will stall the parser. So skip it. */
423 if (nd->vendor == OFONO_VENDOR_NOKIA) {
424 ok = g_at_chat_send(nd->chat, "AT+COPS=3,0", none_prefix,
428 ok = g_at_chat_send(nd->chat, "AT+COPS?", cops_prefix,
431 ok = g_at_chat_send(nd->chat, "AT+COPS=3,2", none_prefix,
435 ok = g_at_chat_send(nd->chat, "AT+COPS?", cops_prefix,
436 cops_numeric_cb, cbd, NULL);
444 CALLBACK_WITH_FAILURE(cb, NULL, data);
447 static void cops_list_cb(gboolean ok, GAtResult *result, gpointer user_data)
449 struct cb_data *cbd = user_data;
450 ofono_netreg_operator_list_cb_t cb = cbd->cb;
451 struct ofono_network_operator *list;
454 struct ofono_error error;
456 decode_at_error(&error, g_at_result_final_response(result));
459 cb(&error, 0, NULL, cbd->data);
463 g_at_result_iter_init(&iter, result);
465 while (g_at_result_iter_next(&iter, "+COPS:")) {
466 while (g_at_result_iter_skip_next(&iter))
470 DBG("Got %d elements", num);
472 list = g_try_new0(struct ofono_network_operator, num);
474 CALLBACK_WITH_FAILURE(cb, 0, NULL, cbd->data);
479 g_at_result_iter_init(&iter, result);
481 while (g_at_result_iter_next(&iter, "+COPS:")) {
482 int status, tech, plmn;
483 const char *l, *s, *n;
484 gboolean have_long = FALSE;
487 if (!g_at_result_iter_open_list(&iter))
490 if (!g_at_result_iter_next_number(&iter, &status))
493 list[num].status = status;
495 if (!g_at_result_iter_next_string(&iter, &l))
500 strncpy(list[num].name, l,
501 OFONO_MAX_OPERATOR_NAME_LENGTH);
504 if (!g_at_result_iter_next_string(&iter, &s))
507 if (strlen(s) > 0 && !have_long)
508 strncpy(list[num].name, s,
509 OFONO_MAX_OPERATOR_NAME_LENGTH);
511 list[num].name[OFONO_MAX_OPERATOR_NAME_LENGTH] = '\0';
513 if (!g_at_result_iter_next_string(&iter, &n))
516 extract_mcc_mnc(n, list[num].mcc, list[num].mnc);
518 if (!g_at_result_iter_next_number(&iter, &tech))
519 tech = ACCESS_TECHNOLOGY_GSM;
521 list[num].tech = tech;
523 if (!g_at_result_iter_next_number(&iter, &plmn))
526 if (!g_at_result_iter_close_list(&iter))
533 DBG("Got %d operators", num);
538 for (; i < num; i++) {
539 DBG("Operator: %s, %s, %s, status: %d, %d",
540 list[i].name, list[i].mcc, list[i].mnc,
541 list[i].status, list[i].tech);
545 cb(&error, num, list, cbd->data);
550 static void at_list_operators(struct ofono_netreg *netreg,
551 ofono_netreg_operator_list_cb_t cb, void *data)
553 struct netreg_data *nd = ofono_netreg_get_data(netreg);
554 struct cb_data *cbd = cb_data_new(cb, data);
556 if (g_at_chat_send(nd->chat, "AT+COPS=?", cops_prefix,
557 cops_list_cb, cbd, g_free) > 0)
562 CALLBACK_WITH_FAILURE(cb, 0, NULL, data);
565 static void register_cb(gboolean ok, GAtResult *result, gpointer user_data)
567 struct cb_data *cbd = user_data;
568 ofono_netreg_register_cb_t cb = cbd->cb;
569 struct ofono_error error;
571 decode_at_error(&error, g_at_result_final_response(result));
573 cb(&error, cbd->data);
576 static void at_register_auto(struct ofono_netreg *netreg,
577 ofono_netreg_register_cb_t cb, void *data)
579 struct netreg_data *nd = ofono_netreg_get_data(netreg);
580 struct cb_data *cbd = cb_data_new(cb, data);
582 if (g_at_chat_send(nd->chat, "AT+COPS=0", none_prefix,
583 register_cb, cbd, g_free) > 0)
588 CALLBACK_WITH_FAILURE(cb, data);
591 static void at_register_manual(struct ofono_netreg *netreg,
592 const char *mcc, const char *mnc,
593 ofono_netreg_register_cb_t cb, void *data)
595 struct netreg_data *nd = ofono_netreg_get_data(netreg);
596 struct cb_data *cbd = cb_data_new(cb, data);
599 snprintf(buf, sizeof(buf), "AT+COPS=1,2,\"%s%s\"", mcc, mnc);
601 if (g_at_chat_send(nd->chat, buf, none_prefix,
602 register_cb, cbd, g_free) > 0)
607 CALLBACK_WITH_FAILURE(cb, data);
610 static void csq_notify(GAtResult *result, gpointer user_data)
612 struct ofono_netreg *netreg = user_data;
616 g_at_result_iter_init(&iter, result);
618 if (!g_at_result_iter_next(&iter, "+CSQ:"))
621 if (!g_at_result_iter_next_number(&iter, &strength))
624 ofono_netreg_strength_notify(netreg,
625 at_util_convert_signal_strength(strength));
628 static void calypso_csq_notify(GAtResult *result, gpointer user_data)
630 struct ofono_netreg *netreg = user_data;
634 g_at_result_iter_init(&iter, result);
636 if (!g_at_result_iter_next(&iter, "%CSQ:"))
639 if (!g_at_result_iter_next_number(&iter, &strength))
642 ofono_netreg_strength_notify(netreg,
643 at_util_convert_signal_strength(strength));
646 static void option_osigq_notify(GAtResult *result, gpointer user_data)
648 struct ofono_netreg *netreg = user_data;
652 g_at_result_iter_init(&iter, result);
654 if (!g_at_result_iter_next(&iter, "_OSIGQ:"))
657 if (!g_at_result_iter_next_number(&iter, &strength))
660 ofono_netreg_strength_notify(netreg,
661 at_util_convert_signal_strength(strength));
664 static void ifx_xhomezr_notify(GAtResult *result, gpointer user_data)
666 //struct ofono_netreg *netreg = user_data;
670 g_at_result_iter_init(&iter, result);
672 if (!g_at_result_iter_next(&iter, "+XHOMEZR:"))
675 if (!g_at_result_iter_next_string(&iter, &label))
678 ofono_info("Home zone: %s", label);
681 static void ifx_xciev_notify(GAtResult *result, gpointer user_data)
683 //struct ofono_netreg *netreg = user_data;
687 g_at_result_iter_init(&iter, result);
689 if (!g_at_result_iter_next(&iter, "+XCIEV:"))
692 if (!g_at_result_iter_next_number(&iter, &ind))
698 * Radio signal strength indicators are defined for 0-7,
699 * but this notification seems to return CSQ 0-31,99 values.
701 * Ignore this indication for now since it can not be trusted.
705 static void ifx_xcsq_notify(GAtResult *result, gpointer user_data)
707 struct ofono_netreg *netreg = user_data;
708 int rssi, ber, strength;
711 g_at_result_iter_init(&iter, result);
713 if (!g_at_result_iter_next(&iter, "+XCSQ:"))
716 if (!g_at_result_iter_next_number(&iter, &rssi))
719 if (!g_at_result_iter_next_number(&iter, &ber))
722 DBG("rssi %d ber %d", rssi, ber);
727 strength = (rssi * 100) / 31;
729 ofono_netreg_strength_notify(netreg, strength);
732 static void ciev_notify(GAtResult *result, gpointer user_data)
734 struct ofono_netreg *netreg = user_data;
735 struct netreg_data *nd = ofono_netreg_get_data(netreg);
739 g_at_result_iter_init(&iter, result);
741 if (!g_at_result_iter_next(&iter, "+CIEV:"))
744 if (!g_at_result_iter_next_number(&iter, &ind))
747 if (ind != nd->signal_index)
750 if (!g_at_result_iter_next_number(&iter, &strength))
753 if (strength == nd->signal_invalid)
756 strength = (strength * 100) / (nd->signal_max - nd->signal_min);
758 ofono_netreg_strength_notify(netreg, strength);
761 static void ctzv_notify(GAtResult *result, gpointer user_data)
763 struct ofono_netreg *netreg = user_data;
764 struct netreg_data *nd = ofono_netreg_get_data(netreg);
768 g_at_result_iter_init(&iter, result);
770 if (!g_at_result_iter_next(&iter, "+CTZV:"))
773 if (!g_at_result_iter_next_unquoted_string(&iter, &tz))
778 nd->time.utcoff = atoi(tz) * 15 * 60;
780 ofono_netreg_time_notify(netreg, &nd->time);
783 static gboolean notify_time(gpointer user_data)
785 struct ofono_netreg *netreg = user_data;
786 struct netreg_data *nd = ofono_netreg_get_data(netreg);
788 nd->nitz_timeout = 0;
790 ofono_netreg_time_notify(netreg, &nd->time);
795 static void ifx_ctzv_notify(GAtResult *result, gpointer user_data)
797 struct ofono_netreg *netreg = user_data;
798 struct netreg_data *nd = ofono_netreg_get_data(netreg);
799 int year, mon, mday, hour, min, sec;
800 const char *tz, *time;
803 g_at_result_iter_init(&iter, result);
805 if (!g_at_result_iter_next(&iter, "+CTZV:"))
808 if (!g_at_result_iter_next_unquoted_string(&iter, &tz))
811 if (!g_at_result_iter_next_string(&iter, &time))
814 DBG("tz %s time %s", tz, time);
816 if (sscanf(time, "%u/%u/%u,%u:%u:%u", &year, &mon, &mday,
817 &hour, &min, &sec) != 6)
822 nd->time.hour = hour;
823 nd->time.mday = mday;
825 nd->time.year = 2000 + year;
827 if (nd->nitz_timeout > 0)
828 g_source_remove(nd->nitz_timeout);
830 nd->nitz_timeout = g_timeout_add_seconds(1, notify_time, user_data);
833 static void ifx_ctzdst_notify(GAtResult *result, gpointer user_data)
835 struct ofono_netreg *netreg = user_data;
836 struct netreg_data *nd = ofono_netreg_get_data(netreg);
840 g_at_result_iter_init(&iter, result);
842 if (!g_at_result_iter_next(&iter, "+CTZDST:"))
845 if (!g_at_result_iter_next_number(&iter, &dst))
852 if (nd->nitz_timeout > 0) {
853 g_source_remove(nd->nitz_timeout);
854 nd->nitz_timeout = 0;
857 ofono_netreg_time_notify(netreg, &nd->time);
860 static void cind_cb(gboolean ok, GAtResult *result, gpointer user_data)
862 struct cb_data *cbd = user_data;
863 ofono_netreg_strength_cb_t cb = cbd->cb;
864 struct netreg_data *nd = cbd->user;
868 struct ofono_error error;
870 decode_at_error(&error, g_at_result_final_response(result));
873 cb(&error, -1, cbd->data);
877 g_at_result_iter_init(&iter, result);
879 if (!g_at_result_iter_next(&iter, "+CIND:")) {
880 CALLBACK_WITH_FAILURE(cb, -1, cbd->data);
884 for (index = 1; index < nd->signal_index; index++)
885 g_at_result_iter_skip_next(&iter);
887 g_at_result_iter_next_number(&iter, &strength);
889 if (strength == nd->signal_invalid)
892 strength = (strength * 100) / (nd->signal_max - nd->signal_min);
894 cb(&error, strength, cbd->data);
897 static void huawei_rssi_notify(GAtResult *result, gpointer user_data)
899 struct ofono_netreg *netreg = user_data;
903 g_at_result_iter_init(&iter, result);
905 if (!g_at_result_iter_next(&iter, "^RSSI:"))
908 if (!g_at_result_iter_next_number(&iter, &strength))
911 ofono_netreg_strength_notify(netreg,
912 at_util_convert_signal_strength(strength));
915 static void huawei_mode_notify(GAtResult *result, gpointer user_data)
917 struct ofono_netreg *netreg = user_data;
918 struct netreg_data *nd = ofono_netreg_get_data(netreg);
922 g_at_result_iter_init(&iter, result);
924 if (!g_at_result_iter_next(&iter, "^MODE:"))
927 if (!g_at_result_iter_next_number(&iter, &mode))
930 if (!g_at_result_iter_next_number(&iter, &submode))
935 nd->tech = ACCESS_TECHNOLOGY_GSM;
938 nd->tech = ACCESS_TECHNOLOGY_UTRAN;
943 static void huawei_nwtime_notify(GAtResult *result, gpointer user_data)
945 struct ofono_netreg *netreg = user_data;
946 struct netreg_data *nd = ofono_netreg_get_data(netreg);
947 int year, mon, mday, hour, min, sec;
949 const char *date, *time, *dst;
952 g_at_result_iter_init(&iter, result);
954 if (!g_at_result_iter_next(&iter, "^NWTIME:"))
957 if (!g_at_result_iter_next_unquoted_string(&iter, &date))
960 if (!g_at_result_iter_next_unquoted_string(&iter, &time))
963 if (!g_at_result_iter_next_unquoted_string(&iter, &dst))
966 DBG("date %s time %s dst %s", date, time, dst);
968 if (sscanf(date, "%u/%u/%u", &year, &mon, &mday) != 3)
971 if (sscanf(time, "%u:%u:%u%s", &hour, &min, &sec, tz) != 4)
974 nd->time.utcoff = atoi(tz) * 15 * 60;
975 nd->time.dst = atoi(dst);
979 nd->time.hour = hour;
980 nd->time.mday = mday;
982 nd->time.year = 2000 + year;
984 ofono_netreg_time_notify(netreg, &nd->time);
987 static void csq_cb(gboolean ok, GAtResult *result, gpointer user_data)
989 struct cb_data *cbd = user_data;
990 ofono_netreg_strength_cb_t cb = cbd->cb;
993 struct ofono_error error;
995 decode_at_error(&error, g_at_result_final_response(result));
998 cb(&error, -1, cbd->data);
1002 g_at_result_iter_init(&iter, result);
1004 if (!g_at_result_iter_next(&iter, "+CSQ:")) {
1005 CALLBACK_WITH_FAILURE(cb, -1, cbd->data);
1009 g_at_result_iter_next_number(&iter, &strength);
1011 DBG("csq_cb: %d", strength);
1016 strength = (strength * 100) / 31;
1018 cb(&error, strength, cbd->data);
1021 static void at_signal_strength(struct ofono_netreg *netreg,
1022 ofono_netreg_strength_cb_t cb, void *data)
1024 struct netreg_data *nd = ofono_netreg_get_data(netreg);
1025 struct cb_data *cbd = cb_data_new(cb, data);
1030 * If we defaulted to using CIND, then keep using it,
1031 * otherwise fall back to CSQ
1033 if (nd->signal_index > 0) {
1034 if (g_at_chat_send(nd->chat, "AT+CIND?", cind_prefix,
1035 cind_cb, cbd, g_free) > 0)
1038 if (g_at_chat_send(nd->chat, "AT+CSQ", csq_prefix,
1039 csq_cb, cbd, g_free) > 0)
1045 CALLBACK_WITH_FAILURE(cb, -1, data);
1048 static void mbm_etzv_notify(GAtResult *result, gpointer user_data)
1050 struct ofono_netreg *netreg = user_data;
1051 struct netreg_data *nd = ofono_netreg_get_data(netreg);
1052 int year, mon, mday, hour, min, sec;
1053 const char *tz, *time, *timestamp;
1056 g_at_result_iter_init(&iter, result);
1058 if (g_at_result_iter_next(&iter, "*ETZV:") == FALSE)
1061 if (g_at_result_iter_next_string(&iter, &tz) == FALSE)
1064 if (g_at_result_iter_next_string(&iter, &time) == FALSE)
1067 if (g_at_result_iter_next_string(&iter, ×tamp) == FALSE)
1070 DBG("tz %s time %s timestamp %s", tz, time, timestamp);
1080 if (sscanf(time, "%u/%u/%u,%u:%u:%u", &year, &mon, &mday,
1081 &hour, &min, &sec) != 6)
1085 nd->time.utcoff = atoi(tz) * 15 * 60;
1089 nd->time.hour = hour;
1090 nd->time.mday = mday;
1092 nd->time.year = year;
1094 ofono_netreg_time_notify(netreg, &nd->time);
1097 static void mbm_erinfo_notify(GAtResult *result, gpointer user_data)
1099 struct ofono_netreg *netreg = user_data;
1100 struct netreg_data *nd = ofono_netreg_get_data(netreg);
1102 int mode, gsm, umts;
1104 g_at_result_iter_init(&iter, result);
1106 if (g_at_result_iter_next(&iter, "*ERINFO:") == FALSE)
1109 if (g_at_result_iter_next_number(&iter, &mode) == FALSE)
1112 if (g_at_result_iter_next_number(&iter, &gsm) == FALSE)
1116 * According to MBM the ERINFO unsolicited response does not contain
1117 * the mode parameter, however at least the MD300 does report it. So
1118 * we handle both 2 and 3 argument versions
1120 if (g_at_result_iter_next_number(&iter, &umts) == FALSE) {
1125 ofono_info("network capability: GSM %d UMTS %d", gsm, umts);
1127 /* Convert to tech values from 27.007 */
1130 nd->tech = ACCESS_TECHNOLOGY_GSM;
1133 nd->tech = ACCESS_TECHNOLOGY_GSM_EGPRS;
1141 nd->tech = ACCESS_TECHNOLOGY_UTRAN;
1143 case 2: /* UMTS + HSDPA */
1144 nd->tech = ACCESS_TECHNOLOGY_UTRAN_HSDPA;
1149 static int cnti_to_tech(const char *cnti)
1151 if (g_str_equal(cnti, "GSM") == TRUE ||
1152 g_str_equal(cnti, "GPRS") == TRUE)
1153 return ACCESS_TECHNOLOGY_GSM;
1154 else if (g_str_equal(cnti, "EDGE") == TRUE)
1155 return ACCESS_TECHNOLOGY_GSM_EGPRS;
1156 else if (g_str_equal(cnti, "UMTS") == TRUE)
1157 return ACCESS_TECHNOLOGY_UTRAN;
1158 else if (g_str_equal(cnti, "HSDPA") == TRUE)
1159 return ACCESS_TECHNOLOGY_UTRAN_HSDPA;
1160 else if (g_str_equal(cnti, "HSUPA") == TRUE)
1161 return ACCESS_TECHNOLOGY_UTRAN_HSUPA;
1166 static void gobi_cnti_notify(GAtResult *result, gpointer user_data)
1168 struct ofono_netreg *netreg = user_data;
1169 struct netreg_data *nd = ofono_netreg_get_data(netreg);
1174 g_at_result_iter_init(&iter, result);
1176 if (g_at_result_iter_next(&iter, "*CNTI:") == FALSE)
1179 if (g_at_result_iter_next_number(&iter, &option) == FALSE)
1185 if (g_at_result_iter_next_unquoted_string(&iter, &tech) == FALSE)
1188 nd->tech = cnti_to_tech(tech);
1191 static void nw_cnti_notify(GAtResult *result, gpointer user_data)
1193 struct ofono_netreg *netreg = user_data;
1194 struct netreg_data *nd = ofono_netreg_get_data(netreg);
1199 g_at_result_iter_init(&iter, result);
1201 if (g_at_result_iter_next(&iter, "$CNTI:") == FALSE)
1204 if (g_at_result_iter_next_number(&iter, &option) == FALSE)
1210 if (g_at_result_iter_next_unquoted_string(&iter, &tech) == FALSE)
1213 nd->tech = cnti_to_tech(tech);
1216 static void cnti_query_tech_cb(gboolean ok, GAtResult *result,
1219 struct tech_query *tq = user_data;
1220 struct netreg_data *nd = ofono_netreg_get_data(tq->netreg);
1222 ofono_netreg_status_notify(tq->netreg,
1223 tq->status, tq->lac, tq->ci, nd->tech);
1226 static void zte_query_tech_cb(gboolean ok, GAtResult *result,
1229 struct tech_query *tq = user_data;
1233 tech = zte_parse_tech(result);
1237 ofono_netreg_status_notify(tq->netreg,
1238 tq->status, tq->lac, tq->ci, tech);
1241 static void option_query_tech_cb(gboolean ok, GAtResult *result,
1244 struct tech_query *tq = user_data;
1248 tech = option_parse_tech(result);
1252 ofono_netreg_status_notify(tq->netreg,
1253 tq->status, tq->lac, tq->ci, tech);
1256 static void creg_notify(GAtResult *result, gpointer user_data)
1258 struct ofono_netreg *netreg = user_data;
1259 int status, lac, ci, tech;
1260 struct netreg_data *nd = ofono_netreg_get_data(netreg);
1261 struct tech_query *tq;
1263 if (at_util_parse_reg_unsolicited(result, "+CREG:", &status,
1264 &lac, &ci, &tech, nd->vendor) == FALSE)
1267 if (status != 1 && status != 5)
1270 tq = g_try_new0(struct tech_query, 1);
1274 tq->status = status;
1277 tq->netreg = netreg;
1279 switch (nd->vendor) {
1280 case OFONO_VENDOR_GOBI:
1281 if (g_at_chat_send(nd->chat, "AT*CNTI=0", none_prefix,
1282 cnti_query_tech_cb, tq, g_free) > 0)
1285 case OFONO_VENDOR_NOVATEL:
1286 if (g_at_chat_send(nd->chat, "AT$CNTI=0", none_prefix,
1287 cnti_query_tech_cb, tq, g_free) > 0)
1290 case OFONO_VENDOR_ZTE:
1291 if (g_at_chat_send(nd->chat, "AT+ZPAS?", zpas_prefix,
1292 zte_query_tech_cb, tq, g_free) > 0)
1295 case OFONO_VENDOR_OPTION_HSO:
1296 if (g_at_chat_send(nd->chat, "AT_OCTI?;_OUWCTI?",
1298 option_query_tech_cb, tq, g_free) > 0)
1305 if ((status == 1 || status == 5) && tech == -1)
1309 ofono_netreg_status_notify(netreg, status, lac, ci, tech);
1312 static void cind_support_cb(gboolean ok, GAtResult *result, gpointer user_data)
1314 struct ofono_netreg *netreg = user_data;
1315 struct netreg_data *nd = ofono_netreg_get_data(netreg);
1318 char *signal_identifier = "signal";
1322 int tmp_min, tmp_max, invalid;
1327 g_at_result_iter_init(&iter, result);
1328 if (!g_at_result_iter_next(&iter, "+CIND:"))
1334 * Telit encapsulates the CIND=? tokens with braces
1335 * so we need to skip them
1337 if (nd->vendor == OFONO_VENDOR_TELIT) {
1338 g_at_result_iter_open_list(&iter);
1339 signal_identifier = "rssi";
1342 while (g_at_result_iter_open_list(&iter)) {
1343 /* Reset invalid default value for every token */
1346 if (!g_at_result_iter_next_string(&iter, &str))
1349 if (!g_at_result_iter_open_list(&iter))
1352 while (g_at_result_iter_next_range(&iter, &tmp_min, &tmp_max)) {
1353 if (tmp_min != tmp_max) {
1360 if (!g_at_result_iter_close_list(&iter))
1363 if (!g_at_result_iter_close_list(&iter))
1366 if (g_str_equal(signal_identifier, str) == TRUE) {
1367 nd->signal_index = index;
1368 nd->signal_min = min;
1369 nd->signal_max = max;
1370 nd->signal_invalid = invalid;
1376 if (nd->vendor == OFONO_VENDOR_TELIT)
1377 g_at_result_iter_close_list(&iter);
1379 if (nd->signal_index == 0)
1382 g_at_chat_send(nd->chat, "AT+CMER=3,0,0,1", NULL,
1384 g_at_chat_register(nd->chat, "+CIEV:",
1385 ciev_notify, FALSE, netreg, NULL);
1386 g_at_chat_register(nd->chat, "+CREG:",
1387 creg_notify, FALSE, netreg, NULL);
1389 ofono_netreg_register(netreg);
1393 ofono_error("This driver is not setup with Signal Strength reporting"
1394 " via CIND indications, please write proper netreg"
1395 " handling for this device");
1397 ofono_netreg_remove(netreg);
1400 static void at_creg_set_cb(gboolean ok, GAtResult *result, gpointer user_data)
1402 struct ofono_netreg *netreg = user_data;
1403 struct netreg_data *nd = ofono_netreg_get_data(netreg);
1406 ofono_error("Unable to initialize Network Registration");
1407 ofono_netreg_remove(netreg);
1411 switch (nd->vendor) {
1412 case OFONO_VENDOR_PHONESIM:
1413 g_at_chat_register(nd->chat, "+CSQ:",
1414 csq_notify, FALSE, netreg, NULL);
1416 case OFONO_VENDOR_CALYPSO:
1417 g_at_chat_send(nd->chat, "AT%CSQ=1", none_prefix,
1419 g_at_chat_register(nd->chat, "%CSQ:", calypso_csq_notify,
1420 FALSE, netreg, NULL);
1422 case OFONO_VENDOR_OPTION_HSO:
1423 g_at_chat_send(nd->chat, "AT_OSSYS=1", none_prefix,
1425 g_at_chat_send(nd->chat, "AT_OSQI=1", none_prefix,
1427 g_at_chat_register(nd->chat, "_OSIGQ:", option_osigq_notify,
1428 FALSE, netreg, NULL);
1430 g_at_chat_send(nd->chat, "AT_OSSYS?", none_prefix,
1432 g_at_chat_send(nd->chat, "AT_OSQI?", none_prefix,
1435 /* Register for network time update reports */
1436 g_at_chat_register(nd->chat, "+CTZV:", ctzv_notify,
1437 FALSE, netreg, NULL);
1438 g_at_chat_send(nd->chat, "AT+CTZR=1", none_prefix,
1441 case OFONO_VENDOR_MBM:
1442 /* Enable network registration updates */
1443 g_at_chat_send(nd->chat, "AT*E2REG=1", none_prefix,
1445 g_at_chat_send(nd->chat, "AT*EREG=2", none_prefix,
1447 g_at_chat_send(nd->chat, "AT*EPSB=1", none_prefix,
1450 /* Register for network technology updates */
1451 g_at_chat_send(nd->chat, "AT*ERINFO=1", none_prefix,
1453 g_at_chat_register(nd->chat, "*ERINFO:", mbm_erinfo_notify,
1454 FALSE, netreg, NULL);
1456 /* Register for network time update reports */
1457 g_at_chat_register(nd->chat, "*ETZV:", mbm_etzv_notify,
1458 FALSE, netreg, NULL);
1459 g_at_chat_send(nd->chat, "AT*ETZR=2", none_prefix,
1462 g_at_chat_send(nd->chat, "AT+CIND=?", cind_prefix,
1463 cind_support_cb, netreg, NULL);
1465 case OFONO_VENDOR_GOBI:
1467 * Gobi devices don't support unsolicited notifications
1468 * of technology changes, but register a handle for
1469 * CNTI so we get notified by any query.
1471 g_at_chat_register(nd->chat, "*CNTI:", gobi_cnti_notify,
1472 FALSE, netreg, NULL);
1474 case OFONO_VENDOR_NOVATEL:
1476 * Novatel doesn't support unsolicited notifications
1477 * of technology changes, but register a handle for
1478 * CNTI so we get notified by any query.
1480 g_at_chat_register(nd->chat, "$CNTI:", nw_cnti_notify,
1481 FALSE, netreg, NULL);
1483 case OFONO_VENDOR_HUAWEI:
1484 /* Register for RSSI reports */
1485 g_at_chat_register(nd->chat, "^RSSI:", huawei_rssi_notify,
1486 FALSE, netreg, NULL);
1488 /* Register for system mode reports */
1489 g_at_chat_register(nd->chat, "^MODE:", huawei_mode_notify,
1490 FALSE, netreg, NULL);
1492 /* Register for network time reports */
1493 g_at_chat_register(nd->chat, "^NWTIME:", huawei_nwtime_notify,
1494 FALSE, netreg, NULL);
1496 case OFONO_VENDOR_IFX:
1497 /* Register for specific signal strength reports */
1498 g_at_chat_register(nd->chat, "+XCIEV:", ifx_xciev_notify,
1499 FALSE, netreg, NULL);
1500 g_at_chat_register(nd->chat, "+XCSQ:", ifx_xcsq_notify,
1501 FALSE, netreg, NULL);
1502 g_at_chat_send(nd->chat, "AT+XCSQ=1", none_prefix,
1504 g_at_chat_send(nd->chat, "AT+XMER=1", none_prefix,
1507 /* Register for home zone reports */
1508 g_at_chat_register(nd->chat, "+XHOMEZR:", ifx_xhomezr_notify,
1509 FALSE, netreg, NULL);
1510 g_at_chat_send(nd->chat, "AT+XHOMEZR=1", none_prefix,
1513 /* Register for network time update reports */
1514 g_at_chat_register(nd->chat, "+CTZV:", ifx_ctzv_notify,
1515 FALSE, netreg, NULL);
1516 g_at_chat_register(nd->chat, "+CTZDST:", ifx_ctzdst_notify,
1517 FALSE, netreg, NULL);
1518 g_at_chat_send(nd->chat, "AT+CTZR=1", none_prefix,
1521 case OFONO_VENDOR_ZTE:
1522 /* Register for network time update reports */
1523 g_at_chat_register(nd->chat, "+CTZV:", ctzv_notify,
1524 FALSE, netreg, NULL);
1525 g_at_chat_send(nd->chat, "AT+CTZR=1", none_prefix,
1528 case OFONO_VENDOR_NOKIA:
1529 case OFONO_VENDOR_SAMSUNG:
1530 case OFONO_VENDOR_SIMCOM:
1531 /* Signal strength reporting via CIND is not supported */
1534 g_at_chat_send(nd->chat, "AT+CIND=?", cind_prefix,
1535 cind_support_cb, netreg, NULL);
1539 g_at_chat_register(nd->chat, "+CREG:",
1540 creg_notify, FALSE, netreg, NULL);
1541 ofono_netreg_register(netreg);
1544 static void at_creg_test_cb(gboolean ok, GAtResult *result, gpointer user_data)
1546 struct ofono_netreg *netreg = user_data;
1547 struct netreg_data *nd = ofono_netreg_get_data(netreg);
1556 g_at_result_iter_init(&iter, result);
1558 if (!g_at_result_iter_next(&iter, "+CREG:"))
1561 if (!g_at_result_iter_open_list(&iter))
1564 while (g_at_result_iter_next_range(&iter, &range[0], &range[1])) {
1565 if (1 >= range[0] && 1 <= range[1])
1567 if (2 >= range[0] && 2 <= range[1])
1571 g_at_result_iter_close_list(&iter);
1574 g_at_chat_send(nd->chat, "AT+CREG=2", none_prefix,
1575 at_creg_set_cb, netreg, NULL);
1580 g_at_chat_send(nd->chat, "AT+CREG=1", none_prefix,
1581 at_creg_set_cb, netreg, NULL);
1586 ofono_error("Unable to initialize Network Registration");
1587 ofono_netreg_remove(netreg);
1590 static int at_netreg_probe(struct ofono_netreg *netreg, unsigned int vendor,
1593 GAtChat *chat = data;
1594 struct netreg_data *nd;
1596 nd = g_new0(struct netreg_data, 1);
1598 nd->chat = g_at_chat_clone(chat);
1599 nd->vendor = vendor;
1608 nd->time.utcoff = 0;
1609 ofono_netreg_set_data(netreg, nd);
1611 g_at_chat_send(nd->chat, "AT+CREG=?", creg_prefix,
1612 at_creg_test_cb, netreg, NULL);
1617 static void at_netreg_remove(struct ofono_netreg *netreg)
1619 struct netreg_data *nd = ofono_netreg_get_data(netreg);
1621 if (nd->nitz_timeout)
1622 g_source_remove(nd->nitz_timeout);
1624 ofono_netreg_set_data(netreg, NULL);
1626 g_at_chat_unref(nd->chat);
1630 static struct ofono_netreg_driver driver = {
1632 .probe = at_netreg_probe,
1633 .remove = at_netreg_remove,
1634 .registration_status = at_registration_status,
1635 .current_operator = at_current_operator,
1636 .list_operators = at_list_operators,
1637 .register_auto = at_register_auto,
1638 .register_manual = at_register_manual,
1639 .strength = at_signal_strength,
1642 void at_netreg_init(void)
1644 ofono_netreg_driver_register(&driver);
1647 void at_netreg_exit(void)
1649 ofono_netreg_driver_unregister(&driver);