6 #include <pulse/proplist.h>
7 #include <pulse/util.h>
8 #include <pulse/rtclock.h>
9 #include <pulsecore/log.h>
10 #include <pulsecore/core-util.h>
11 #include <pulsecore/strbuf.h>
13 #include "tizen-device.h"
15 #define DBUS_SERVICE_HFP_AGENT "org.bluez.ag_agent"
16 #define DBUS_OBJECT_HFP_AGENT "/org/bluez/hfp_agent"
17 #define DBUS_INTERFACE_HFP_AGENT "Org.Hfp.App.Interface"
19 #define BT_CVSD_CODEC_ID 1 // narrow-band
20 #define BT_MSBC_CODEC_ID 2 // wide-band
23 int values[MAX_INTSET_NUM];
28 int device_id_max_g = 1;
29 uint32_t event_id_max_g = 1;
31 static uint32_t _new_event_id() {
32 return event_id_max_g++;
35 int pa_intset_put(pa_intset *s, int val) {
40 if (s->write_index >= MAX_INTSET_NUM)
43 for(i = 0; i < s->write_index; i++) {
44 if (s->values[i] == val)
48 s->values[s->write_index++] = val;
53 pa_intset* pa_intset_new() {
56 s = pa_xmalloc0(sizeof(pa_intset));
63 void pa_intset_free(pa_intset *s) {
68 int pa_intset_first(pa_intset *s, int *val) {
72 if (s->write_index == 0)
76 *val = s->values[s->read_index++];
81 int pa_intset_next(pa_intset *s, int *val) {
85 if (s->read_index >= s->write_index)
88 *val = s->values[s->read_index++];
93 static char* get_playback_list_str(pa_hashmap *playback_devices) {
99 if (!playback_devices || !pa_hashmap_size(playback_devices))
102 buf = pa_strbuf_new();
103 pa_strbuf_printf(buf, " Playback device list\n");
104 PA_HASHMAP_FOREACH_KV(role, sink, playback_devices, state)
105 pa_strbuf_printf(buf, " %-13s -> %s\n", role, sink->name);
107 return pa_strbuf_tostring_free(buf);
110 static char* get_capture_list_str(pa_hashmap *capture_devices) {
111 pa_source *source = NULL;
116 if (!capture_devices || !pa_hashmap_size(capture_devices))
119 buf = pa_strbuf_new();
120 pa_strbuf_printf(buf, " Capture device list\n");
121 PA_HASHMAP_FOREACH_KV(role, source, capture_devices, state)
122 pa_strbuf_printf(buf, " %-13s -> %s\n", role, source->name);
124 return pa_strbuf_tostring_free(buf);
127 static char* _device_get_info_str(pa_tz_device *device) {
129 char *playback_str, *capture_str;
134 buf = pa_strbuf_new();
135 pa_strbuf_printf(buf, "[Device #%u]\n", device->id);
136 pa_strbuf_printf(buf, " ID : %u\n", device->id);
137 pa_strbuf_printf(buf, " Type : %s\n", device->type);
138 pa_strbuf_printf(buf, " Name : %s\n", device->name);
139 pa_strbuf_printf(buf, " System ID : %s\n", device->system_id);
140 pa_strbuf_printf(buf, " Direction : %s\n", device_direction_to_string(device->direction));
141 pa_strbuf_printf(buf, " Is activated : %s\n", pa_yes_no(device->state == DM_DEVICE_STATE_ACTIVATED));
142 pa_strbuf_printf(buf, " Internal : %s\n", pa_yes_no(device->use_internal_codec));
143 if (device_type_is_equal(device->type, DEVICE_TYPE_USB_AUDIO)) {
144 pa_strbuf_printf(buf, " Vendor ID : %04x\n", device->vendor_id);
145 pa_strbuf_printf(buf, " Product ID : %04x\n", device->product_id);
147 if (device_type_is_equal(device->type, DEVICE_TYPE_BT_SCO))
148 pa_strbuf_printf(buf, " SCO opened : %s\n", pa_yes_no(device->sco_opened));
149 playback_str = get_playback_list_str(device->playback_devices);
150 capture_str = get_capture_list_str(device->capture_devices);
153 pa_strbuf_puts(buf, playback_str);
155 pa_strbuf_puts(buf, capture_str);
157 pa_xfree(playback_str);
158 pa_xfree(capture_str);
160 return pa_strbuf_tostring_free(buf);
164 void pa_tz_device_dump_info(pa_tz_device *device, pa_log_level_t log_level) {
170 if ((info = _device_get_info_str(device))) {
171 pa_logl(log_level, "%s", info);
176 static void notify_device_connection_changed(pa_tz_device *device, bool connected) {
177 pa_tz_device_hook_data_for_conn_changed hook_data;
179 hook_data.event_id = _new_event_id();
180 hook_data.is_connected = connected;
181 hook_data.device = device;
183 pa_log_info("Fire hook for device connection changed, device(%s/%u) %s",
184 device->type, device->id, connected ? "connected" : "disconnected");
185 pa_hook_fire(pa_communicator_hook(device->comm, PA_COMMUNICATOR_HOOK_DEVICE_CONNECTION_CHANGED), &hook_data);
188 static void notify_device_state_changed(pa_tz_device *device, dm_device_state_t state) {
189 pa_tz_device_hook_data_for_state_changed hook_data;
191 hook_data.event_id = _new_event_id();
192 hook_data.activated = (state == DM_DEVICE_STATE_ACTIVATED) ? true : false;
193 hook_data.device = device;
195 pa_log_info("Fire hook for device state changed, device(%s/%u) %s",
196 device->type, device->id, hook_data.activated ? "Activated" : "De-activated");
197 pa_hook_fire(pa_communicator_hook(device->comm, PA_COMMUNICATOR_HOOK_DEVICE_STATE_CHANGED), &hook_data);
200 /* pa_tz_device_new_data */
201 void pa_tz_device_new_data_init(pa_tz_device_new_data *data, pa_idxset *list,
202 pa_communicator *comm, pa_dbus_connection *conn) {
209 data->dbus_conn = conn;
213 data->system_id = NULL;
214 data->vendor_id = -1;
215 data->product_id = -1;
216 data->direction = DM_DEVICE_DIRECTION_NONE;
218 data->playback_pcms= pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func);
219 data->capture_pcms = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func);
222 void pa_tz_device_new_data_set_type(pa_tz_device_new_data *data, const char *type) {
225 data->type = pa_xstrdup(type);
228 void pa_tz_device_new_data_set_name(pa_tz_device_new_data *data, const char *name) {
231 data->name = pa_xstrdup(name);
234 void pa_tz_device_new_data_set_direction(pa_tz_device_new_data *data, dm_device_direction_t direction) {
237 data->direction = direction;
239 /* only for external? */
240 void pa_tz_device_new_data_set_system_id(pa_tz_device_new_data *data, const char *system_id) {
243 data->system_id = pa_xstrdup(system_id);
246 void pa_tz_device_new_data_set_vendor_id(pa_tz_device_new_data *data, int vendor_id) {
249 data->vendor_id = vendor_id;
252 void pa_tz_device_new_data_set_product_id(pa_tz_device_new_data *data, int product_id) {
255 data->product_id = product_id;
258 void pa_tz_device_new_data_set_use_internal_codec(pa_tz_device_new_data *data, bool use_internal_codec) {
261 data->use_internal_codec = use_internal_codec;
264 void pa_tz_device_new_data_add_sink(pa_tz_device_new_data *data, const char *role, pa_sink *sink) {
267 pa_hashmap_put(data->playback_pcms, (void*)role, sink);
270 void pa_tz_device_new_data_add_source(pa_tz_device_new_data *data, const char *role, pa_source *source) {
273 pa_hashmap_put(data->capture_pcms, (void*)role, source);
276 void pa_tz_device_new_data_done(pa_tz_device_new_data *data) {
279 pa_xfree(data->type);
280 pa_xfree(data->name);
283 static int _check_valid_device_new_data(pa_tz_device_new_data *data) {
286 if (data->type == NULL) {
287 pa_log_error("new data type is null");
291 if (data->direction == DM_DEVICE_DIRECTION_NONE) {
292 pa_log_error("new data direction is none");
299 static int device_add_sink(pa_tz_device *device, const char *role, pa_sink *sink) {
301 pa_assert(device->playback_devices);
303 if (pa_hashmap_put(device->playback_devices, (void*)role, sink) < 0) {
304 pa_log_error("Failed to add sink : put sink failed");
310 static int device_add_source(pa_tz_device *device, const char *role, pa_source *source) {
312 pa_assert(device->capture_devices);
314 if (pa_hashmap_put(device->capture_devices, (void*)role, source) < 0) {
315 pa_log_error("Failed to add source : put source failed");
321 static pa_sink* device_get_sink(pa_tz_device *device, const char *role) {
324 pa_assert(device->playback_devices);
326 if ((sink = pa_hashmap_get(device->playback_devices, role)) == NULL) {
327 pa_log_warn("Failed to get sink for %s", role);
334 static pa_source* device_get_source(pa_tz_device *device, const char *role) {
337 pa_assert(device->capture_devices);
339 if ((source = pa_hashmap_get(device->capture_devices, role)) == NULL) {
340 pa_log_warn("Failed to get source for %s", role);
347 static void device_set_state(pa_tz_device *device, dm_device_state_t new_state) {
350 if (device->state != new_state) {
351 pa_log_debug("change state %d -> %d", device->state, new_state);
352 device->state = new_state;
353 notify_device_state_changed(device, new_state);
354 pa_tz_device_dump_info(device, PA_LOG_DEBUG);
356 pa_log_debug("same state, not change it");
360 static dm_device_state_t device_get_state(pa_tz_device *device) {
362 return device->state;
365 static int device_remove_sink_with_role(pa_tz_device *device, const char *role) {
369 sink = pa_hashmap_remove(device->playback_devices, role);
370 return sink ? 0 : -1;
373 static int device_remove_source_with_role(pa_tz_device *device, const char *role) {
377 source = pa_hashmap_remove(device->capture_devices, role);
378 return source ? 0 : -1;
381 static int device_remove_sink(pa_tz_device *device, pa_sink *sink) {
388 PA_HASHMAP_FOREACH_KV(role, _sink, device->playback_devices, state) {
390 return device_remove_sink_with_role(device, role);
396 static int device_remove_source(pa_tz_device *device, pa_source *source) {
403 PA_HASHMAP_FOREACH_KV(role, _source, device->capture_devices, state) {
404 if (source == _source)
405 return device_remove_source_with_role(device, role);
412 pa_tz_device* pa_tz_device_new(pa_tz_device_new_data *data) {
413 pa_tz_device *device;
419 if (_check_valid_device_new_data(data) < 0) {
420 pa_log_error("Invalid device_new_data");
424 device = pa_xmalloc(sizeof(pa_tz_device));
425 device->list = data->list;
426 device->comm = data->comm;
427 device->dbus_conn = data->dbus_conn;
429 device->id = device_id_max_g++;
430 device->type = pa_xstrdup(data->type);
432 device->name = pa_xstrdup(data->name);
434 device->name = pa_xstrdup(data->type);
435 device->system_id = pa_xstrdup(data->system_id);
436 device->vendor_id = data->vendor_id;
437 device->product_id = data->product_id;
439 pa_log_info("New device type(%s) id(%u) name(%s) system_id(%s) vendor_id(%04x) product_id(%04x)",
440 device->type, device->id, device->name, pa_strempty(device->system_id),
441 device->vendor_id, device->product_id);
443 device->playback_devices = data->playback_pcms;
444 device->capture_devices = data->capture_pcms;
445 device->direction = data->direction;
446 device->state = DM_DEVICE_STATE_DEACTIVATED;
447 device->creation_time = pa_rtclock_now();
448 device->use_internal_codec = data->use_internal_codec;
449 device->sco_opened = false;
451 if (device_type_is_use_external_card(device->type)) {
452 if ((sink = device_get_sink(device, DEVICE_ROLE_NORMAL)))
453 sink->device_item = device;
454 if ((source = device_get_source(device, DEVICE_ROLE_NORMAL)))
455 source->device_item = device;
458 pa_idxset_put(device->list, device, NULL);
459 notify_device_connection_changed(device, true);
461 pa_tz_device_dump_info(device, PA_LOG_INFO);
466 int pa_tz_device_add_sink(pa_tz_device *device, const char *role, pa_sink *sink) {
468 pa_assert(device_role_is_valid(role));
471 pa_log_info("device add sink, device(%u) role(%s) sink(%s)",
472 device->id, role, sink->name);
474 if (device_add_sink(device, role, sink) < 0) {
475 pa_log_error("Failed to add sink : Can't add to device");
482 int pa_tz_device_add_source(pa_tz_device *device, const char *role, pa_source *source) {
484 pa_assert(device_role_is_valid(role));
487 pa_log_info("device add source, device(%u) role(%s) source(%s)",
488 device->id, role, source->name);
490 if (device_add_source(device, role, source) < 0) {
491 pa_log_error("Failed to add source : Can't add to device");
498 int pa_tz_device_remove_sink(pa_tz_device *device, pa_sink *sink) {
502 pa_log_info("device remove sink, device(%u) sink(%s)",
503 device->id, sink->name);
505 return device_remove_sink(device, sink);
508 int pa_tz_device_remove_source(pa_tz_device *device, pa_source *source) {
512 pa_log_info("device remove source, device(%u) source(%s)",
513 device->id, source->name);
515 return device_remove_source(device, source);
518 int pa_tz_device_remove_sink_with_role(pa_tz_device *device, const char *role) {
520 pa_assert(device_role_is_valid(role));
522 pa_log_info("device remove sink with role, device(%u) role(%s)",
525 return device_remove_sink_with_role(device, role);
528 int pa_tz_device_remove_source_with_role(pa_tz_device *device, const char *role) {
530 pa_assert(device_role_is_valid(role));
532 pa_log_info("device remove source with role, device(%u) role(%s)",
535 return device_remove_source_with_role(device, role);
538 void pa_tz_device_free(pa_tz_device *device) {
541 pa_log_info("Free device type(%s) id(%u) name(%s) system_id(%s)",
542 device->type, device->id, device->name, device->system_id);
544 pa_tz_device_dump_info(device, PA_LOG_INFO);
546 pa_idxset_remove_by_data(device->list, device, NULL);
547 notify_device_connection_changed(device, false);
549 pa_xfree(device->type);
550 pa_xfree(device->name);
551 pa_xfree(device->system_id);
557 pa_sink* pa_tz_device_get_sink(pa_tz_device *device, const char *role) {
562 pa_log_info("device get sink, device(%u) role(%s)", device->id, role);
564 if ((sink = device_get_sink(device, role)) == NULL) {
565 pa_log_error("Failed to get sink from device");
572 pa_source* pa_tz_device_get_source(pa_tz_device *device, const char *role) {
577 pa_log_info("device get source, device(%u) role(%s)",
580 if ((source = device_get_source(device, role)) == NULL) {
581 pa_log_error("Failed to get source from device");
588 /* TODO : Change param dm_device_state_t to bool or pa_tz_device_state_t,
589 * Because this state represent pa_tz_device's own state */
590 void pa_tz_device_set_state(pa_tz_device *device, dm_device_state_t state) {
593 pa_log_info("device set state, device(%u) type(%s) -> %d",
594 device->id, device->type, state);
595 device_set_state(device, state);
598 dm_device_state_t pa_tz_device_get_state(pa_tz_device *device) {
601 return device_get_state(device);
604 uint32_t pa_tz_device_get_id(pa_tz_device *device) {
610 char* pa_tz_device_get_type(pa_tz_device *device) {
616 char* pa_tz_device_get_name(pa_tz_device *device) {
622 char* pa_tz_device_get_system_id(pa_tz_device *device) {
625 return device->system_id;
628 int pa_tz_device_get_vendor_id(pa_tz_device *device) {
631 return device->vendor_id;
634 int pa_tz_device_get_product_id(pa_tz_device *device) {
637 return device->product_id;
640 dm_device_direction_t pa_tz_device_get_direction(pa_tz_device *device) {
643 return device->direction;
646 pa_usec_t pa_tz_device_get_creation_time(pa_tz_device *device) {
649 return device->creation_time;
652 bool pa_tz_device_is_use_internal_codec(pa_tz_device *device) {
655 return device->use_internal_codec;
658 bool pa_tz_device_is_all_suspended(pa_tz_device *device) {
663 PA_HASHMAP_FOREACH(sink, device->playback_devices, state) {
664 if (pa_sink_get_state(sink) != PA_SINK_SUSPENDED)
668 PA_HASHMAP_FOREACH(source, device->capture_devices, state) {
669 if (pa_source_get_state(source) != PA_SOURCE_SUSPENDED)
676 pa_intset* pa_tz_device_get_stream_list(pa_tz_device *device) {
679 pa_sink_input *input;
680 pa_source_output *output;
682 const char *p_id_str;
685 pa_intset *stream_id_set;
689 stream_id_set = pa_intset_new();
690 PA_HASHMAP_FOREACH(sink, device->playback_devices, state) {
691 PA_IDXSET_FOREACH(input, sink->inputs, idx) {
692 p_id_str = pa_proplist_gets(input->proplist, PA_PROP_MEDIA_PARENT_ID);
693 if (p_id_str && !pa_atoi(p_id_str, &p_id)) {
694 pa_intset_put(stream_id_set, p_id);
696 pa_log_warn("Invalid Parent ID : '%s'", pa_strnull(p_id_str));
700 PA_HASHMAP_FOREACH(source, device->capture_devices, state) {
701 PA_IDXSET_FOREACH(output, source->outputs, idx) {
702 p_id_str = pa_proplist_gets(output->proplist, PA_PROP_MEDIA_PARENT_ID);
703 if (p_id_str && !pa_atoi(p_id_str, &p_id)) {
704 pa_intset_put(stream_id_set, p_id);
706 pa_log_warn("Invalid Parent ID : '%s'", pa_strnull(p_id_str));
711 return stream_id_set;
714 static int method_call_bt_sco(pa_dbus_connection *conn, bool onoff) {
715 DBusMessage *msg, *reply;
721 method = onoff ? "Play" : "Stop";
722 if (!(msg = dbus_message_new_method_call(DBUS_SERVICE_HFP_AGENT, DBUS_OBJECT_HFP_AGENT, DBUS_INTERFACE_HFP_AGENT, method))) {
723 pa_log_error("dbus method call failed");
727 dbus_error_init(&err);
728 if (!(reply = dbus_connection_send_with_reply_and_block(pa_dbus_connection_get(conn), msg, -1, &err))) {
729 pa_log_error("Failed to method call %s.%s, %s", DBUS_INTERFACE_HFP_AGENT, method, err.message);
730 dbus_error_free(&err);
734 dbus_message_unref(reply);
738 static int method_call_bt_sco_get_property(pa_dbus_connection *conn, bool *is_wide_band, bool *is_nrec) {
739 DBusMessage *msg, *reply;
740 DBusMessageIter reply_iter, reply_iter_entry;
744 const char *property;
748 if (!is_wide_band && !is_nrec) {
752 if (!(msg = dbus_message_new_method_call(DBUS_SERVICE_HFP_AGENT, DBUS_OBJECT_HFP_AGENT, DBUS_INTERFACE_HFP_AGENT, "GetProperties"))) {
753 pa_log_error("dbus method call failed");
757 dbus_error_init(&err);
758 if (!(reply = dbus_connection_send_with_reply_and_block(pa_dbus_connection_get(conn), msg, -1, &err))) {
759 pa_log_error("Failed to method call %s.%s, %s", DBUS_INTERFACE_HFP_AGENT, "GetProperties", err.message);
760 dbus_error_free(&err);
764 dbus_message_iter_init(reply, &reply_iter);
766 if (dbus_message_iter_get_arg_type(&reply_iter) != DBUS_TYPE_ARRAY) {
767 pa_log_error("Cannot get reply argument");
771 dbus_message_iter_recurse(&reply_iter, &reply_iter_entry);
773 while (dbus_message_iter_get_arg_type(&reply_iter_entry) == DBUS_TYPE_DICT_ENTRY) {
774 DBusMessageIter dict_entry, dict_entry_val;
775 dbus_message_iter_recurse(&reply_iter_entry, &dict_entry);
776 dbus_message_iter_get_basic(&dict_entry, &property);
777 pa_log_debug("String received = %s", property);
779 if (pa_streq("codec", property) && is_wide_band) {
780 dbus_message_iter_next(&dict_entry);
781 dbus_message_iter_recurse(&dict_entry, &dict_entry_val);
782 if (dbus_message_iter_get_arg_type(&dict_entry_val) != DBUS_TYPE_UINT32)
784 dbus_message_iter_get_basic(&dict_entry_val, &codec);
785 pa_log_debug("Codec = [%d]", codec);
786 *is_wide_band = (codec == BT_MSBC_CODEC_ID) ? true : false;
787 } else if (pa_streq("nrec", property) && is_nrec) {
788 dbus_message_iter_next(&dict_entry);
789 dbus_message_iter_recurse(&dict_entry, &dict_entry_val);
790 if (dbus_message_iter_get_arg_type(&dict_entry_val) != DBUS_TYPE_BOOLEAN)
792 dbus_message_iter_get_basic(&dict_entry_val, &nrec);
793 pa_log_debug("nrec= [%d]", nrec);
797 dbus_message_iter_next(&reply_iter_entry);
801 dbus_message_unref(reply);
805 /* only for bt sco device */
806 int pa_tz_device_sco_open(pa_tz_device *device) {
808 pa_assert(device->dbus_conn);
810 pa_log_info("BT SCO Open for device(%u)", device->id);
811 if (device_type_is_equal(device->type, DEVICE_TYPE_BT_SCO) == false) {
812 pa_log_error("Not BT SCO device");
816 if (device->sco_opened) {
817 pa_log_warn("SCO already opened");
821 if (method_call_bt_sco(device->dbus_conn, true) < 0) {
822 pa_log_error("Failed to bt sco on");
826 device->sco_opened = true;
827 device->creation_time = pa_rtclock_now();
828 pa_tz_device_dump_info(device, PA_LOG_DEBUG);
829 pa_log_info("BT SCO Open - SUCCESS");
834 /* only for bt sco device */
835 int pa_tz_device_sco_close(pa_tz_device *device) {
838 pa_log_info("BT SCO Close for device(%u)", device->id);
840 if (device_type_is_equal(device->type, DEVICE_TYPE_BT_SCO) == false) {
841 pa_log_error("Not BT SCO device");
845 if (device->sco_opened == false) {
846 pa_log_warn("SCO not opened");
850 if (method_call_bt_sco(device->dbus_conn, false) < 0) {
851 pa_log_error("Failed to bt sco on");
855 device->sco_opened = false;
856 pa_tz_device_dump_info(device, PA_LOG_DEBUG);
857 pa_log_info("BT SCO Close - Success");
862 /* only for bt sco device */
863 int pa_tz_device_sco_get_property(pa_tz_device *device, bool *is_wide_band, bool *nrec) {
866 pa_log_info("BT SCO get property for device(%u)", device->id);
867 if (device_type_is_equal(device->type, DEVICE_TYPE_BT_SCO) == false) {
868 pa_log_error("Not BT device");
872 if (method_call_bt_sco_get_property(device->dbus_conn, is_wide_band, nrec) < 0) {
873 pa_log_error("Failed to get BT SCO Property");
877 pa_log_info("BT SCO Get Property - Success, is wide band : %s, nrec : %s", pa_yes_no(*is_wide_band), pa_yes_no(*nrec));
882 /* only for bt sco device */
883 int pa_tz_device_sco_get_status(pa_tz_device *device, dm_device_bt_sco_status_t *status) {
886 pa_log_debug("BT SCO get status for device(%u)", device->id);
887 if (device_type_is_equal(device->type, DEVICE_TYPE_BT_SCO) == false) {
888 pa_log_error("Not BT device");
891 if (status == NULL) {
892 pa_log_error("invalid parameter");
896 if (device->sco_opened == false)
897 *status = DM_DEVICE_BT_SCO_STATUS_CONNECTED;
899 *status = DM_DEVICE_BT_SCO_STATUS_OPENED;
901 pa_log_info("BT SCO (%u) Get Status, %d", device->id, *status);