c3ae6f71fe28def457e3e2469d16b519eff88e56
[profile/ivi/ofono.git] / src / call-meter.c
1 /*
2  *
3  *  oFono - Open Source Telephony
4  *
5  *  Copyright (C) 2008-2011  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 <string.h>
27 #include <stdio.h>
28 #include <time.h>
29 #include <errno.h>
30
31 #include <glib.h>
32 #include <gdbus.h>
33
34 #include "ofono.h"
35
36 #include "common.h"
37
38 #define CALL_METER_FLAG_CACHED 0x1
39 #define CALL_METER_FLAG_HAVE_PUCT 0x2
40
41 static GSList *g_drivers = NULL;
42
43 struct ofono_call_meter {
44         int flags;
45         DBusMessage *pending;
46         int call_meter;
47         int acm;
48         int acm_max;
49         double ppu;
50         char currency[4];
51         const struct ofono_call_meter_driver *driver;
52         void *driver_data;
53         struct ofono_atom *atom;
54 };
55
56 static void set_call_meter(struct ofono_call_meter *cm, int value)
57 {
58         DBusConnection *conn;
59         const char *path;
60
61         if (cm->call_meter == value)
62                 return;
63
64         cm->call_meter = value;
65
66         conn = ofono_dbus_get_connection();
67         path = __ofono_atom_get_path(cm->atom);
68
69         ofono_dbus_signal_property_changed(conn, path,
70                                                 OFONO_CALL_METER_INTERFACE,
71                                                 "CallMeter", DBUS_TYPE_UINT32,
72                                                 &cm->call_meter);
73 }
74
75 static void set_acm(struct ofono_call_meter *cm, int value)
76 {
77         DBusConnection *conn;
78         const char *path;
79
80         if (cm->acm == value)
81                 return;
82
83         cm->acm = value;
84
85         conn = ofono_dbus_get_connection();
86         path = __ofono_atom_get_path(cm->atom);
87
88         ofono_dbus_signal_property_changed(conn, path,
89                                                 OFONO_CALL_METER_INTERFACE,
90                                                 "AccumulatedCallMeter",
91                                                 DBUS_TYPE_UINT32, &cm->acm);
92 }
93
94 static void set_acm_max(struct ofono_call_meter *cm, int value)
95 {
96         DBusConnection *conn;
97         const char *path;
98
99         if (cm->acm_max == value)
100                 return;
101
102         cm->acm_max = value;
103
104         conn = ofono_dbus_get_connection();
105         path = __ofono_atom_get_path(cm->atom);
106
107         ofono_dbus_signal_property_changed(conn, path,
108                                                 OFONO_CALL_METER_INTERFACE,
109                                                 "AccumulatedCallMeterMaximum",
110                                                 DBUS_TYPE_UINT32, &cm->acm_max);
111 }
112
113 static void set_ppu(struct ofono_call_meter *cm, double value)
114 {
115         DBusConnection *conn;
116         const char *path;
117
118         if (cm->ppu == value)
119                 return;
120
121         cm->ppu = value;
122
123         conn = ofono_dbus_get_connection();
124         path = __ofono_atom_get_path(cm->atom);
125
126         ofono_dbus_signal_property_changed(conn, path,
127                                                 OFONO_CALL_METER_INTERFACE,
128                                                 "PricePerUnit",
129                                                 DBUS_TYPE_DOUBLE, &cm->ppu);
130 }
131
132 static void set_currency(struct ofono_call_meter *cm, const char *value)
133 {
134         DBusConnection *conn;
135         const char *path;
136         const char *dbusval;
137
138         if (strlen(value) > 3) {
139                 ofono_error("Currency reported with size > 3: %s", value);
140                 return;
141         }
142
143         if (!strcmp(cm->currency, value))
144                 return;
145
146         strncpy(cm->currency, value, 3);
147         cm->currency[3] = '\0';
148
149         conn = ofono_dbus_get_connection();
150         path = __ofono_atom_get_path(cm->atom);
151         dbusval = cm->currency;
152
153         ofono_dbus_signal_property_changed(conn, path,
154                                                 OFONO_CALL_METER_INTERFACE,
155                                                 "Currency", DBUS_TYPE_STRING,
156                                                 &dbusval);
157 }
158
159 static void cm_get_properties_reply(struct ofono_call_meter *cm)
160 {
161         DBusMessage *reply;
162         DBusMessageIter iter, dict;
163         const char *currency = cm->currency;
164
165         reply = dbus_message_new_method_return(cm->pending);
166         if (reply == NULL)
167                 return;
168
169         dbus_message_iter_init_append(reply, &iter);
170
171         dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY,
172                                         OFONO_PROPERTIES_ARRAY_SIGNATURE,
173                                         &dict);
174
175         ofono_dbus_dict_append(&dict, "CallMeter", DBUS_TYPE_UINT32,
176                                 &cm->call_meter);
177
178         ofono_dbus_dict_append(&dict, "AccumulatedCallMeter", DBUS_TYPE_UINT32,
179                                 &cm->acm);
180
181         ofono_dbus_dict_append(&dict, "AccumulatedCallMeterMaximum",
182                                 DBUS_TYPE_UINT32, &cm->acm_max);
183
184         ofono_dbus_dict_append(&dict, "PricePerUnit", DBUS_TYPE_DOUBLE,
185                                 &cm->ppu);
186
187         ofono_dbus_dict_append(&dict, "Currency", DBUS_TYPE_STRING, &currency);
188
189         dbus_message_iter_close_container(&iter, &dict);
190
191         __ofono_dbus_pending_reply(&cm->pending, reply);
192 }
193
194 static void query_call_meter_callback(const struct ofono_error *error,
195                                         int value, void *data)
196 {
197         struct ofono_call_meter *cm = data;
198
199         if (error->type == OFONO_ERROR_TYPE_NO_ERROR)
200                 set_call_meter(cm, value);
201
202         if (cm->pending)
203                 cm_get_properties_reply(cm);
204 }
205
206 static void query_call_meter(struct ofono_call_meter *cm)
207 {
208         if (cm->driver->call_meter_query == NULL) {
209                 if (cm->pending)
210                         cm_get_properties_reply(cm);
211
212                 return;
213         }
214
215         cm->driver->call_meter_query(cm, query_call_meter_callback, cm);
216 }
217
218 static void query_acm_callback(const struct ofono_error *error, int value,
219                                         void *data)
220 {
221         struct ofono_call_meter *cm = data;
222
223         if (error->type == OFONO_ERROR_TYPE_NO_ERROR)
224                 set_acm(cm, value);
225
226         query_call_meter(cm);
227 }
228
229 static void query_acm(struct ofono_call_meter *cm)
230 {
231         if (cm->driver->acm_query == NULL) {
232                 query_call_meter(cm);
233                 return;
234         }
235
236         cm->driver->acm_query(cm, query_acm_callback, cm);
237 }
238
239 static void query_acm_max_callback(const struct ofono_error *error, int value,
240                                         void *data)
241 {
242         struct ofono_call_meter *cm = data;
243
244         if (error->type == OFONO_ERROR_TYPE_NO_ERROR)
245                 set_acm_max(cm, value);
246
247         cm->flags |= CALL_METER_FLAG_CACHED;
248
249         query_acm(cm);
250 }
251
252 static void query_acm_max(struct ofono_call_meter *cm)
253 {
254         if (cm->driver->acm_max_query == NULL) {
255                 cm->flags |= CALL_METER_FLAG_CACHED;
256
257                 query_acm(cm);
258                 return;
259         }
260
261         cm->driver->acm_max_query(cm, query_acm_max_callback, cm);
262 }
263
264 static void query_puct_callback(const struct ofono_error *error,
265                                 const char *currency, double ppu, void *data)
266 {
267         struct ofono_call_meter *cm = data;
268
269         if (error->type == OFONO_ERROR_TYPE_NO_ERROR) {
270                 cm->flags |= CALL_METER_FLAG_HAVE_PUCT;
271                 set_currency(cm, currency);
272                 set_ppu(cm, ppu);
273         }
274
275         query_acm_max(cm);
276 }
277
278 static void query_puct(struct ofono_call_meter *cm)
279 {
280         if (cm->driver->puct_query == NULL)
281                 query_acm_max(cm);
282         else
283                 cm->driver->puct_query(cm, query_puct_callback, cm);
284 }
285
286 static DBusMessage *cm_get_properties(DBusConnection *conn, DBusMessage *msg,
287                                         void *data)
288 {
289         struct ofono_call_meter *cm = data;
290
291         if (cm->pending)
292                 return __ofono_error_busy(msg);
293
294         cm->pending = dbus_message_ref(msg);
295
296         /*
297          * We don't need to query ppu, currency & acm_max every time
298          * Not sure if we have to query acm & call_meter every time
299          * so lets play on the safe side and query them.  They should be
300          * fast to query anyway
301          */
302         if (cm->flags & CALL_METER_FLAG_CACHED)
303                 query_acm(cm);
304         else
305                 query_puct(cm);
306
307         return NULL;
308 }
309
310 static void set_acm_max_query_callback(const struct ofono_error *error,
311                                         int value, void *data)
312 {
313         struct ofono_call_meter *cm = data;
314         DBusMessage *reply;
315
316         if (cm->pending == NULL)
317                 return;
318
319         if (error->type != OFONO_ERROR_TYPE_NO_ERROR) {
320                 ofono_error("Setting acm_max successful, but query was not");
321
322                 cm->flags &= ~CALL_METER_FLAG_CACHED;
323
324                 __ofono_dbus_pending_reply(&cm->pending,
325                                         __ofono_error_failed(cm->pending));
326                 return;
327         }
328
329         reply = dbus_message_new_method_return(cm->pending);
330         __ofono_dbus_pending_reply(&cm->pending, reply);
331
332         set_acm_max(cm, value);
333 }
334
335 static void check_pin2_state(struct ofono_call_meter *cm)
336 {
337         struct ofono_atom *sim_atom;
338
339         sim_atom = __ofono_modem_find_atom(__ofono_atom_get_modem(cm->atom),
340                                                 OFONO_ATOM_TYPE_SIM);
341         if (sim_atom == NULL)
342                 return;
343
344         __ofono_sim_recheck_pin(__ofono_atom_get_data(sim_atom));
345 }
346
347 static void set_acm_max_callback(const struct ofono_error *error, void *data)
348 {
349         struct ofono_call_meter *cm = data;
350
351         if (error->type != OFONO_ERROR_TYPE_NO_ERROR) {
352                 DBG("Setting acm_max failed");
353                 __ofono_dbus_pending_reply(&cm->pending,
354                                         __ofono_error_failed(cm->pending));
355                 check_pin2_state(cm);
356                 return;
357         }
358
359         /* Assume if we have acm_reset, we have acm_query */
360         cm->driver->acm_max_query(cm, set_acm_max_query_callback, cm);
361 }
362
363 static DBusMessage *prop_set_acm_max(DBusMessage *msg,
364                                         struct ofono_call_meter *cm,
365                                         DBusMessageIter *dbus_value,
366                                         const char *pin2)
367 {
368         dbus_uint32_t value;
369
370         if (cm->driver->acm_max_set == NULL)
371                 return __ofono_error_not_implemented(msg);
372
373         dbus_message_iter_get_basic(dbus_value, &value);
374
375         cm->pending = dbus_message_ref(msg);
376
377         cm->driver->acm_max_set(cm, value, pin2, set_acm_max_callback, cm);
378
379         return NULL;
380 }
381
382 static void set_puct_query_callback(const struct ofono_error *error,
383                                         const char *currency, double ppu,
384                                         void *data)
385 {
386         struct ofono_call_meter *cm = data;
387         DBusMessage *reply;
388
389         if (cm->pending == NULL)
390                 return;
391
392         if (error->type != OFONO_ERROR_TYPE_NO_ERROR) {
393                 ofono_error("Setting PUCT successful, but query was not");
394
395                 cm->flags &= ~CALL_METER_FLAG_CACHED;
396
397                 __ofono_dbus_pending_reply(&cm->pending,
398                                         __ofono_error_failed(cm->pending));
399                 return;
400         }
401
402         reply = dbus_message_new_method_return(cm->pending);
403         __ofono_dbus_pending_reply(&cm->pending, reply);
404
405         set_currency(cm, currency);
406         set_ppu(cm, ppu);
407 }
408
409 static void set_puct_callback(const struct ofono_error *error, void *data)
410 {
411         struct ofono_call_meter *cm = data;
412
413         if (error->type != OFONO_ERROR_TYPE_NO_ERROR) {
414                 DBG("setting puct failed");
415                 __ofono_dbus_pending_reply(&cm->pending,
416                                         __ofono_error_failed(cm->pending));
417                 check_pin2_state(cm);
418                 return;
419         }
420
421         /* Assume if we have puct_set, we have puct_query */
422         cm->driver->puct_query(cm, set_puct_query_callback, cm);
423 }
424
425 /*
426  * This function is for the really bizarre case of someone trying to call
427  * SetProperty before GetProperties.  But we must handle it...
428  */
429 static void set_puct_initial_query_callback(const struct ofono_error *error,
430                                                 const char *currency,
431                                                 double ppu, void *data)
432 {
433         struct ofono_call_meter *cm = data;
434         DBusMessageIter iter;
435         DBusMessageIter var;
436         const char *name;
437         const char *pin2;
438
439         if (cm->pending == NULL)
440                 return;
441
442         if (error->type != OFONO_ERROR_TYPE_NO_ERROR) {
443                 __ofono_dbus_pending_reply(&cm->pending,
444                                         __ofono_error_failed(cm->pending));
445                 return;
446         }
447
448         set_currency(cm, currency);
449         set_ppu(cm, ppu);
450
451         cm->flags |= CALL_METER_FLAG_HAVE_PUCT;
452
453         dbus_message_iter_init(cm->pending, &iter);
454         dbus_message_iter_get_basic(&iter, &name);
455         dbus_message_iter_next(&iter);
456         dbus_message_iter_recurse(&iter, &var);
457         dbus_message_iter_next(&iter);
458         dbus_message_iter_get_basic(&iter, &pin2);
459
460         if (!strcmp(name, "PricePerUnit"))
461                 dbus_message_iter_get_basic(&var, &ppu);
462         else
463                 dbus_message_iter_get_basic(&var, &currency);
464
465         cm->driver->puct_set(cm, currency, ppu, pin2,
466                                 set_puct_callback, cm);
467 }
468
469 static DBusMessage *prop_set_ppu(DBusMessage *msg, struct ofono_call_meter *cm,
470                                 DBusMessageIter *var, const char *pin2)
471 {
472         double ppu;
473
474         if (cm->driver->puct_set == NULL || cm->driver->puct_query == NULL)
475                 return __ofono_error_not_implemented(msg);
476
477         dbus_message_iter_get_basic(var, &ppu);
478
479         if (ppu < 0.0)
480                 return __ofono_error_invalid_format(msg);
481
482         cm->pending = dbus_message_ref(msg);
483
484         if (cm->flags & CALL_METER_FLAG_HAVE_PUCT)
485                 cm->driver->puct_set(cm, cm->currency, ppu, pin2,
486                                         set_puct_callback, cm);
487         else
488                 cm->driver->puct_query(cm, set_puct_initial_query_callback, cm);
489
490         return NULL;
491 }
492
493 static DBusMessage *prop_set_cur(DBusMessage *msg, struct ofono_call_meter *cm,
494                                 DBusMessageIter *var, const char *pin2)
495 {
496         const char *value;
497
498         if (cm->driver->puct_set == NULL || cm->driver->puct_query == NULL)
499                 return __ofono_error_not_implemented(msg);
500
501         dbus_message_iter_get_basic(var, &value);
502
503         if (strlen(value) > 3)
504                 return __ofono_error_invalid_format(msg);
505
506         cm->pending = dbus_message_ref(msg);
507
508         if (cm->flags & CALL_METER_FLAG_HAVE_PUCT)
509                 cm->driver->puct_set(cm, value, cm->ppu, pin2,
510                                         set_puct_callback, cm);
511         else
512                 cm->driver->puct_query(cm, set_puct_initial_query_callback, cm);
513
514         return NULL;
515 }
516
517 struct call_meter_property {
518         const char *name;
519         int type;
520         DBusMessage* (*set)(DBusMessage *msg, struct ofono_call_meter *cm,
521                                 DBusMessageIter *var, const char *pin2);
522 };
523
524 static struct call_meter_property cm_properties[] = {
525         { "AccumulatedCallMeterMaximum",DBUS_TYPE_UINT32,       prop_set_acm_max },
526         { "PricePerUnit",               DBUS_TYPE_DOUBLE,       prop_set_ppu },
527         { "Currency",                   DBUS_TYPE_STRING,       prop_set_cur },
528         { NULL, 0, 0 },
529 };
530
531 static DBusMessage *cm_set_property(DBusConnection *conn, DBusMessage *msg,
532                                         void *data)
533 {
534         struct ofono_call_meter *cm = data;
535         DBusMessageIter iter;
536         DBusMessageIter var;
537         const char *name, *passwd = "";
538         struct call_meter_property *property;
539
540         if (cm->pending)
541                 return __ofono_error_busy(msg);
542
543         if (!dbus_message_iter_init(msg, &iter))
544                 return __ofono_error_invalid_args(msg);
545
546         if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_STRING)
547                 return __ofono_error_invalid_args(msg);
548
549         dbus_message_iter_get_basic(&iter, &name);
550
551         dbus_message_iter_next(&iter);
552
553         if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_VARIANT)
554                 return __ofono_error_invalid_args(msg);
555
556         dbus_message_iter_recurse(&iter, &var);
557
558         if (!dbus_message_iter_next(&iter))
559                 return __ofono_error_invalid_args(msg);
560
561         if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_STRING)
562                 return __ofono_error_invalid_args(msg);
563
564         dbus_message_iter_get_basic(&iter, &passwd);
565
566         if (!__ofono_is_valid_sim_pin(passwd, OFONO_SIM_PASSWORD_SIM_PIN2))
567                 return __ofono_error_invalid_format(msg);
568
569         for (property = cm_properties; property->name; property++) {
570                 if (strcmp(name, property->name))
571                         continue;
572
573                 if (dbus_message_iter_get_arg_type(&var) != property->type)
574                         return __ofono_error_invalid_args(msg);
575
576                 return property->set(msg, cm, &var, passwd);
577         }
578
579         return __ofono_error_invalid_args(msg);
580 }
581
582 static void reset_acm_query_callback(const struct ofono_error *error, int value,
583                                         void *data)
584 {
585         struct ofono_call_meter *cm = data;
586         DBusMessage *reply;
587
588         if (cm->pending == NULL)
589                 return;
590
591         if (error->type != OFONO_ERROR_TYPE_NO_ERROR) {
592                 ofono_error("Reseting ACM successful, but query was not");
593
594                 cm->flags &= ~CALL_METER_FLAG_CACHED;
595
596                 __ofono_dbus_pending_reply(&cm->pending,
597                                         __ofono_error_failed(cm->pending));
598                 return;
599         }
600
601         reply = dbus_message_new_method_return(cm->pending);
602         __ofono_dbus_pending_reply(&cm->pending, reply);
603
604         set_acm(cm, value);
605 }
606
607 static void acm_reset_callback(const struct ofono_error *error, void *data)
608 {
609         struct ofono_call_meter *cm = data;
610
611         if (error->type != OFONO_ERROR_TYPE_NO_ERROR) {
612                 DBG("reseting acm failed");
613                 __ofono_dbus_pending_reply(&cm->pending,
614                                         __ofono_error_failed(cm->pending));
615                 check_pin2_state(cm);
616                 return;
617         }
618
619         /* Assume if we have acm_reset, we have acm_query */
620         cm->driver->acm_query(cm, reset_acm_query_callback, cm);
621 }
622
623 static DBusMessage *cm_acm_reset(DBusConnection *conn, DBusMessage *msg,
624                                         void *data)
625 {
626         struct ofono_call_meter *cm = data;
627         const char *pin2;
628
629         if (cm->driver->acm_reset == NULL)
630                 return __ofono_error_not_implemented(msg);
631
632         if (cm->pending)
633                 return __ofono_error_busy(msg);
634
635         if (dbus_message_get_args(msg, NULL, DBUS_TYPE_STRING, &pin2,
636                                         DBUS_TYPE_INVALID) == FALSE)
637                 return __ofono_error_invalid_args(msg);
638
639         if (!__ofono_is_valid_sim_pin(pin2, OFONO_SIM_PASSWORD_SIM_PIN2))
640                 return __ofono_error_invalid_format(msg);
641
642         cm->pending = dbus_message_ref(msg);
643
644         cm->driver->acm_reset(cm, pin2, acm_reset_callback, cm);
645
646         return NULL;
647 }
648
649 static GDBusMethodTable cm_methods[] = {
650         { "GetProperties",      "",     "a{sv}",        cm_get_properties,
651                                                         G_DBUS_METHOD_FLAG_ASYNC },
652         { "SetProperty",        "svs",  "",             cm_set_property,
653                                                         G_DBUS_METHOD_FLAG_ASYNC },
654         { "Reset",              "s",    "",             cm_acm_reset,
655                                                         G_DBUS_METHOD_FLAG_ASYNC },
656         { }
657 };
658
659 static GDBusSignalTable cm_signals[] = {
660         { "PropertyChanged",    "sv" },
661         { "NearMaximumWarning", "" },
662         { }
663 };
664
665 void ofono_call_meter_changed_notify(struct ofono_call_meter *cm, int new_value)
666 {
667         set_call_meter(cm, new_value);
668 }
669
670 void ofono_call_meter_maximum_notify(struct ofono_call_meter *cm)
671 {
672         DBusConnection *conn = ofono_dbus_get_connection();
673         const char *path = __ofono_atom_get_path(cm->atom);
674
675         g_dbus_emit_signal(conn, path, OFONO_CALL_METER_INTERFACE,
676                         "NearMaximumWarning", DBUS_TYPE_INVALID);
677 }
678
679 int ofono_call_meter_driver_register(const struct ofono_call_meter_driver *d)
680 {
681         DBG("driver: %p, name: %s", d, d->name);
682
683         if (d->probe == NULL)
684                 return -EINVAL;
685
686         g_drivers = g_slist_prepend(g_drivers, (void *) d);
687
688         return 0;
689 }
690
691 void ofono_call_meter_driver_unregister(const struct ofono_call_meter_driver *d)
692 {
693         DBG("driver: %p, name: %s", d, d->name);
694
695         g_drivers = g_slist_remove(g_drivers, (void *) d);
696 }
697
698 static void call_meter_unregister(struct ofono_atom *atom)
699 {
700         struct ofono_call_meter *cm = __ofono_atom_get_data(atom);
701         const char *path = __ofono_atom_get_path(cm->atom);
702         DBusConnection *conn = ofono_dbus_get_connection();
703         struct ofono_modem *modem = __ofono_atom_get_modem(cm->atom);
704
705         ofono_modem_remove_interface(modem, OFONO_CALL_METER_INTERFACE);
706         g_dbus_unregister_interface(conn, path, OFONO_CALL_METER_INTERFACE);
707 }
708
709 static void call_meter_remove(struct ofono_atom *atom)
710 {
711         struct ofono_call_meter *cm = __ofono_atom_get_data(atom);
712
713         DBG("atom: %p", atom);
714
715         if (cm == NULL)
716                 return;
717
718         if (cm->driver && cm->driver->remove)
719                 cm->driver->remove(cm);
720
721         g_free(cm);
722 }
723
724 struct ofono_call_meter *ofono_call_meter_create(struct ofono_modem *modem,
725                                                         unsigned int vendor,
726                                                         const char *driver,
727                                                         void *data)
728 {
729         struct ofono_call_meter *cm;
730         GSList *l;
731
732         if (driver == NULL)
733                 return NULL;
734
735         cm = g_try_new0(struct ofono_call_meter, 1);
736
737         if (cm == NULL)
738                 return NULL;
739
740         cm->atom = __ofono_modem_add_atom(modem,
741                                                 OFONO_ATOM_TYPE_CALL_METER,
742                                                 call_meter_remove, cm);
743
744         for (l = g_drivers; l; l = l->next) {
745                 const struct ofono_call_meter_driver *drv = l->data;
746
747                 if (g_strcmp0(drv->name, driver))
748                         continue;
749
750                 if (drv->probe(cm, vendor, data) < 0)
751                         continue;
752
753                 cm->driver = drv;
754                 break;
755         }
756
757         return cm;
758 }
759
760 void ofono_call_meter_register(struct ofono_call_meter *cm)
761 {
762         DBusConnection *conn = ofono_dbus_get_connection();
763         const char *path = __ofono_atom_get_path(cm->atom);
764         struct ofono_modem *modem = __ofono_atom_get_modem(cm->atom);
765
766         if (!g_dbus_register_interface(conn, path, OFONO_CALL_METER_INTERFACE,
767                                         cm_methods, cm_signals, NULL, cm,
768                                         NULL)) {
769                 ofono_error("Could not create %s interface",
770                                 OFONO_CALL_METER_INTERFACE);
771
772                 return;
773         }
774
775         ofono_modem_add_interface(modem, OFONO_CALL_METER_INTERFACE);
776
777         __ofono_atom_register(cm->atom, call_meter_unregister);
778 }
779
780 void ofono_call_meter_remove(struct ofono_call_meter *cm)
781 {
782         __ofono_atom_free(cm->atom);
783 }
784
785 void ofono_call_meter_set_data(struct ofono_call_meter *cm, void *data)
786 {
787         cm->driver_data = data;
788 }
789
790 void *ofono_call_meter_get_data(struct ofono_call_meter *cm)
791 {
792         return cm->driver_data;
793 }