0ec66087852947a0fda211b757b1f671750df6ea
[profile/ivi/ofono.git] / drivers / atmodem / network-registration.c
1 /*
2  *
3  *  oFono - Open Source Telephony
4  *
5  *  Copyright (C) 2008-2011  Intel Corporation. All rights reserved.
6  *  Copyright (C) 2010  ST-Ericsson AB.
7  *
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.
11  *
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.
16  *
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
20  *
21  */
22
23 #ifdef HAVE_CONFIG_H
24 #include <config.h>
25 #endif
26
27 #define _GNU_SOURCE
28 #include <string.h>
29 #include <stdlib.h>
30 #include <stdio.h>
31
32 #include <glib.h>
33
34 #include <ofono/log.h>
35 #include <ofono/modem.h>
36 #include <ofono/netreg.h>
37
38 #include "gatchat.h"
39 #include "gatresult.h"
40
41 #include "common.h"
42 #include "atmodem.h"
43 #include "vendor.h"
44
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 };
52
53 struct netreg_data {
54         GAtChat *chat;
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 */
61         int tech;
62         struct ofono_network_time time;
63         guint nitz_timeout;
64         unsigned int vendor;
65 };
66
67 struct tech_query {
68         int status;
69         int lac;
70         int ci;
71         struct ofono_netreg *netreg;
72 };
73
74 static void extract_mcc_mnc(const char *str, char *mcc, char *mnc)
75 {
76         /* Three digit country code */
77         strncpy(mcc, str, OFONO_MAX_MCC_LENGTH);
78         mcc[OFONO_MAX_MCC_LENGTH] = '\0';
79
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';
83 }
84
85 static int zte_parse_tech(GAtResult *result)
86 {
87         GAtResultIter iter;
88         const char *network, *domain;
89         int tech;
90
91         g_at_result_iter_init(&iter, result);
92
93         if (!g_at_result_iter_next(&iter, "+ZPAS:"))
94                 return -1;
95
96         if (!g_at_result_iter_next_string(&iter, &network))
97                 return -1;
98
99         if (!g_at_result_iter_next_string(&iter, &domain))
100                 return -1;
101
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;
111         else
112                 tech = -1;
113
114         DBG("network %s domain %s tech %d", network, domain, tech);
115
116         return tech;
117 }
118
119 static int option_parse_tech(GAtResult *result)
120 {
121         GAtResultIter iter;
122         int s, octi, ouwcti;
123         int tech;
124
125         g_at_result_iter_init(&iter, result);
126
127         if (!g_at_result_iter_next(&iter, "_OCTI:"))
128                 return -1;
129
130         if (!g_at_result_iter_next_number(&iter, &s))
131                 return -1;
132
133         if (!g_at_result_iter_next_number(&iter, &octi))
134                 return -1;
135
136         if (!g_at_result_iter_next(&iter, "_OUWCTI:"))
137                 return -1;
138
139         if (!g_at_result_iter_next_number(&iter, &s))
140                 return -1;
141
142         if (!g_at_result_iter_next_number(&iter, &ouwcti))
143                 return -1;
144
145         switch (octi) {
146         case 1: /* GSM */
147                 tech = ACCESS_TECHNOLOGY_GSM;
148                 break;
149         case 2: /* GPRS */
150                 tech = ACCESS_TECHNOLOGY_GSM;
151                 break;
152         case 3: /* EDGE */
153                 tech = ACCESS_TECHNOLOGY_GSM_EGPRS;
154                 break;
155         default:
156                 tech = -1;
157                 break;
158         }
159
160         switch (ouwcti) {
161         case 1: /* UMTS */
162                 tech = ACCESS_TECHNOLOGY_UTRAN;
163                 break;
164         case 2: /* HSDPA */
165                 tech = ACCESS_TECHNOLOGY_UTRAN_HSDPA;
166                 break;
167         case 3: /* HSUPA */
168                 tech = ACCESS_TECHNOLOGY_UTRAN_HSUPA;
169                 break;
170         case 4: /* HSPA */
171                 tech = ACCESS_TECHNOLOGY_UTRAN_HSDPA_HSUPA;
172                 break;
173         }
174
175         DBG("octi %d ouwcti %d tech %d", octi, ouwcti, tech);
176
177         return tech;
178 }
179
180 static void at_creg_cb(gboolean ok, GAtResult *result, gpointer user_data)
181 {
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;
187
188         decode_at_error(&error, g_at_result_final_response(result));
189
190         if (!ok) {
191                 cb(&error, -1, -1, -1, -1, cbd->data);
192                 return;
193         }
194
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);
198                 return;
199         }
200
201         if ((status == 1 || status == 5) && (tech == -1))
202                 tech = nd->tech;
203
204         cb(&error, status, lac, ci, tech, cbd->data);
205 }
206
207 static void zte_tech_cb(gboolean ok, GAtResult *result, gpointer user_data)
208 {
209         struct cb_data *cbd = user_data;
210         struct ofono_netreg *netreg = cbd->data;
211         struct netreg_data *nd = ofono_netreg_get_data(netreg);
212
213         if (ok)
214                 nd->tech = zte_parse_tech(result);
215         else
216                 nd->tech = -1;
217 }
218
219 static void option_tech_cb(gboolean ok, GAtResult *result, gpointer user_data)
220 {
221         struct cb_data *cbd = user_data;
222         struct ofono_netreg *netreg = cbd->data;
223         struct netreg_data *nd = ofono_netreg_get_data(netreg);
224
225         if (ok)
226                 nd->tech = option_parse_tech(result);
227         else
228                 nd->tech = -1;
229 }
230
231 static void at_registration_status(struct ofono_netreg *netreg,
232                                         ofono_netreg_status_cb_t cb,
233                                         void *data)
234 {
235         struct netreg_data *nd = ofono_netreg_get_data(netreg);
236         struct cb_data *cbd = cb_data_new(cb, data);
237
238         cbd->user = nd;
239
240         switch (nd->vendor) {
241         case OFONO_VENDOR_MBM:
242                 /*
243                  * Send *ERINFO to find out the current tech, it will be
244                  * intercepted in mbm_erinfo_notify
245                  */
246                 g_at_chat_send(nd->chat, "AT*ERINFO?", none_prefix,
247                                 NULL, NULL, NULL);
248                 break;
249         case OFONO_VENDOR_GOBI:
250                 /*
251                  * Send *CNTI=0 to find out the current tech, it will be
252                  * intercepted in gobi_cnti_notify
253                  */
254                 g_at_chat_send(nd->chat, "AT*CNTI=0", none_prefix,
255                                 NULL, NULL, NULL);
256                 break;
257         case OFONO_VENDOR_NOVATEL:
258                 /*
259                  * Send $CNTI=0 to find out the current tech, it will be
260                  * intercepted in nw_cnti_notify
261                  */
262                 g_at_chat_send(nd->chat, "AT$CNTI=0", none_prefix,
263                                 NULL, NULL, NULL);
264                 break;
265         case OFONO_VENDOR_ZTE:
266                 /*
267                  * Send +ZPAS? to find out the current tech, zte_tech_cb
268                  * will call, fire CREG? to do the rest.
269                  */
270                 if (g_at_chat_send(nd->chat, "AT+ZPAS?", zpas_prefix,
271                                         zte_tech_cb, cbd, NULL) == 0)
272                         nd->tech = -1;
273                 break;
274         case OFONO_VENDOR_OPTION_HSO:
275                 /*
276                  * Send AT_OCTI?;_OUWCTI? to find out the current tech,
277                  * option_tech_cb will call, fire CREG? to do the rest.
278                  */
279                 if (g_at_chat_send(nd->chat, "AT_OCTI?;_OUWCTI?",
280                                         option_tech_prefix,
281                                         option_tech_cb, cbd, NULL) == 0)
282                         nd->tech = -1;
283                 break;
284         }
285
286         if (g_at_chat_send(nd->chat, "AT+CREG?", creg_prefix,
287                                 at_creg_cb, cbd, g_free) > 0)
288                 return;
289
290         g_free(cbd);
291
292         CALLBACK_WITH_FAILURE(cb, -1, -1, -1, -1, data);
293 }
294
295 static void cops_cb(gboolean ok, GAtResult *result, gpointer user_data)
296 {
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;
301         GAtResultIter iter;
302         int format, tech;
303         const char *name;
304         struct ofono_error error;
305
306         decode_at_error(&error, g_at_result_final_response(result));
307
308         if (!ok)
309                 goto error;
310
311         g_at_result_iter_init(&iter, result);
312
313         if (!g_at_result_iter_next(&iter, "+COPS:"))
314                 goto error;
315
316         g_at_result_iter_skip_next(&iter);
317
318         ok = g_at_result_iter_next_number(&iter, &format);
319
320         if (ok == FALSE || format != 0)
321                 goto error;
322
323         if (g_at_result_iter_next_string(&iter, &name) == FALSE)
324                 goto error;
325
326         /* Default to GSM */
327         if (g_at_result_iter_next_number(&iter, &tech) == FALSE)
328                 tech = ACCESS_TECHNOLOGY_GSM;
329
330         strncpy(op.name, name, OFONO_MAX_OPERATOR_NAME_LENGTH);
331         op.name[OFONO_MAX_OPERATOR_NAME_LENGTH] = '\0';
332
333         strncpy(op.mcc, nd->mcc, OFONO_MAX_MCC_LENGTH);
334         op.mcc[OFONO_MAX_MCC_LENGTH] = '\0';
335
336         strncpy(op.mnc, nd->mnc, OFONO_MAX_MNC_LENGTH);
337         op.mnc[OFONO_MAX_MNC_LENGTH] = '\0';
338
339         /* Set to current */
340         op.status = 2;
341         op.tech = tech;
342
343         DBG("cops_cb: %s, %s %s %d", name, nd->mcc, nd->mnc, tech);
344
345         cb(&error, &op, cbd->data);
346         g_free(cbd);
347
348         return;
349
350 error:
351         cb(&error, NULL, cbd->data);
352
353         g_free(cbd);
354 }
355
356 static void cops_numeric_cb(gboolean ok, GAtResult *result, gpointer user_data)
357 {
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;
361         GAtResultIter iter;
362         const char *str;
363         int format;
364         int len;
365         struct ofono_error error;
366
367         decode_at_error(&error, g_at_result_final_response(result));
368
369         if (!ok)
370                 goto error;
371
372         g_at_result_iter_init(&iter, result);
373
374         if (!g_at_result_iter_next(&iter, "+COPS:"))
375                 goto error;
376
377         g_at_result_iter_skip_next(&iter);
378
379         ok = g_at_result_iter_next_number(&iter, &format);
380
381         if (ok == FALSE || format != 2)
382                 goto error;
383
384         if (g_at_result_iter_next_string(&iter, &str) == FALSE)
385                 goto error;
386
387         len = strspn(str, "0123456789");
388
389         if (len != 5 && len != 6)
390                 goto error;
391
392         extract_mcc_mnc(str, nd->mcc, nd->mnc);
393
394         DBG("Cops numeric got mcc: %s, mnc: %s", nd->mcc, nd->mnc);
395
396         ok = g_at_chat_send(nd->chat, "AT+COPS=3,0", none_prefix,
397                                         NULL, NULL, NULL);
398
399         if (ok)
400                 ok = g_at_chat_send(nd->chat, "AT+COPS?", cops_prefix,
401                                         cops_cb, cbd, NULL);
402
403         if (ok)
404                 return;
405
406 error:
407         cb(&error, NULL, cbd->data);
408         g_free(cbd);
409 }
410
411 static void at_current_operator(struct ofono_netreg *netreg,
412                                 ofono_netreg_operator_cb_t cb, void *data)
413 {
414         struct netreg_data *nd = ofono_netreg_get_data(netreg);
415         struct cb_data *cbd = cb_data_new(cb, data);
416         gboolean ok;
417
418         cbd->user = netreg;
419
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,
425                                                         NULL, NULL, NULL);
426
427                 if (ok)
428                         ok = g_at_chat_send(nd->chat, "AT+COPS?", cops_prefix,
429                                                         cops_cb, cbd, NULL);
430         } else {
431                 ok = g_at_chat_send(nd->chat, "AT+COPS=3,2", none_prefix,
432                                                         NULL, NULL, NULL);
433
434                 if (ok)
435                         ok = g_at_chat_send(nd->chat, "AT+COPS?", cops_prefix,
436                                                 cops_numeric_cb, cbd, NULL);
437         }
438
439         if (ok)
440                 return;
441
442         g_free(cbd);
443
444         CALLBACK_WITH_FAILURE(cb, NULL, data);
445 }
446
447 static void cops_list_cb(gboolean ok, GAtResult *result, gpointer user_data)
448 {
449         struct cb_data *cbd = user_data;
450         ofono_netreg_operator_list_cb_t cb = cbd->cb;
451         struct ofono_network_operator *list;
452         GAtResultIter iter;
453         int num = 0;
454         struct ofono_error error;
455
456         decode_at_error(&error, g_at_result_final_response(result));
457
458         if (!ok) {
459                 cb(&error, 0, NULL, cbd->data);
460                 return;
461         }
462
463         g_at_result_iter_init(&iter, result);
464
465         while (g_at_result_iter_next(&iter, "+COPS:")) {
466                 while (g_at_result_iter_skip_next(&iter))
467                         num += 1;
468         }
469
470         DBG("Got %d elements", num);
471
472         list = g_try_new0(struct ofono_network_operator, num);
473         if (list == NULL) {
474                 CALLBACK_WITH_FAILURE(cb, 0, NULL, cbd->data);
475                 return;
476         }
477
478         num = 0;
479         g_at_result_iter_init(&iter, result);
480
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;
485
486                 while (1) {
487                         if (!g_at_result_iter_open_list(&iter))
488                                 break;
489
490                         if (!g_at_result_iter_next_number(&iter, &status))
491                                 break;
492
493                         list[num].status = status;
494
495                         if (!g_at_result_iter_next_string(&iter, &l))
496                                 break;
497
498                         if (strlen(l) > 0) {
499                                 have_long = TRUE;
500                                 strncpy(list[num].name, l,
501                                         OFONO_MAX_OPERATOR_NAME_LENGTH);
502                         }
503
504                         if (!g_at_result_iter_next_string(&iter, &s))
505                                 break;
506
507                         if (strlen(s) > 0 && !have_long)
508                                 strncpy(list[num].name, s,
509                                         OFONO_MAX_OPERATOR_NAME_LENGTH);
510
511                         list[num].name[OFONO_MAX_OPERATOR_NAME_LENGTH] = '\0';
512
513                         if (!g_at_result_iter_next_string(&iter, &n))
514                                 break;
515
516                         extract_mcc_mnc(n, list[num].mcc, list[num].mnc);
517
518                         if (!g_at_result_iter_next_number(&iter, &tech))
519                                 tech = ACCESS_TECHNOLOGY_GSM;
520
521                         list[num].tech = tech;
522
523                         if (!g_at_result_iter_next_number(&iter, &plmn))
524                                 plmn = 0;
525
526                         if (!g_at_result_iter_close_list(&iter))
527                                 break;
528
529                         num += 1;
530                 }
531         }
532
533         DBG("Got %d operators", num);
534
535 {
536         int i = 0;
537
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);
542         }
543 }
544
545         cb(&error, num, list, cbd->data);
546
547         g_free(list);
548 }
549
550 static void at_list_operators(struct ofono_netreg *netreg,
551                                 ofono_netreg_operator_list_cb_t cb, void *data)
552 {
553         struct netreg_data *nd = ofono_netreg_get_data(netreg);
554         struct cb_data *cbd = cb_data_new(cb, data);
555
556         if (g_at_chat_send(nd->chat, "AT+COPS=?", cops_prefix,
557                                 cops_list_cb, cbd, g_free) > 0)
558                 return;
559
560         g_free(cbd);
561
562         CALLBACK_WITH_FAILURE(cb, 0, NULL, data);
563 }
564
565 static void register_cb(gboolean ok, GAtResult *result, gpointer user_data)
566 {
567         struct cb_data *cbd = user_data;
568         ofono_netreg_register_cb_t cb = cbd->cb;
569         struct ofono_error error;
570
571         decode_at_error(&error, g_at_result_final_response(result));
572
573         cb(&error, cbd->data);
574 }
575
576 static void at_register_auto(struct ofono_netreg *netreg,
577                                 ofono_netreg_register_cb_t cb, void *data)
578 {
579         struct netreg_data *nd = ofono_netreg_get_data(netreg);
580         struct cb_data *cbd = cb_data_new(cb, data);
581
582         if (g_at_chat_send(nd->chat, "AT+COPS=0", none_prefix,
583                                 register_cb, cbd, g_free) > 0)
584                 return;
585
586         g_free(cbd);
587
588         CALLBACK_WITH_FAILURE(cb, data);
589 }
590
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)
594 {
595         struct netreg_data *nd = ofono_netreg_get_data(netreg);
596         struct cb_data *cbd = cb_data_new(cb, data);
597         char buf[128];
598
599         snprintf(buf, sizeof(buf), "AT+COPS=1,2,\"%s%s\"", mcc, mnc);
600
601         if (g_at_chat_send(nd->chat, buf, none_prefix,
602                                 register_cb, cbd, g_free) > 0)
603                 return;
604
605         g_free(cbd);
606
607         CALLBACK_WITH_FAILURE(cb, data);
608 }
609
610 static void csq_notify(GAtResult *result, gpointer user_data)
611 {
612         struct ofono_netreg *netreg = user_data;
613         int strength;
614         GAtResultIter iter;
615
616         g_at_result_iter_init(&iter, result);
617
618         if (!g_at_result_iter_next(&iter, "+CSQ:"))
619                 return;
620
621         if (!g_at_result_iter_next_number(&iter, &strength))
622                 return;
623
624         ofono_netreg_strength_notify(netreg,
625                                 at_util_convert_signal_strength(strength));
626 }
627
628 static void calypso_csq_notify(GAtResult *result, gpointer user_data)
629 {
630         struct ofono_netreg *netreg = user_data;
631         int strength;
632         GAtResultIter iter;
633
634         g_at_result_iter_init(&iter, result);
635
636         if (!g_at_result_iter_next(&iter, "%CSQ:"))
637                 return;
638
639         if (!g_at_result_iter_next_number(&iter, &strength))
640                 return;
641
642         ofono_netreg_strength_notify(netreg,
643                                 at_util_convert_signal_strength(strength));
644 }
645
646 static void option_osigq_notify(GAtResult *result, gpointer user_data)
647 {
648         struct ofono_netreg *netreg = user_data;
649         int strength;
650         GAtResultIter iter;
651
652         g_at_result_iter_init(&iter, result);
653
654         if (!g_at_result_iter_next(&iter, "_OSIGQ:"))
655                 return;
656
657         if (!g_at_result_iter_next_number(&iter, &strength))
658                 return;
659
660         ofono_netreg_strength_notify(netreg,
661                                 at_util_convert_signal_strength(strength));
662 }
663
664 static void ifx_xhomezr_notify(GAtResult *result, gpointer user_data)
665 {
666         //struct ofono_netreg *netreg = user_data;
667         const char *label;
668         GAtResultIter iter;
669
670         g_at_result_iter_init(&iter, result);
671
672         if (!g_at_result_iter_next(&iter, "+XHOMEZR:"))
673                 return;
674
675         if (!g_at_result_iter_next_string(&iter, &label))
676                 return;
677
678         ofono_info("Home zone: %s", label);
679 }
680
681 static void ifx_xciev_notify(GAtResult *result, gpointer user_data)
682 {
683         //struct ofono_netreg *netreg = user_data;
684         int ind;
685         GAtResultIter iter;
686
687         g_at_result_iter_init(&iter, result);
688
689         if (!g_at_result_iter_next(&iter, "+XCIEV:"))
690                 return;
691
692         if (!g_at_result_iter_next_number(&iter, &ind))
693                 return;
694
695         DBG("ind %d", ind);
696
697         /*
698          * Radio signal strength indicators are defined for 0-7,
699          * but this notification seems to return CSQ 0-31,99 values.
700          *
701          * Ignore this indication for now since it can not be trusted.
702          */
703 }
704
705 static void ifx_xcsq_notify(GAtResult *result, gpointer user_data)
706 {
707         struct ofono_netreg *netreg = user_data;
708         int rssi, ber, strength;
709         GAtResultIter iter;
710
711         g_at_result_iter_init(&iter, result);
712
713         if (!g_at_result_iter_next(&iter, "+XCSQ:"))
714                 return;
715
716         if (!g_at_result_iter_next_number(&iter, &rssi))
717                 return;
718
719         if (!g_at_result_iter_next_number(&iter, &ber))
720                 return;
721
722         DBG("rssi %d ber %d", rssi, ber);
723
724         if (rssi == 99)
725                 strength = -1;
726         else
727                 strength = (rssi * 100) / 31;
728
729         ofono_netreg_strength_notify(netreg, strength);
730 }
731
732 static void ciev_notify(GAtResult *result, gpointer user_data)
733 {
734         struct ofono_netreg *netreg = user_data;
735         struct netreg_data *nd = ofono_netreg_get_data(netreg);
736         int strength, ind;
737         GAtResultIter iter;
738
739         g_at_result_iter_init(&iter, result);
740
741         if (!g_at_result_iter_next(&iter, "+CIEV:"))
742                 return;
743
744         if (!g_at_result_iter_next_number(&iter, &ind))
745                 return;
746
747         if (ind != nd->signal_index)
748                 return;
749
750         if (!g_at_result_iter_next_number(&iter, &strength))
751                 return;
752
753         if (strength == nd->signal_invalid)
754                 strength = -1;
755         else
756                 strength = (strength * 100) / (nd->signal_max - nd->signal_min);
757
758         ofono_netreg_strength_notify(netreg, strength);
759 }
760
761 static void ctzv_notify(GAtResult *result, gpointer user_data)
762 {
763         struct ofono_netreg *netreg = user_data;
764         struct netreg_data *nd = ofono_netreg_get_data(netreg);
765         const char *tz;
766         GAtResultIter iter;
767
768         g_at_result_iter_init(&iter, result);
769
770         if (!g_at_result_iter_next(&iter, "+CTZV:"))
771                 return;
772
773         if (!g_at_result_iter_next_unquoted_string(&iter, &tz))
774                 return;
775
776         DBG("tz %s", tz);
777
778         nd->time.utcoff = atoi(tz) * 15 * 60;
779
780         ofono_netreg_time_notify(netreg, &nd->time);
781 }
782
783 static gboolean notify_time(gpointer user_data)
784 {
785         struct ofono_netreg *netreg = user_data;
786         struct netreg_data *nd = ofono_netreg_get_data(netreg);
787
788         nd->nitz_timeout = 0;
789
790         ofono_netreg_time_notify(netreg, &nd->time);
791
792         return FALSE;
793 }
794
795 static void ifx_ctzv_notify(GAtResult *result, gpointer user_data)
796 {
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;
801         GAtResultIter iter;
802
803         g_at_result_iter_init(&iter, result);
804
805         if (!g_at_result_iter_next(&iter, "+CTZV:"))
806                 return;
807
808         if (!g_at_result_iter_next_unquoted_string(&iter, &tz))
809                 return;
810
811         if (!g_at_result_iter_next_string(&iter, &time))
812                 return;
813
814         DBG("tz %s time %s", tz, time);
815
816         if (sscanf(time, "%u/%u/%u,%u:%u:%u", &year, &mon, &mday,
817                                                 &hour, &min, &sec) != 6)
818                 return;
819
820         nd->time.sec = sec;
821         nd->time.min = min;
822         nd->time.hour = hour;
823         nd->time.mday = mday;
824         nd->time.mon = mon;
825         nd->time.year = 2000 + year;
826
827         if (nd->nitz_timeout > 0)
828                 g_source_remove(nd->nitz_timeout);
829
830         nd->nitz_timeout = g_timeout_add_seconds(1, notify_time, user_data);
831 }
832
833 static void ifx_ctzdst_notify(GAtResult *result, gpointer user_data)
834 {
835         struct ofono_netreg *netreg = user_data;
836         struct netreg_data *nd = ofono_netreg_get_data(netreg);
837         int dst;
838         GAtResultIter iter;
839
840         g_at_result_iter_init(&iter, result);
841
842         if (!g_at_result_iter_next(&iter, "+CTZDST:"))
843                 return;
844
845         if (!g_at_result_iter_next_number(&iter, &dst))
846                 return;
847
848         DBG("dst %d", dst);
849
850         nd->time.dst = dst;
851
852         if (nd->nitz_timeout > 0) {
853                 g_source_remove(nd->nitz_timeout);
854                 nd->nitz_timeout = 0;
855         }
856
857         ofono_netreg_time_notify(netreg, &nd->time);
858 }
859
860 static void cind_cb(gboolean ok, GAtResult *result, gpointer user_data)
861 {
862         struct cb_data *cbd = user_data;
863         ofono_netreg_strength_cb_t cb = cbd->cb;
864         struct netreg_data *nd = cbd->user;
865         int index;
866         int strength;
867         GAtResultIter iter;
868         struct ofono_error error;
869
870         decode_at_error(&error, g_at_result_final_response(result));
871
872         if (!ok) {
873                 cb(&error, -1, cbd->data);
874                 return;
875         }
876
877         g_at_result_iter_init(&iter, result);
878
879         if (!g_at_result_iter_next(&iter, "+CIND:")) {
880                 CALLBACK_WITH_FAILURE(cb, -1, cbd->data);
881                 return;
882         }
883
884         for (index = 1; index < nd->signal_index; index++)
885                 g_at_result_iter_skip_next(&iter);
886
887         g_at_result_iter_next_number(&iter, &strength);
888
889         if (strength == nd->signal_invalid)
890                 strength = -1;
891         else
892                 strength = (strength * 100) / (nd->signal_max - nd->signal_min);
893
894         cb(&error, strength, cbd->data);
895 }
896
897 static void huawei_rssi_notify(GAtResult *result, gpointer user_data)
898 {
899         struct ofono_netreg *netreg = user_data;
900         GAtResultIter iter;
901         int strength;
902
903         g_at_result_iter_init(&iter, result);
904
905         if (!g_at_result_iter_next(&iter, "^RSSI:"))
906                 return;
907
908         if (!g_at_result_iter_next_number(&iter, &strength))
909                 return;
910
911         ofono_netreg_strength_notify(netreg,
912                                 at_util_convert_signal_strength(strength));
913 }
914
915 static void huawei_mode_notify(GAtResult *result, gpointer user_data)
916 {
917         struct ofono_netreg *netreg = user_data;
918         struct netreg_data *nd = ofono_netreg_get_data(netreg);
919         GAtResultIter iter;
920         int mode, submode;
921
922         g_at_result_iter_init(&iter, result);
923
924         if (!g_at_result_iter_next(&iter, "^MODE:"))
925                 return;
926
927         if (!g_at_result_iter_next_number(&iter, &mode))
928                 return;
929
930         if (!g_at_result_iter_next_number(&iter, &submode))
931                 return;
932
933         switch (mode) {
934         case 3:
935                 nd->tech = ACCESS_TECHNOLOGY_GSM;
936                 break;
937         case 5:
938                 nd->tech = ACCESS_TECHNOLOGY_UTRAN;
939                 break;
940         }
941 }
942
943 static void huawei_nwtime_notify(GAtResult *result, gpointer user_data)
944 {
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;
948         char tz[4];
949         const char *date, *time, *dst;
950         GAtResultIter iter;
951
952         g_at_result_iter_init(&iter, result);
953
954         if (!g_at_result_iter_next(&iter, "^NWTIME:"))
955                 return;
956
957         if (!g_at_result_iter_next_unquoted_string(&iter, &date))
958                 return;
959
960         if (!g_at_result_iter_next_unquoted_string(&iter, &time))
961                 return;
962
963         if (!g_at_result_iter_next_unquoted_string(&iter, &dst))
964                 return;
965
966         DBG("date %s time %s dst %s", date, time, dst);
967
968         if (sscanf(date, "%u/%u/%u", &year, &mon, &mday) != 3)
969                 return;
970
971         if (sscanf(time, "%u:%u:%u%s", &hour, &min, &sec, tz) != 4)
972                 return;
973
974         nd->time.utcoff = atoi(tz) * 15 * 60;
975         nd->time.dst = atoi(dst);
976
977         nd->time.sec = sec;
978         nd->time.min = min;
979         nd->time.hour = hour;
980         nd->time.mday = mday;
981         nd->time.mon = mon;
982         nd->time.year = 2000 + year;
983
984         ofono_netreg_time_notify(netreg, &nd->time);
985 }
986
987 static void csq_cb(gboolean ok, GAtResult *result, gpointer user_data)
988 {
989         struct cb_data *cbd = user_data;
990         ofono_netreg_strength_cb_t cb = cbd->cb;
991         int strength;
992         GAtResultIter iter;
993         struct ofono_error error;
994
995         decode_at_error(&error, g_at_result_final_response(result));
996
997         if (!ok) {
998                 cb(&error, -1, cbd->data);
999                 return;
1000         }
1001
1002         g_at_result_iter_init(&iter, result);
1003
1004         if (!g_at_result_iter_next(&iter, "+CSQ:")) {
1005                 CALLBACK_WITH_FAILURE(cb, -1, cbd->data);
1006                 return;
1007         }
1008
1009         g_at_result_iter_next_number(&iter, &strength);
1010
1011         DBG("csq_cb: %d", strength);
1012
1013         if (strength == 99)
1014                 strength = -1;
1015         else
1016                 strength = (strength * 100) / 31;
1017
1018         cb(&error, strength, cbd->data);
1019 }
1020
1021 static void at_signal_strength(struct ofono_netreg *netreg,
1022                                 ofono_netreg_strength_cb_t cb, void *data)
1023 {
1024         struct netreg_data *nd = ofono_netreg_get_data(netreg);
1025         struct cb_data *cbd = cb_data_new(cb, data);
1026
1027         cbd->user = nd;
1028
1029         /*
1030          * If we defaulted to using CIND, then keep using it,
1031          * otherwise fall back to CSQ
1032          */
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)
1036                         return;
1037         } else {
1038                 if (g_at_chat_send(nd->chat, "AT+CSQ", csq_prefix,
1039                                 csq_cb, cbd, g_free) > 0)
1040                         return;
1041         }
1042
1043         g_free(cbd);
1044
1045         CALLBACK_WITH_FAILURE(cb, -1, data);
1046 }
1047
1048 static void mbm_etzv_notify(GAtResult *result, gpointer user_data)
1049 {
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;
1054         GAtResultIter iter;
1055
1056         g_at_result_iter_init(&iter, result);
1057
1058         if (g_at_result_iter_next(&iter, "*ETZV:") == FALSE)
1059                 return;
1060
1061         if (g_at_result_iter_next_string(&iter, &tz) == FALSE)
1062                 return;
1063
1064         if (g_at_result_iter_next_string(&iter, &time) == FALSE)
1065                 time = NULL;
1066
1067         if (g_at_result_iter_next_string(&iter, &timestamp) == FALSE)
1068                 timestamp = NULL;
1069
1070         DBG("tz %s time %s timestamp %s", tz, time, timestamp);
1071
1072         if (time == NULL) {
1073                 year = -1;
1074                 mon = -1;
1075                 mday = -1;
1076                 hour = -1;
1077                 min = -1;
1078                 sec = -1;
1079         } else {
1080                 if (sscanf(time, "%u/%u/%u,%u:%u:%u", &year, &mon, &mday,
1081                                                 &hour, &min, &sec) != 6)
1082                 return;
1083         }
1084
1085         nd->time.utcoff = atoi(tz) * 15 * 60;
1086
1087         nd->time.sec = sec;
1088         nd->time.min = min;
1089         nd->time.hour = hour;
1090         nd->time.mday = mday;
1091         nd->time.mon = mon;
1092         nd->time.year = year;
1093
1094         ofono_netreg_time_notify(netreg, &nd->time);
1095 }
1096
1097 static void mbm_erinfo_notify(GAtResult *result, gpointer user_data)
1098 {
1099         struct ofono_netreg *netreg = user_data;
1100         struct netreg_data *nd = ofono_netreg_get_data(netreg);
1101         GAtResultIter iter;
1102         int mode, gsm, umts;
1103
1104         g_at_result_iter_init(&iter, result);
1105
1106         if (g_at_result_iter_next(&iter, "*ERINFO:") == FALSE)
1107                 return;
1108
1109         if (g_at_result_iter_next_number(&iter, &mode) == FALSE)
1110                 return;
1111
1112         if (g_at_result_iter_next_number(&iter, &gsm) == FALSE)
1113                 return;
1114
1115         /*
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
1119          */
1120         if (g_at_result_iter_next_number(&iter, &umts) == FALSE) {
1121                 gsm = mode;
1122                 umts = gsm;
1123         }
1124
1125         ofono_info("network capability: GSM %d UMTS %d", gsm, umts);
1126
1127         /* Convert to tech values from 27.007 */
1128         switch (gsm) {
1129         case 1: /* GSM */
1130                 nd->tech = ACCESS_TECHNOLOGY_GSM;
1131                 break;
1132         case 2: /* EDGE */
1133                 nd->tech = ACCESS_TECHNOLOGY_GSM_EGPRS;
1134                 break;
1135         default:
1136                 nd->tech = -1;
1137         }
1138
1139         switch (umts) {
1140         case 1: /* UMTS */
1141                 nd->tech = ACCESS_TECHNOLOGY_UTRAN;
1142                 break;
1143         case 2: /* UMTS + HSDPA */
1144                 nd->tech = ACCESS_TECHNOLOGY_UTRAN_HSDPA;
1145                 break;
1146         }
1147 }
1148
1149 static int cnti_to_tech(const char *cnti)
1150 {
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;
1162
1163         return -1;
1164 }
1165
1166 static void gobi_cnti_notify(GAtResult *result, gpointer user_data)
1167 {
1168         struct ofono_netreg *netreg = user_data;
1169         struct netreg_data *nd = ofono_netreg_get_data(netreg);
1170         GAtResultIter iter;
1171         const char *tech;
1172         int option;
1173
1174         g_at_result_iter_init(&iter, result);
1175
1176         if (g_at_result_iter_next(&iter, "*CNTI:") == FALSE)
1177                 return;
1178
1179         if (g_at_result_iter_next_number(&iter, &option) == FALSE)
1180                 return;
1181
1182         if (option != 0)
1183                 return;
1184
1185         if (g_at_result_iter_next_unquoted_string(&iter, &tech) == FALSE)
1186                 return;
1187
1188         nd->tech = cnti_to_tech(tech);
1189 }
1190
1191 static void nw_cnti_notify(GAtResult *result, gpointer user_data)
1192 {
1193         struct ofono_netreg *netreg = user_data;
1194         struct netreg_data *nd = ofono_netreg_get_data(netreg);
1195         GAtResultIter iter;
1196         const char *tech;
1197         int option;
1198
1199         g_at_result_iter_init(&iter, result);
1200
1201         if (g_at_result_iter_next(&iter, "$CNTI:") == FALSE)
1202                 return;
1203
1204         if (g_at_result_iter_next_number(&iter, &option) == FALSE)
1205                 return;
1206
1207         if (option != 0)
1208                 return;
1209
1210         if (g_at_result_iter_next_unquoted_string(&iter, &tech) == FALSE)
1211                 return;
1212
1213         nd->tech = cnti_to_tech(tech);
1214 }
1215
1216 static void cnti_query_tech_cb(gboolean ok, GAtResult *result,
1217                                                 gpointer user_data)
1218 {
1219         struct tech_query *tq = user_data;
1220         struct netreg_data *nd = ofono_netreg_get_data(tq->netreg);
1221
1222         ofono_netreg_status_notify(tq->netreg,
1223                         tq->status, tq->lac, tq->ci, nd->tech);
1224 }
1225
1226 static void zte_query_tech_cb(gboolean ok, GAtResult *result,
1227                                                 gpointer user_data)
1228 {
1229         struct tech_query *tq = user_data;
1230         int tech;
1231
1232         if (ok)
1233                 tech = zte_parse_tech(result);
1234         else
1235                 tech = -1;
1236
1237         ofono_netreg_status_notify(tq->netreg,
1238                         tq->status, tq->lac, tq->ci, tech);
1239 }
1240
1241 static void option_query_tech_cb(gboolean ok, GAtResult *result,
1242                                                 gpointer user_data)
1243 {
1244         struct tech_query *tq = user_data;
1245         int tech;
1246
1247         if (ok)
1248                 tech = option_parse_tech(result);
1249         else
1250                 tech = -1;
1251
1252         ofono_netreg_status_notify(tq->netreg,
1253                         tq->status, tq->lac, tq->ci, tech);
1254 }
1255
1256 static void creg_notify(GAtResult *result, gpointer user_data)
1257 {
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;
1262
1263         if (at_util_parse_reg_unsolicited(result, "+CREG:", &status,
1264                                 &lac, &ci, &tech, nd->vendor) == FALSE)
1265                 return;
1266
1267         if (status != 1 && status != 5)
1268                 goto notify;
1269
1270         tq = g_try_new0(struct tech_query, 1);
1271         if (tq == NULL)
1272                 goto notify;
1273
1274         tq->status = status;
1275         tq->lac = lac;
1276         tq->ci = ci;
1277         tq->netreg = netreg;
1278
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)
1283                         return;
1284                 break;
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)
1288                         return;
1289                 break;
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)
1293                         return;
1294                 break;
1295         case OFONO_VENDOR_OPTION_HSO:
1296                 if (g_at_chat_send(nd->chat, "AT_OCTI?;_OUWCTI?",
1297                                         option_tech_prefix,
1298                                         option_query_tech_cb, tq, g_free) > 0)
1299                         return;
1300                 break;
1301         }
1302
1303         g_free(tq);
1304
1305         if ((status == 1 || status == 5) && tech == -1)
1306                 tech = nd->tech;
1307
1308 notify:
1309         ofono_netreg_status_notify(netreg, status, lac, ci, tech);
1310 }
1311
1312 static void cind_support_cb(gboolean ok, GAtResult *result, gpointer user_data)
1313 {
1314         struct ofono_netreg *netreg = user_data;
1315         struct netreg_data *nd = ofono_netreg_get_data(netreg);
1316         GAtResultIter iter;
1317         const char *str;
1318         char *signal_identifier = "signal";
1319         int index;
1320         int min = 0;
1321         int max = 0;
1322         int tmp_min, tmp_max, invalid;
1323
1324         if (!ok)
1325                 goto error;
1326
1327         g_at_result_iter_init(&iter, result);
1328         if (!g_at_result_iter_next(&iter, "+CIND:"))
1329                 goto error;
1330
1331         index = 1;
1332
1333         /*
1334          * Telit encapsulates the CIND=? tokens with braces
1335          * so we need to skip them
1336          */
1337         if (nd->vendor == OFONO_VENDOR_TELIT) {
1338                 g_at_result_iter_open_list(&iter);
1339                 signal_identifier = "rssi";
1340         }
1341
1342         while (g_at_result_iter_open_list(&iter)) {
1343                 /* Reset invalid default value for every token */
1344                 invalid = 99;
1345
1346                 if (!g_at_result_iter_next_string(&iter, &str))
1347                         goto error;
1348
1349                 if (!g_at_result_iter_open_list(&iter))
1350                         goto error;
1351
1352                 while (g_at_result_iter_next_range(&iter, &tmp_min, &tmp_max)) {
1353                         if (tmp_min != tmp_max) {
1354                                 min = tmp_min;
1355                                 max = tmp_max;
1356                         } else
1357                                 invalid = tmp_min;
1358                 }
1359
1360                 if (!g_at_result_iter_close_list(&iter))
1361                         goto error;
1362
1363                 if (!g_at_result_iter_close_list(&iter))
1364                         goto error;
1365
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;
1371                 }
1372
1373                 index += 1;
1374         }
1375
1376         if (nd->vendor == OFONO_VENDOR_TELIT)
1377                 g_at_result_iter_close_list(&iter);
1378
1379         if (nd->signal_index == 0)
1380                 goto error;
1381
1382         g_at_chat_send(nd->chat, "AT+CMER=3,0,0,1", NULL,
1383                         NULL, NULL, 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);
1388
1389         ofono_netreg_register(netreg);
1390         return;
1391
1392 error:
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");
1396
1397         ofono_netreg_remove(netreg);
1398 }
1399
1400 static void at_creg_set_cb(gboolean ok, GAtResult *result, gpointer user_data)
1401 {
1402         struct ofono_netreg *netreg = user_data;
1403         struct netreg_data *nd = ofono_netreg_get_data(netreg);
1404
1405         if (!ok) {
1406                 ofono_error("Unable to initialize Network Registration");
1407                 ofono_netreg_remove(netreg);
1408                 return;
1409         }
1410
1411         switch (nd->vendor) {
1412         case OFONO_VENDOR_PHONESIM:
1413                 g_at_chat_register(nd->chat, "+CSQ:",
1414                                         csq_notify, FALSE, netreg, NULL);
1415                 break;
1416         case OFONO_VENDOR_CALYPSO:
1417                 g_at_chat_send(nd->chat, "AT%CSQ=1", none_prefix,
1418                                 NULL, NULL, NULL);
1419                 g_at_chat_register(nd->chat, "%CSQ:", calypso_csq_notify,
1420                                         FALSE, netreg, NULL);
1421                 break;
1422         case OFONO_VENDOR_OPTION_HSO:
1423                 g_at_chat_send(nd->chat, "AT_OSSYS=1", none_prefix,
1424                                 NULL, NULL, NULL);
1425                 g_at_chat_send(nd->chat, "AT_OSQI=1", none_prefix,
1426                                 NULL, NULL, NULL);
1427                 g_at_chat_register(nd->chat, "_OSIGQ:", option_osigq_notify,
1428                                         FALSE, netreg, NULL);
1429
1430                 g_at_chat_send(nd->chat, "AT_OSSYS?", none_prefix,
1431                                 NULL, NULL, NULL);
1432                 g_at_chat_send(nd->chat, "AT_OSQI?", none_prefix,
1433                                 NULL, NULL, NULL);
1434
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,
1439                                                 NULL, NULL, NULL);
1440                 break;
1441         case OFONO_VENDOR_MBM:
1442                 /* Enable network registration updates */
1443                 g_at_chat_send(nd->chat, "AT*E2REG=1", none_prefix,
1444                                                 NULL, NULL, NULL);
1445                 g_at_chat_send(nd->chat, "AT*EREG=2", none_prefix,
1446                                                 NULL, NULL, NULL);
1447                 g_at_chat_send(nd->chat, "AT*EPSB=1", none_prefix,
1448                                                 NULL, NULL, NULL);
1449
1450                 /* Register for network technology updates */
1451                 g_at_chat_send(nd->chat, "AT*ERINFO=1", none_prefix,
1452                                                 NULL, NULL, NULL);
1453                 g_at_chat_register(nd->chat, "*ERINFO:", mbm_erinfo_notify,
1454                                                 FALSE, netreg, NULL);
1455
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,
1460                                                 NULL, NULL, NULL);
1461
1462                 g_at_chat_send(nd->chat, "AT+CIND=?", cind_prefix,
1463                                         cind_support_cb, netreg, NULL);
1464                 return;
1465         case OFONO_VENDOR_GOBI:
1466                 /*
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.
1470                  */
1471                 g_at_chat_register(nd->chat, "*CNTI:", gobi_cnti_notify,
1472                                         FALSE, netreg, NULL);
1473                 break;
1474         case OFONO_VENDOR_NOVATEL:
1475                 /*
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.
1479                  */
1480                 g_at_chat_register(nd->chat, "$CNTI:", nw_cnti_notify,
1481                                         FALSE, netreg, NULL);
1482                 break;
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);
1487
1488                 /* Register for system mode reports */
1489                 g_at_chat_register(nd->chat, "^MODE:", huawei_mode_notify,
1490                                                 FALSE, netreg, NULL);
1491
1492                 /* Register for network time reports */
1493                 g_at_chat_register(nd->chat, "^NWTIME:", huawei_nwtime_notify,
1494                                                 FALSE, netreg, NULL);
1495                 break;
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,
1503                                                 NULL, NULL, NULL);
1504                 g_at_chat_send(nd->chat, "AT+XMER=1", none_prefix,
1505                                                 NULL, NULL, NULL);
1506
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,
1511                                                 NULL, NULL, NULL);
1512
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,
1519                                                 NULL, NULL, NULL);
1520                 break;
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,
1526                                                 NULL, NULL, NULL);
1527                 break;
1528         case OFONO_VENDOR_NOKIA:
1529         case OFONO_VENDOR_SAMSUNG:
1530         case OFONO_VENDOR_SIMCOM:
1531                 /* Signal strength reporting via CIND is not supported */
1532                 break;
1533         default:
1534                 g_at_chat_send(nd->chat, "AT+CIND=?", cind_prefix,
1535                                 cind_support_cb, netreg, NULL);
1536                 return;
1537         }
1538
1539         g_at_chat_register(nd->chat, "+CREG:",
1540                                 creg_notify, FALSE, netreg, NULL);
1541         ofono_netreg_register(netreg);
1542 }
1543
1544 static void at_creg_test_cb(gboolean ok, GAtResult *result, gpointer user_data)
1545 {
1546         struct ofono_netreg *netreg = user_data;
1547         struct netreg_data *nd = ofono_netreg_get_data(netreg);
1548         gint range[2];
1549         GAtResultIter iter;
1550         int creg1 = 0;
1551         int creg2 = 0;
1552
1553         if (!ok)
1554                 goto error;
1555
1556         g_at_result_iter_init(&iter, result);
1557
1558         if (!g_at_result_iter_next(&iter, "+CREG:"))
1559                 goto error;
1560
1561         if (!g_at_result_iter_open_list(&iter))
1562                 goto error;
1563
1564         while (g_at_result_iter_next_range(&iter, &range[0], &range[1])) {
1565                 if (1 >= range[0] && 1 <= range[1])
1566                         creg1 = 1;
1567                 if (2 >= range[0] && 2 <= range[1])
1568                         creg2 = 1;
1569         }
1570
1571         g_at_result_iter_close_list(&iter);
1572
1573         if (creg2) {
1574                 g_at_chat_send(nd->chat, "AT+CREG=2", none_prefix,
1575                                 at_creg_set_cb, netreg, NULL);
1576                 return;
1577         }
1578
1579         if (creg1) {
1580                 g_at_chat_send(nd->chat, "AT+CREG=1", none_prefix,
1581                                 at_creg_set_cb, netreg, NULL);
1582                 return;
1583         }
1584
1585 error:
1586         ofono_error("Unable to initialize Network Registration");
1587         ofono_netreg_remove(netreg);
1588 }
1589
1590 static int at_netreg_probe(struct ofono_netreg *netreg, unsigned int vendor,
1591                                 void *data)
1592 {
1593         GAtChat *chat = data;
1594         struct netreg_data *nd;
1595
1596         nd = g_new0(struct netreg_data, 1);
1597
1598         nd->chat = g_at_chat_clone(chat);
1599         nd->vendor = vendor;
1600         nd->tech = -1;
1601         nd->time.sec = -1;
1602         nd->time.min = -1;
1603         nd->time.hour = -1;
1604         nd->time.mday = -1;
1605         nd->time.mon = -1;
1606         nd->time.year = -1;
1607         nd->time.dst = 0;
1608         nd->time.utcoff = 0;
1609         ofono_netreg_set_data(netreg, nd);
1610
1611         g_at_chat_send(nd->chat, "AT+CREG=?", creg_prefix,
1612                         at_creg_test_cb, netreg, NULL);
1613
1614         return 0;
1615 }
1616
1617 static void at_netreg_remove(struct ofono_netreg *netreg)
1618 {
1619         struct netreg_data *nd = ofono_netreg_get_data(netreg);
1620
1621         if (nd->nitz_timeout)
1622                 g_source_remove(nd->nitz_timeout);
1623
1624         ofono_netreg_set_data(netreg, NULL);
1625
1626         g_at_chat_unref(nd->chat);
1627         g_free(nd);
1628 }
1629
1630 static struct ofono_netreg_driver driver = {
1631         .name                           = "atmodem",
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,
1640 };
1641
1642 void at_netreg_init(void)
1643 {
1644         ofono_netreg_driver_register(&driver);
1645 }
1646
1647 void at_netreg_exit(void)
1648 {
1649         ofono_netreg_driver_unregister(&driver);
1650 }