5 * Copyright (C) 2007-2010 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
34 #define IFF_LOWER_UP 0x10000
39 #define CONNMAN_API_SUBJECT_TO_CHANGE
40 #include <connman/plugin.h>
41 #include <connman/device.h>
42 #include <connman/inet.h>
43 #include <connman/rtnl.h>
44 #include <connman/log.h>
49 static const char *cfun_prefix[] = { "+CFUN:", NULL };
50 static const char *cind_prefix[] = { "+CIND:", NULL };
51 static const char *cops_prefix[] = { "+COPS:", NULL };
52 static const char *creg_prefix[] = { "+CREG:", NULL };
53 static const char *cgreg_prefix[] = { "+CGREG:", NULL };
59 struct connman_network *network;
61 unsigned int cimi_counter;
62 unsigned int creg_status;
65 static void mbm_debug(const char *str, void *user_data)
67 connman_info("%s", str);
70 static void emrdy_notifier(GAtResult *result, gpointer user_data)
74 static void erinfo_notifier(GAtResult *result, gpointer user_data)
79 g_at_result_iter_init(&iter, result);
81 if (g_at_result_iter_next(&iter, "*ERINFO:") == FALSE)
84 g_at_result_iter_next_number(&iter, &mode);
85 g_at_result_iter_next_number(&iter, &gsm);
86 g_at_result_iter_next_number(&iter, &umts);
88 connman_info("network capability: GSM %d UMTS %d", gsm, umts);
91 static void erinfo_callback(gboolean ok, GAtResult *result,
97 erinfo_notifier(result, user_data);
100 static void cgdcont_callback(gboolean ok, GAtResult *result,
105 g_at_result_iter_init(&iter, result);
108 static void cgreg_query(gboolean ok, GAtResult *result,
111 struct mbm_data *data = user_data;
115 if (data->network == NULL)
118 g_at_result_iter_init(&iter, result);
120 if (g_at_result_iter_next(&iter, "+CGREG:") == FALSE)
123 g_at_result_iter_skip_next(&iter);
124 g_at_result_iter_next_number(&iter, &status);
125 g_at_result_iter_skip_next(&iter);
126 g_at_result_iter_skip_next(&iter);
127 g_at_result_iter_next_number(&iter, &mode);
129 connman_network_set_uint8(data->network, "Cellular.Mode", mode);
130 connman_network_set_group(data->network, data->imsi);
133 static void enap_query(gboolean ok, GAtResult *result,
138 g_at_result_iter_init(&iter, result);
141 static void enap_enable(gboolean ok, GAtResult *result,
144 struct mbm_data *data = user_data;
147 g_at_result_iter_init(&iter, result);
149 g_at_chat_send(data->chat, "AT+CGREG?", cgreg_prefix,
150 cgreg_query, data, NULL);
153 static void enap_disable(gboolean ok, GAtResult *result,
158 g_at_result_iter_init(&iter, result);
161 static void cind_callback(gboolean ok, GAtResult *result,
164 struct connman_device *device = user_data;
165 struct mbm_data *data = connman_device_get_data(device);
172 if (data->network == NULL)
175 g_at_result_iter_init(&iter, result);
177 if (g_at_result_iter_next(&iter, "+CIND:") == FALSE)
180 g_at_result_iter_next_number(&iter, &dummy);
181 g_at_result_iter_next_number(&iter, &strength);
183 connman_network_set_strength(data->network, strength * 20);
184 connman_network_set_group(data->network, data->imsi);
187 static void network_callback(gboolean ok, GAtResult *result,
190 struct connman_device *device = user_data;
191 struct mbm_data *data = connman_device_get_data(device);
195 int mode, format, tech;
200 g_at_result_iter_init(&iter, result);
202 if (g_at_result_iter_next(&iter, "+COPS:") == FALSE)
205 g_at_result_iter_next_number(&iter, &mode);
206 g_at_result_iter_next_number(&iter, &format);
207 g_at_result_iter_next_string(&iter, &oper);
208 mccmnc = g_strdup(oper);
209 g_at_result_iter_next_number(&iter, &tech);
211 if (g_at_result_iter_next(&iter, "+COPS:") == FALSE)
214 g_at_result_iter_next_number(&iter, &mode);
215 g_at_result_iter_next_number(&iter, &format);
216 g_at_result_iter_next_string(&iter, &oper);
217 name = g_strdup(oper);
218 g_at_result_iter_next_number(&iter, &tech);
220 data->network = connman_network_create(mccmnc,
221 CONNMAN_NETWORK_TYPE_MBM);
222 if (data->network != NULL) {
226 index = connman_device_get_index(device);
227 connman_network_set_index(data->network, index);
229 connman_network_set_protocol(data->network,
230 CONNMAN_NETWORK_PROTOCOL_IP);
232 mcc = g_strndup(mccmnc, 3);
233 connman_network_set_string(data->network, "Cellular.MCC", mcc);
236 mnc = g_strdup(mccmnc + 3);
237 connman_network_set_string(data->network, "Cellular.MNC", mnc);
240 connman_network_set_name(data->network, name);
241 connman_network_set_group(data->network, data->imsi);
243 connman_device_add_network(device, data->network);
249 g_at_chat_send(data->chat, "AT+CIND?", cind_prefix,
250 cind_callback, device, NULL);
253 static void network_ready(struct connman_device *device)
255 struct mbm_data *data = connman_device_get_data(device);
257 g_at_chat_send(data->chat, "AT*E2NAP=1", NULL, NULL, NULL, NULL);
258 g_at_chat_send(data->chat, "AT*ERINFO=1", NULL, NULL, NULL, NULL);
260 g_at_chat_send(data->chat, "AT+COPS=3,2;+COPS?;+COPS=3,0;+COPS?",
261 cops_prefix, network_callback, device, NULL);
263 g_at_chat_send(data->chat, "AT*ERINFO?", NULL, erinfo_callback,
267 static gboolean lost_network(int old, int new)
269 if (old != 1 && old != 5)
272 if (new == 1 || new == 5)
278 static gboolean get_network(int old, int new)
280 if (old == 1 || old == 5)
283 if (new != 1 && new != 5)
289 static void cleanup_network(struct connman_device *device)
291 struct mbm_data *data = connman_device_get_data(device);
292 const char *identifier;
296 if (data->network == NULL)
299 connman_network_set_connected(data->network, FALSE);
301 identifier = connman_network_get_identifier(data->network);
303 connman_device_remove_network(device, identifier);
305 data->network = NULL;
308 static void update_roaming(struct connman_device *device, int status)
310 struct mbm_data *data = connman_device_get_data(device);
312 if (data->network == NULL)
315 if (status != 1 && status != 5)
319 connman_network_set_roaming(data->network, FALSE);
321 connman_network_set_roaming(data->network, TRUE);
323 connman_network_set_group(data->network, data->imsi);
326 static void creg_update(struct connman_device *device, int status)
328 struct mbm_data *data = connman_device_get_data(device);
329 int old_status = data->creg_status;
331 DBG("old_status %d status %d", old_status, status);
333 data->creg_status = status;
335 if (lost_network(old_status, status) == TRUE) {
336 cleanup_network(device);
340 if (get_network(old_status, status) == TRUE)
341 network_ready(device);
343 update_roaming(device, status);
346 static void creg_query(gboolean ok, GAtResult *result,
349 struct connman_device *device = user_data;
356 g_at_result_iter_init(&iter, result);
358 if (g_at_result_iter_next(&iter, "+CREG:") == FALSE)
361 g_at_result_iter_skip_next(&iter);
362 g_at_result_iter_next_number(&iter, &status);
364 creg_update(device, status);
367 static void cops_callback(gboolean ok, GAtResult *result,
370 struct connman_device *device = user_data;
371 struct mbm_data *data = connman_device_get_data(device);
376 g_at_chat_send(data->chat, "AT+CREG?", creg_prefix,
377 creg_query, device, NULL);
380 static void register_network(struct connman_device *device)
382 struct mbm_data *data = connman_device_get_data(device);
384 g_at_chat_send(data->chat, "AT+CREG=1",
385 NULL, NULL, NULL, NULL);
386 g_at_chat_send(data->chat, "AT+CGREG=2",
387 NULL, NULL, NULL, NULL);
388 g_at_chat_send(data->chat, "AT+CMER=3,0,0,1",
389 NULL, NULL, NULL, NULL);
391 g_at_chat_send(data->chat, "AT+COPS=0", cops_prefix,
392 cops_callback, device, NULL);
395 static void e2nap_notifier(GAtResult *result, gpointer user_data)
397 struct connman_device *device = user_data;
398 struct mbm_data *data = connman_device_get_data(device);
402 g_at_result_iter_init(&iter, result);
404 if (g_at_result_iter_next(&iter, "*E2NAP:") == FALSE)
407 g_at_result_iter_next_number(&iter, &state);
409 connman_info("network connection: state %d", state);
411 g_at_chat_send(data->chat, "AT+CIND?", cind_prefix,
412 cind_callback, device, NULL);
415 static void pacsp0_notifier(GAtResult *result, gpointer user_data)
419 static void ciev_notifier(GAtResult *result, gpointer user_data)
421 struct connman_device *device = user_data;
422 struct mbm_data *data = connman_device_get_data(device);
426 if (data->network == NULL)
429 g_at_result_iter_init(&iter, result);
431 if (g_at_result_iter_next(&iter, "+CIEV:") == FALSE)
434 g_at_result_iter_next_number(&iter, &index);
438 g_at_result_iter_next_number(&iter, &strength);
440 connman_network_set_strength(data->network, strength * 20);
441 connman_network_set_group(data->network, data->imsi);
444 static void creg_notifier(GAtResult *result, gpointer user_data)
446 struct connman_device *device = user_data;
450 g_at_result_iter_init(&iter, result);
452 if (g_at_result_iter_next(&iter, "+CREG:") == FALSE)
455 g_at_result_iter_next_number(&iter, &status);
457 creg_update(device, status);
460 static void cgreg_notifier(GAtResult *result, gpointer user_data)
462 struct connman_device *device = user_data;
463 struct mbm_data *data = connman_device_get_data(device);
467 if (data->network == NULL)
470 g_at_result_iter_init(&iter, result);
472 if (g_at_result_iter_next(&iter, "+CGREG:") == FALSE)
475 g_at_result_iter_next_number(&iter, &status);
476 g_at_result_iter_skip_next(&iter);
477 g_at_result_iter_skip_next(&iter);
478 g_at_result_iter_next_number(&iter, &mode);
480 connman_network_set_uint8(data->network, "Cellular.Mode", mode);
481 connman_network_set_group(data->network, data->imsi);
484 static void cimi_callback(gboolean ok, GAtResult *result, gpointer user_data);
486 static gboolean cimi_timeout(gpointer user_data)
488 struct connman_device *device = user_data;
489 struct mbm_data *data = connman_device_get_data(device);
491 data->cimi_counter++;
493 if (data->cimi_counter > 5) {
494 connman_device_set_powered(device, FALSE);
498 g_at_chat_send(data->chat, "AT+CIMI", NULL, cimi_callback,
504 static void cimi_callback(gboolean ok, GAtResult *result, gpointer user_data)
506 struct connman_device *device = user_data;
507 struct mbm_data *data = connman_device_get_data(device);
513 g_timeout_add_seconds(1, cimi_timeout, device);
517 g_at_result_iter_init(&iter, result);
519 for (i = 0; i < g_at_result_num_response_lines(result); i++)
520 g_at_result_iter_next(&iter, NULL);
522 imsi = g_at_result_iter_raw_line(&iter);
524 data->imsi = g_strdup(imsi);
526 register_network(device);
529 static void cfun_enable(gboolean ok, GAtResult *result,
532 struct connman_device *device = user_data;
533 struct mbm_data *data = connman_device_get_data(device);
536 connman_device_set_powered(device, FALSE);
540 connman_device_set_powered(device, TRUE);
542 g_at_chat_send(data->chat, "AT+CIMI", NULL, cimi_callback,
546 static void cfun_disable(gboolean ok, GAtResult *result,
549 struct connman_device *device = user_data;
550 struct mbm_data *data = connman_device_get_data(device);
552 connman_device_set_powered(device, FALSE);
554 if (data->chat != NULL) {
555 g_at_chat_unref(data->chat);
560 static void cfun_query(gboolean ok, GAtResult *result,
563 struct connman_device *device = user_data;
564 struct mbm_data *data = connman_device_get_data(device);
571 g_at_result_iter_init(&iter, result);
573 if (g_at_result_iter_next(&iter, "+CFUN:") == FALSE)
576 g_at_result_iter_next_number(&iter, &status);
579 connman_device_set_powered(device, TRUE);
581 g_at_chat_send(data->chat, "AT+CIMI", NULL, cimi_callback,
584 g_at_chat_send(data->chat, "AT+CFUN=1", cfun_prefix,
585 cfun_enable, device, NULL);
589 static int network_probe(struct connman_network *network)
591 struct connman_device *device = connman_network_get_device(network);
592 struct mbm_data *data;
594 DBG("network %p", network);
596 data = connman_device_get_data(device);
597 connman_network_set_data(network, data);
599 g_at_chat_send(data->chat, "AT*ENAP?", NULL,
600 enap_query, device, NULL);
605 static void network_remove(struct connman_network *network)
607 struct connman_device *device = connman_network_get_device(network);
608 struct mbm_data *data;
610 DBG("network %p", network);
612 data = connman_device_get_data(device);
613 data->network = NULL;
615 connman_network_set_data(network, NULL);
618 static int network_connect(struct connman_network *network)
620 struct mbm_data *data = connman_network_get_data(network);
624 DBG("network %p", network);
626 apn = connman_network_get_string(network, "Cellular.APN");
630 cmd = g_strdup_printf("AT+CGDCONT=1,\"IP\",\"%s\"", apn);
631 g_at_chat_send(data->chat, cmd, NULL, cgdcont_callback, NULL, NULL);
634 g_at_chat_send(data->chat, "AT*ENAP=1,1", NULL,
635 enap_enable, data, NULL);
640 static int network_disconnect(struct connman_network *network)
642 struct mbm_data *data = connman_network_get_data(network);
644 DBG("network %p", network);
646 g_at_chat_send(data->chat, "AT*ENAP=0", NULL,
647 enap_disable, data, NULL);
652 static struct connman_network_driver network_driver = {
654 .type = CONNMAN_NETWORK_TYPE_MBM,
655 .probe = network_probe,
656 .remove = network_remove,
657 .connect = network_connect,
658 .disconnect = network_disconnect,
661 static void mbm_newlink(unsigned flags, unsigned change, void *user_data)
663 struct connman_device *device = user_data;
664 struct mbm_data *data = connman_device_get_data(device);
666 if (data->network == NULL)
669 DBG("device %p flags %d change %d", device, flags, change);
671 if ((data->flags & IFF_LOWER_UP) != (flags & IFF_LOWER_UP)) {
672 if (flags & IFF_LOWER_UP) {
673 connman_network_set_method(data->network,
674 CONNMAN_IPCONFIG_METHOD_DHCP);
675 connman_network_set_connected(data->network, TRUE);
677 connman_network_set_connected(data->network, FALSE);
685 static int mbm_probe(struct connman_device *device)
687 struct mbm_data *data;
690 DBG("device %p", device);
692 data = g_try_new0(struct mbm_data, 1);
696 connman_device_set_data(device, data);
698 index = connman_device_get_index(device);
700 data->watch = connman_rtnl_add_newlink_watch(index,
701 mbm_newlink, device);
706 static void mbm_remove(struct connman_device *device)
708 struct mbm_data *data = connman_device_get_data(device);
710 DBG("device %p", device);
712 connman_device_set_data(device, NULL);
714 connman_rtnl_remove_watch(data->watch);
716 if (data->chat != NULL) {
717 g_at_chat_unref(data->chat);
725 static int mbm_enable(struct connman_device *device)
727 struct mbm_data *data = connman_device_get_data(device);
733 DBG("device %p", device);
735 devnode = connman_device_get_control(device);
739 channel = g_at_tty_open(devnode, NULL);
743 syntax = g_at_syntax_new_gsmv1();
744 data->chat = g_at_chat_new(channel, syntax);
745 g_at_syntax_unref(syntax);
747 g_io_channel_unref(channel);
749 if (data->chat == NULL)
752 if (getenv("MBM_DEBUG"))
753 g_at_chat_set_debug(data->chat, mbm_debug, NULL);
755 g_at_chat_register(data->chat, "*EMRDY:", emrdy_notifier,
756 FALSE, device, NULL);
757 g_at_chat_register(data->chat, "*ERINFO:", erinfo_notifier,
758 FALSE, device, NULL);
759 g_at_chat_register(data->chat, "*E2NAP:", e2nap_notifier,
760 FALSE, device, NULL);
761 g_at_chat_register(data->chat, "+PACSP0", pacsp0_notifier,
762 FALSE, device, NULL);
763 g_at_chat_register(data->chat, "+CIEV:", ciev_notifier,
764 FALSE, device, NULL);
766 g_at_chat_register(data->chat, "+CREG:", creg_notifier,
767 FALSE, device, NULL);
768 g_at_chat_register(data->chat, "+CGREG:", cgreg_notifier,
769 FALSE, device, NULL);
771 index = connman_device_get_index(device);
772 connman_inet_ifup(index);
774 g_at_chat_send(data->chat, "AT&F E0 V1 X4 &C1 +CMEE=1", NULL,
777 g_at_chat_send(data->chat, "AT*EMRDY?", NULL, NULL, NULL, NULL);
779 g_at_chat_send(data->chat, "AT+CFUN?", cfun_prefix,
780 cfun_query, device, NULL);
785 static int mbm_disable(struct connman_device *device)
787 struct mbm_data *data = connman_device_get_data(device);
790 DBG("device %p", device);
792 g_at_chat_send(data->chat, "AT+CMER=0", NULL, NULL, NULL, NULL);
793 g_at_chat_send(data->chat, "AT+CREG=0", NULL, NULL, NULL, NULL);
794 g_at_chat_send(data->chat, "AT+CGREG=0", NULL, NULL, NULL, NULL);
796 g_at_chat_send(data->chat, "AT+CFUN=4", cfun_prefix,
797 cfun_disable, device, NULL);
799 index = connman_device_get_index(device);
800 connman_inet_ifdown(index);
805 static struct connman_device_driver mbm_driver = {
807 .type = CONNMAN_DEVICE_TYPE_MBM,
809 .remove = mbm_remove,
810 .enable = mbm_enable,
811 .disable = mbm_disable,
814 static int mbm_init(void)
818 err = connman_network_driver_register(&network_driver);
822 err = connman_device_driver_register(&mbm_driver);
824 connman_network_driver_unregister(&network_driver);
831 static void mbm_exit(void)
833 connman_device_driver_unregister(&mbm_driver);
834 connman_network_driver_register(&network_driver);
837 CONNMAN_PLUGIN_DEFINE(mbm, "Ericsson MBM device plugin", VERSION,
838 CONNMAN_PLUGIN_PRIORITY_DEFAULT, mbm_init, mbm_exit)