Fix some WPA Enterprise privacy issues
[platform/upstream/connman.git] / plugins / mbm.c
1 /*
2  *
3  *  Connection Manager
4  *
5  *  Copyright (C) 2007-2010  Intel Corporation. All rights reserved.
6  *
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.
10  *
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.
15  *
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
19  *
20  */
21
22 #ifdef HAVE_CONFIG_H
23 #include <config.h>
24 #endif
25
26 #include <stdio.h>
27 #include <errno.h>
28 #include <unistd.h>
29 #include <stdlib.h>
30 #include <string.h>
31 #include <net/if.h>
32
33 #ifndef IFF_LOWER_UP
34 #define IFF_LOWER_UP    0x10000
35 #endif
36
37 #include <glib.h>
38
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>
45
46 #include <gatchat.h>
47 #include <gattty.h>
48
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 };
54
55 struct mbm_data {
56         GAtChat *chat;
57         unsigned flags;
58         unsigned int watch;
59         struct connman_network *network;
60         char *imsi;
61         unsigned int cimi_counter;
62         unsigned int creg_status;
63 };
64
65 static void mbm_debug(const char *str, void *user_data)
66 {
67         connman_info("%s", str);
68 }
69
70 static void emrdy_notifier(GAtResult *result, gpointer user_data)
71 {
72 }
73
74 static void erinfo_notifier(GAtResult *result, gpointer user_data)
75 {
76         GAtResultIter iter;
77         int mode, gsm, umts;
78
79         g_at_result_iter_init(&iter, result);
80
81         if (g_at_result_iter_next(&iter, "*ERINFO:") == FALSE)
82                 return;
83
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);
87
88         connman_info("network capability: GSM %d UMTS %d", gsm, umts);
89 }
90
91 static void erinfo_callback(gboolean ok, GAtResult *result,
92                                                 gpointer user_data)
93 {
94         if (ok == FALSE)
95                 return;
96
97         erinfo_notifier(result, user_data);
98 }
99
100 static void cgdcont_callback(gboolean ok, GAtResult *result,
101                                                 gpointer user_data)
102 {
103         GAtResultIter iter;
104
105         g_at_result_iter_init(&iter, result);
106 }
107
108 static void cgreg_query(gboolean ok, GAtResult *result,
109                                                 gpointer user_data)
110 {
111         struct mbm_data *data = user_data;
112         GAtResultIter iter;
113         int status, mode;
114
115         if (data->network == NULL)
116                 return;
117
118         g_at_result_iter_init(&iter, result);
119
120         if (g_at_result_iter_next(&iter, "+CGREG:") == FALSE)
121                 return;
122
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);
128
129         connman_network_set_uint8(data->network, "Cellular.Mode", mode);
130         connman_network_set_group(data->network, data->imsi);
131 }
132
133 static void enap_query(gboolean ok, GAtResult *result,
134                                                 gpointer user_data)
135 {
136         GAtResultIter iter;
137
138         g_at_result_iter_init(&iter, result);
139 }
140
141 static void enap_enable(gboolean ok, GAtResult *result,
142                                                 gpointer user_data)
143 {
144         struct mbm_data *data = user_data;
145         GAtResultIter iter;
146
147         g_at_result_iter_init(&iter, result);
148
149         g_at_chat_send(data->chat, "AT+CGREG?", cgreg_prefix,
150                                                 cgreg_query, data, NULL);
151 }
152
153 static void enap_disable(gboolean ok, GAtResult *result,
154                                                 gpointer user_data)
155 {
156         GAtResultIter iter;
157
158         g_at_result_iter_init(&iter, result);
159 }
160
161 static void cind_callback(gboolean ok, GAtResult *result,
162                                                 gpointer user_data)
163 {
164         struct connman_device *device = user_data;
165         struct mbm_data *data = connman_device_get_data(device);
166         GAtResultIter iter;
167         int dummy, strength;
168
169         if (ok == FALSE)
170                 return;
171
172         if (data->network == NULL)
173                 return;
174
175         g_at_result_iter_init(&iter, result);
176
177         if (g_at_result_iter_next(&iter, "+CIND:") == FALSE)
178                 return;
179
180         g_at_result_iter_next_number(&iter, &dummy);
181         g_at_result_iter_next_number(&iter, &strength);
182
183         connman_network_set_strength(data->network, strength * 20);
184         connman_network_set_group(data->network, data->imsi);
185 }
186
187 static void network_callback(gboolean ok, GAtResult *result,
188                                                 gpointer user_data)
189 {
190         struct connman_device *device = user_data;
191         struct mbm_data *data = connman_device_get_data(device);
192         GAtResultIter iter;
193         char *name, *mccmnc;
194         const char *oper;
195         int mode, format, tech;
196
197         if (ok == FALSE)
198                 return;
199
200         g_at_result_iter_init(&iter, result);
201
202         if (g_at_result_iter_next(&iter, "+COPS:") == FALSE)
203                 return;
204
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);
210
211         if (g_at_result_iter_next(&iter, "+COPS:") == FALSE)
212                 return;
213
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);
219
220         data->network = connman_network_create(mccmnc,
221                                                 CONNMAN_NETWORK_TYPE_MBM);
222         if (data->network != NULL) {
223                 char *mcc, *mnc;
224                 int index;
225
226                 index = connman_device_get_index(device);
227                 connman_network_set_index(data->network, index);
228
229                 connman_network_set_protocol(data->network,
230                                                 CONNMAN_NETWORK_PROTOCOL_IP);
231
232                 mcc = g_strndup(mccmnc, 3);
233                 connman_network_set_string(data->network, "Cellular.MCC", mcc);
234                 g_free(mcc);
235
236                 mnc = g_strdup(mccmnc + 3);
237                 connman_network_set_string(data->network, "Cellular.MNC", mnc);
238                 g_free(mnc);
239
240                 connman_network_set_name(data->network, name);
241                 connman_network_set_group(data->network, data->imsi);
242
243                 connman_device_add_network(device, data->network);
244         }
245
246         g_free(name);
247         g_free(mccmnc);
248
249         g_at_chat_send(data->chat, "AT+CIND?", cind_prefix,
250                                                 cind_callback, device, NULL);
251 }
252
253 static void network_ready(struct connman_device *device)
254 {
255         struct mbm_data *data = connman_device_get_data(device);
256
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);
259
260         g_at_chat_send(data->chat, "AT+COPS=3,2;+COPS?;+COPS=3,0;+COPS?",
261                                 cops_prefix, network_callback, device, NULL);
262
263         g_at_chat_send(data->chat, "AT*ERINFO?", NULL, erinfo_callback,
264                                                                 device, NULL);
265 }
266
267 static gboolean lost_network(int old, int new)
268 {
269         if (old != 1 && old != 5)
270                 return FALSE;
271
272         if (new == 1 || new == 5)
273                 return FALSE;
274
275         return TRUE;
276 }
277
278 static gboolean get_network(int old, int new)
279 {
280         if (old == 1 || old == 5)
281                 return FALSE;
282
283         if (new != 1 && new != 5)
284                 return FALSE;
285
286         return TRUE;
287 }
288
289 static void cleanup_network(struct connman_device *device)
290 {
291         struct mbm_data *data = connman_device_get_data(device);
292         const char *identifier;
293
294         DBG("");
295
296         if (data->network == NULL)
297                 return;
298
299         connman_network_set_connected(data->network, FALSE);
300
301         identifier = connman_network_get_identifier(data->network);
302
303         connman_device_remove_network(device, identifier);
304
305         data->network = NULL;
306 }
307
308 static void update_roaming(struct connman_device *device, int status)
309 {
310         struct mbm_data *data = connman_device_get_data(device);
311
312         if (data->network == NULL)
313                 return;
314
315         if (status != 1 && status != 5)
316                 return;
317
318         if (status == 1)
319                 connman_network_set_roaming(data->network, FALSE);
320         else
321                 connman_network_set_roaming(data->network, TRUE);
322
323         connman_network_set_group(data->network, data->imsi);
324 }
325
326 static void creg_update(struct connman_device *device, int status)
327 {
328         struct mbm_data *data = connman_device_get_data(device);
329         int old_status = data->creg_status;
330
331         DBG("old_status %d status %d", old_status, status);
332
333         data->creg_status = status;
334
335         if (lost_network(old_status, status) == TRUE) {
336                 cleanup_network(device);
337                 return;
338         }
339
340         if (get_network(old_status, status) == TRUE)
341                 network_ready(device);
342
343         update_roaming(device, status);
344 }
345
346 static void creg_query(gboolean ok, GAtResult *result,
347                                                 gpointer user_data)
348 {
349         struct connman_device *device = user_data;
350         GAtResultIter iter;
351         int status;
352
353         if (ok == FALSE)
354                 return;
355
356         g_at_result_iter_init(&iter, result);
357
358         if (g_at_result_iter_next(&iter, "+CREG:") == FALSE)
359                 return;
360
361         g_at_result_iter_skip_next(&iter);
362         g_at_result_iter_next_number(&iter, &status);
363
364         creg_update(device, status);
365 }
366
367 static void cops_callback(gboolean ok, GAtResult *result,
368                                                 gpointer user_data)
369 {
370         struct connman_device *device = user_data;
371         struct mbm_data *data = connman_device_get_data(device);
372
373         if (ok == FALSE)
374                 return;
375
376         g_at_chat_send(data->chat, "AT+CREG?", creg_prefix,
377                                                 creg_query, device, NULL);
378 }
379
380 static void register_network(struct connman_device *device)
381 {
382         struct mbm_data *data = connman_device_get_data(device);
383
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);
390
391         g_at_chat_send(data->chat, "AT+COPS=0", cops_prefix,
392                                                 cops_callback, device, NULL);
393 }
394
395 static void e2nap_notifier(GAtResult *result, gpointer user_data)
396 {
397         struct connman_device *device = user_data;
398         struct mbm_data *data = connman_device_get_data(device);
399         GAtResultIter iter;
400         int state;
401
402         g_at_result_iter_init(&iter, result);
403
404         if (g_at_result_iter_next(&iter, "*E2NAP:") == FALSE)
405                 return;
406
407         g_at_result_iter_next_number(&iter, &state);
408
409         connman_info("network connection: state %d", state);
410
411         g_at_chat_send(data->chat, "AT+CIND?", cind_prefix,
412                                                 cind_callback, device, NULL);
413 }
414
415 static void pacsp0_notifier(GAtResult *result, gpointer user_data)
416 {
417 }
418
419 static void ciev_notifier(GAtResult *result, gpointer user_data)
420 {
421         struct connman_device *device = user_data;
422         struct mbm_data *data = connman_device_get_data(device);
423         GAtResultIter iter;
424         int index, strength;
425
426         if (data->network == NULL)
427                 return;
428
429         g_at_result_iter_init(&iter, result);
430
431         if (g_at_result_iter_next(&iter, "+CIEV:") == FALSE)
432                 return;
433
434         g_at_result_iter_next_number(&iter, &index);
435         if (index != 2)
436                 return;
437
438         g_at_result_iter_next_number(&iter, &strength);
439
440         connman_network_set_strength(data->network, strength * 20);
441         connman_network_set_group(data->network, data->imsi);
442 }
443
444 static void creg_notifier(GAtResult *result, gpointer user_data)
445 {
446         struct connman_device *device = user_data;
447         GAtResultIter iter;
448         int status;
449
450         g_at_result_iter_init(&iter, result);
451
452         if (g_at_result_iter_next(&iter, "+CREG:") == FALSE)
453                 return;
454
455         g_at_result_iter_next_number(&iter, &status);
456
457         creg_update(device, status);
458 }
459
460 static void cgreg_notifier(GAtResult *result, gpointer user_data)
461 {
462         struct connman_device *device = user_data;
463         struct mbm_data *data = connman_device_get_data(device);
464         GAtResultIter iter;
465         int status, mode;
466
467         if (data->network == NULL)
468                 return;
469
470         g_at_result_iter_init(&iter, result);
471
472         if (g_at_result_iter_next(&iter, "+CGREG:") == FALSE)
473                 return;
474
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);
479
480         connman_network_set_uint8(data->network, "Cellular.Mode", mode);
481         connman_network_set_group(data->network, data->imsi);
482 }
483
484 static void cimi_callback(gboolean ok, GAtResult *result, gpointer user_data);
485
486 static gboolean cimi_timeout(gpointer user_data)
487 {
488         struct connman_device *device = user_data;
489         struct mbm_data *data = connman_device_get_data(device);
490
491         data->cimi_counter++;
492
493         if (data->cimi_counter > 5) {
494                 connman_device_set_powered(device, FALSE);
495                 return FALSE;
496         }
497
498         g_at_chat_send(data->chat, "AT+CIMI", NULL, cimi_callback,
499                                                         device, NULL);
500
501         return FALSE;
502 }
503
504 static void cimi_callback(gboolean ok, GAtResult *result, gpointer user_data)
505 {
506         struct connman_device *device = user_data;
507         struct mbm_data *data = connman_device_get_data(device);
508         GAtResultIter iter;
509         const char *imsi;
510         int i;
511
512         if (ok == FALSE) {
513                 g_timeout_add_seconds(1, cimi_timeout, device);
514                 return;
515         }
516
517         g_at_result_iter_init(&iter, result);
518
519         for (i = 0; i < g_at_result_num_response_lines(result); i++)
520                 g_at_result_iter_next(&iter, NULL);
521
522         imsi = g_at_result_iter_raw_line(&iter);
523
524         data->imsi = g_strdup(imsi);
525
526         register_network(device);
527 }
528
529 static void cfun_enable(gboolean ok, GAtResult *result,
530                                                 gpointer user_data)
531 {
532         struct connman_device *device = user_data;
533         struct mbm_data *data = connman_device_get_data(device);
534
535         if (ok == FALSE) {
536                 connman_device_set_powered(device, FALSE);
537                 return;
538         }
539
540         connman_device_set_powered(device, TRUE);
541
542         g_at_chat_send(data->chat, "AT+CIMI", NULL, cimi_callback,
543                                                         device, NULL);
544 }
545
546 static void cfun_disable(gboolean ok, GAtResult *result,
547                                                 gpointer user_data)
548 {
549         struct connman_device *device = user_data;
550         struct mbm_data *data = connman_device_get_data(device);
551
552         connman_device_set_powered(device, FALSE);
553
554         if (data->chat != NULL) {
555                 g_at_chat_unref(data->chat);
556                 data->chat = NULL;
557         }
558 }
559
560 static void cfun_query(gboolean ok, GAtResult *result,
561                                                 gpointer user_data)
562 {
563         struct connman_device *device = user_data;
564         struct mbm_data *data = connman_device_get_data(device);
565         GAtResultIter iter;
566         int status;
567
568         if (ok == FALSE)
569                 return;
570
571         g_at_result_iter_init(&iter, result);
572
573         if (g_at_result_iter_next(&iter, "+CFUN:") == FALSE)
574                 return;
575
576         g_at_result_iter_next_number(&iter, &status);
577
578         if (status == 1) {
579                 connman_device_set_powered(device, TRUE);
580
581                 g_at_chat_send(data->chat, "AT+CIMI", NULL, cimi_callback,
582                                                                 device, NULL);
583         } else {
584                 g_at_chat_send(data->chat, "AT+CFUN=1", cfun_prefix,
585                                                 cfun_enable, device, NULL);
586         }
587 }
588
589 static int network_probe(struct connman_network *network)
590 {
591         struct connman_device *device = connman_network_get_device(network);
592         struct mbm_data *data;
593
594         DBG("network %p", network);
595
596         data = connman_device_get_data(device);
597         connman_network_set_data(network, data);
598
599         g_at_chat_send(data->chat, "AT*ENAP?", NULL,
600                                         enap_query, device, NULL);
601
602         return 0;
603 }
604
605 static void network_remove(struct connman_network *network)
606 {
607         struct connman_device *device = connman_network_get_device(network);
608         struct mbm_data *data;
609
610         DBG("network %p", network);
611
612         data = connman_device_get_data(device);
613         data->network = NULL;
614
615         connman_network_set_data(network, NULL);
616 }
617
618 static int network_connect(struct connman_network *network)
619 {
620         struct mbm_data *data = connman_network_get_data(network);
621         const char *apn;
622         char *cmd;
623
624         DBG("network %p", network);
625
626         apn = connman_network_get_string(network, "Cellular.APN");
627         if (apn == NULL)
628                 return -EINVAL;
629
630         cmd = g_strdup_printf("AT+CGDCONT=1,\"IP\",\"%s\"", apn);
631         g_at_chat_send(data->chat, cmd, NULL, cgdcont_callback, NULL, NULL);
632         g_free(cmd);
633
634         g_at_chat_send(data->chat, "AT*ENAP=1,1", NULL,
635                                         enap_enable, data, NULL);
636
637         return 0;
638 }
639
640 static int network_disconnect(struct connman_network *network)
641 {
642         struct mbm_data *data = connman_network_get_data(network);
643
644         DBG("network %p", network);
645
646         g_at_chat_send(data->chat, "AT*ENAP=0", NULL,
647                                         enap_disable, data, NULL);
648
649         return 0;
650 }
651
652 static struct connman_network_driver network_driver = {
653         .name           = "mbm",
654         .type           = CONNMAN_NETWORK_TYPE_MBM,
655         .probe          = network_probe,
656         .remove         = network_remove,
657         .connect        = network_connect,
658         .disconnect     = network_disconnect,
659 };
660
661 static void mbm_newlink(unsigned flags, unsigned change, void *user_data)
662 {
663         struct connman_device *device = user_data;
664         struct mbm_data *data = connman_device_get_data(device);
665
666         if (data->network == NULL)
667                 goto done;
668
669         DBG("device %p flags %d change %d", device, flags, change);
670
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);
676                 } else {
677                         connman_network_set_connected(data->network, FALSE);
678                 }
679         }
680
681 done:
682         data->flags = flags;
683 }
684
685 static int mbm_probe(struct connman_device *device)
686 {
687         struct mbm_data *data;
688         int index;
689
690         DBG("device %p", device);
691
692         data = g_try_new0(struct mbm_data, 1);
693         if (data == NULL)
694                 return -ENOMEM;
695
696         connman_device_set_data(device, data);
697
698         index = connman_device_get_index(device);
699
700         data->watch = connman_rtnl_add_newlink_watch(index,
701                                                 mbm_newlink, device);
702
703         return 0;
704 }
705
706 static void mbm_remove(struct connman_device *device)
707 {
708         struct mbm_data *data = connman_device_get_data(device);
709
710         DBG("device %p", device);
711
712         connman_device_set_data(device, NULL);
713
714         connman_rtnl_remove_watch(data->watch);
715
716         if (data->chat != NULL) {
717                 g_at_chat_unref(data->chat);
718                 data->chat = NULL;
719         }
720
721         g_free(data->imsi);
722         g_free(data);
723 }
724
725 static int mbm_enable(struct connman_device *device)
726 {
727         struct mbm_data *data = connman_device_get_data(device);
728         GAtSyntax *syntax;
729         GIOChannel *channel;
730         const char *devnode;
731         int index;
732
733         DBG("device %p", device);
734
735         devnode = connman_device_get_control(device);
736         if (devnode == NULL)
737                 return -EIO;
738
739         channel = g_at_tty_open(devnode, NULL);
740         if (channel == NULL)
741                 return -EIO;
742
743         syntax = g_at_syntax_new_gsmv1();
744         data->chat = g_at_chat_new(channel, syntax);
745         g_at_syntax_unref(syntax);
746
747         g_io_channel_unref(channel);
748
749         if (data->chat == NULL)
750                 return -EIO;
751
752         if (getenv("MBM_DEBUG"))
753                 g_at_chat_set_debug(data->chat, mbm_debug, NULL);
754
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);
765
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);
770
771         index = connman_device_get_index(device);
772         connman_inet_ifup(index);
773
774         g_at_chat_send(data->chat, "AT&F E0 V1 X4 &C1 +CMEE=1", NULL,
775                                                         NULL, NULL, NULL);
776
777         g_at_chat_send(data->chat, "AT*EMRDY?", NULL, NULL, NULL, NULL);
778
779         g_at_chat_send(data->chat, "AT+CFUN?", cfun_prefix,
780                                                 cfun_query, device, NULL);
781
782         return -EINPROGRESS;
783 }
784
785 static int mbm_disable(struct connman_device *device)
786 {
787         struct mbm_data *data = connman_device_get_data(device);
788         int index;
789
790         DBG("device %p", device);
791
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);
795
796         g_at_chat_send(data->chat, "AT+CFUN=4", cfun_prefix,
797                                                 cfun_disable, device, NULL);
798
799         index = connman_device_get_index(device);
800         connman_inet_ifdown(index);
801
802         return -EINPROGRESS;
803 }
804
805 static struct connman_device_driver mbm_driver = {
806         .name           = "mbm",
807         .type           = CONNMAN_DEVICE_TYPE_MBM,
808         .probe          = mbm_probe,
809         .remove         = mbm_remove,
810         .enable         = mbm_enable,
811         .disable        = mbm_disable,
812 };
813
814 static int mbm_init(void)
815 {
816         int err;
817
818         err = connman_network_driver_register(&network_driver);
819         if (err < 0)
820                 return err;
821
822         err = connman_device_driver_register(&mbm_driver);
823         if (err < 0) {
824                 connman_network_driver_unregister(&network_driver);
825                 return err;
826         }
827
828         return 0;
829 }
830
831 static void mbm_exit(void)
832 {
833         connman_device_driver_unregister(&mbm_driver);
834         connman_network_driver_register(&network_driver);
835 }
836
837 CONNMAN_PLUGIN_DEFINE(mbm, "Ericsson MBM device plugin", VERSION,
838                 CONNMAN_PLUGIN_PRIORITY_DEFAULT, mbm_init, mbm_exit)