bluez: implement profile on device

Enumerate the available profiles on the device. Make params
visible.

Implement set_profile, only expose the nodes that belong to the
profile.
This commit is contained in:
Wim Taymans 2020-01-10 13:25:40 +01:00
parent 5f70a2713e
commit 9db240e786
3 changed files with 90 additions and 35 deletions

View file

@ -911,6 +911,7 @@ static int impl_node_send_command(void *object, const struct spa_command *comman
if ((res = do_start(this)) < 0)
return res;
break;
case SPA_NODE_COMMAND_Suspend:
case SPA_NODE_COMMAND_Pause:
if ((res = do_stop(this)) < 0)
return res;

View file

@ -73,11 +73,10 @@ struct impl {
struct spa_bt_device *bt_dev;
uint32_t next_id;
uint32_t profile;
};
static void emit_node (struct impl *this, struct spa_bt_transport *t, const char *factory_name)
static void emit_node (struct impl *this, struct spa_bt_transport *t, uint32_t id, const char *factory_name)
{
struct spa_device_object_info info;
struct spa_dict_item items[2];
@ -93,39 +92,74 @@ static void emit_node (struct impl *this, struct spa_bt_transport *t, const char
info.change_mask = SPA_DEVICE_OBJECT_CHANGE_MASK_PROPS;
info.props = &SPA_DICT_INIT_ARRAY(items);
spa_device_emit_object_info(&this->hooks, this->next_id++, &info);
spa_device_emit_object_info(&this->hooks, id, &info);
}
static int emit_nodes(struct impl *this)
static struct spa_bt_transport *find_transport(struct impl *this, int profile)
{
struct spa_bt_device *device = this->bt_dev;
struct spa_bt_transport *t;
spa_list_for_each(t, &device->transport_list, device_link) {
if (t->profile & device->connected_profiles) {
switch (t->profile) {
case SPA_BT_PROFILE_A2DP_SOURCE:
emit_node (this, t, SPA_NAME_API_BLUEZ5_A2DP_SOURCE);
break;
case SPA_BT_PROFILE_A2DP_SINK:
emit_node (this, t, SPA_NAME_API_BLUEZ5_A2DP_SINK);
break;
case SPA_BT_PROFILE_HSP_HS:
case SPA_BT_PROFILE_HSP_AG:
case SPA_BT_PROFILE_HFP_HF:
case SPA_BT_PROFILE_HFP_AG:
emit_node (this, t, SPA_NAME_API_BLUEZ5_SCO_SOURCE);
emit_node (this, t, SPA_NAME_API_BLUEZ5_SCO_SINK);
break;
default:
return -EINVAL;
}
}
if (t->profile & device->connected_profiles &&
(t->profile & profile) == t->profile)
return t;
}
return NULL;
}
static int emit_nodes(struct impl *this)
{
struct spa_bt_transport *t;
t = find_transport(this, this->profile);
if (t == NULL)
return 0;
switch (this->profile) {
case SPA_BT_PROFILE_A2DP_SOURCE:
emit_node(this, t, 0, SPA_NAME_API_BLUEZ5_A2DP_SOURCE);
break;
case SPA_BT_PROFILE_A2DP_SINK:
emit_node(this, t, 0, SPA_NAME_API_BLUEZ5_A2DP_SINK);
break;
case SPA_BT_PROFILE_HSP_HS:
case SPA_BT_PROFILE_HSP_AG:
case SPA_BT_PROFILE_HFP_HF:
case SPA_BT_PROFILE_HFP_AG:
emit_node(this, t, 0, SPA_NAME_API_BLUEZ5_SCO_SOURCE);
emit_node(this, t, 1, SPA_NAME_API_BLUEZ5_SCO_SINK);
break;
default:
return -EINVAL;
}
return 0;
}
static int set_profile(struct impl *this, uint32_t profile)
{
if (this->profile == profile)
return 0;
switch (this->profile) {
case SPA_BT_PROFILE_A2DP_SOURCE:
case SPA_BT_PROFILE_A2DP_SINK:
spa_device_emit_object_info(&this->hooks, 0, NULL);
break;
case SPA_BT_PROFILE_HSP_HS:
case SPA_BT_PROFILE_HSP_AG:
case SPA_BT_PROFILE_HFP_HF:
case SPA_BT_PROFILE_HFP_AG:
spa_device_emit_object_info(&this->hooks, 0, NULL);
spa_device_emit_object_info(&this->hooks, 1, NULL);
break;
default:
break;
}
this->profile = profile;
return emit_nodes(this);
}
static const struct spa_dict_item info_items[] = {
{ SPA_KEY_DEVICE_API, "bluez5" },
{ SPA_KEY_MEDIA_CLASS, "Audio/Device" },
@ -146,6 +180,7 @@ static int impl_add_listener(void *object,
if (events->info) {
struct spa_device_info info;
struct spa_param_info params[2];
info = SPA_DEVICE_INFO_INIT();
@ -153,8 +188,10 @@ static int impl_add_listener(void *object,
info.props = &SPA_DICT_INIT_ARRAY(info_items);
info.change_mask |= SPA_DEVICE_CHANGE_MASK_PARAMS;
info.n_params = 0;
info.params = NULL;
params[0] = SPA_PARAM_INFO(SPA_PARAM_EnumProfile, SPA_PARAM_INFO_READ);
params[1] = SPA_PARAM_INFO(SPA_PARAM_Profile, SPA_PARAM_INFO_READWRITE);
info.n_params = SPA_N_ELEMENTS(params);
info.params = params;
spa_device_emit_info(&this->hooks, &info);
}
@ -178,6 +215,18 @@ static int impl_sync(void *object, int seq)
return 0;
}
static inline int maskn(int v, int n)
{
int pos = 0, cnt = 0;
while (v) {
if ((v & 1) == 1 && (++cnt == n))
return 1 << pos;
v >>= 1;
pos++;
}
return 0;
}
static int impl_enum_params(void *object, int seq,
uint32_t id, uint32_t start, uint32_t num,
const struct spa_pod *filter)
@ -188,6 +237,7 @@ static int impl_enum_params(void *object, int seq,
uint8_t buffer[1024];
struct spa_result_device_params result;
uint32_t count = 0;
struct spa_bt_device *device = this->bt_dev;
spa_return_val_if_fail(this != NULL, -EINVAL);
spa_return_val_if_fail(num != 0, -EINVAL);
@ -202,6 +252,8 @@ static int impl_enum_params(void *object, int seq,
switch (id) {
case SPA_PARAM_EnumProfile:
{
int profile;
switch (result.index) {
case 0:
param = spa_pod_builder_add_object(&b,
@ -209,14 +261,18 @@ static int impl_enum_params(void *object, int seq,
SPA_PARAM_PROFILE_index, SPA_POD_Int(0),
SPA_PARAM_PROFILE_name, SPA_POD_String("Off"));
break;
case 1:
default:
{
profile = maskn(device->connected_profiles, result.index);
if (profile == 0)
return 0;
param = spa_pod_builder_add_object(&b,
SPA_TYPE_OBJECT_ParamProfile, id,
SPA_PARAM_PROFILE_index, SPA_POD_Int(1),
SPA_PARAM_PROFILE_name, SPA_POD_String("On"));
SPA_PARAM_PROFILE_index, SPA_POD_Int(profile),
SPA_PARAM_PROFILE_name, SPA_POD_String(spa_bt_profile_name(profile)));
break;
default:
return 0;
}
}
break;
}
@ -270,7 +326,7 @@ static int impl_set_param(void *object,
spa_debug_pod(0, NULL, param);
return res;
}
set_profile(this, id);
break;
}
default:
@ -352,8 +408,6 @@ impl_init(const struct spa_handle_factory *factory,
reset_props(&this->props);
this->next_id = 0;
return 0;
}

View file

@ -109,8 +109,8 @@ extern "C" {
enum spa_bt_profile {
SPA_BT_PROFILE_NULL = 0,
SPA_BT_PROFILE_A2DP_SOURCE = (1 << 0),
SPA_BT_PROFILE_A2DP_SINK = (1 << 1),
SPA_BT_PROFILE_A2DP_SINK = (1 << 0),
SPA_BT_PROFILE_A2DP_SOURCE = (1 << 1),
SPA_BT_PROFILE_HSP_HS = (1 << 2),
SPA_BT_PROFILE_HSP_AG = (1 << 3),
SPA_BT_PROFILE_HFP_HF = (1 << 4),