5 #include <murphy/common/debug.h>
6 #include <murphy/common/dbus-libdbus.h>
7 #include <murphy/common/hashtbl.h>
8 #include <murphy/common/list.h>
9 #include <murphy/common/utils.h>
15 #define MAKE_DBUS_VERSION(major, minor, patch) \
16 (((major) << 16) | ((minor) << 8) | (patch))
21 mrp_list_hook_t modems;
24 static int mrp_dbus_is_basic_type(mrp_dbus_type_t t)
26 return t != MRP_DBUS_TYPE_ARRAY && t != MRP_DBUS_TYPE_VARIANT &&
27 t != MRP_DBUS_TYPE_STRUCT && t != MRP_DBUS_TYPE_DICT_ENTRY &&
28 t != MRP_DBUS_TYPE_INVALID;
31 static const char * mrp_dbus_type_to_string(mrp_dbus_type_t t)
33 static char type_str[2] = "";
40 static modem_t *reference_modem(modem_t *modem)
42 if (modem && modem->refcnt >= 0)
48 static void unreference_modem(modem_t *modem)
53 if (modem->refcnt > 1)
56 mrp_log_info("remove bluetooth modem '%s' @ %s (paths %s)",
57 modem->name, modem->addr, modem->path);
59 if ((dev = modem->device)) {
61 clients_remove_device(dev);
64 mrp_list_delete(&modem->link);
66 mrp_free((void *)modem->path);
67 mrp_free((void *)modem->name);
68 mrp_free((void *)modem->addr);
70 mrp_free((void *)modem);
75 static modem_t *find_modem_by_path(context_t *ctx, const char *path)
77 modem_t *modem = NULL;
78 dbusif_t *dbusif = NULL;
79 mrp_list_hook_t *entry, *n;
81 if (!ctx || !path || !(dbusif = ctx->dbusif))
84 mrp_list_foreach(&dbusif->modems, entry, n) {
85 modem = mrp_list_entry(entry, modem_t, link);
86 if (!strcmp(path, modem->path))
93 static modem_t *create_modem(context_t *ctx,
98 modem_t *modem = NULL;
99 dbusif_t *dbusif = NULL;
101 if (!ctx || !path || !addr || !(dbusif = ctx->dbusif))
104 if (find_modem_by_path(ctx, path))
107 if (!(modem = mrp_allocz(sizeof(modem_t))))
110 modem->path = mrp_strdup(path);
111 modem->name = mrp_strdup(name ? name : "<unknown>");
112 modem->addr = mrp_strdup(addr);
115 reference_modem(modem);
117 mrp_list_prepend(&dbusif->modems, &modem->link);
122 static void destroy_modem(modem_t *modem)
127 if (modem && (ctx = modem->ctx) && (dbusif = ctx->dbusif)) {
128 mrp_list_delete(&modem->link);
129 unreference_modem(modem);
134 mrp_dbus_type_t type;
136 size_t n_values; /* for arrays */
139 static property_info_t * property_info_new(mrp_dbus_type_t type)
141 property_info_t *p = NULL;
143 if ((p = mrp_allocz(sizeof(*p)))) {
152 static void property_hash_free(void *key, void *object)
154 property_info_t *p = object;
162 static int parse_property_value_basic(mrp_dbus_msg_t *msg,
164 mrp_dbus_type_t type,
167 const char *n = NULL;
169 if (!name || !value_out || !mrp_dbus_is_basic_type(type))
172 if (mrp_dbus_msg_arg_type(msg, NULL) != MRP_DBUS_TYPE_STRING)
175 mrp_dbus_msg_read_basic(msg, MRP_DBUS_TYPE_STRING, &n);
180 if (mrp_dbus_msg_arg_type(msg, NULL) != MRP_DBUS_TYPE_VARIANT)
183 mrp_dbus_msg_enter_container(msg, MRP_DBUS_TYPE_VARIANT,
184 mrp_dbus_type_to_string(type));
186 mrp_dbus_msg_read_basic(msg, type, value_out);
188 mrp_dbus_msg_exit_container(msg); /* v */
193 static void parse_propertites(mrp_dbus_msg_t *msg,
194 mrp_htbl_t *prop_tbl,
199 if (mrp_dbus_msg_arg_type(msg, NULL) != MRP_DBUS_TYPE_ARRAY)
202 if (!mrp_dbus_msg_enter_container(msg, MRP_DBUS_TYPE_ARRAY, "{sv}"))
205 while (mrp_dbus_msg_enter_container(msg, MRP_DBUS_TYPE_DICT_ENTRY, NULL)) {
206 const char *prop = NULL;
207 property_info_t *p_info = NULL;
209 mrp_dbus_msg_read_basic(msg, MRP_DBUS_TYPE_STRING, &prop);
211 p_info = (property_info_t *)mrp_htbl_lookup(prop_tbl, (void*)prop);
213 mrp_dbus_msg_enter_container(msg, MRP_DBUS_TYPE_VARIANT, NULL);
215 /* NOTE: Currently we handle only string arrays, should modify
218 if (p_info->type == MRP_DBUS_TYPE_ARRAY)
219 mrp_dbus_msg_read_array(msg, MRP_DBUS_TYPE_STRING,
220 &p_info->value, &p_info->n_values);
221 else if (mrp_dbus_is_basic_type(p_info->type))
222 mrp_dbus_msg_read_basic(msg, p_info->type, &p_info->value);
224 mrp_dbus_msg_exit_container(msg); /* v */
228 mrp_dbus_msg_exit_container(msg); /* "{sv}" */
230 /* check if found all requested properties */
235 mrp_dbus_msg_exit_container(msg); /* a{sv} */
238 static void parse_modem_properties(mrp_dbus_msg_t *msg,
243 bool has_handsfree_interface = FALSE;
245 if (!msg || !btaddr || !btname || !online)
248 const char **interfaces = NULL;
249 mrp_htbl_config_t conf = {
252 .comp = mrp_string_comp,
253 .hash = mrp_string_hash,
254 .free = property_hash_free
256 property_info_t *info = NULL;
257 mrp_htbl_t *prop_tbl = mrp_htbl_create(&conf);
259 info = property_info_new(MRP_DBUS_TYPE_STRING);
260 mrp_htbl_insert(prop_tbl, "Name", info);
261 info = property_info_new(MRP_DBUS_TYPE_STRING);
262 mrp_htbl_insert(prop_tbl, "Serial", info);
263 info = property_info_new(MRP_DBUS_TYPE_BOOLEAN);
264 mrp_htbl_insert(prop_tbl, "Online", info);
265 info = property_info_new(MRP_DBUS_TYPE_ARRAY);
266 mrp_htbl_insert(prop_tbl, "Interfaces", info);
268 parse_propertites(msg, prop_tbl, 4);
270 info = (property_info_t *)mrp_htbl_lookup(prop_tbl, (void*)"Name");
271 *btname = (const char *)info->value;
272 info = (property_info_t *)mrp_htbl_lookup(prop_tbl, (void*)"Serial");
273 *btaddr = (const char *)info->value;
274 info = (property_info_t *)mrp_htbl_lookup(prop_tbl, (void*)"Online");
275 *online = *(uint32_t *)info->value;
276 info = (property_info_t *)mrp_htbl_lookup(prop_tbl, (void*)"Interfaces");
277 interfaces = (const char **)info->value;
281 for (i = 0; i < info->n_values; i++) {
282 if (!strcmp(interfaces[i], "org.ofono.Handsfree")) {
283 has_handsfree_interface = TRUE;
289 mrp_htbl_destroy(prop_tbl, TRUE);
291 if (!has_handsfree_interface) {
292 // *btname = "<unknown>";
293 // *btaddr = "<unknown>";
298 static void modem_query_cb(mrp_dbus_t *dbus,
302 modem_t *modem = (modem_t *)user_data;
303 context_t *ctx = NULL;
307 if (modem && (ctx = modem->ctx)) {
308 if (mrp_dbus_msg_type(msg) != MRP_DBUS_MESSAGE_TYPE_ERROR) {
309 const char *addr = NULL;
310 const char *name = NULL;
312 device_t *dev = NULL;
314 parse_modem_properties(msg, &addr, &name, &online);
316 if (!online || !name || !(dev = clients_add_device(ctx, addr)))
317 destroy_modem(modem);
319 mrp_free((void *)modem->addr);
320 mrp_free((void *)modem->name);
322 modem->addr = mrp_strdup(addr);
323 modem->name = mrp_strdup(name);
328 mrp_log_info("created bluetooth modem '%s' @ %s (path %s)",
329 modem->name, modem->addr, modem->path);
333 unreference_modem(modem);
337 static void query_modem(modem_t *modem)
339 context_t *ctx = NULL;
340 dbusif_t *dbusif = NULL;
343 if (!modem || !modem->path || !(ctx = modem->ctx) ||
344 !(dbusif = ctx->dbusif))
347 if ((ref = reference_modem(modem))) {
348 mrp_dbus_msg_t *msg = mrp_dbus_msg_method_call(dbusif->dbus,
353 mrp_dbus_send(dbusif->dbus, "org.ofono", modem->path, "org.ofono.Modem",
354 "GetProperties", 1000, modem_query_cb, ref, msg);
355 mrp_dbus_msg_unref(msg);
359 static void parse_handsfree_properties(modem_t *modem,
363 mrp_htbl_config_t conf = {
366 .comp = mrp_string_comp,
367 .hash = mrp_string_hash,
368 .free = property_hash_free
370 property_info_t *info = NULL;
371 mrp_htbl_t *prop_tbl = NULL;
373 if (!modem || !msg || !state)
376 *state = VOICE_RECOGNITION_UNKNOWN;
378 prop_tbl = mrp_htbl_create(&conf);
379 info = property_info_new(MRP_DBUS_TYPE_BOOLEAN);
380 mrp_htbl_insert(prop_tbl, "VoiceRecognition", info);
382 parse_propertites(msg, prop_tbl, 1);
384 *state = (uint32_t)(ptrdiff_t)((property_info_t *)mrp_htbl_lookup(
385 prop_tbl, (void*)"VoiceRecognition"))->value ?
386 VOICE_RECOGNITION_ON : VOICE_RECOGNITION_OFF;
388 mrp_htbl_destroy(prop_tbl, TRUE);
391 static void set_modem_state(modem_t *modem, hfp_state_t state)
396 if (state == modem->state)
399 modem->state = state;
401 if (!(device = modem->device))
406 case VOICE_RECOGNITION_ON:
407 mrp_log_info("bluetooth modem: setting voicerecognition on "
408 "for modem %s", modem->addr);
409 if ((card = device->card)) {
410 pulseif_set_card_profile(card, "hfgw");
414 case VOICE_RECOGNITION_OFF:
415 mrp_log_info("bluetooth modem: setting voicerecognition off "
416 "for modem %s", modem->addr);
417 clients_stop_recognising_voice(device);
421 mrp_log_error("bluetooth plugin: attempt to set invalid stte "
422 "for modem %s", modem->addr);
427 static void handsfree_query_cb(mrp_dbus_t *dbus,
431 modem_t *modem = (modem_t *)user_data;
432 hfp_state_t state = VOICE_RECOGNITION_UNKNOWN;
438 if (mrp_dbus_msg_type(msg) != MRP_DBUS_MESSAGE_TYPE_ERROR) {
439 parse_handsfree_properties(modem, msg, &state);
440 set_modem_state(modem, state);
443 unreference_modem(modem);
446 static void query_handsfree(modem_t *modem)
448 context_t *ctx = NULL;;
449 dbusif_t *dbusif = NULL;
452 if (!modem || !modem->path || !(ctx = modem->ctx) ||
453 !(dbusif = ctx->dbusif))
456 if ((ref = reference_modem(modem))) {
457 mrp_dbus_msg_t *msg = mrp_dbus_msg_method_call(dbusif->dbus,
460 "org.ofono.Handsfree",
462 mrp_dbus_send(dbusif->dbus, "org.ofono", modem->path,
463 "org.ofono.Handsfree", "GetProperties", 1000,
464 handsfree_query_cb, ref, msg);
465 mrp_dbus_msg_unref(msg);
469 static int modem_property_changed_cb(mrp_dbus_t *dbus,
473 context_t *ctx = (context_t *)user_data;
474 const char *path = NULL;
475 uint32_t is_online = FALSE;
479 if (!ctx || !ctx->dbusif ||
480 !(path = mrp_dbus_msg_path(msg)))
483 if (parse_property_value_basic(msg, "Online", MRP_DBUS_TYPE_BOOLEAN,
485 modem_t *modem = find_modem_by_path(ctx, path);
489 if ((modem = create_modem(ctx, path, "", ""))) {
491 query_handsfree(modem);
495 destroy_modem(modem);
503 static int handsfree_property_changed_cb(mrp_dbus_t *dbus,
507 context_t *ctx = (context_t *)user_data;
508 const char *path = mrp_dbus_msg_path(msg);
509 modem_t *modem = NULL;
510 uint32_t state = FALSE;
514 if (!ctx || !ctx->dbusif || !path)
517 if (!(modem = find_modem_by_path(ctx, path)))
520 if (parse_property_value_basic(msg, "VoiceRecognition",
521 MRP_DBUS_TYPE_BOOLEAN, &state)) {
522 set_modem_state(modem,
523 state ? VOICE_RECOGNITION_ON : VOICE_RECOGNITION_OFF);
529 static void modem_query_all_cb(mrp_dbus_t *dbus,
533 context_t *ctx = (context_t *)user_data;
538 if (!ctx || !ctx->dbusif)
541 if (mrp_dbus_msg_arg_type(msg, NULL) != MRP_DBUS_TYPE_ARRAY)
544 mrp_dbus_msg_enter_container(msg, MRP_DBUS_TYPE_ARRAY, "(oa{sv})");
546 while (mrp_dbus_msg_enter_container(msg, MRP_DBUS_TYPE_STRUCT, NULL)) {
548 const char *path = NULL;
553 mrp_dbus_msg_read_basic(msg, MRP_DBUS_TYPE_OBJECT_PATH, &path);
555 parse_modem_properties(msg, &addr, &name, &online);
557 mrp_log_info("Modem details: %s %s %s %d", path, addr, name, online);
559 if (path && online) {
560 if ((dev = clients_add_device(ctx, addr)) &&
561 (modem = create_modem(ctx, path, name, addr)))
566 mrp_log_info("created bluetooth modem '%s' @ %s "
567 "(path %s)", modem->name, modem->addr,
569 query_handsfree(modem);
573 mrp_dbus_msg_exit_container(msg); /* (oa{sv}) */
576 mrp_dbus_msg_exit_container(msg); /* a(oa{sv}) */
579 static void track_modems(context_t *ctx, bool track)
581 static const char *modem_interface = "org.ofono.Modem";
582 static const char *handsfree_interface = "org.ofono.Handsfree";
583 static const char *member = "PropertyChanged";
584 dbusif_t *dbusif = NULL;
586 if (!ctx || !(dbusif = ctx->dbusif))
590 mrp_dbus_add_signal_handler(dbusif->dbus, NULL, NULL,
591 modem_interface, member,
592 modem_property_changed_cb, ctx);
593 mrp_dbus_add_signal_handler(dbusif->dbus, NULL, NULL,
594 handsfree_interface, member,
595 handsfree_property_changed_cb, ctx);
596 mrp_dbus_install_filter(dbusif->dbus, NULL, NULL,
597 modem_interface, member, NULL);
598 mrp_dbus_install_filter(dbusif->dbus, NULL, NULL,
599 handsfree_interface, member, NULL);
602 mrp_dbus_del_signal_handler(dbusif->dbus, NULL, NULL,
603 modem_interface, member,
604 modem_property_changed_cb, ctx);
605 mrp_dbus_del_signal_handler(dbusif->dbus, NULL, NULL,
606 handsfree_interface, member,
607 handsfree_property_changed_cb, ctx);
608 mrp_dbus_remove_filter(dbusif->dbus, NULL, NULL,
609 modem_interface, member, NULL);
610 mrp_dbus_remove_filter(dbusif->dbus, NULL, NULL,
611 handsfree_interface, member, NULL);
615 static void query_all_modems(context_t *ctx)
618 dbusif_t *dbusif = NULL;
620 if (!ctx || !(dbusif = ctx->dbusif))
623 query_id = mrp_dbus_call(dbusif->dbus,
624 "org.ofono", "/", "org.ofono.Manager",
625 "GetModems", 1000, modem_query_all_cb, ctx,
626 MRP_DBUS_TYPE_INVALID);
628 /* query modems failed */
632 static void set_property(mrp_dbus_msg_t *msg,
634 mrp_dbus_type_t type,
637 mrp_dbus_msg_open_container(msg, MRP_DBUS_TYPE_DICT_ENTRY, NULL);
639 mrp_dbus_msg_append_basic(msg, DBUS_TYPE_STRING, (void*)name);
641 mrp_dbus_msg_open_container(msg, MRP_DBUS_TYPE_VARIANT, NULL);
642 mrp_dbus_msg_append_basic(msg, (char)type, value);
643 mrp_dbus_msg_close_container(msg); /* variant */
645 mrp_dbus_msg_close_container(msg); /* dictionary */
648 int dbusif_create(context_t *ctx, mrp_mainloop_t *ml)
652 if (!(dbusif = mrp_allocz(sizeof(dbusif_t))))
655 dbusif->bustype = mrp_strdup("system");
656 dbusif->dbus = mrp_dbus_get(ml, dbusif->bustype, NULL);
659 mrp_log_error("bluetooth voice recognition plugin: "
660 "failed to obtain DBus");
665 mrp_list_init(&dbusif->modems);
667 ctx->dbusif = dbusif;
672 void dbusif_destroy(context_t *ctx)
676 if (ctx && (dbusif = ctx->dbusif)) {
679 mrp_dbus_unref(dbusif->dbus);
681 mrp_free((void *)dbusif->bustype);
682 mrp_free((void *)dbusif);
686 int dbusif_start(context_t *ctx)
688 track_modems(ctx, TRUE);
689 query_all_modems(ctx);
693 void dbusif_stop(context_t *ctx)
697 mrp_list_hook_t *entry, *n;
699 track_modems(ctx, FALSE);
701 if (ctx && (dbusif = ctx->dbusif)) {
702 mrp_list_foreach(&dbusif->modems, entry, n) {
703 modem = mrp_list_entry(entry, modem_t, link);
704 destroy_modem(modem);
709 int dbusif_set_voice_recognition(modem_t *modem, hfp_state_t state)
711 context_t *ctx = NULL;
712 dbusif_t *dbusif = NULL;;
713 mrp_dbus_msg_t *msg = NULL;
716 if (!modem || !modem->path || !(ctx = modem->ctx) ||
717 !(dbusif = ctx->dbusif))
721 case VOICE_RECOGNITION_ON: value = TRUE; break;
722 case VOICE_RECOGNITION_OFF: value = FALSE; break;
723 default: /* invalid */ return -1;
726 msg = mrp_dbus_msg_method_call(dbusif->dbus, "org.ofono",
728 "org.ofono.Handsfree",
734 set_property(msg, "VoiceRecognition", MRP_DBUS_TYPE_BOOLEAN, &value);
736 mrp_dbus_send_msg(dbusif->dbus, msg);
738 mrp_dbus_msg_unref(msg);
747 * indent-tabs-mode: nil