netreg: Don't register for CIEV twice
[platform/upstream/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 *cmer_prefix[] = { "+CMER:", NULL };
51 static const char *zpas_prefix[] = { "+ZPAS:", NULL };
52 static const char *option_tech_prefix[] = { "_OCTI:", "_OUWCTI:", NULL };
53
54 struct netreg_data {
55         GAtChat *chat;
56         char mcc[OFONO_MAX_MCC_LENGTH + 1];
57         char mnc[OFONO_MAX_MNC_LENGTH + 1];
58         int signal_index; /* If strength is reported via CIND */
59         int signal_min; /* min strength reported via CIND */
60         int signal_max; /* max strength reported via CIND */
61         int signal_invalid; /* invalid strength reported via CIND */
62         int tech;
63         struct ofono_network_time time;
64         guint nitz_timeout;
65         unsigned int vendor;
66 };
67
68 struct tech_query {
69         int status;
70         int lac;
71         int ci;
72         struct ofono_netreg *netreg;
73 };
74
75 static void extract_mcc_mnc(const char *str, char *mcc, char *mnc)
76 {
77         /* Three digit country code */
78         strncpy(mcc, str, OFONO_MAX_MCC_LENGTH);
79         mcc[OFONO_MAX_MCC_LENGTH] = '\0';
80
81         /* Usually a 2 but sometimes 3 digit network code */
82         strncpy(mnc, str + OFONO_MAX_MCC_LENGTH, OFONO_MAX_MNC_LENGTH);
83         mnc[OFONO_MAX_MNC_LENGTH] = '\0';
84 }
85
86 static int zte_parse_tech(GAtResult *result)
87 {
88         GAtResultIter iter;
89         const char *network, *domain;
90         int tech;
91
92         g_at_result_iter_init(&iter, result);
93
94         if (!g_at_result_iter_next(&iter, "+ZPAS:"))
95                 return -1;
96
97         if (!g_at_result_iter_next_string(&iter, &network))
98                 return -1;
99
100         if (!g_at_result_iter_next_string(&iter, &domain))
101                 return -1;
102
103         if (g_str_equal(network, "GSM") == TRUE ||
104                         g_str_equal(network, "GPRS") == TRUE)
105                 tech = ACCESS_TECHNOLOGY_GSM;
106         else if (g_str_equal(network, "EDGE") == TRUE)
107                 tech = ACCESS_TECHNOLOGY_GSM_EGPRS;
108         else if (g_str_equal(network, "UMTS") == TRUE)
109                 tech = ACCESS_TECHNOLOGY_UTRAN;
110         else if (g_str_equal(network, "HSDPA") == TRUE)
111                 tech = ACCESS_TECHNOLOGY_UTRAN_HSDPA;
112         else
113                 tech = -1;
114
115         DBG("network %s domain %s tech %d", network, domain, tech);
116
117         return tech;
118 }
119
120 static int option_parse_tech(GAtResult *result)
121 {
122         GAtResultIter iter;
123         int s, octi, ouwcti;
124         int tech;
125
126         g_at_result_iter_init(&iter, result);
127
128         if (!g_at_result_iter_next(&iter, "_OCTI:"))
129                 return -1;
130
131         if (!g_at_result_iter_next_number(&iter, &s))
132                 return -1;
133
134         if (!g_at_result_iter_next_number(&iter, &octi))
135                 return -1;
136
137         if (!g_at_result_iter_next(&iter, "_OUWCTI:"))
138                 return -1;
139
140         if (!g_at_result_iter_next_number(&iter, &s))
141                 return -1;
142
143         if (!g_at_result_iter_next_number(&iter, &ouwcti))
144                 return -1;
145
146         switch (octi) {
147         case 1: /* GSM */
148                 tech = ACCESS_TECHNOLOGY_GSM;
149                 break;
150         case 2: /* GPRS */
151                 tech = ACCESS_TECHNOLOGY_GSM;
152                 break;
153         case 3: /* EDGE */
154                 tech = ACCESS_TECHNOLOGY_GSM_EGPRS;
155                 break;
156         default:
157                 tech = -1;
158                 break;
159         }
160
161         switch (ouwcti) {
162         case 1: /* UMTS */
163                 tech = ACCESS_TECHNOLOGY_UTRAN;
164                 break;
165         case 2: /* HSDPA */
166                 tech = ACCESS_TECHNOLOGY_UTRAN_HSDPA;
167                 break;
168         case 3: /* HSUPA */
169                 tech = ACCESS_TECHNOLOGY_UTRAN_HSUPA;
170                 break;
171         case 4: /* HSPA */
172                 tech = ACCESS_TECHNOLOGY_UTRAN_HSDPA_HSUPA;
173                 break;
174         }
175
176         DBG("octi %d ouwcti %d tech %d", octi, ouwcti, tech);
177
178         return tech;
179 }
180
181 static void at_creg_cb(gboolean ok, GAtResult *result, gpointer user_data)
182 {
183         struct cb_data *cbd = user_data;
184         ofono_netreg_status_cb_t cb = cbd->cb;
185         int status, lac, ci, tech;
186         struct ofono_error error;
187         struct netreg_data *nd = cbd->user;
188
189         decode_at_error(&error, g_at_result_final_response(result));
190
191         if (!ok) {
192                 cb(&error, -1, -1, -1, -1, cbd->data);
193                 return;
194         }
195
196         if (at_util_parse_reg(result, "+CREG:", NULL, &status,
197                                 &lac, &ci, &tech, nd->vendor) == FALSE) {
198                 CALLBACK_WITH_FAILURE(cb, -1, -1, -1, -1, cbd->data);
199                 return;
200         }
201
202         if ((status == 1 || status == 5) && (tech == -1))
203                 tech = nd->tech;
204
205         cb(&error, status, lac, ci, tech, cbd->data);
206 }
207
208 static void zte_tech_cb(gboolean ok, GAtResult *result, gpointer user_data)
209 {
210         struct cb_data *cbd = user_data;
211         struct ofono_netreg *netreg = cbd->data;
212         struct netreg_data *nd = ofono_netreg_get_data(netreg);
213
214         if (ok)
215                 nd->tech = zte_parse_tech(result);
216         else
217                 nd->tech = -1;
218 }
219
220 static void option_tech_cb(gboolean ok, GAtResult *result, gpointer user_data)
221 {
222         struct cb_data *cbd = user_data;
223         struct ofono_netreg *netreg = cbd->data;
224         struct netreg_data *nd = ofono_netreg_get_data(netreg);
225
226         if (ok)
227                 nd->tech = option_parse_tech(result);
228         else
229                 nd->tech = -1;
230 }
231
232 static void at_registration_status(struct ofono_netreg *netreg,
233                                         ofono_netreg_status_cb_t cb,
234                                         void *data)
235 {
236         struct netreg_data *nd = ofono_netreg_get_data(netreg);
237         struct cb_data *cbd = cb_data_new(cb, data);
238
239         cbd->user = nd;
240
241         switch (nd->vendor) {
242         case OFONO_VENDOR_MBM:
243                 /*
244                  * Send *ERINFO to find out the current tech, it will be
245                  * intercepted in mbm_erinfo_notify
246                  */
247                 g_at_chat_send(nd->chat, "AT*ERINFO?", none_prefix,
248                                 NULL, NULL, NULL);
249                 break;
250         case OFONO_VENDOR_GOBI:
251                 /*
252                  * Send *CNTI=0 to find out the current tech, it will be
253                  * intercepted in gobi_cnti_notify
254                  */
255                 g_at_chat_send(nd->chat, "AT*CNTI=0", none_prefix,
256                                 NULL, NULL, NULL);
257                 break;
258         case OFONO_VENDOR_NOVATEL:
259                 /*
260                  * Send $CNTI=0 to find out the current tech, it will be
261                  * intercepted in nw_cnti_notify
262                  */
263                 g_at_chat_send(nd->chat, "AT$CNTI=0", none_prefix,
264                                 NULL, NULL, NULL);
265                 break;
266         case OFONO_VENDOR_ZTE:
267                 /*
268                  * Send +ZPAS? to find out the current tech, zte_tech_cb
269                  * will call, fire CREG? to do the rest.
270                  */
271                 if (g_at_chat_send(nd->chat, "AT+ZPAS?", zpas_prefix,
272                                         zte_tech_cb, cbd, NULL) == 0)
273                         nd->tech = -1;
274                 break;
275         case OFONO_VENDOR_OPTION_HSO:
276                 /*
277                  * Send AT_OCTI?;_OUWCTI? to find out the current tech,
278                  * option_tech_cb will call, fire CREG? to do the rest.
279                  */
280                 if (g_at_chat_send(nd->chat, "AT_OCTI?;_OUWCTI?",
281                                         option_tech_prefix,
282                                         option_tech_cb, cbd, NULL) == 0)
283                         nd->tech = -1;
284                 break;
285         }
286
287         if (g_at_chat_send(nd->chat, "AT+CREG?", creg_prefix,
288                                 at_creg_cb, cbd, g_free) > 0)
289                 return;
290
291         g_free(cbd);
292
293         CALLBACK_WITH_FAILURE(cb, -1, -1, -1, -1, data);
294 }
295
296 static void cops_cb(gboolean ok, GAtResult *result, gpointer user_data)
297 {
298         struct cb_data *cbd = user_data;
299         struct netreg_data *nd = ofono_netreg_get_data(cbd->user);
300         ofono_netreg_operator_cb_t cb = cbd->cb;
301         struct ofono_network_operator op;
302         GAtResultIter iter;
303         int format, tech;
304         const char *name;
305         struct ofono_error error;
306
307         decode_at_error(&error, g_at_result_final_response(result));
308
309         if (!ok)
310                 goto error;
311
312         g_at_result_iter_init(&iter, result);
313
314         if (!g_at_result_iter_next(&iter, "+COPS:"))
315                 goto error;
316
317         g_at_result_iter_skip_next(&iter);
318
319         ok = g_at_result_iter_next_number(&iter, &format);
320
321         if (ok == FALSE || format != 0)
322                 goto error;
323
324         if (g_at_result_iter_next_string(&iter, &name) == FALSE)
325                 goto error;
326
327         /* Default to GSM */
328         if (g_at_result_iter_next_number(&iter, &tech) == FALSE)
329                 tech = ACCESS_TECHNOLOGY_GSM;
330
331         strncpy(op.name, name, OFONO_MAX_OPERATOR_NAME_LENGTH);
332         op.name[OFONO_MAX_OPERATOR_NAME_LENGTH] = '\0';
333
334         strncpy(op.mcc, nd->mcc, OFONO_MAX_MCC_LENGTH);
335         op.mcc[OFONO_MAX_MCC_LENGTH] = '\0';
336
337         strncpy(op.mnc, nd->mnc, OFONO_MAX_MNC_LENGTH);
338         op.mnc[OFONO_MAX_MNC_LENGTH] = '\0';
339
340         /* Set to current */
341         op.status = 2;
342         op.tech = tech;
343
344         DBG("cops_cb: %s, %s %s %d", name, nd->mcc, nd->mnc, tech);
345
346         cb(&error, &op, cbd->data);
347         g_free(cbd);
348
349         return;
350
351 error:
352         cb(&error, NULL, cbd->data);
353
354         g_free(cbd);
355 }
356
357 static void cops_numeric_cb(gboolean ok, GAtResult *result, gpointer user_data)
358 {
359         struct cb_data *cbd = user_data;
360         struct netreg_data *nd = ofono_netreg_get_data(cbd->user);
361         ofono_netreg_operator_cb_t cb = cbd->cb;
362         GAtResultIter iter;
363         const char *str;
364         int format;
365         int len;
366         struct ofono_error error;
367
368         decode_at_error(&error, g_at_result_final_response(result));
369
370         if (!ok)
371                 goto error;
372
373         g_at_result_iter_init(&iter, result);
374
375         if (!g_at_result_iter_next(&iter, "+COPS:"))
376                 goto error;
377
378         g_at_result_iter_skip_next(&iter);
379
380         ok = g_at_result_iter_next_number(&iter, &format);
381
382         if (ok == FALSE || format != 2)
383                 goto error;
384
385         if (g_at_result_iter_next_string(&iter, &str) == FALSE)
386                 goto error;
387
388         len = strspn(str, "0123456789");
389
390         if (len != 5 && len != 6)
391                 goto error;
392
393         extract_mcc_mnc(str, nd->mcc, nd->mnc);
394
395         DBG("Cops numeric got mcc: %s, mnc: %s", nd->mcc, nd->mnc);
396
397         ok = g_at_chat_send(nd->chat, "AT+COPS=3,0", none_prefix,
398                                         NULL, NULL, NULL);
399
400         if (ok)
401                 ok = g_at_chat_send(nd->chat, "AT+COPS?", cops_prefix,
402                                         cops_cb, cbd, NULL);
403
404         if (ok)
405                 return;
406
407 error:
408         cb(&error, NULL, cbd->data);
409         g_free(cbd);
410 }
411
412 static void at_current_operator(struct ofono_netreg *netreg,
413                                 ofono_netreg_operator_cb_t cb, void *data)
414 {
415         struct netreg_data *nd = ofono_netreg_get_data(netreg);
416         struct cb_data *cbd = cb_data_new(cb, data);
417         gboolean ok;
418
419         cbd->user = netreg;
420
421         /* Nokia modems have a broken return value for the string
422          * returned for the numeric value. It misses a " at the end.
423          * Trying to read this will stall the parser. So skip it. */
424         if (nd->vendor == OFONO_VENDOR_NOKIA) {
425                 ok = g_at_chat_send(nd->chat, "AT+COPS=3,0", none_prefix,
426                                                         NULL, NULL, NULL);
427
428                 if (ok)
429                         ok = g_at_chat_send(nd->chat, "AT+COPS?", cops_prefix,
430                                                         cops_cb, cbd, NULL);
431         } else {
432                 ok = g_at_chat_send(nd->chat, "AT+COPS=3,2", none_prefix,
433                                                         NULL, NULL, NULL);
434
435                 if (ok)
436                         ok = g_at_chat_send(nd->chat, "AT+COPS?", cops_prefix,
437                                                 cops_numeric_cb, cbd, NULL);
438         }
439
440         if (ok)
441                 return;
442
443         g_free(cbd);
444
445         CALLBACK_WITH_FAILURE(cb, NULL, data);
446 }
447
448 static void cops_list_cb(gboolean ok, GAtResult *result, gpointer user_data)
449 {
450         struct cb_data *cbd = user_data;
451         ofono_netreg_operator_list_cb_t cb = cbd->cb;
452         struct ofono_network_operator *list;
453         GAtResultIter iter;
454         int num = 0;
455         struct ofono_error error;
456
457         decode_at_error(&error, g_at_result_final_response(result));
458
459         if (!ok) {
460                 cb(&error, 0, NULL, cbd->data);
461                 return;
462         }
463
464         g_at_result_iter_init(&iter, result);
465
466         while (g_at_result_iter_next(&iter, "+COPS:")) {
467                 while (g_at_result_iter_skip_next(&iter))
468                         num += 1;
469         }
470
471         DBG("Got %d elements", num);
472
473         list = g_try_new0(struct ofono_network_operator, num);
474         if (list == NULL) {
475                 CALLBACK_WITH_FAILURE(cb, 0, NULL, cbd->data);
476                 return;
477         }
478
479         num = 0;
480         g_at_result_iter_init(&iter, result);
481
482         while (g_at_result_iter_next(&iter, "+COPS:")) {
483                 int status, tech, plmn;
484                 const char *l, *s, *n;
485                 gboolean have_long = FALSE;
486
487                 while (1) {
488                         if (!g_at_result_iter_open_list(&iter))
489                                 break;
490
491                         if (!g_at_result_iter_next_number(&iter, &status))
492                                 break;
493
494                         list[num].status = status;
495
496                         if (!g_at_result_iter_next_string(&iter, &l))
497                                 break;
498
499                         if (strlen(l) > 0) {
500                                 have_long = TRUE;
501                                 strncpy(list[num].name, l,
502                                         OFONO_MAX_OPERATOR_NAME_LENGTH);
503                         }
504
505                         if (!g_at_result_iter_next_string(&iter, &s))
506                                 break;
507
508                         if (strlen(s) > 0 && !have_long)
509                                 strncpy(list[num].name, s,
510                                         OFONO_MAX_OPERATOR_NAME_LENGTH);
511
512                         list[num].name[OFONO_MAX_OPERATOR_NAME_LENGTH] = '\0';
513
514                         if (!g_at_result_iter_next_string(&iter, &n))
515                                 break;
516
517                         extract_mcc_mnc(n, list[num].mcc, list[num].mnc);
518
519                         if (!g_at_result_iter_next_number(&iter, &tech))
520                                 tech = ACCESS_TECHNOLOGY_GSM;
521
522                         list[num].tech = tech;
523
524                         if (!g_at_result_iter_next_number(&iter, &plmn))
525                                 plmn = 0;
526
527                         if (!g_at_result_iter_close_list(&iter))
528                                 break;
529
530                         num += 1;
531                 }
532         }
533
534         DBG("Got %d operators", num);
535
536 {
537         int i = 0;
538
539         for (; i < num; i++) {
540                 DBG("Operator: %s, %s, %s, status: %d, %d",
541                         list[i].name, list[i].mcc, list[i].mnc,
542                         list[i].status, list[i].tech);
543         }
544 }
545
546         cb(&error, num, list, cbd->data);
547
548         g_free(list);
549 }
550
551 static void at_list_operators(struct ofono_netreg *netreg,
552                                 ofono_netreg_operator_list_cb_t cb, void *data)
553 {
554         struct netreg_data *nd = ofono_netreg_get_data(netreg);
555         struct cb_data *cbd = cb_data_new(cb, data);
556
557         if (g_at_chat_send(nd->chat, "AT+COPS=?", cops_prefix,
558                                 cops_list_cb, cbd, g_free) > 0)
559                 return;
560
561         g_free(cbd);
562
563         CALLBACK_WITH_FAILURE(cb, 0, NULL, data);
564 }
565
566 static void register_cb(gboolean ok, GAtResult *result, gpointer user_data)
567 {
568         struct cb_data *cbd = user_data;
569         ofono_netreg_register_cb_t cb = cbd->cb;
570         struct ofono_error error;
571
572         decode_at_error(&error, g_at_result_final_response(result));
573
574         cb(&error, cbd->data);
575 }
576
577 static void at_register_auto(struct ofono_netreg *netreg,
578                                 ofono_netreg_register_cb_t cb, void *data)
579 {
580         struct netreg_data *nd = ofono_netreg_get_data(netreg);
581         struct cb_data *cbd = cb_data_new(cb, data);
582
583         if (g_at_chat_send(nd->chat, "AT+COPS=0", none_prefix,
584                                 register_cb, cbd, g_free) > 0)
585                 return;
586
587         g_free(cbd);
588
589         CALLBACK_WITH_FAILURE(cb, data);
590 }
591
592 static void at_register_manual(struct ofono_netreg *netreg,
593                                 const char *mcc, const char *mnc,
594                                 ofono_netreg_register_cb_t cb, void *data)
595 {
596         struct netreg_data *nd = ofono_netreg_get_data(netreg);
597         struct cb_data *cbd = cb_data_new(cb, data);
598         char buf[128];
599
600         snprintf(buf, sizeof(buf), "AT+COPS=1,2,\"%s%s\"", mcc, mnc);
601
602         if (g_at_chat_send(nd->chat, buf, none_prefix,
603                                 register_cb, cbd, g_free) > 0)
604                 return;
605
606         g_free(cbd);
607
608         CALLBACK_WITH_FAILURE(cb, data);
609 }
610
611 static void csq_notify(GAtResult *result, gpointer user_data)
612 {
613         struct ofono_netreg *netreg = user_data;
614         int strength;
615         GAtResultIter iter;
616
617         g_at_result_iter_init(&iter, result);
618
619         if (!g_at_result_iter_next(&iter, "+CSQ:"))
620                 return;
621
622         if (!g_at_result_iter_next_number(&iter, &strength))
623                 return;
624
625         ofono_netreg_strength_notify(netreg,
626                                 at_util_convert_signal_strength(strength));
627 }
628
629 static void calypso_csq_notify(GAtResult *result, gpointer user_data)
630 {
631         struct ofono_netreg *netreg = user_data;
632         int strength;
633         GAtResultIter iter;
634
635         g_at_result_iter_init(&iter, result);
636
637         if (!g_at_result_iter_next(&iter, "%CSQ:"))
638                 return;
639
640         if (!g_at_result_iter_next_number(&iter, &strength))
641                 return;
642
643         ofono_netreg_strength_notify(netreg,
644                                 at_util_convert_signal_strength(strength));
645 }
646
647 static void option_osigq_notify(GAtResult *result, gpointer user_data)
648 {
649         struct ofono_netreg *netreg = user_data;
650         int strength;
651         GAtResultIter iter;
652
653         g_at_result_iter_init(&iter, result);
654
655         if (!g_at_result_iter_next(&iter, "_OSIGQ:"))
656                 return;
657
658         if (!g_at_result_iter_next_number(&iter, &strength))
659                 return;
660
661         ofono_netreg_strength_notify(netreg,
662                                 at_util_convert_signal_strength(strength));
663 }
664
665 static void ifx_xhomezr_notify(GAtResult *result, gpointer user_data)
666 {
667         //struct ofono_netreg *netreg = user_data;
668         const char *label;
669         GAtResultIter iter;
670
671         g_at_result_iter_init(&iter, result);
672
673         if (!g_at_result_iter_next(&iter, "+XHOMEZR:"))
674                 return;
675
676         if (!g_at_result_iter_next_string(&iter, &label))
677                 return;
678
679         ofono_info("Home zone: %s", label);
680 }
681
682 static void ifx_xciev_notify(GAtResult *result, gpointer user_data)
683 {
684         //struct ofono_netreg *netreg = user_data;
685         int ind;
686         GAtResultIter iter;
687
688         g_at_result_iter_init(&iter, result);
689
690         if (!g_at_result_iter_next(&iter, "+XCIEV:"))
691                 return;
692
693         if (!g_at_result_iter_next_number(&iter, &ind))
694                 return;
695
696         DBG("ind %d", ind);
697
698         /*
699          * Radio signal strength indicators are defined for 0-7,
700          * but this notification seems to return CSQ 0-31,99 values.
701          *
702          * Ignore this indication for now since it can not be trusted.
703          */
704 }
705
706 static void ifx_xcsq_notify(GAtResult *result, gpointer user_data)
707 {
708         struct ofono_netreg *netreg = user_data;
709         int rssi, ber, strength;
710         GAtResultIter iter;
711
712         g_at_result_iter_init(&iter, result);
713
714         if (!g_at_result_iter_next(&iter, "+XCSQ:"))
715                 return;
716
717         if (!g_at_result_iter_next_number(&iter, &rssi))
718                 return;
719
720         if (!g_at_result_iter_next_number(&iter, &ber))
721                 return;
722
723         DBG("rssi %d ber %d", rssi, ber);
724
725         if (rssi == 99)
726                 strength = -1;
727         else
728                 strength = (rssi * 100) / 31;
729
730         ofono_netreg_strength_notify(netreg, strength);
731 }
732
733 static void ciev_notify(GAtResult *result, gpointer user_data)
734 {
735         struct ofono_netreg *netreg = user_data;
736         struct netreg_data *nd = ofono_netreg_get_data(netreg);
737         int strength, ind;
738         GAtResultIter iter;
739
740         g_at_result_iter_init(&iter, result);
741
742         if (!g_at_result_iter_next(&iter, "+CIEV:"))
743                 return;
744
745         if (!g_at_result_iter_next_number(&iter, &ind))
746                 return;
747
748         if (ind != nd->signal_index)
749                 return;
750
751         if (!g_at_result_iter_next_number(&iter, &strength))
752                 return;
753
754         if (strength == nd->signal_invalid)
755                 strength = -1;
756         else
757                 strength = (strength * 100) / (nd->signal_max - nd->signal_min);
758
759         ofono_netreg_strength_notify(netreg, strength);
760 }
761
762 static void telit_ciev_notify(GAtResult *result, gpointer user_data)
763 {
764         struct ofono_netreg *netreg = user_data;
765         struct netreg_data *nd = ofono_netreg_get_data(netreg);
766         const char *signal_identifier = "rssi";
767         const char *ind_str;
768         int strength;
769         GAtResultIter iter;
770
771         g_at_result_iter_init(&iter, result);
772
773         if (!g_at_result_iter_next(&iter, "+CIEV:"))
774                 return;
775
776         if (!g_at_result_iter_next_unquoted_string(&iter, &ind_str))
777                 return;
778
779         if (!g_str_equal(signal_identifier, ind_str))
780                 return;
781
782         if (!g_at_result_iter_next_number(&iter, &strength))
783                 return;
784
785         if (strength == nd->signal_invalid)
786                 strength = -1;
787         else
788                 strength = (strength * 100) / (nd->signal_max - nd->signal_min);
789
790         ofono_netreg_strength_notify(netreg, strength);
791 }
792
793 static void ctzv_notify(GAtResult *result, gpointer user_data)
794 {
795         struct ofono_netreg *netreg = user_data;
796         struct netreg_data *nd = ofono_netreg_get_data(netreg);
797         const char *tz;
798         GAtResultIter iter;
799
800         g_at_result_iter_init(&iter, result);
801
802         if (!g_at_result_iter_next(&iter, "+CTZV:"))
803                 return;
804
805         if (!g_at_result_iter_next_unquoted_string(&iter, &tz))
806                 return;
807
808         DBG("tz %s", tz);
809
810         nd->time.utcoff = atoi(tz) * 15 * 60;
811
812         ofono_netreg_time_notify(netreg, &nd->time);
813 }
814
815 static void tlts_notify(GAtResult *result, gpointer user_data)
816 {
817         struct ofono_netreg *netreg = user_data;
818         struct netreg_data *nd = ofono_netreg_get_data(netreg);
819         int year, mon, mday, hour, min, sec;
820         char tz[4];
821         const char *time;
822         GAtResultIter iter;
823
824         g_at_result_iter_init(&iter, result);
825
826         if (!g_at_result_iter_next(&iter, "*TLTS:"))
827                 return;
828
829         if (!g_at_result_iter_next_string(&iter, &time))
830                 return;
831
832         DBG("time %s", time);
833
834         if (sscanf(time, "%02u/%02u/%02u,%02u:%02u:%02u%s", &year, &mon, &mday,
835                                                 &hour, &min, &sec, tz) != 7)
836                 return;
837
838         nd->time.sec = sec;
839         nd->time.min = min;
840         nd->time.hour = hour;
841         nd->time.mday = mday;
842         nd->time.mon = mon;
843         nd->time.year = 2000 + year;
844
845         nd->time.utcoff = atoi(tz) * 15 * 60;
846
847         ofono_netreg_time_notify(netreg, &nd->time);
848 }
849
850 static gboolean notify_time(gpointer user_data)
851 {
852         struct ofono_netreg *netreg = user_data;
853         struct netreg_data *nd = ofono_netreg_get_data(netreg);
854
855         nd->nitz_timeout = 0;
856
857         ofono_netreg_time_notify(netreg, &nd->time);
858
859         return FALSE;
860 }
861
862 static void ifx_ctzv_notify(GAtResult *result, gpointer user_data)
863 {
864         struct ofono_netreg *netreg = user_data;
865         struct netreg_data *nd = ofono_netreg_get_data(netreg);
866         int year, mon, mday, hour, min, sec;
867         const char *tz, *time;
868         GAtResultIter iter;
869
870         g_at_result_iter_init(&iter, result);
871
872         if (!g_at_result_iter_next(&iter, "+CTZV:"))
873                 return;
874
875         if (!g_at_result_iter_next_unquoted_string(&iter, &tz))
876                 return;
877
878         if (!g_at_result_iter_next_string(&iter, &time))
879                 return;
880
881         DBG("tz %s time %s", tz, time);
882
883         if (sscanf(time, "%u/%u/%u,%u:%u:%u", &year, &mon, &mday,
884                                                 &hour, &min, &sec) != 6)
885                 return;
886
887         nd->time.sec = sec;
888         nd->time.min = min;
889         nd->time.hour = hour;
890         nd->time.mday = mday;
891         nd->time.mon = mon;
892         nd->time.year = 2000 + year;
893
894         if (nd->nitz_timeout > 0)
895                 g_source_remove(nd->nitz_timeout);
896
897         nd->nitz_timeout = g_timeout_add_seconds(1, notify_time, user_data);
898 }
899
900 static void ifx_ctzdst_notify(GAtResult *result, gpointer user_data)
901 {
902         struct ofono_netreg *netreg = user_data;
903         struct netreg_data *nd = ofono_netreg_get_data(netreg);
904         int dst;
905         GAtResultIter iter;
906
907         g_at_result_iter_init(&iter, result);
908
909         if (!g_at_result_iter_next(&iter, "+CTZDST:"))
910                 return;
911
912         if (!g_at_result_iter_next_number(&iter, &dst))
913                 return;
914
915         DBG("dst %d", dst);
916
917         nd->time.dst = dst;
918
919         if (nd->nitz_timeout > 0) {
920                 g_source_remove(nd->nitz_timeout);
921                 nd->nitz_timeout = 0;
922         }
923
924         ofono_netreg_time_notify(netreg, &nd->time);
925 }
926
927 static void cind_cb(gboolean ok, GAtResult *result, gpointer user_data)
928 {
929         struct cb_data *cbd = user_data;
930         ofono_netreg_strength_cb_t cb = cbd->cb;
931         struct netreg_data *nd = cbd->user;
932         int index;
933         int strength;
934         GAtResultIter iter;
935         struct ofono_error error;
936
937         decode_at_error(&error, g_at_result_final_response(result));
938
939         if (!ok) {
940                 cb(&error, -1, cbd->data);
941                 return;
942         }
943
944         g_at_result_iter_init(&iter, result);
945
946         if (!g_at_result_iter_next(&iter, "+CIND:")) {
947                 CALLBACK_WITH_FAILURE(cb, -1, cbd->data);
948                 return;
949         }
950
951         for (index = 1; index < nd->signal_index; index++)
952                 g_at_result_iter_skip_next(&iter);
953
954         g_at_result_iter_next_number(&iter, &strength);
955
956         if (strength == nd->signal_invalid)
957                 strength = -1;
958         else
959                 strength = (strength * 100) / (nd->signal_max - nd->signal_min);
960
961         cb(&error, strength, cbd->data);
962 }
963
964 static void huawei_rssi_notify(GAtResult *result, gpointer user_data)
965 {
966         struct ofono_netreg *netreg = user_data;
967         GAtResultIter iter;
968         int strength;
969
970         g_at_result_iter_init(&iter, result);
971
972         if (!g_at_result_iter_next(&iter, "^RSSI:"))
973                 return;
974
975         if (!g_at_result_iter_next_number(&iter, &strength))
976                 return;
977
978         ofono_netreg_strength_notify(netreg,
979                                 at_util_convert_signal_strength(strength));
980 }
981
982 static void huawei_mode_notify(GAtResult *result, gpointer user_data)
983 {
984         struct ofono_netreg *netreg = user_data;
985         struct netreg_data *nd = ofono_netreg_get_data(netreg);
986         GAtResultIter iter;
987         int mode, submode;
988
989         g_at_result_iter_init(&iter, result);
990
991         if (!g_at_result_iter_next(&iter, "^MODE:"))
992                 return;
993
994         if (!g_at_result_iter_next_number(&iter, &mode))
995                 return;
996
997         if (!g_at_result_iter_next_number(&iter, &submode))
998                 return;
999
1000         switch (mode) {
1001         case 3:
1002                 nd->tech = ACCESS_TECHNOLOGY_GSM;
1003                 break;
1004         case 5:
1005                 nd->tech = ACCESS_TECHNOLOGY_UTRAN;
1006                 break;
1007         }
1008 }
1009
1010 static void huawei_nwtime_notify(GAtResult *result, gpointer user_data)
1011 {
1012         struct ofono_netreg *netreg = user_data;
1013         struct netreg_data *nd = ofono_netreg_get_data(netreg);
1014         int year, mon, mday, hour, min, sec;
1015         char tz[4];
1016         const char *date, *time, *dst;
1017         GAtResultIter iter;
1018
1019         g_at_result_iter_init(&iter, result);
1020
1021         if (!g_at_result_iter_next(&iter, "^NWTIME:"))
1022                 return;
1023
1024         if (!g_at_result_iter_next_unquoted_string(&iter, &date))
1025                 return;
1026
1027         if (!g_at_result_iter_next_unquoted_string(&iter, &time))
1028                 return;
1029
1030         if (!g_at_result_iter_next_unquoted_string(&iter, &dst))
1031                 return;
1032
1033         DBG("date %s time %s dst %s", date, time, dst);
1034
1035         if (sscanf(date, "%u/%u/%u", &year, &mon, &mday) != 3)
1036                 return;
1037
1038         if (sscanf(time, "%u:%u:%u%s", &hour, &min, &sec, tz) != 4)
1039                 return;
1040
1041         nd->time.utcoff = atoi(tz) * 15 * 60;
1042         nd->time.dst = atoi(dst);
1043
1044         nd->time.sec = sec;
1045         nd->time.min = min;
1046         nd->time.hour = hour;
1047         nd->time.mday = mday;
1048         nd->time.mon = mon;
1049         nd->time.year = 2000 + year;
1050
1051         ofono_netreg_time_notify(netreg, &nd->time);
1052 }
1053
1054 static void csq_cb(gboolean ok, GAtResult *result, gpointer user_data)
1055 {
1056         struct cb_data *cbd = user_data;
1057         ofono_netreg_strength_cb_t cb = cbd->cb;
1058         int strength;
1059         GAtResultIter iter;
1060         struct ofono_error error;
1061
1062         decode_at_error(&error, g_at_result_final_response(result));
1063
1064         if (!ok) {
1065                 cb(&error, -1, cbd->data);
1066                 return;
1067         }
1068
1069         g_at_result_iter_init(&iter, result);
1070
1071         if (!g_at_result_iter_next(&iter, "+CSQ:")) {
1072                 CALLBACK_WITH_FAILURE(cb, -1, cbd->data);
1073                 return;
1074         }
1075
1076         g_at_result_iter_next_number(&iter, &strength);
1077
1078         DBG("csq_cb: %d", strength);
1079
1080         if (strength == 99)
1081                 strength = -1;
1082         else
1083                 strength = (strength * 100) / 31;
1084
1085         cb(&error, strength, cbd->data);
1086 }
1087
1088 static void at_signal_strength(struct ofono_netreg *netreg,
1089                                 ofono_netreg_strength_cb_t cb, void *data)
1090 {
1091         struct netreg_data *nd = ofono_netreg_get_data(netreg);
1092         struct cb_data *cbd = cb_data_new(cb, data);
1093
1094         cbd->user = nd;
1095
1096         /*
1097          * If we defaulted to using CIND, then keep using it,
1098          * otherwise fall back to CSQ
1099          */
1100         if (nd->signal_index > 0) {
1101                 if (g_at_chat_send(nd->chat, "AT+CIND?", cind_prefix,
1102                                         cind_cb, cbd, g_free) > 0)
1103                         return;
1104         } else {
1105                 if (g_at_chat_send(nd->chat, "AT+CSQ", csq_prefix,
1106                                 csq_cb, cbd, g_free) > 0)
1107                         return;
1108         }
1109
1110         g_free(cbd);
1111
1112         CALLBACK_WITH_FAILURE(cb, -1, data);
1113 }
1114
1115 static void mbm_etzv_notify(GAtResult *result, gpointer user_data)
1116 {
1117         struct ofono_netreg *netreg = user_data;
1118         struct netreg_data *nd = ofono_netreg_get_data(netreg);
1119         int year, mon, mday, hour, min, sec;
1120         const char *tz, *time, *timestamp;
1121         GAtResultIter iter;
1122
1123         g_at_result_iter_init(&iter, result);
1124
1125         if (g_at_result_iter_next(&iter, "*ETZV:") == FALSE)
1126                 return;
1127
1128         if (g_at_result_iter_next_string(&iter, &tz) == FALSE)
1129                 return;
1130
1131         if (g_at_result_iter_next_string(&iter, &time) == FALSE)
1132                 time = NULL;
1133
1134         if (g_at_result_iter_next_string(&iter, &timestamp) == FALSE)
1135                 timestamp = NULL;
1136
1137         DBG("tz %s time %s timestamp %s", tz, time, timestamp);
1138
1139         if (time == NULL) {
1140                 year = -1;
1141                 mon = -1;
1142                 mday = -1;
1143                 hour = -1;
1144                 min = -1;
1145                 sec = -1;
1146         } else {
1147                 if (sscanf(time, "%u/%u/%u,%u:%u:%u", &year, &mon, &mday,
1148                                                 &hour, &min, &sec) != 6)
1149                 return;
1150         }
1151
1152         nd->time.utcoff = atoi(tz) * 15 * 60;
1153
1154         nd->time.sec = sec;
1155         nd->time.min = min;
1156         nd->time.hour = hour;
1157         nd->time.mday = mday;
1158         nd->time.mon = mon;
1159         nd->time.year = year;
1160
1161         ofono_netreg_time_notify(netreg, &nd->time);
1162 }
1163
1164 static void mbm_erinfo_notify(GAtResult *result, gpointer user_data)
1165 {
1166         struct ofono_netreg *netreg = user_data;
1167         struct netreg_data *nd = ofono_netreg_get_data(netreg);
1168         GAtResultIter iter;
1169         int mode, gsm, umts;
1170
1171         g_at_result_iter_init(&iter, result);
1172
1173         if (g_at_result_iter_next(&iter, "*ERINFO:") == FALSE)
1174                 return;
1175
1176         if (g_at_result_iter_next_number(&iter, &mode) == FALSE)
1177                 return;
1178
1179         if (g_at_result_iter_next_number(&iter, &gsm) == FALSE)
1180                 return;
1181
1182         /*
1183          * According to MBM the ERINFO unsolicited response does not contain
1184          * the mode parameter, however at least the MD300 does report it.  So
1185          * we handle both 2 and 3 argument versions
1186          */
1187         if (g_at_result_iter_next_number(&iter, &umts) == FALSE) {
1188                 gsm = mode;
1189                 umts = gsm;
1190         }
1191
1192         ofono_info("network capability: GSM %d UMTS %d", gsm, umts);
1193
1194         /* Convert to tech values from 27.007 */
1195         switch (gsm) {
1196         case 1: /* GSM */
1197                 nd->tech = ACCESS_TECHNOLOGY_GSM;
1198                 break;
1199         case 2: /* EDGE */
1200                 nd->tech = ACCESS_TECHNOLOGY_GSM_EGPRS;
1201                 break;
1202         default:
1203                 nd->tech = -1;
1204         }
1205
1206         switch (umts) {
1207         case 1: /* UMTS */
1208                 nd->tech = ACCESS_TECHNOLOGY_UTRAN;
1209                 break;
1210         case 2: /* UMTS + HSDPA */
1211                 nd->tech = ACCESS_TECHNOLOGY_UTRAN_HSDPA;
1212                 break;
1213         }
1214 }
1215
1216 static void icera_nwstate_notify(GAtResult *result, gpointer user_data)
1217 {
1218         struct ofono_netreg *netreg = user_data;
1219         struct netreg_data *nd = ofono_netreg_get_data(netreg);
1220         GAtResultIter iter;
1221         const char *mccmnc, *tech, *state;
1222         int rssi;
1223
1224         g_at_result_iter_init(&iter, result);
1225
1226         if (g_at_result_iter_next(&iter, "%NWSTATE:") == FALSE)
1227                 return;
1228
1229         if (g_at_result_iter_next_number(&iter, &rssi) == FALSE)
1230                 return;
1231
1232         if (g_at_result_iter_next_unquoted_string(&iter, &mccmnc) == FALSE)
1233                 return;
1234
1235         if (g_at_result_iter_next_unquoted_string(&iter, &tech) == FALSE)
1236                 return;
1237
1238         if (g_at_result_iter_next_unquoted_string(&iter, &state) == FALSE)
1239                 return;
1240
1241         DBG("rssi %d tech %s state %s", rssi, tech, state);
1242
1243         /* small 'g' means CS, big 'G' means PS */
1244         if (g_str_equal(tech, "2g") == TRUE ||
1245                                 g_str_equal(tech, "2G") == TRUE ||
1246                                 g_str_equal(tech, "2G-GPRS") == TRUE) {
1247                 nd->tech = ACCESS_TECHNOLOGY_GSM;
1248         } else if (g_str_equal(tech, "2G-EDGE") == TRUE) {
1249                 nd->tech = ACCESS_TECHNOLOGY_GSM_EGPRS;
1250         } else if (g_str_equal(tech, "3g") == TRUE ||
1251                                 g_str_equal(tech, "3G") == TRUE ||
1252                                 g_str_equal(tech, "R99") == TRUE) {
1253                 if (g_str_equal(state, "HSDPA") == TRUE)
1254                         nd->tech = ACCESS_TECHNOLOGY_UTRAN_HSDPA;
1255                 else if (g_str_equal(state, "HSUPA") == TRUE)
1256                         nd->tech = ACCESS_TECHNOLOGY_UTRAN_HSUPA;
1257                 else if (g_str_equal(state, "HSDPA-HSUPA") == TRUE)
1258                         nd->tech = ACCESS_TECHNOLOGY_UTRAN_HSDPA_HSUPA;
1259                 else if (g_str_equal(state, "HSDPA-HSUPA-HSPA+") == TRUE)
1260                         nd->tech = ACCESS_TECHNOLOGY_UTRAN_HSDPA_HSUPA;
1261                 else
1262                         nd->tech = ACCESS_TECHNOLOGY_UTRAN;
1263         } else
1264                 nd->tech = -1;
1265 }
1266
1267 static int cnti_to_tech(const char *cnti)
1268 {
1269         if (g_str_equal(cnti, "GSM") == TRUE ||
1270                         g_str_equal(cnti, "GPRS") == TRUE)
1271                 return ACCESS_TECHNOLOGY_GSM;
1272         else if (g_str_equal(cnti, "EDGE") == TRUE)
1273                 return ACCESS_TECHNOLOGY_GSM_EGPRS;
1274         else if (g_str_equal(cnti, "UMTS") == TRUE)
1275                 return ACCESS_TECHNOLOGY_UTRAN;
1276         else if (g_str_equal(cnti, "HSDPA") == TRUE)
1277                 return ACCESS_TECHNOLOGY_UTRAN_HSDPA;
1278         else if (g_str_equal(cnti, "HSUPA") == TRUE)
1279                 return ACCESS_TECHNOLOGY_UTRAN_HSUPA;
1280
1281         return -1;
1282 }
1283
1284 static void gobi_cnti_notify(GAtResult *result, gpointer user_data)
1285 {
1286         struct ofono_netreg *netreg = user_data;
1287         struct netreg_data *nd = ofono_netreg_get_data(netreg);
1288         GAtResultIter iter;
1289         const char *tech;
1290         int option;
1291
1292         g_at_result_iter_init(&iter, result);
1293
1294         if (g_at_result_iter_next(&iter, "*CNTI:") == FALSE)
1295                 return;
1296
1297         if (g_at_result_iter_next_number(&iter, &option) == FALSE)
1298                 return;
1299
1300         if (option != 0)
1301                 return;
1302
1303         if (g_at_result_iter_next_unquoted_string(&iter, &tech) == FALSE)
1304                 return;
1305
1306         nd->tech = cnti_to_tech(tech);
1307 }
1308
1309 static void nw_cnti_notify(GAtResult *result, gpointer user_data)
1310 {
1311         struct ofono_netreg *netreg = user_data;
1312         struct netreg_data *nd = ofono_netreg_get_data(netreg);
1313         GAtResultIter iter;
1314         const char *tech;
1315         int option;
1316
1317         g_at_result_iter_init(&iter, result);
1318
1319         if (g_at_result_iter_next(&iter, "$CNTI:") == FALSE)
1320                 return;
1321
1322         if (g_at_result_iter_next_number(&iter, &option) == FALSE)
1323                 return;
1324
1325         if (option != 0)
1326                 return;
1327
1328         if (g_at_result_iter_next_unquoted_string(&iter, &tech) == FALSE)
1329                 return;
1330
1331         nd->tech = cnti_to_tech(tech);
1332 }
1333
1334 static void cnti_query_tech_cb(gboolean ok, GAtResult *result,
1335                                                 gpointer user_data)
1336 {
1337         struct tech_query *tq = user_data;
1338         struct netreg_data *nd = ofono_netreg_get_data(tq->netreg);
1339
1340         ofono_netreg_status_notify(tq->netreg,
1341                         tq->status, tq->lac, tq->ci, nd->tech);
1342 }
1343
1344 static void zte_query_tech_cb(gboolean ok, GAtResult *result,
1345                                                 gpointer user_data)
1346 {
1347         struct tech_query *tq = user_data;
1348         int tech;
1349
1350         if (ok)
1351                 tech = zte_parse_tech(result);
1352         else
1353                 tech = -1;
1354
1355         ofono_netreg_status_notify(tq->netreg,
1356                         tq->status, tq->lac, tq->ci, tech);
1357 }
1358
1359 static void option_query_tech_cb(gboolean ok, GAtResult *result,
1360                                                 gpointer user_data)
1361 {
1362         struct tech_query *tq = user_data;
1363         int tech;
1364
1365         if (ok)
1366                 tech = option_parse_tech(result);
1367         else
1368                 tech = -1;
1369
1370         ofono_netreg_status_notify(tq->netreg,
1371                         tq->status, tq->lac, tq->ci, tech);
1372 }
1373
1374 static void creg_notify(GAtResult *result, gpointer user_data)
1375 {
1376         struct ofono_netreg *netreg = user_data;
1377         int status, lac, ci, tech;
1378         struct netreg_data *nd = ofono_netreg_get_data(netreg);
1379         struct tech_query *tq;
1380
1381         if (at_util_parse_reg_unsolicited(result, "+CREG:", &status,
1382                                 &lac, &ci, &tech, nd->vendor) == FALSE)
1383                 return;
1384
1385         if (status != 1 && status != 5)
1386                 goto notify;
1387
1388         tq = g_try_new0(struct tech_query, 1);
1389         if (tq == NULL)
1390                 goto notify;
1391
1392         tq->status = status;
1393         tq->lac = lac;
1394         tq->ci = ci;
1395         tq->netreg = netreg;
1396
1397         switch (nd->vendor) {
1398         case OFONO_VENDOR_GOBI:
1399                 if (g_at_chat_send(nd->chat, "AT*CNTI=0", none_prefix,
1400                                         cnti_query_tech_cb, tq, g_free) > 0)
1401                         return;
1402                 break;
1403         case OFONO_VENDOR_NOVATEL:
1404                 if (g_at_chat_send(nd->chat, "AT$CNTI=0", none_prefix,
1405                                         cnti_query_tech_cb, tq, g_free) > 0)
1406                         return;
1407                 break;
1408         case OFONO_VENDOR_ZTE:
1409                 if (g_at_chat_send(nd->chat, "AT+ZPAS?", zpas_prefix,
1410                                         zte_query_tech_cb, tq, g_free) > 0)
1411                         return;
1412                 break;
1413         case OFONO_VENDOR_OPTION_HSO:
1414                 if (g_at_chat_send(nd->chat, "AT_OCTI?;_OUWCTI?",
1415                                         option_tech_prefix,
1416                                         option_query_tech_cb, tq, g_free) > 0)
1417                         return;
1418                 break;
1419         }
1420
1421         g_free(tq);
1422
1423         if ((status == 1 || status == 5) && tech == -1)
1424                 tech = nd->tech;
1425
1426 notify:
1427         ofono_netreg_status_notify(netreg, status, lac, ci, tech);
1428 }
1429
1430 static void at_cmer_not_supported(struct ofono_netreg *netreg)
1431 {
1432         ofono_error("+CMER not supported by this modem.  If this is an error"
1433                         " please submit patches to support this hardware");
1434 }
1435
1436 static void at_cmer_set_cb(gboolean ok, GAtResult *result, gpointer user_data)
1437 {
1438         struct ofono_netreg *netreg = user_data;
1439
1440         if (!ok)
1441                 at_cmer_not_supported(netreg);
1442 }
1443
1444 static inline char wanted_cmer(int supported, const char *pref)
1445 {
1446         while (*pref) {
1447                 if (supported & (1 << (*pref - '0')))
1448                         return *pref;
1449
1450                 pref++;
1451         }
1452
1453         return '\0';
1454 }
1455
1456 static inline ofono_bool_t append_cmer_element(char *buf, int *len, int cap,
1457                                                 const char *wanted,
1458                                                 ofono_bool_t last)
1459 {
1460         char setting = wanted_cmer(cap, wanted);
1461
1462         if (!setting)
1463                 return FALSE;
1464
1465         buf[*len] = setting;
1466
1467         if (last)
1468                 buf[*len + 1] = '\0';
1469         else
1470                 buf[*len + 1] = ',';
1471
1472         *len += 2;
1473
1474         return TRUE;
1475 }
1476
1477 static ofono_bool_t build_cmer_string(char *buf, int *cmer_opts,
1478                                         struct netreg_data *nd)
1479 {
1480         const char *mode;
1481         int len = sprintf(buf, "AT+CMER=");
1482
1483         DBG("");
1484
1485         /*
1486          * Forward unsolicited result codes directly to the TE;
1487          * TA‑TE link specific inband technique used to embed result codes and
1488          * data when TA is in on‑line data mode
1489          */
1490         if (!append_cmer_element(buf, &len, cmer_opts[0], "3", FALSE))
1491                 return FALSE;
1492
1493         /* No keypad event reporting */
1494         if (!append_cmer_element(buf, &len, cmer_opts[1], "0", FALSE))
1495                 return FALSE;
1496
1497         /* No display event reporting */
1498         if (!append_cmer_element(buf, &len, cmer_opts[2], "0", FALSE))
1499                 return FALSE;
1500
1501         switch (nd->vendor) {
1502         case OFONO_VENDOR_TELIT:
1503                 /*
1504                  * Telit does not support mode 1.
1505                  * All indicator events shall be directed from TA to TE.
1506                  */
1507                 mode = "2";
1508                 break;
1509         default:
1510                 /*
1511                  * Only those indicator events, which are not caused by +CIND
1512                  * shall be indicated by the TA to the TE.
1513                  */
1514                 mode = "1";
1515                 break;
1516         }
1517
1518         /*
1519          * Indicator event reporting using URC +CIEV: <ind>,<value>.
1520          * <ind> indicates the indicator order number (as specified for +CIND)
1521          * and <value> is the new value of indicator.
1522          */
1523         if (!append_cmer_element(buf, &len, cmer_opts[3], mode, TRUE))
1524                 return FALSE;
1525
1526         return TRUE;
1527 }
1528
1529 static void at_cmer_query_cb(ofono_bool_t ok, GAtResult *result,
1530                                 gpointer user_data)
1531 {
1532         struct ofono_netreg *netreg = user_data;
1533         struct netreg_data *nd = ofono_netreg_get_data(netreg);
1534         GAtResultIter iter;
1535         int cmer_opts_cnt = 5; /* See 27.007 Section 8.10 */
1536         int cmer_opts[cmer_opts_cnt];
1537         int opt;
1538         int mode;
1539         char buf[128];
1540
1541         if (!ok)
1542                 goto error;
1543
1544         memset(cmer_opts, 0, sizeof(cmer_opts));
1545
1546         g_at_result_iter_init(&iter, result);
1547
1548         if (!g_at_result_iter_next(&iter, "+CMER:"))
1549                 goto error;
1550
1551         for (opt = 0; opt < cmer_opts_cnt; opt++) {
1552                 int min, max;
1553
1554                 if (!g_at_result_iter_open_list(&iter))
1555                         goto error;
1556
1557                 while (g_at_result_iter_next_range(&iter, &min, &max)) {
1558                         for (mode = min; mode <= max; mode++)
1559                                 cmer_opts[opt] |= 1 << mode;
1560                 }
1561
1562                 if (!g_at_result_iter_close_list(&iter))
1563                         goto error;
1564         }
1565
1566         if (build_cmer_string(buf, cmer_opts, nd) == FALSE)
1567                 goto error;
1568
1569         g_at_chat_send(nd->chat, buf, cmer_prefix,
1570                         at_cmer_set_cb, netreg, NULL);
1571
1572         return;
1573
1574 error:
1575         at_cmer_not_supported(netreg);
1576 }
1577
1578 static void cind_support_cb(gboolean ok, GAtResult *result, gpointer user_data)
1579 {
1580         struct ofono_netreg *netreg = user_data;
1581         struct netreg_data *nd = ofono_netreg_get_data(netreg);
1582         GAtResultIter iter;
1583         const char *str;
1584         char *signal_identifier = "signal";
1585         int index;
1586         int min = 0;
1587         int max = 0;
1588         int tmp_min, tmp_max, invalid;
1589
1590         if (!ok)
1591                 goto error;
1592
1593         g_at_result_iter_init(&iter, result);
1594         if (!g_at_result_iter_next(&iter, "+CIND:"))
1595                 goto error;
1596
1597         index = 1;
1598
1599         /*
1600          * Telit encapsulates the CIND=? tokens with braces
1601          * so we need to skip them
1602          */
1603         if (nd->vendor == OFONO_VENDOR_TELIT) {
1604                 g_at_result_iter_open_list(&iter);
1605                 signal_identifier = "rssi";
1606         }
1607
1608         while (g_at_result_iter_open_list(&iter)) {
1609                 /* Reset invalid default value for every token */
1610                 invalid = 99;
1611
1612                 if (!g_at_result_iter_next_string(&iter, &str))
1613                         goto error;
1614
1615                 if (!g_at_result_iter_open_list(&iter))
1616                         goto error;
1617
1618                 while (g_at_result_iter_next_range(&iter, &tmp_min, &tmp_max)) {
1619                         if (tmp_min != tmp_max) {
1620                                 min = tmp_min;
1621                                 max = tmp_max;
1622                         } else
1623                                 invalid = tmp_min;
1624                 }
1625
1626                 if (!g_at_result_iter_close_list(&iter))
1627                         goto error;
1628
1629                 if (!g_at_result_iter_close_list(&iter))
1630                         goto error;
1631
1632                 if (g_str_equal(signal_identifier, str) == TRUE) {
1633                         nd->signal_index = index;
1634                         nd->signal_min = min;
1635                         nd->signal_max = max;
1636                         nd->signal_invalid = invalid;
1637                 }
1638
1639                 index += 1;
1640         }
1641
1642         if (nd->vendor == OFONO_VENDOR_TELIT)
1643                 g_at_result_iter_close_list(&iter);
1644
1645         if (nd->signal_index == 0)
1646                 goto error;
1647
1648         g_at_chat_send(nd->chat, "AT+CMER=?", cmer_prefix,
1649                                 at_cmer_query_cb, netreg, NULL);
1650
1651         /*
1652          * Telit uses strings instead of numbers to identify indicators
1653          * in a +CIEV URC.
1654          * Handle them in a separate function to keep the code clean.
1655          */
1656         if (nd->vendor == OFONO_VENDOR_TELIT)
1657                 g_at_chat_register(nd->chat, "+CIEV:",
1658                                 telit_ciev_notify, FALSE, netreg, NULL);
1659         else
1660                 g_at_chat_register(nd->chat, "+CIEV:",
1661                                 ciev_notify, FALSE, netreg, NULL);
1662
1663         g_at_chat_register(nd->chat, "+CREG:",
1664                                 creg_notify, FALSE, netreg, NULL);
1665
1666         ofono_netreg_register(netreg);
1667         return;
1668
1669 error:
1670         ofono_error("This driver is not setup with Signal Strength reporting"
1671                         " via CIND indications, please write proper netreg"
1672                         " handling for this device");
1673
1674         ofono_netreg_remove(netreg);
1675 }
1676
1677 static void at_creg_set_cb(gboolean ok, GAtResult *result, gpointer user_data)
1678 {
1679         struct ofono_netreg *netreg = user_data;
1680         struct netreg_data *nd = ofono_netreg_get_data(netreg);
1681
1682         if (!ok) {
1683                 ofono_error("Unable to initialize Network Registration");
1684                 ofono_netreg_remove(netreg);
1685                 return;
1686         }
1687
1688         switch (nd->vendor) {
1689         case OFONO_VENDOR_PHONESIM:
1690                 g_at_chat_register(nd->chat, "+CSQ:",
1691                                         csq_notify, FALSE, netreg, NULL);
1692                 break;
1693         case OFONO_VENDOR_CALYPSO:
1694                 g_at_chat_send(nd->chat, "AT%CSQ=1", none_prefix,
1695                                 NULL, NULL, NULL);
1696                 g_at_chat_register(nd->chat, "%CSQ:", calypso_csq_notify,
1697                                         FALSE, netreg, NULL);
1698                 break;
1699         case OFONO_VENDOR_OPTION_HSO:
1700                 g_at_chat_send(nd->chat, "AT_OSSYS=1", none_prefix,
1701                                 NULL, NULL, NULL);
1702                 g_at_chat_send(nd->chat, "AT_OSQI=1", none_prefix,
1703                                 NULL, NULL, NULL);
1704                 g_at_chat_register(nd->chat, "_OSIGQ:", option_osigq_notify,
1705                                         FALSE, netreg, NULL);
1706
1707                 g_at_chat_send(nd->chat, "AT_OSSYS?", none_prefix,
1708                                 NULL, NULL, NULL);
1709                 g_at_chat_send(nd->chat, "AT_OSQI?", none_prefix,
1710                                 NULL, NULL, NULL);
1711
1712                 /* Register for network time update reports */
1713                 g_at_chat_register(nd->chat, "+CTZV:", ctzv_notify,
1714                                                 FALSE, netreg, NULL);
1715                 g_at_chat_send(nd->chat, "AT+CTZR=1", none_prefix,
1716                                                 NULL, NULL, NULL);
1717                 break;
1718         case OFONO_VENDOR_MBM:
1719                 /* Enable network registration updates */
1720                 g_at_chat_send(nd->chat, "AT*E2REG=1", none_prefix,
1721                                                 NULL, NULL, NULL);
1722                 g_at_chat_send(nd->chat, "AT*EREG=2", none_prefix,
1723                                                 NULL, NULL, NULL);
1724                 g_at_chat_send(nd->chat, "AT*EPSB=1", none_prefix,
1725                                                 NULL, NULL, NULL);
1726
1727                 /* Register for network technology updates */
1728                 g_at_chat_send(nd->chat, "AT*ERINFO=1", none_prefix,
1729                                                 NULL, NULL, NULL);
1730                 g_at_chat_register(nd->chat, "*ERINFO:", mbm_erinfo_notify,
1731                                                 FALSE, netreg, NULL);
1732
1733                 /* Register for network time update reports */
1734                 g_at_chat_register(nd->chat, "*ETZV:", mbm_etzv_notify,
1735                                                 FALSE, netreg, NULL);
1736                 g_at_chat_send(nd->chat, "AT*ETZR=2", none_prefix,
1737                                                 NULL, NULL, NULL);
1738
1739                 g_at_chat_send(nd->chat, "AT+CIND=?", cind_prefix,
1740                                         cind_support_cb, netreg, NULL);
1741                 return;
1742         case OFONO_VENDOR_GOBI:
1743                 /*
1744                  * Gobi devices don't support unsolicited notifications
1745                  * of technology changes, but register a handle for
1746                  * CNTI so we get notified by any query.
1747                  */
1748                 g_at_chat_register(nd->chat, "*CNTI:", gobi_cnti_notify,
1749                                         FALSE, netreg, NULL);
1750                 break;
1751         case OFONO_VENDOR_NOVATEL:
1752                 /*
1753                  * Novatel doesn't support unsolicited notifications
1754                  * of technology changes, but register a handle for
1755                  * CNTI so we get notified by any query.
1756                  */
1757                 g_at_chat_register(nd->chat, "$CNTI:", nw_cnti_notify,
1758                                         FALSE, netreg, NULL);
1759                 break;
1760         case OFONO_VENDOR_HUAWEI:
1761                 /* Register for RSSI reports */
1762                 g_at_chat_register(nd->chat, "^RSSI:", huawei_rssi_notify,
1763                                                 FALSE, netreg, NULL);
1764
1765                 /* Register for system mode reports */
1766                 g_at_chat_register(nd->chat, "^MODE:", huawei_mode_notify,
1767                                                 FALSE, netreg, NULL);
1768
1769                 /* Register for network time reports */
1770                 g_at_chat_register(nd->chat, "^NWTIME:", huawei_nwtime_notify,
1771                                                 FALSE, netreg, NULL);
1772                 break;
1773         case OFONO_VENDOR_IFX:
1774                 /* Register for specific signal strength reports */
1775                 g_at_chat_register(nd->chat, "+XCIEV:", ifx_xciev_notify,
1776                                                 FALSE, netreg, NULL);
1777                 g_at_chat_register(nd->chat, "+XCSQ:", ifx_xcsq_notify,
1778                                                 FALSE, netreg, NULL);
1779                 g_at_chat_send(nd->chat, "AT+XCSQ=1", none_prefix,
1780                                                 NULL, NULL, NULL);
1781                 g_at_chat_send(nd->chat, "AT+XMER=1", none_prefix,
1782                                                 NULL, NULL, NULL);
1783
1784                 /* Register for home zone reports */
1785                 g_at_chat_register(nd->chat, "+XHOMEZR:", ifx_xhomezr_notify,
1786                                                 FALSE, netreg, NULL);
1787                 g_at_chat_send(nd->chat, "AT+XHOMEZR=1", none_prefix,
1788                                                 NULL, NULL, NULL);
1789
1790                 /* Register for network time update reports */
1791                 g_at_chat_register(nd->chat, "+CTZV:", ifx_ctzv_notify,
1792                                                 FALSE, netreg, NULL);
1793                 g_at_chat_register(nd->chat, "+CTZDST:", ifx_ctzdst_notify,
1794                                                 FALSE, netreg, NULL);
1795                 g_at_chat_send(nd->chat, "AT+CTZR=1", none_prefix,
1796                                                 NULL, NULL, NULL);
1797                 break;
1798         case OFONO_VENDOR_ZTE:
1799                 /* Register for network time update reports */
1800                 g_at_chat_register(nd->chat, "+CTZV:", ctzv_notify,
1801                                                 FALSE, netreg, NULL);
1802                 g_at_chat_send(nd->chat, "AT+CTZR=1", none_prefix,
1803                                                 NULL, NULL, NULL);
1804                 break;
1805         case OFONO_VENDOR_ICERA:
1806                 /* Register for network technology updates */
1807                 g_at_chat_register(nd->chat, "%NWSTATE:", icera_nwstate_notify,
1808                                                 FALSE, netreg, NULL);
1809                 g_at_chat_send(nd->chat, "AT%NWSTATE=1", none_prefix,
1810                                                 NULL, NULL, NULL);
1811
1812                 /* Register for radio access technology updates */
1813                 g_at_chat_send(nd->chat, "AT*TRATD=1", none_prefix,
1814                                                 NULL, NULL, NULL);
1815
1816                 /* Register for network time update reports */
1817                 g_at_chat_register(nd->chat, "*TLTS:", tlts_notify,
1818                                                 FALSE, netreg, NULL);
1819                 g_at_chat_send(nd->chat, "AT*TLTS=1", none_prefix,
1820                                                 NULL, NULL, NULL);
1821                 break;
1822         case OFONO_VENDOR_NOKIA:
1823         case OFONO_VENDOR_SAMSUNG:
1824         case OFONO_VENDOR_SIMCOM:
1825                 /* Signal strength reporting via CIND is not supported */
1826                 break;
1827         default:
1828                 g_at_chat_send(nd->chat, "AT+CIND=?", cind_prefix,
1829                                 cind_support_cb, netreg, NULL);
1830                 return;
1831         }
1832
1833         g_at_chat_register(nd->chat, "+CREG:",
1834                                 creg_notify, FALSE, netreg, NULL);
1835         ofono_netreg_register(netreg);
1836 }
1837
1838 static void at_creg_test_cb(gboolean ok, GAtResult *result, gpointer user_data)
1839 {
1840         struct ofono_netreg *netreg = user_data;
1841         struct netreg_data *nd = ofono_netreg_get_data(netreg);
1842         gint range[2];
1843         GAtResultIter iter;
1844         int creg1 = 0;
1845         int creg2 = 0;
1846
1847         if (!ok)
1848                 goto error;
1849
1850         g_at_result_iter_init(&iter, result);
1851
1852 retry:
1853         if (!g_at_result_iter_next(&iter, "+CREG:"))
1854                 goto error;
1855
1856         if (!g_at_result_iter_open_list(&iter))
1857                 goto retry;
1858
1859         while (g_at_result_iter_next_range(&iter, &range[0], &range[1])) {
1860                 if (1 >= range[0] && 1 <= range[1])
1861                         creg1 = 1;
1862                 if (2 >= range[0] && 2 <= range[1])
1863                         creg2 = 1;
1864         }
1865
1866         g_at_result_iter_close_list(&iter);
1867
1868         if (creg2) {
1869                 g_at_chat_send(nd->chat, "AT+CREG=2", none_prefix,
1870                                 at_creg_set_cb, netreg, NULL);
1871                 return;
1872         }
1873
1874         if (creg1) {
1875                 g_at_chat_send(nd->chat, "AT+CREG=1", none_prefix,
1876                                 at_creg_set_cb, netreg, NULL);
1877                 return;
1878         }
1879
1880 error:
1881         ofono_error("Unable to initialize Network Registration");
1882         ofono_netreg_remove(netreg);
1883 }
1884
1885 static int at_netreg_probe(struct ofono_netreg *netreg, unsigned int vendor,
1886                                 void *data)
1887 {
1888         GAtChat *chat = data;
1889         struct netreg_data *nd;
1890
1891         nd = g_new0(struct netreg_data, 1);
1892
1893         nd->chat = g_at_chat_clone(chat);
1894         nd->vendor = vendor;
1895         nd->tech = -1;
1896         nd->time.sec = -1;
1897         nd->time.min = -1;
1898         nd->time.hour = -1;
1899         nd->time.mday = -1;
1900         nd->time.mon = -1;
1901         nd->time.year = -1;
1902         nd->time.dst = 0;
1903         nd->time.utcoff = 0;
1904         ofono_netreg_set_data(netreg, nd);
1905
1906         g_at_chat_send(nd->chat, "AT+CREG=?", creg_prefix,
1907                         at_creg_test_cb, netreg, NULL);
1908
1909         return 0;
1910 }
1911
1912 static void at_netreg_remove(struct ofono_netreg *netreg)
1913 {
1914         struct netreg_data *nd = ofono_netreg_get_data(netreg);
1915
1916         if (nd->nitz_timeout)
1917                 g_source_remove(nd->nitz_timeout);
1918
1919         ofono_netreg_set_data(netreg, NULL);
1920
1921         g_at_chat_unref(nd->chat);
1922         g_free(nd);
1923 }
1924
1925 static struct ofono_netreg_driver driver = {
1926         .name                           = "atmodem",
1927         .probe                          = at_netreg_probe,
1928         .remove                         = at_netreg_remove,
1929         .registration_status            = at_registration_status,
1930         .current_operator               = at_current_operator,
1931         .list_operators                 = at_list_operators,
1932         .register_auto                  = at_register_auto,
1933         .register_manual                = at_register_manual,
1934         .strength                       = at_signal_strength,
1935 };
1936
1937 void at_netreg_init(void)
1938 {
1939         ofono_netreg_driver_register(&driver);
1940 }
1941
1942 void at_netreg_exit(void)
1943 {
1944         ofono_netreg_driver_unregister(&driver);
1945 }