3
* oFono - Open Source Telephony
5
* Copyright (C) 2008-2009 Intel Corporation. All rights reserved.
7
* This program is free software; you can redistribute it and/or modify
8
* it under the terms of the GNU General Public License version 2 as
9
* published by the Free Software Foundation.
11
* This program is distributed in the hope that it will be useful,
12
* but WITHOUT ANY WARRANTY; without even the implied warranty of
13
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14
* GNU General Public License for more details.
16
* You should have received a copy of the GNU General Public License
17
* along with this program; if not, write to the Free Software
18
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
33
#include <ofono/log.h>
34
#include <ofono/modem.h>
38
#include "gatresult.h"
42
static const char *none_prefix[] = { NULL };
43
static const char *creg_prefix[] = { "+CREG:", NULL };
44
static const char *cops_prefix[] = { "+COPS:", NULL };
45
static const char *csq_prefix[] = { "+CSQ:", NULL };
48
char mcc[OFONO_MAX_MCC_LENGTH + 1];
49
char mnc[OFONO_MAX_MNC_LENGTH + 1];
52
static void extract_mcc_mnc(const char *str, char *mcc, char *mnc)
54
/* Three digit country code */
55
strncpy(mcc, str, OFONO_MAX_MCC_LENGTH);
56
mcc[OFONO_MAX_MCC_LENGTH] = '\0';
58
/* Usually a 2 but sometimes 3 digit network code */
59
strncpy(mnc, str + OFONO_MAX_MCC_LENGTH, OFONO_MAX_MNC_LENGTH);
60
mnc[OFONO_MAX_MNC_LENGTH] = '\0';
63
static void at_creg_cb(gboolean ok, GAtResult *result, gpointer user_data)
65
struct cb_data *cbd = user_data;
67
ofono_registration_status_cb_t cb = cbd->cb;
70
int lac = -1, ci = -1, tech = -1;
71
struct ofono_error error;
73
dump_response("at_creg_cb", ok, result);
74
decode_at_error(&error, g_at_result_final_response(result));
77
cb(&error, -1, -1, -1, -1, cbd->data);
81
g_at_result_iter_init(&iter, result);
83
if (!g_at_result_iter_next(&iter, "+CREG:")) {
86
cb(&e, -1, -1, -1, -1, cbd->data);
90
/* Skip <n> the unsolicited result code */
91
g_at_result_iter_skip_next(&iter);
93
g_at_result_iter_next_number(&iter, &status);
95
if (g_at_result_iter_next_string(&iter, &str) == TRUE)
96
lac = strtol(str, NULL, 16);
100
if (g_at_result_iter_next_string(&iter, &str) == TRUE)
101
ci = strtol(str, NULL, 16);
105
g_at_result_iter_next_number(&iter, &tech);
108
ofono_debug("creg_cb: %d, %d, %d, %d", status, lac, ci, tech);
110
cb(&error, status, lac, ci, tech, cbd->data);
113
static void at_registration_status(struct ofono_modem *modem,
114
ofono_registration_status_cb_t cb,
117
struct at_data *at = ofono_modem_get_userdata(modem);
118
struct cb_data *cbd = cb_data_new(modem, cb, data);
123
if (g_at_chat_send(at->parser, "AT+CREG?", creg_prefix,
124
at_creg_cb, cbd, g_free) > 0)
132
DECLARE_FAILURE(error);
133
cb(&error, -1, -1, -1, -1, data);
137
static void cops_cb(gboolean ok, GAtResult *result, gpointer user_data)
139
struct cb_data *cbd = user_data;
140
struct at_data *at = ofono_modem_get_userdata(cbd->modem);
141
ofono_current_operator_cb_t cb = cbd->cb;
142
struct ofono_network_operator op;
146
struct ofono_error error;
148
dump_response("cops_cb", ok, result);
149
decode_at_error(&error, g_at_result_final_response(result));
151
if (!ok || at->netreg->mcc[0] == '\0' || at->netreg->mnc[0] == '\0') {
152
cb(&error, NULL, cbd->data);
156
g_at_result_iter_init(&iter, result);
158
if (!g_at_result_iter_next(&iter, "+COPS:"))
161
g_at_result_iter_skip_next(&iter);
163
ok = g_at_result_iter_next_number(&iter, &format);
165
if (ok == FALSE || format != 0)
168
if (g_at_result_iter_next_string(&iter, &name) == FALSE)
172
if (g_at_result_iter_next_number(&iter, &tech) == FALSE)
175
strncpy(op.name, name, OFONO_MAX_OPERATOR_NAME_LENGTH);
176
op.name[OFONO_MAX_OPERATOR_NAME_LENGTH] = '\0';
178
strncpy(op.mcc, at->netreg->mcc, OFONO_MAX_MCC_LENGTH);
179
op.mcc[OFONO_MAX_MCC_LENGTH] = '\0';
181
strncpy(op.mnc, at->netreg->mnc, OFONO_MAX_MNC_LENGTH);
182
op.mnc[OFONO_MAX_MNC_LENGTH] = '\0';
187
ofono_debug("cops_cb: %s, %s %s %d", name, at->netreg->mcc,
188
at->netreg->mnc, tech);
190
cb(&error, &op, cbd->data);
201
cb(&e, NULL, cbd->data);
207
static void cops_numeric_cb(gboolean ok, GAtResult *result, gpointer user_data)
209
struct cb_data *cbd = user_data;
210
struct at_data *at = ofono_modem_get_userdata(cbd->modem);
215
dump_response("cops_numeric_cb", ok, result);
220
g_at_result_iter_init(&iter, result);
222
if (!g_at_result_iter_next(&iter, "+COPS:"))
225
g_at_result_iter_skip_next(&iter);
227
ok = g_at_result_iter_next_number(&iter, &format);
229
if (ok == FALSE || format != 2)
232
if (g_at_result_iter_next_string(&iter, &str) == FALSE ||
236
extract_mcc_mnc(str, at->netreg->mcc, at->netreg->mnc);
238
ofono_debug("Cops numeric got mcc: %s, mnc: %s",
239
at->netreg->mcc, at->netreg->mnc);
244
at->netreg->mcc[0] = '\0';
245
at->netreg->mnc[0] = '\0';
248
static void at_current_operator(struct ofono_modem *modem,
249
ofono_current_operator_cb_t cb, void *data)
251
struct at_data *at = ofono_modem_get_userdata(modem);
252
struct cb_data *cbd = cb_data_new(modem, cb, data);
258
ok = g_at_chat_send(at->parser, "AT+COPS=3,2", none_prefix,
262
ok = g_at_chat_send(at->parser, "AT+COPS?", cops_prefix,
263
cops_numeric_cb, cbd, NULL);
266
ok = g_at_chat_send(at->parser, "AT+COPS=3,0", none_prefix,
270
ok = g_at_chat_send(at->parser, "AT+COPS?", cops_prefix,
281
DECLARE_FAILURE(error);
282
cb(&error, NULL, data);
286
static void cops_list_cb(gboolean ok, GAtResult *result, gpointer user_data)
288
struct cb_data *cbd = user_data;
289
ofono_operator_list_cb_t cb = cbd->cb;
290
struct ofono_network_operator *list;
293
struct ofono_error error;
295
dump_response("cops_list_cb", ok, result);
296
decode_at_error(&error, g_at_result_final_response(result));
299
cb(&error, 0, NULL, cbd->data);
303
g_at_result_iter_init(&iter, result);
305
while (g_at_result_iter_next(&iter, "+COPS:")) {
306
while (g_at_result_iter_skip_next(&iter))
310
ofono_debug("Got %d elements", num);
312
list = g_try_new0(struct ofono_network_operator, num);
316
cb(&e, 0, NULL, cbd->data);
321
g_at_result_iter_init(&iter, result);
323
while (g_at_result_iter_next(&iter, "+COPS:")) {
325
const char *l, *s, *n;
326
gboolean have_long = FALSE;
329
if (!g_at_result_iter_open_list(&iter))
332
if (!g_at_result_iter_next_number(&iter, &status))
335
list[num].status = status;
337
if (!g_at_result_iter_next_string(&iter, &l))
342
strncpy(list[num].name, l,
343
OFONO_MAX_OPERATOR_NAME_LENGTH);
346
if (!g_at_result_iter_next_string(&iter, &s))
349
if (strlen(s) > 0 && !have_long)
350
strncpy(list[num].name, s,
351
OFONO_MAX_OPERATOR_NAME_LENGTH);
353
list[num].name[OFONO_MAX_OPERATOR_NAME_LENGTH] = '\0';
355
if (!g_at_result_iter_next_string(&iter, &n))
358
extract_mcc_mnc(n, list[num].mcc, list[num].mnc);
360
if (!g_at_result_iter_next_number(&iter, &tech))
363
list[num].tech = tech;
365
if (!g_at_result_iter_close_list(&iter))
372
ofono_debug("Got %d operators", num);
377
for (; i < num; i++) {
378
ofono_debug("Operator: %s, %s, %s, status: %d, %d",
379
list[i].name, list[i].mcc, list[i].mnc,
380
list[i].status, list[i].tech);
384
cb(&error, num, list, cbd->data);
389
static void at_list_operators(struct ofono_modem *modem, ofono_operator_list_cb_t cb,
392
struct at_data *at = ofono_modem_get_userdata(modem);
393
struct cb_data *cbd = cb_data_new(modem, cb, data);
398
if (g_at_chat_send(at->parser, "AT+COPS=?", cops_prefix,
399
cops_list_cb, cbd, g_free) > 0)
407
DECLARE_FAILURE(error);
408
cb(&error, 0, NULL, data);
412
static void register_cb(gboolean ok, GAtResult *result, gpointer user_data)
414
struct cb_data *cbd = user_data;
415
ofono_generic_cb_t cb = cbd->cb;
416
struct ofono_error error;
418
dump_response("register_cb", ok, result);
419
decode_at_error(&error, g_at_result_final_response(result));
421
cb(&error, cbd->data);
424
static void at_register_auto(struct ofono_modem *modem, ofono_generic_cb_t cb,
427
struct at_data *at = ofono_modem_get_userdata(modem);
428
struct cb_data *cbd = cb_data_new(modem, cb, data);
433
if (g_at_chat_send(at->parser, "AT+COPS=0", none_prefix,
434
register_cb, cbd, g_free) > 0)
442
DECLARE_FAILURE(error);
447
static void at_register_manual(struct ofono_modem *modem,
448
const struct ofono_network_operator *oper,
449
ofono_generic_cb_t cb, void *data)
451
struct at_data *at = ofono_modem_get_userdata(modem);
452
struct cb_data *cbd = cb_data_new(modem, cb, data);
458
sprintf(buf, "AT+COPS=1,2,\"%s%s\"", oper->mcc, oper->mnc);
460
if (g_at_chat_send(at->parser, buf, none_prefix,
461
register_cb, cbd, g_free) > 0)
469
DECLARE_FAILURE(error);
474
static void at_deregister(struct ofono_modem *modem, ofono_generic_cb_t cb,
477
struct at_data *at = ofono_modem_get_userdata(modem);
478
struct cb_data *cbd = cb_data_new(modem, cb, data);
483
if (g_at_chat_send(at->parser, "AT+COPS=2", none_prefix,
484
register_cb, cbd, g_free) > 0)
492
DECLARE_FAILURE(error);
497
static void csq_notify(GAtResult *result, gpointer user_data)
499
struct ofono_modem *modem = user_data;
503
dump_response("csq_notify", TRUE, result);
505
g_at_result_iter_init(&iter, result);
507
if (!g_at_result_iter_next(&iter, "+CSQ:"))
510
if (!g_at_result_iter_next_number(&iter, &strength))
513
ofono_debug("csq_notify: %d", strength);
518
strength = strength * 100 / 31;
520
ofono_signal_strength_notify(modem, strength);
523
static void csq_cb(gboolean ok, GAtResult *result, gpointer user_data)
525
struct cb_data *cbd = user_data;
526
ofono_signal_strength_cb_t cb = cbd->cb;
529
struct ofono_error error;
531
dump_response("csq_cb", ok, result);
532
decode_at_error(&error, g_at_result_final_response(result));
535
cb(&error, -1, cbd->data);
539
g_at_result_iter_init(&iter, result);
541
if (!g_at_result_iter_next(&iter, "+CSQ:")) {
544
cb(&e, -1, cbd->data);
548
g_at_result_iter_next_number(&iter, &strength);
550
ofono_debug("csq_cb: %d", strength);
555
strength = strength * 100 / 31;
557
cb(&error, strength, cbd->data);
560
static void at_signal_strength(struct ofono_modem *modem,
561
ofono_signal_strength_cb_t cb, void *data)
563
struct at_data *at = ofono_modem_get_userdata(modem);
564
struct cb_data *cbd = cb_data_new(modem, cb, data);
569
if (g_at_chat_send(at->parser, "AT+CSQ", csq_prefix,
570
csq_cb, cbd, g_free) > 0)
578
DECLARE_FAILURE(error);
579
cb(&error, -1, data);
583
static void creg_notify(GAtResult *result, gpointer user_data)
585
struct ofono_modem *modem = user_data;
588
int lac = -1, ci = -1, tech = -1;
591
dump_response("creg_notify", TRUE, result);
593
g_at_result_iter_init(&iter, result);
595
if (!g_at_result_iter_next(&iter, "+CREG:"))
598
g_at_result_iter_next_number(&iter, &status);
600
if (g_at_result_iter_next_string(&iter, &str) == TRUE)
601
lac = strtol(str, NULL, 16);
605
if (g_at_result_iter_next_string(&iter, &str) == TRUE)
606
ci = strtol(str, NULL, 16);
610
g_at_result_iter_next_number(&iter, &tech);
613
ofono_debug("creg_notify: %d, %d, %d, %d", status, lac, ci, tech);
615
ofono_network_registration_notify(modem, status, lac, ci, tech);
618
static struct ofono_network_registration_ops ops = {
619
.registration_status = at_registration_status,
620
.current_operator = at_current_operator,
621
.list_operators = at_list_operators,
622
.register_auto = at_register_auto,
623
.register_manual = at_register_manual,
624
.deregister = at_deregister,
625
.signal_strength = at_signal_strength,
628
static void at_network_registration_initialized(gboolean ok, GAtResult *result,
631
struct ofono_modem *modem = user_data;
632
struct at_data *at = ofono_modem_get_userdata(modem);
635
ofono_error("Unable to initialize Network Registration");
639
g_at_chat_register(at->parser, "+CREG:",
640
creg_notify, FALSE, modem, NULL);
641
g_at_chat_register(at->parser, "+CSQ:",
642
csq_notify, FALSE, modem, NULL);
644
ofono_network_registration_register(modem, &ops);
647
void at_network_registration_init(struct ofono_modem *modem)
649
struct at_data *at = ofono_modem_get_userdata(modem);
651
at->netreg = g_try_new0(struct netreg_data, 1);
656
g_at_chat_send(at->parser, "AT+CREG=2", NULL,
657
at_network_registration_initialized,
661
void at_network_registration_exit(struct ofono_modem *modem)
663
struct at_data *at = ofono_modem_get_userdata(modem);
671
ofono_network_registration_unregister(modem);