3 * oFono - Open Source Telephony
5 * Copyright (C) 2008-2011 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
38 #define CALL_METER_FLAG_CACHED 0x1
39 #define CALL_METER_FLAG_HAVE_PUCT 0x2
41 static GSList *g_drivers = NULL;
43 struct ofono_call_meter {
51 const struct ofono_call_meter_driver *driver;
53 struct ofono_atom *atom;
56 static void set_call_meter(struct ofono_call_meter *cm, int value)
61 if (cm->call_meter == value)
64 cm->call_meter = value;
66 conn = ofono_dbus_get_connection();
67 path = __ofono_atom_get_path(cm->atom);
69 ofono_dbus_signal_property_changed(conn, path,
70 OFONO_CALL_METER_INTERFACE,
71 "CallMeter", DBUS_TYPE_UINT32,
75 static void set_acm(struct ofono_call_meter *cm, int value)
85 conn = ofono_dbus_get_connection();
86 path = __ofono_atom_get_path(cm->atom);
88 ofono_dbus_signal_property_changed(conn, path,
89 OFONO_CALL_METER_INTERFACE,
90 "AccumulatedCallMeter",
91 DBUS_TYPE_UINT32, &cm->acm);
94 static void set_acm_max(struct ofono_call_meter *cm, int value)
99 if (cm->acm_max == value)
104 conn = ofono_dbus_get_connection();
105 path = __ofono_atom_get_path(cm->atom);
107 ofono_dbus_signal_property_changed(conn, path,
108 OFONO_CALL_METER_INTERFACE,
109 "AccumulatedCallMeterMaximum",
110 DBUS_TYPE_UINT32, &cm->acm_max);
113 static void set_ppu(struct ofono_call_meter *cm, double value)
115 DBusConnection *conn;
118 if (cm->ppu == value)
123 conn = ofono_dbus_get_connection();
124 path = __ofono_atom_get_path(cm->atom);
126 ofono_dbus_signal_property_changed(conn, path,
127 OFONO_CALL_METER_INTERFACE,
129 DBUS_TYPE_DOUBLE, &cm->ppu);
132 static void set_currency(struct ofono_call_meter *cm, const char *value)
134 DBusConnection *conn;
138 if (strlen(value) > 3) {
139 ofono_error("Currency reported with size > 3: %s", value);
143 if (!strcmp(cm->currency, value))
146 strncpy(cm->currency, value, 3);
147 cm->currency[3] = '\0';
149 conn = ofono_dbus_get_connection();
150 path = __ofono_atom_get_path(cm->atom);
151 dbusval = cm->currency;
153 ofono_dbus_signal_property_changed(conn, path,
154 OFONO_CALL_METER_INTERFACE,
155 "Currency", DBUS_TYPE_STRING,
159 static void cm_get_properties_reply(struct ofono_call_meter *cm)
162 DBusMessageIter iter, dict;
163 const char *currency = cm->currency;
165 reply = dbus_message_new_method_return(cm->pending);
169 dbus_message_iter_init_append(reply, &iter);
171 dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY,
172 OFONO_PROPERTIES_ARRAY_SIGNATURE,
175 ofono_dbus_dict_append(&dict, "CallMeter", DBUS_TYPE_UINT32,
178 ofono_dbus_dict_append(&dict, "AccumulatedCallMeter", DBUS_TYPE_UINT32,
181 ofono_dbus_dict_append(&dict, "AccumulatedCallMeterMaximum",
182 DBUS_TYPE_UINT32, &cm->acm_max);
184 ofono_dbus_dict_append(&dict, "PricePerUnit", DBUS_TYPE_DOUBLE,
187 ofono_dbus_dict_append(&dict, "Currency", DBUS_TYPE_STRING, ¤cy);
189 dbus_message_iter_close_container(&iter, &dict);
191 __ofono_dbus_pending_reply(&cm->pending, reply);
194 static void query_call_meter_callback(const struct ofono_error *error,
195 int value, void *data)
197 struct ofono_call_meter *cm = data;
199 if (error->type == OFONO_ERROR_TYPE_NO_ERROR)
200 set_call_meter(cm, value);
203 cm_get_properties_reply(cm);
206 static void query_call_meter(struct ofono_call_meter *cm)
208 if (cm->driver->call_meter_query == NULL) {
210 cm_get_properties_reply(cm);
215 cm->driver->call_meter_query(cm, query_call_meter_callback, cm);
218 static void query_acm_callback(const struct ofono_error *error, int value,
221 struct ofono_call_meter *cm = data;
223 if (error->type == OFONO_ERROR_TYPE_NO_ERROR)
226 query_call_meter(cm);
229 static void query_acm(struct ofono_call_meter *cm)
231 if (cm->driver->acm_query == NULL) {
232 query_call_meter(cm);
236 cm->driver->acm_query(cm, query_acm_callback, cm);
239 static void query_acm_max_callback(const struct ofono_error *error, int value,
242 struct ofono_call_meter *cm = data;
244 if (error->type == OFONO_ERROR_TYPE_NO_ERROR)
245 set_acm_max(cm, value);
247 cm->flags |= CALL_METER_FLAG_CACHED;
252 static void query_acm_max(struct ofono_call_meter *cm)
254 if (cm->driver->acm_max_query == NULL) {
255 cm->flags |= CALL_METER_FLAG_CACHED;
261 cm->driver->acm_max_query(cm, query_acm_max_callback, cm);
264 static void query_puct_callback(const struct ofono_error *error,
265 const char *currency, double ppu, void *data)
267 struct ofono_call_meter *cm = data;
269 if (error->type == OFONO_ERROR_TYPE_NO_ERROR) {
270 cm->flags |= CALL_METER_FLAG_HAVE_PUCT;
271 set_currency(cm, currency);
278 static void query_puct(struct ofono_call_meter *cm)
280 if (cm->driver->puct_query == NULL)
283 cm->driver->puct_query(cm, query_puct_callback, cm);
286 static DBusMessage *cm_get_properties(DBusConnection *conn, DBusMessage *msg,
289 struct ofono_call_meter *cm = data;
292 return __ofono_error_busy(msg);
294 cm->pending = dbus_message_ref(msg);
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
302 if (cm->flags & CALL_METER_FLAG_CACHED)
310 static void set_acm_max_query_callback(const struct ofono_error *error,
311 int value, void *data)
313 struct ofono_call_meter *cm = data;
316 if (cm->pending == NULL)
319 if (error->type != OFONO_ERROR_TYPE_NO_ERROR) {
320 ofono_error("Setting acm_max successful, but query was not");
322 cm->flags &= ~CALL_METER_FLAG_CACHED;
324 __ofono_dbus_pending_reply(&cm->pending,
325 __ofono_error_failed(cm->pending));
329 reply = dbus_message_new_method_return(cm->pending);
330 __ofono_dbus_pending_reply(&cm->pending, reply);
332 set_acm_max(cm, value);
335 static void check_pin2_state(struct ofono_call_meter *cm)
337 struct ofono_atom *sim_atom;
339 sim_atom = __ofono_modem_find_atom(__ofono_atom_get_modem(cm->atom),
340 OFONO_ATOM_TYPE_SIM);
341 if (sim_atom == NULL)
344 __ofono_sim_recheck_pin(__ofono_atom_get_data(sim_atom));
347 static void set_acm_max_callback(const struct ofono_error *error, void *data)
349 struct ofono_call_meter *cm = data;
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);
359 /* Assume if we have acm_reset, we have acm_query */
360 cm->driver->acm_max_query(cm, set_acm_max_query_callback, cm);
363 static DBusMessage *prop_set_acm_max(DBusMessage *msg,
364 struct ofono_call_meter *cm,
365 DBusMessageIter *dbus_value,
370 if (cm->driver->acm_max_set == NULL)
371 return __ofono_error_not_implemented(msg);
373 dbus_message_iter_get_basic(dbus_value, &value);
375 cm->pending = dbus_message_ref(msg);
377 cm->driver->acm_max_set(cm, value, pin2, set_acm_max_callback, cm);
382 static void set_puct_query_callback(const struct ofono_error *error,
383 const char *currency, double ppu,
386 struct ofono_call_meter *cm = data;
389 if (cm->pending == NULL)
392 if (error->type != OFONO_ERROR_TYPE_NO_ERROR) {
393 ofono_error("Setting PUCT successful, but query was not");
395 cm->flags &= ~CALL_METER_FLAG_CACHED;
397 __ofono_dbus_pending_reply(&cm->pending,
398 __ofono_error_failed(cm->pending));
402 reply = dbus_message_new_method_return(cm->pending);
403 __ofono_dbus_pending_reply(&cm->pending, reply);
405 set_currency(cm, currency);
409 static void set_puct_callback(const struct ofono_error *error, void *data)
411 struct ofono_call_meter *cm = data;
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);
421 /* Assume if we have puct_set, we have puct_query */
422 cm->driver->puct_query(cm, set_puct_query_callback, cm);
426 * This function is for the really bizarre case of someone trying to call
427 * SetProperty before GetProperties. But we must handle it...
429 static void set_puct_initial_query_callback(const struct ofono_error *error,
430 const char *currency,
431 double ppu, void *data)
433 struct ofono_call_meter *cm = data;
434 DBusMessageIter iter;
439 if (cm->pending == NULL)
442 if (error->type != OFONO_ERROR_TYPE_NO_ERROR) {
443 __ofono_dbus_pending_reply(&cm->pending,
444 __ofono_error_failed(cm->pending));
448 set_currency(cm, currency);
451 cm->flags |= CALL_METER_FLAG_HAVE_PUCT;
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);
460 if (!strcmp(name, "PricePerUnit"))
461 dbus_message_iter_get_basic(&var, &ppu);
463 dbus_message_iter_get_basic(&var, ¤cy);
465 cm->driver->puct_set(cm, currency, ppu, pin2,
466 set_puct_callback, cm);
469 static DBusMessage *prop_set_ppu(DBusMessage *msg, struct ofono_call_meter *cm,
470 DBusMessageIter *var, const char *pin2)
474 if (cm->driver->puct_set == NULL || cm->driver->puct_query == NULL)
475 return __ofono_error_not_implemented(msg);
477 dbus_message_iter_get_basic(var, &ppu);
480 return __ofono_error_invalid_format(msg);
482 cm->pending = dbus_message_ref(msg);
484 if (cm->flags & CALL_METER_FLAG_HAVE_PUCT)
485 cm->driver->puct_set(cm, cm->currency, ppu, pin2,
486 set_puct_callback, cm);
488 cm->driver->puct_query(cm, set_puct_initial_query_callback, cm);
493 static DBusMessage *prop_set_cur(DBusMessage *msg, struct ofono_call_meter *cm,
494 DBusMessageIter *var, const char *pin2)
498 if (cm->driver->puct_set == NULL || cm->driver->puct_query == NULL)
499 return __ofono_error_not_implemented(msg);
501 dbus_message_iter_get_basic(var, &value);
503 if (strlen(value) > 3)
504 return __ofono_error_invalid_format(msg);
506 cm->pending = dbus_message_ref(msg);
508 if (cm->flags & CALL_METER_FLAG_HAVE_PUCT)
509 cm->driver->puct_set(cm, value, cm->ppu, pin2,
510 set_puct_callback, cm);
512 cm->driver->puct_query(cm, set_puct_initial_query_callback, cm);
517 struct call_meter_property {
520 DBusMessage* (*set)(DBusMessage *msg, struct ofono_call_meter *cm,
521 DBusMessageIter *var, const char *pin2);
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 },
531 static DBusMessage *cm_set_property(DBusConnection *conn, DBusMessage *msg,
534 struct ofono_call_meter *cm = data;
535 DBusMessageIter iter;
537 const char *name, *passwd = "";
538 struct call_meter_property *property;
541 return __ofono_error_busy(msg);
543 if (!dbus_message_iter_init(msg, &iter))
544 return __ofono_error_invalid_args(msg);
546 if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_STRING)
547 return __ofono_error_invalid_args(msg);
549 dbus_message_iter_get_basic(&iter, &name);
551 dbus_message_iter_next(&iter);
553 if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_VARIANT)
554 return __ofono_error_invalid_args(msg);
556 dbus_message_iter_recurse(&iter, &var);
558 if (!dbus_message_iter_next(&iter))
559 return __ofono_error_invalid_args(msg);
561 if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_STRING)
562 return __ofono_error_invalid_args(msg);
564 dbus_message_iter_get_basic(&iter, &passwd);
566 if (!__ofono_is_valid_sim_pin(passwd, OFONO_SIM_PASSWORD_SIM_PIN2))
567 return __ofono_error_invalid_format(msg);
569 for (property = cm_properties; property->name; property++) {
570 if (strcmp(name, property->name))
573 if (dbus_message_iter_get_arg_type(&var) != property->type)
574 return __ofono_error_invalid_args(msg);
576 return property->set(msg, cm, &var, passwd);
579 return __ofono_error_invalid_args(msg);
582 static void reset_acm_query_callback(const struct ofono_error *error, int value,
585 struct ofono_call_meter *cm = data;
588 if (cm->pending == NULL)
591 if (error->type != OFONO_ERROR_TYPE_NO_ERROR) {
592 ofono_error("Reseting ACM successful, but query was not");
594 cm->flags &= ~CALL_METER_FLAG_CACHED;
596 __ofono_dbus_pending_reply(&cm->pending,
597 __ofono_error_failed(cm->pending));
601 reply = dbus_message_new_method_return(cm->pending);
602 __ofono_dbus_pending_reply(&cm->pending, reply);
607 static void acm_reset_callback(const struct ofono_error *error, void *data)
609 struct ofono_call_meter *cm = data;
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);
619 /* Assume if we have acm_reset, we have acm_query */
620 cm->driver->acm_query(cm, reset_acm_query_callback, cm);
623 static DBusMessage *cm_acm_reset(DBusConnection *conn, DBusMessage *msg,
626 struct ofono_call_meter *cm = data;
629 if (cm->driver->acm_reset == NULL)
630 return __ofono_error_not_implemented(msg);
633 return __ofono_error_busy(msg);
635 if (dbus_message_get_args(msg, NULL, DBUS_TYPE_STRING, &pin2,
636 DBUS_TYPE_INVALID) == FALSE)
637 return __ofono_error_invalid_args(msg);
639 if (!__ofono_is_valid_sim_pin(pin2, OFONO_SIM_PASSWORD_SIM_PIN2))
640 return __ofono_error_invalid_format(msg);
642 cm->pending = dbus_message_ref(msg);
644 cm->driver->acm_reset(cm, pin2, acm_reset_callback, cm);
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 },
659 static GDBusSignalTable cm_signals[] = {
660 { "PropertyChanged", "sv" },
661 { "NearMaximumWarning", "" },
665 void ofono_call_meter_changed_notify(struct ofono_call_meter *cm, int new_value)
667 set_call_meter(cm, new_value);
670 void ofono_call_meter_maximum_notify(struct ofono_call_meter *cm)
672 DBusConnection *conn = ofono_dbus_get_connection();
673 const char *path = __ofono_atom_get_path(cm->atom);
675 g_dbus_emit_signal(conn, path, OFONO_CALL_METER_INTERFACE,
676 "NearMaximumWarning", DBUS_TYPE_INVALID);
679 int ofono_call_meter_driver_register(const struct ofono_call_meter_driver *d)
681 DBG("driver: %p, name: %s", d, d->name);
683 if (d->probe == NULL)
686 g_drivers = g_slist_prepend(g_drivers, (void *) d);
691 void ofono_call_meter_driver_unregister(const struct ofono_call_meter_driver *d)
693 DBG("driver: %p, name: %s", d, d->name);
695 g_drivers = g_slist_remove(g_drivers, (void *) d);
698 static void call_meter_unregister(struct ofono_atom *atom)
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);
705 ofono_modem_remove_interface(modem, OFONO_CALL_METER_INTERFACE);
706 g_dbus_unregister_interface(conn, path, OFONO_CALL_METER_INTERFACE);
709 static void call_meter_remove(struct ofono_atom *atom)
711 struct ofono_call_meter *cm = __ofono_atom_get_data(atom);
713 DBG("atom: %p", atom);
718 if (cm->driver && cm->driver->remove)
719 cm->driver->remove(cm);
724 struct ofono_call_meter *ofono_call_meter_create(struct ofono_modem *modem,
729 struct ofono_call_meter *cm;
735 cm = g_try_new0(struct ofono_call_meter, 1);
740 cm->atom = __ofono_modem_add_atom(modem,
741 OFONO_ATOM_TYPE_CALL_METER,
742 call_meter_remove, cm);
744 for (l = g_drivers; l; l = l->next) {
745 const struct ofono_call_meter_driver *drv = l->data;
747 if (g_strcmp0(drv->name, driver))
750 if (drv->probe(cm, vendor, data) < 0)
760 void ofono_call_meter_register(struct ofono_call_meter *cm)
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);
766 if (!g_dbus_register_interface(conn, path, OFONO_CALL_METER_INTERFACE,
767 cm_methods, cm_signals, NULL, cm,
769 ofono_error("Could not create %s interface",
770 OFONO_CALL_METER_INTERFACE);
775 ofono_modem_add_interface(modem, OFONO_CALL_METER_INTERFACE);
777 __ofono_atom_register(cm->atom, call_meter_unregister);
780 void ofono_call_meter_remove(struct ofono_call_meter *cm)
782 __ofono_atom_free(cm->atom);
785 void ofono_call_meter_set_data(struct ofono_call_meter *cm, void *data)
787 cm->driver_data = data;
790 void *ofono_call_meter_get_data(struct ofono_call_meter *cm)
792 return cm->driver_data;