2018-01-25 15:53:29

by Grzegorz Kołodziejczyk

[permalink] [raw]
Subject: [PATCH BlueZ 1/5] tools: btpclient: Clear advertising data on reset

Advertising data should be cleared on gap reset command.
---
tools/btpclient.c | 40 +++++++++++++++++++++-------------------
1 file changed, 21 insertions(+), 19 deletions(-)

diff --git a/tools/btpclient.c b/tools/btpclient.c
index 3c88f1496..bdc404dd9 100644
--- a/tools/btpclient.c
+++ b/tools/btpclient.c
@@ -390,6 +390,24 @@ static void unreg_advertising_setup(struct l_dbus_message *message,
l_dbus_message_builder_destroy(builder);
}

+static void ad_cleanup_service(void *service)
+{
+ struct service_data *s = service;
+
+ l_free(s->uuid);
+ l_free(s);
+}
+
+static void ad_cleanup(void)
+{
+ l_free(ad.local_name);
+ l_queue_destroy(ad.uuids, l_free);
+ l_queue_destroy(ad.services, ad_cleanup_service);
+ l_queue_destroy(ad.manufacturers, l_free);
+
+ memset(&ad, 0, sizeof(ad));
+}
+
static void unreg_advertising_reply(struct l_dbus_proxy *proxy,
struct l_dbus_message *result,
void *user_data)
@@ -417,6 +435,8 @@ static void unreg_advertising_reply(struct l_dbus_proxy *proxy,
l_info("Unable to remove propety instance");
if (!l_dbus_unregister_interface(dbus, AD_IFACE))
l_info("Unable to unregister ad interface");
+
+ ad_cleanup();
}

static void btp_gap_reset(uint8_t index, const void *param, uint16_t length,
@@ -449,7 +469,7 @@ static void btp_gap_reset(uint8_t index, const void *param, uint16_t length,
NULL);
}

- if (adapter->ad_proxy)
+ if (adapter->ad_proxy && ad.registered)
if (!l_dbus_proxy_method_call(adapter->ad_proxy,
"UnregisterAdvertisement",
unreg_advertising_setup,
@@ -646,24 +666,6 @@ failed:
btp_send_error(btp, BTP_GAP_SERVICE, index, status);
}

-static void ad_cleanup_service(void *service)
-{
- struct service_data *s = service;
-
- l_free(s->uuid);
- l_free(s);
-}
-
-static void ad_cleanup(void)
-{
- l_free(ad.local_name);
- l_queue_destroy(ad.uuids, l_free);
- l_queue_destroy(ad.services, ad_cleanup_service);
- l_queue_destroy(ad.manufacturers, l_free);
-
- memset(&ad, 0, sizeof(ad));
-}
-
static void ad_init(void)
{
ad.uuids = l_queue_new();
--
2.13.6



2018-01-31 09:09:13

by Szymon Janc

[permalink] [raw]
Subject: Re: [PATCH BlueZ 1/5] tools: btpclient: Clear advertising data on reset

Hi Grzegorz,

On Thursday, 25 January 2018 16:53:29 CET Grzegorz Kolodziejczyk wrote:
> Advertising data should be cleared on gap reset command.
> ---
> tools/btpclient.c | 40 +++++++++++++++++++++-------------------
> 1 file changed, 21 insertions(+), 19 deletions(-)
>
> diff --git a/tools/btpclient.c b/tools/btpclient.c
> index 3c88f1496..bdc404dd9 100644
> --- a/tools/btpclient.c
> +++ b/tools/btpclient.c
> @@ -390,6 +390,24 @@ static void unreg_advertising_setup(struct
> l_dbus_message *message, l_dbus_message_builder_destroy(builder);
> }
>
> +static void ad_cleanup_service(void *service)
> +{
> + struct service_data *s = service;
> +
> + l_free(s->uuid);
> + l_free(s);
> +}
> +
> +static void ad_cleanup(void)
> +{
> + l_free(ad.local_name);
> + l_queue_destroy(ad.uuids, l_free);
> + l_queue_destroy(ad.services, ad_cleanup_service);
> + l_queue_destroy(ad.manufacturers, l_free);
> +
> + memset(&ad, 0, sizeof(ad));
> +}
> +
> static void unreg_advertising_reply(struct l_dbus_proxy *proxy,
> struct l_dbus_message *result,
> void *user_data)
> @@ -417,6 +435,8 @@ static void unreg_advertising_reply(struct l_dbus_proxy
> *proxy, l_info("Unable to remove propety instance");
> if (!l_dbus_unregister_interface(dbus, AD_IFACE))
> l_info("Unable to unregister ad interface");
> +
> + ad_cleanup();
> }
>
> static void btp_gap_reset(uint8_t index, const void *param, uint16_t
> length, @@ -449,7 +469,7 @@ static void btp_gap_reset(uint8_t index, const
> void *param, uint16_t length, NULL);
> }
>
> - if (adapter->ad_proxy)
> + if (adapter->ad_proxy && ad.registered)
> if (!l_dbus_proxy_method_call(adapter->ad_proxy,
> "UnregisterAdvertisement",
> unreg_advertising_setup,
> @@ -646,24 +666,6 @@ failed:
> btp_send_error(btp, BTP_GAP_SERVICE, index, status);
> }
>
> -static void ad_cleanup_service(void *service)
> -{
> - struct service_data *s = service;
> -
> - l_free(s->uuid);
> - l_free(s);
> -}
> -
> -static void ad_cleanup(void)
> -{
> - l_free(ad.local_name);
> - l_queue_destroy(ad.uuids, l_free);
> - l_queue_destroy(ad.services, ad_cleanup_service);
> - l_queue_destroy(ad.manufacturers, l_free);
> -
> - memset(&ad, 0, sizeof(ad));
> -}
> -
> static void ad_init(void)
> {
> ad.uuids = l_queue_new();


All patches applied (2/5 v2), thanks.

--
pozdrawiam
Szymon Janc



2018-01-25 15:53:33

by Grzegorz Kołodziejczyk

[permalink] [raw]
Subject: [PATCH BlueZ 5/5] tools/btpclient: Add passkey confirm ev and passkey confirm rsp cmd

This patch adds passkey confirm command handler and passkey confirm
event.
---
tools/btpclient.c | 100 +++++++++++++++++++++++++++++++++++++++++++++++++++---
1 file changed, 96 insertions(+), 4 deletions(-)

diff --git a/tools/btpclient.c b/tools/btpclient.c
index 00c4e5fac..5320003f9 100644
--- a/tools/btpclient.c
+++ b/tools/btpclient.c
@@ -1700,12 +1700,42 @@ static struct l_dbus_message *ag_request_confirmation_call(struct l_dbus *dbus,
struct l_dbus_message *message,
void *user_data)
{
- struct l_dbus_message *reply;
+ struct btp_gap_passkey_confirm_ev ev;
+ struct btp_device *device;
+ struct btp_adapter *adapter;
+ const char *path, *str_addr, *str_addr_type;
+ uint32_t passkey;

- reply = l_dbus_message_new_method_return(message);
- l_dbus_message_set_arguments(reply, "");
+ l_dbus_message_get_arguments(message, "ou", &path, &passkey);

- return reply;
+ device = find_device_by_path(path);
+
+ if (!l_dbus_proxy_get_property(device->proxy, "Address", "s", &str_addr)
+ || !l_dbus_proxy_get_property(device->proxy, "AddressType", "s",
+ &str_addr_type)) {
+ l_info("Cannot get device properties");
+
+ return NULL;
+ }
+
+ ev.passkey = L_CPU_TO_LE32(passkey);
+ ev.address_type = strcmp(str_addr_type, "public") ?
+ BTP_GAP_ADDR_RANDOM :
+ BTP_GAP_ADDR_PUBLIC;
+ if (str2ba(str_addr, &ev.address) < 0) {
+ l_info("Incorrect device address");
+
+ return NULL;
+ }
+
+ adapter = find_adapter_by_device(device);
+
+ ag.pending_req = l_dbus_message_ref(message);
+
+ btp_send(btp, BTP_GAP_SERVICE, BTP_EV_GAP_PASSKEY_CONFIRM,
+ adapter->index, sizeof(ev), &ev);
+
+ return NULL;
}

static struct l_dbus_message *ag_authorize_service_call(struct l_dbus *dbus,
@@ -2286,6 +2316,65 @@ failed:
btp_send_error(btp, BTP_GAP_SERVICE, index, status);
}

+static void passkey_confirm_rsp_reply(struct l_dbus_message *result,
+ void *user_data)
+{
+ struct btp_adapter *adapter = user_data;
+
+ if (l_dbus_message_is_error(result)) {
+ const char *name, *desc;
+
+ l_dbus_message_get_error(result, &name, &desc);
+ l_error("Failed to confirm passkey (%s), %s", name, desc);
+
+ btp_send_error(btp, BTP_GAP_SERVICE, adapter->index,
+ BTP_ERROR_FAIL);
+ return;
+ }
+
+ l_dbus_message_unref(ag.pending_req);
+
+ btp_send(btp, BTP_GAP_SERVICE, BTP_OP_GAP_PASSKEY_CONFIRM_RSP,
+ adapter->index, 0, NULL);
+}
+
+static void btp_gap_confirm_entry_rsp(uint8_t index, const void *param,
+ uint16_t length, void *user_data)
+{
+ const struct btp_gap_passkey_confirm_rsp_cp *cp = param;
+ struct btp_adapter *adapter = find_adapter_by_index(index);
+ struct l_dbus_message *reply;
+ uint8_t status = BTP_ERROR_FAIL;
+ bool prop;
+
+ if (!adapter) {
+ status = BTP_ERROR_INVALID_INDEX;
+ goto failed;
+ }
+
+ /* Adapter needs to be powered to be able to confirm passkey */
+ if (!l_dbus_proxy_get_property(adapter->proxy, "Powered", "b", &prop) ||
+ !prop || !ag.pending_req)
+ goto failed;
+
+ if (cp->match) {
+ reply = l_dbus_message_new_method_return(ag.pending_req);
+ l_dbus_message_set_arguments(reply, "");
+ } else {
+ reply = l_dbus_message_new_error(ag.pending_req,
+ "org.bluez.Error.Rejected",
+ "Passkey missmatch");
+ }
+
+ l_dbus_send_with_reply(dbus, ag.pending_req, passkey_confirm_rsp_reply,
+ adapter, NULL);
+
+ return;
+
+failed:
+ btp_send_error(btp, BTP_GAP_SERVICE, index, status);
+}
+
static void btp_gap_device_found_ev(struct l_dbus_proxy *proxy)
{
struct btp_device_found_ev ev;
@@ -2419,6 +2508,9 @@ static void register_gap_service(void)

btp_register(btp, BTP_GAP_SERVICE, BTP_OP_GAP_PASSKEY_ENTRY_RSP,
btp_gap_passkey_entry_rsp, NULL, NULL);
+
+ btp_register(btp, BTP_GAP_SERVICE, BTP_OP_GAP_PASSKEY_CONFIRM_RSP,
+ btp_gap_confirm_entry_rsp, NULL, NULL);
}

static void btp_core_read_commands(uint8_t index, const void *param,
--
2.13.6


2018-01-25 15:53:32

by Grzegorz Kołodziejczyk

[permalink] [raw]
Subject: [PATCH BlueZ 4/5] tools/btpclient: Add passkey entry cmd and passkey display event

This patch adds passkey entry command handler and passkey display event.
---
tools/btpclient.c | 92 +++++++++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 92 insertions(+)

diff --git a/tools/btpclient.c b/tools/btpclient.c
index 95d369b51..00c4e5fac 100644
--- a/tools/btpclient.c
+++ b/tools/btpclient.c
@@ -1655,11 +1655,44 @@ static struct l_dbus_message *ag_display_passkey_call(struct l_dbus *dbus,
struct l_dbus_message *message,
void *user_data)
{
+ struct btp_gap_passkey_display_ev ev;
+ struct btp_device *device;
+ struct btp_adapter *adapter;
struct l_dbus_message *reply;
+ const char *path, *str_addr, *str_addr_type;
+ uint32_t passkey;
+ uint16_t entered;

reply = l_dbus_message_new_method_return(message);
l_dbus_message_set_arguments(reply, "");

+ l_dbus_message_get_arguments(message, "ouq", &path, &passkey, &entered);
+
+ device = find_device_by_path(path);
+
+ if (!l_dbus_proxy_get_property(device->proxy, "Address", "s", &str_addr)
+ || !l_dbus_proxy_get_property(device->proxy, "AddressType", "s",
+ &str_addr_type)) {
+ l_info("Cannot get device properties");
+
+ return reply;
+ }
+
+ ev.passkey = L_CPU_TO_LE32(passkey);
+ ev.address_type = strcmp(str_addr_type, "public") ?
+ BTP_GAP_ADDR_RANDOM :
+ BTP_GAP_ADDR_PUBLIC;
+ if (str2ba(str_addr, &ev.address) < 0) {
+ l_info("Incorrect device addres");
+
+ return reply;
+ }
+
+ adapter = find_adapter_by_device(device);
+
+ btp_send(btp, BTP_GAP_SERVICE, BTP_EV_GAP_PASSKEY_DISPLAY,
+ adapter->index, sizeof(ev), &ev);
+
return reply;
}

@@ -2197,6 +2230,62 @@ failed:
btp_send_error(btp, BTP_GAP_SERVICE, index, status);
}

+static void passkey_entry_rsp_reply(struct l_dbus_message *result,
+ void *user_data)
+{
+ struct btp_adapter *adapter = user_data;
+
+ if (l_dbus_message_is_error(result)) {
+ const char *name, *desc;
+
+ l_dbus_message_get_error(result, &name, &desc);
+ l_error("Failed to reply with passkey (%s), %s", name, desc);
+
+ btp_send_error(btp, BTP_GAP_SERVICE, adapter->index,
+ BTP_ERROR_FAIL);
+ return;
+ }
+
+ l_dbus_message_unref(ag.pending_req);
+
+ btp_send(btp, BTP_GAP_SERVICE, BTP_OP_GAP_PASSKEY_ENTRY_RSP,
+ adapter->index, 0, NULL);
+}
+
+static void btp_gap_passkey_entry_rsp(uint8_t index, const void *param,
+ uint16_t length, void *user_data)
+{
+ const struct btp_gap_passkey_entry_rsp_cp *cp = param;
+ struct btp_adapter *adapter = find_adapter_by_index(index);
+ struct l_dbus_message_builder *builder;
+ uint8_t status = BTP_ERROR_FAIL;
+ uint32_t passkey = L_CPU_TO_LE32(cp->passkey);
+ bool prop;
+
+ if (!adapter) {
+ status = BTP_ERROR_INVALID_INDEX;
+ goto failed;
+ }
+
+ /* Adapter needs to be powered to be able to response with passkey */
+ if (!l_dbus_proxy_get_property(adapter->proxy, "Powered", "b", &prop) ||
+ !prop || !ag.pending_req)
+ goto failed;
+
+ builder = l_dbus_message_builder_new(ag.pending_req);
+ l_dbus_message_builder_append_basic(builder, 'u', &passkey);
+ l_dbus_message_builder_finalize(builder);
+ l_dbus_message_builder_destroy(builder);
+
+ l_dbus_send_with_reply(dbus, ag.pending_req, passkey_entry_rsp_reply,
+ adapter, NULL);
+
+ return;
+
+failed:
+ btp_send_error(btp, BTP_GAP_SERVICE, index, status);
+}
+
static void btp_gap_device_found_ev(struct l_dbus_proxy *proxy)
{
struct btp_device_found_ev ev;
@@ -2327,6 +2416,9 @@ static void register_gap_service(void)

btp_register(btp, BTP_GAP_SERVICE, BTP_OP_GAP_UNPAIR, btp_gap_unpair,
NULL, NULL);
+
+ btp_register(btp, BTP_GAP_SERVICE, BTP_OP_GAP_PASSKEY_ENTRY_RSP,
+ btp_gap_passkey_entry_rsp, NULL, NULL);
}

static void btp_core_read_commands(uint8_t index, const void *param,
--
2.13.6


2018-01-25 15:53:31

by Grzegorz Kołodziejczyk

[permalink] [raw]
Subject: [PATCH BlueZ 3/5] tools/btpclient: Add pair, unpair commands

This patch adds pair and unpair commands for btp client.
---
tools/btpclient.c | 145 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 145 insertions(+)

diff --git a/tools/btpclient.c b/tools/btpclient.c
index 7340a8812..95d369b51 100644
--- a/tools/btpclient.c
+++ b/tools/btpclient.c
@@ -306,6 +306,8 @@ static void btp_gap_read_commands(uint8_t index, const void *param,
commands |= (1 << BTP_OP_GAP_CONNECT);
commands |= (1 << BTP_OP_GAP_DISCONNECT);
commands |= (1 << BTP_OP_GAP_SET_IO_CAPA);
+ commands |= (1 << BTP_OP_GAP_PAIR);
+ commands |= (1 << BTP_OP_GAP_UNPAIR);

commands = L_CPU_TO_LE16(commands);

@@ -2058,6 +2060,143 @@ failed:
btp_send_error(btp, BTP_GAP_SERVICE, index, status);
}

+static void pair_reply(struct l_dbus_proxy *proxy,
+ struct l_dbus_message *result, void *user_data)
+{
+ uint8_t adapter_index = L_PTR_TO_UINT(user_data);
+ struct btp_adapter *adapter = find_adapter_by_index(adapter_index);
+
+ if (!adapter)
+ return;
+
+ if (l_dbus_message_is_error(result)) {
+ const char *name, *desc;
+
+ l_dbus_message_get_error(result, &name, &desc);
+ l_error("Failed to pair (%s), %s", name, desc);
+
+ return;
+ }
+}
+
+static void btp_gap_pair(uint8_t index, const void *param, uint16_t length,
+ void *user_data)
+{
+ struct btp_adapter *adapter = find_adapter_by_index(index);
+ const struct btp_gap_pair_cp *cp = param;
+ uint8_t status = BTP_ERROR_FAIL;
+ struct btp_device *device;
+ bool prop;
+
+ if (!adapter) {
+ status = BTP_ERROR_INVALID_INDEX;
+ goto failed;
+ }
+
+ /* Adapter needs to be powered to be able to pair */
+ if (!l_dbus_proxy_get_property(adapter->proxy, "Powered", "b", &prop) ||
+ !prop)
+ goto failed;
+
+ device = find_device_by_address(adapter, &cp->address,
+ cp->address_type);
+
+ if (!device)
+ goto failed;
+
+ /* This command is asynchronous, send reply immediatelly to not block
+ * pairing process eg. passkey request.
+ */
+ btp_send(btp, BTP_GAP_SERVICE, BTP_OP_GAP_PAIR, adapter->index, 0,
+ NULL);
+
+ l_dbus_proxy_method_call(device->proxy, "Pair", NULL, pair_reply,
+ L_UINT_TO_PTR(adapter->index), NULL);
+
+ return;
+
+failed:
+ btp_send_error(btp, BTP_GAP_SERVICE, index, status);
+}
+
+static void unpair_reply(struct l_dbus_proxy *proxy,
+ struct l_dbus_message *result, void *user_data)
+{
+ struct btp_device *device = user_data;
+ struct btp_adapter *adapter = find_adapter_by_device(device);
+
+ if (!adapter) {
+ btp_send_error(btp, BTP_GAP_SERVICE, BTP_INDEX_NON_CONTROLLER,
+ BTP_ERROR_FAIL);
+ return;
+ }
+
+ if (l_dbus_message_is_error(result)) {
+ const char *name, *desc;
+
+ l_dbus_message_get_error(result, &name, &desc);
+ l_error("Failed to unpair (%s), %s", name, desc);
+
+ btp_send_error(btp, BTP_GAP_SERVICE, adapter->index,
+ BTP_ERROR_FAIL);
+ return;
+ }
+
+ btp_send(btp, BTP_GAP_SERVICE, BTP_OP_GAP_UNPAIR, adapter->index, 0,
+ NULL);
+}
+
+static void unpair_setup(struct l_dbus_message *message, void *user_data)
+{
+ struct btp_device *device = user_data;
+ const char *path = l_dbus_proxy_get_path(device->proxy);
+ struct l_dbus_message_builder *builder;
+
+ builder = l_dbus_message_builder_new(message);
+
+ l_dbus_message_builder_append_basic(builder, 'o', path);
+
+ l_dbus_message_builder_finalize(builder);
+ l_dbus_message_builder_destroy(builder);
+}
+
+static void btp_gap_unpair(uint8_t index, const void *param, uint16_t length,
+ void *user_data)
+{
+ struct btp_adapter *adapter = find_adapter_by_index(index);
+ const struct btp_gap_pair_cp *cp = param;
+ uint8_t status = BTP_ERROR_FAIL;
+ struct btp_device *device;
+ bool prop;
+
+ if (!adapter) {
+ status = BTP_ERROR_INVALID_INDEX;
+ goto failed;
+ }
+
+ /* Adapter needs to be powered to be able to unpair */
+ if (!l_dbus_proxy_get_property(adapter->proxy, "Powered", "b", &prop) ||
+ !prop)
+ goto failed;
+
+ device = find_device_by_address(adapter, &cp->address,
+ cp->address_type);
+
+ if (!device)
+ goto failed;
+
+ /* There is no direct unpair method, removing device will clear pairing
+ * information.
+ */
+ l_dbus_proxy_method_call(adapter->proxy, "RemoveDevice", unpair_setup,
+ unpair_reply, device, NULL);
+
+ return;
+
+failed:
+ btp_send_error(btp, BTP_GAP_SERVICE, index, status);
+}
+
static void btp_gap_device_found_ev(struct l_dbus_proxy *proxy)
{
struct btp_device_found_ev ev;
@@ -2182,6 +2321,12 @@ static void register_gap_service(void)

btp_register(btp, BTP_GAP_SERVICE, BTP_OP_GAP_SET_IO_CAPA,
btp_gap_set_io_capabilities, NULL, NULL);
+
+ btp_register(btp, BTP_GAP_SERVICE, BTP_OP_GAP_PAIR, btp_gap_pair, NULL,
+ NULL);
+
+ btp_register(btp, BTP_GAP_SERVICE, BTP_OP_GAP_UNPAIR, btp_gap_unpair,
+ NULL, NULL);
}

static void btp_core_read_commands(uint8_t index, const void *param,
--
2.13.6


2018-01-25 15:53:30

by Grzegorz Kołodziejczyk

[permalink] [raw]
Subject: [PATCH BlueZ 2/5] tools/btpclient: Add set io capabilities command

This patch adds support for set io capabilities command.
---
tools/btpclient.c | 558 +++++++++++++++++++++++++++++++++++++++++++++++++++++-
1 file changed, 555 insertions(+), 3 deletions(-)

diff --git a/tools/btpclient.c b/tools/btpclient.c
index bdc404dd9..7340a8812 100644
--- a/tools/btpclient.c
+++ b/tools/btpclient.c
@@ -36,7 +36,9 @@
#include "src/shared/btp.h"

#define AD_PATH "/org/bluez/advertising"
+#define AG_PATH "/org/bluez/agent"
#define AD_IFACE "org.bluez.LEAdvertisement1"
+#define AG_IFACE "org.bluez.Agent1"

/* List of assigned numbers for advetising data and scan response */
#define AD_TYPE_FLAGS 0x01
@@ -46,6 +48,8 @@
#define AD_TYPE_APPEARANCE 0x19
#define AD_TYPE_MANUFACTURER_DATA 0xff

+static void register_gap_service(void);
+
static struct l_dbus *dbus;

struct btp_adapter {
@@ -98,6 +102,12 @@ static struct ad {
bool appearance;
} ad;

+static struct btp_agent {
+ bool registered;
+ struct l_dbus_proxy *proxy;
+ struct l_dbus_message *pending_req;
+} ag;
+
static char *dupuuid2str(const uint8_t *uuid, uint8_t len)
{
switch (len) {
@@ -199,6 +209,31 @@ static struct btp_device *find_device_by_address(struct btp_adapter *adapter,
return NULL;
}

+static bool match_device_paths(const void *device, const void *path)
+{
+ const struct btp_device *dev = device;
+
+ return !strcmp(l_dbus_proxy_get_path(dev->proxy), path);
+}
+
+static struct btp_device *find_device_by_path(const char *path)
+{
+ const struct l_queue_entry *entry;
+ struct btp_device *device;
+
+ for (entry = l_queue_get_entries(adapters); entry;
+ entry = entry->next) {
+ struct btp_adapter *adapter = entry->data;
+
+ device = l_queue_find(adapter->devices, match_device_paths,
+ path);
+ if (device)
+ return device;
+ }
+
+ return NULL;
+}
+
static bool match_adapter_dev_proxy(const void *device, const void *proxy)
{
const struct btp_device *d = device;
@@ -270,6 +305,7 @@ static void btp_gap_read_commands(uint8_t index, const void *param,
commands |= (1 << BTP_OP_GAP_STOP_DISCOVERY);
commands |= (1 << BTP_OP_GAP_CONNECT);
commands |= (1 << BTP_OP_GAP_DISCONNECT);
+ commands |= (1 << BTP_OP_GAP_SET_IO_CAPA);

commands = L_CPU_TO_LE16(commands);

@@ -439,6 +475,43 @@ static void unreg_advertising_reply(struct l_dbus_proxy *proxy,
ad_cleanup();
}

+static void unreg_agent_setup(struct l_dbus_message *message, void *user_data)
+{
+ struct l_dbus_message_builder *builder;
+
+ builder = l_dbus_message_builder_new(message);
+
+ l_dbus_message_builder_append_basic(builder, 'o', AG_PATH);
+
+ l_dbus_message_builder_finalize(builder);
+ l_dbus_message_builder_destroy(builder);
+}
+
+static void reset_unreg_agent_reply(struct l_dbus_proxy *proxy,
+ struct l_dbus_message *result,
+ void *user_data)
+{
+ if (l_dbus_message_is_error(result)) {
+ const char *name;
+
+ l_dbus_message_get_error(result, &name, NULL);
+
+ l_error("Failed to unregister agent %s (%s)",
+ l_dbus_proxy_get_path(proxy), name);
+ return;
+ }
+
+ if (!l_dbus_object_remove_interface(dbus, AG_PATH,
+ L_DBUS_INTERFACE_PROPERTIES))
+ l_info("Unable to remove propety instance");
+ if (!l_dbus_object_remove_interface(dbus, AG_PATH, AG_IFACE))
+ l_info("Unable to remove agent instance");
+ if (!l_dbus_unregister_interface(dbus, AG_IFACE))
+ l_info("Unable to unregister agent interface");
+
+ ag.registered = false;
+}
+
static void btp_gap_reset(uint8_t index, const void *param, uint16_t length,
void *user_data)
{
@@ -479,6 +552,15 @@ static void btp_gap_reset(uint8_t index, const void *param, uint16_t length,
goto failed;
}

+ if (ag.proxy && ag.registered)
+ if (!l_dbus_proxy_method_call(ag.proxy, "UnregisterAgent",
+ unreg_agent_setup,
+ reset_unreg_agent_reply,
+ NULL, NULL)) {
+ status = BTP_ERROR_FAIL;
+ goto failed;
+ }
+
adapter->current_settings = adapter->default_settings;

/* TODO for we assume all went well */
@@ -1518,6 +1600,464 @@ failed:
btp_send_error(btp, BTP_GAP_SERVICE, index, status);
}

+static struct l_dbus_message *ag_release_call(struct l_dbus *dbus,
+ struct l_dbus_message *message,
+ void *user_data)
+{
+ struct l_dbus_message *reply;
+
+ reply = l_dbus_message_new_method_return(message);
+ l_dbus_message_set_arguments(reply, "");
+
+ return reply;
+}
+
+static struct l_dbus_message *ag_request_passkey_call(struct l_dbus *dbus,
+ struct l_dbus_message *message,
+ void *user_data)
+{
+ struct btp_gap_passkey_req_ev ev;
+ struct btp_device *device;
+ struct btp_adapter *adapter;
+ const char *path, *str_addr, *str_addr_type;
+
+ l_dbus_message_get_arguments(message, "o", &path);
+
+ device = find_device_by_path(path);
+
+ if (!l_dbus_proxy_get_property(device->proxy, "Address", "s", &str_addr)
+ || !l_dbus_proxy_get_property(device->proxy, "AddressType", "s",
+ &str_addr_type)) {
+ l_info("Cannot get device properties");
+
+ return NULL;
+ }
+
+ ev.address_type = strcmp(str_addr_type, "public") ?
+ BTP_GAP_ADDR_RANDOM :
+ BTP_GAP_ADDR_PUBLIC;
+ if (!str2ba(str_addr, &ev.address))
+ return NULL;
+
+ adapter = find_adapter_by_device(device);
+
+ ag.pending_req = l_dbus_message_ref(message);
+
+ btp_send(btp, BTP_GAP_SERVICE, BTP_EV_GAP_PASSKEY_REQUEST,
+ adapter->index, sizeof(ev), &ev);
+
+ return NULL;
+}
+
+static struct l_dbus_message *ag_display_passkey_call(struct l_dbus *dbus,
+ struct l_dbus_message *message,
+ void *user_data)
+{
+ struct l_dbus_message *reply;
+
+ reply = l_dbus_message_new_method_return(message);
+ l_dbus_message_set_arguments(reply, "");
+
+ return reply;
+}
+
+static struct l_dbus_message *ag_request_confirmation_call(struct l_dbus *dbus,
+ struct l_dbus_message *message,
+ void *user_data)
+{
+ struct l_dbus_message *reply;
+
+ reply = l_dbus_message_new_method_return(message);
+ l_dbus_message_set_arguments(reply, "");
+
+ return reply;
+}
+
+static struct l_dbus_message *ag_authorize_service_call(struct l_dbus *dbus,
+ struct l_dbus_message *message,
+ void *user_data)
+{
+ struct l_dbus_message *reply;
+
+ reply = l_dbus_message_new_method_return(message);
+ l_dbus_message_set_arguments(reply, "");
+
+ return reply;
+}
+
+static struct l_dbus_message *ag_cancel_call(struct l_dbus *dbus,
+ struct l_dbus_message *message,
+ void *user_data)
+{
+ struct l_dbus_message *reply;
+
+ reply = l_dbus_message_new_method_return(message);
+ l_dbus_message_set_arguments(reply, "");
+
+ return reply;
+}
+
+static void setup_ag_interface(struct l_dbus_interface *iface)
+{
+ l_dbus_interface_method(iface, "Release", 0, ag_release_call, "", "");
+ l_dbus_interface_method(iface, "RequestPasskey", 0,
+ ag_request_passkey_call, "u", "o",
+ "passkey", "device");
+ l_dbus_interface_method(iface, "DisplayPasskey", 0,
+ ag_display_passkey_call, "", "ouq",
+ "device", "passkey", "entered");
+ l_dbus_interface_method(iface, "RequestConfirmation", 0,
+ ag_request_confirmation_call, "", "ou",
+ "device", "passkey");
+ l_dbus_interface_method(iface, "RequestAuthorization", 0,
+ ag_request_confirmation_call, "", "o",
+ "device");
+ l_dbus_interface_method(iface, "AuthorizeService", 0,
+ ag_authorize_service_call, "", "os",
+ "device", "uuid");
+ l_dbus_interface_method(iface, "Cancel", 0, ag_cancel_call, "", "");
+}
+
+struct set_io_capabilities_data {
+ uint8_t capa;
+ struct btp_adapter *adapter;
+};
+
+static void set_io_capabilities_setup(struct l_dbus_message *message,
+ void *user_data)
+{
+ struct set_io_capabilities_data *sicd = user_data;
+ struct l_dbus_message_builder *builder;
+ char *capa_str;
+
+ builder = l_dbus_message_builder_new(message);
+
+ l_dbus_message_builder_append_basic(builder, 'o', AG_PATH);
+
+ switch (sicd->capa) {
+ case BTP_GAP_IOCAPA_DISPLAY_ONLY:
+ capa_str = "DisplayOnly";
+ break;
+ case BTP_GAP_IOCAPA_DISPLAY_YESNO:
+ capa_str = "DisplayYesNo";
+ break;
+ case BTP_GAP_IOCAPA_KEYBOARD_ONLY:
+ capa_str = "KeyboardOnly";
+ break;
+ case BTP_GAP_IOCAPA_KEYBOARD_DISPLAY:
+ capa_str = "KeyboardDisplay";
+ break;
+ case BTP_GAP_IOCAPA_NO_INPUT_NO_OUTPUT:
+ default:
+ capa_str = "NoInputNoOutput";
+ break;
+ }
+
+ l_dbus_message_builder_append_basic(builder, 's', capa_str);
+
+ l_dbus_message_builder_finalize(builder);
+ l_dbus_message_builder_destroy(builder);
+}
+
+static void reg_def_req_default_agent_reply(struct l_dbus_proxy *proxy,
+ struct l_dbus_message *result,
+ void *user_data)
+{
+ if (l_dbus_message_is_error(result)) {
+ const char *name, *desc;
+
+ if (!l_dbus_object_remove_interface(dbus, AG_PATH, AG_IFACE))
+ l_info("Unable to remove agent instance");
+ if (!l_dbus_unregister_interface(dbus, AG_IFACE))
+ l_info("Unable to unregister agent interface");
+
+ l_dbus_message_get_error(result, &name, &desc);
+ l_error("Failed to request default agent (%s), %s", name, desc);
+
+ btp_send_error(btp, BTP_CORE_SERVICE, BTP_INDEX_NON_CONTROLLER,
+ BTP_ERROR_FAIL);
+ return;
+ }
+
+ register_gap_service();
+ gap_service_registered = true;
+
+ ag.registered = true;
+
+ btp_send(btp, BTP_CORE_SERVICE, BTP_OP_CORE_REGISTER,
+ BTP_INDEX_NON_CONTROLLER, 0, NULL);
+}
+
+static void set_io_req_default_agent_reply(struct l_dbus_proxy *proxy,
+ struct l_dbus_message *result,
+ void *user_data)
+{
+ struct btp_adapter *adapter = user_data;
+
+ if (!adapter) {
+ btp_send_error(btp, BTP_GAP_SERVICE, BTP_INDEX_NON_CONTROLLER,
+ BTP_ERROR_FAIL);
+ goto failed;
+ }
+
+ if (l_dbus_message_is_error(result)) {
+ const char *name, *desc;
+
+ l_dbus_message_get_error(result, &name, &desc);
+ l_error("Failed to set io capabilities (%s), %s", name, desc);
+
+ btp_send_error(btp, BTP_GAP_SERVICE, adapter->index,
+ BTP_ERROR_FAIL);
+ goto failed;
+ }
+
+ ag.registered = true;
+
+ btp_send(btp, BTP_GAP_SERVICE, BTP_OP_GAP_SET_IO_CAPA,
+ adapter->index, 0, NULL);
+
+ return;
+
+failed:
+ if (!l_dbus_object_remove_interface(dbus, AG_PATH, AG_IFACE))
+ l_info("Unable to remove agent instance");
+ if (!l_dbus_unregister_interface(dbus, AG_IFACE))
+ l_info("Unable to unregister agent interface");
+}
+
+static void request_default_agent_setup(struct l_dbus_message *message,
+ void *user_data)
+{
+ struct l_dbus_message_builder *builder;
+
+ builder = l_dbus_message_builder_new(message);
+
+ l_dbus_message_builder_append_basic(builder, 'o', AG_PATH);
+
+ l_dbus_message_builder_finalize(builder);
+ l_dbus_message_builder_destroy(builder);
+}
+
+static void set_io_capabilities_reply(struct l_dbus_proxy *proxy,
+ struct l_dbus_message *result,
+ void *user_data)
+{
+ struct set_io_capabilities_data *sicd = user_data;
+
+ if (!sicd->adapter) {
+ btp_send_error(btp, BTP_GAP_SERVICE, BTP_INDEX_NON_CONTROLLER,
+ BTP_ERROR_FAIL);
+ goto failed;
+ }
+
+ if (l_dbus_message_is_error(result)) {
+ const char *name, *desc;
+
+ l_dbus_message_get_error(result, &name, &desc);
+ l_error("Failed to set io capabilities (%s), %s", name, desc);
+
+ btp_send_error(btp, BTP_GAP_SERVICE, sicd->adapter->index,
+ BTP_ERROR_FAIL);
+ goto failed;
+ }
+
+ if (l_dbus_proxy_method_call(ag.proxy, "RequestDefaultAgent",
+ request_default_agent_setup,
+ set_io_req_default_agent_reply,
+ sicd->adapter, NULL))
+ return;
+
+failed:
+ if (!l_dbus_object_remove_interface(dbus, AG_PATH, AG_IFACE))
+ l_info("Unable to remove agent instance");
+ if (!l_dbus_unregister_interface(dbus, AG_IFACE))
+ l_info("Unable to unregister agent interface");
+}
+
+static void register_default_agent_reply(struct l_dbus_proxy *proxy,
+ struct l_dbus_message *result,
+ void *user_data)
+{
+ const char *name, *desc;
+
+ if (l_dbus_message_is_error(result)) {
+ if (!l_dbus_object_remove_interface(dbus, AG_PATH, AG_IFACE))
+ l_info("Unable to remove agent instance");
+ if (!l_dbus_unregister_interface(dbus, AG_IFACE))
+ l_info("Unable to unregister agent interface");
+
+ l_dbus_message_get_error(result, &name, &desc);
+ l_error("Failed to register default agent (%s), %s", name,
+ desc);
+ return;
+ }
+
+ if (!l_dbus_proxy_method_call(ag.proxy, "RequestDefaultAgent",
+ request_default_agent_setup,
+ reg_def_req_default_agent_reply,
+ NULL, NULL)) {
+ if (!l_dbus_object_remove_interface(dbus, AG_PATH, AG_IFACE))
+ l_info("Unable to remove agent instance");
+ if (!l_dbus_unregister_interface(dbus, AG_IFACE))
+ l_info("Unable to unregister agent interface");
+ }
+}
+
+static void set_io_capabilities_destroy(void *user_data)
+{
+ l_free(user_data);
+}
+
+static bool register_default_agent(struct btp_adapter *adapter, uint8_t capa,
+ l_dbus_client_proxy_result_func_t set_io_cb)
+{
+ struct set_io_capabilities_data *data;
+
+ if (!l_dbus_register_interface(dbus, AG_IFACE, setup_ag_interface, NULL,
+ false)) {
+ l_info("Unable to register agent interface");
+ return false;
+ }
+
+ if (!l_dbus_object_add_interface(dbus, AG_PATH, AG_IFACE, NULL)) {
+ l_info("Unable to instantiate agent interface");
+
+ if (!l_dbus_unregister_interface(dbus, AG_IFACE))
+ l_info("Unable to unregister agent interface");
+
+ return false;
+ }
+
+ if (!l_dbus_object_add_interface(dbus, AG_PATH,
+ L_DBUS_INTERFACE_PROPERTIES,
+ NULL)) {
+ l_info("Unable to instantiate the ag properties interface");
+
+ if (!l_dbus_object_remove_interface(dbus, AG_PATH, AG_IFACE))
+ l_info("Unable to remove agent instance");
+ if (!l_dbus_unregister_interface(dbus, AG_IFACE))
+ l_info("Unable to unregister agent interface");
+
+ return false;
+ }
+
+ data = l_new(struct set_io_capabilities_data, 1);
+ data->adapter = adapter;
+ data->capa = capa;
+
+ if (!l_dbus_proxy_method_call(ag.proxy, "RegisterAgent",
+ set_io_capabilities_setup, set_io_cb,
+ data, set_io_capabilities_destroy)) {
+ if (!l_dbus_object_remove_interface(dbus, AG_PATH, AG_IFACE))
+ l_info("Unable to remove agent instance");
+ if (!l_dbus_unregister_interface(dbus, AG_IFACE))
+ l_info("Unable to unregister agent interface");
+
+ return false;
+ }
+
+ return true;
+}
+
+struct rereg_unreg_agent_data {
+ struct btp_adapter *adapter;
+ l_dbus_client_proxy_result_func_t cb;
+ uint8_t capa;
+};
+
+static void rereg_unreg_agent_reply(struct l_dbus_proxy *proxy,
+ struct l_dbus_message *result,
+ void *user_data)
+{
+ struct rereg_unreg_agent_data *ruad = user_data;
+
+ if (l_dbus_message_is_error(result)) {
+ const char *name;
+
+ l_dbus_message_get_error(result, &name, NULL);
+
+ l_error("Failed to unregister agent %s (%s)",
+ l_dbus_proxy_get_path(proxy), name);
+ return;
+ }
+
+ if (!l_dbus_object_remove_interface(dbus, AG_PATH,
+ L_DBUS_INTERFACE_PROPERTIES))
+ l_info("Unable to remove propety instance");
+ if (!l_dbus_object_remove_interface(dbus, AG_PATH, AG_IFACE))
+ l_info("Unable to remove agent instance");
+ if (!l_dbus_unregister_interface(dbus, AG_IFACE))
+ l_info("Unable to unregister agent interface");
+
+ ag.registered = false;
+
+ if (!register_default_agent(ruad->adapter, ruad->capa, ruad->cb))
+ btp_send_error(btp, BTP_GAP_SERVICE, ruad->adapter->index,
+ BTP_ERROR_FAIL);
+}
+
+static void rereg_unreg_agent_destroy(void *rereg_unreg_agent_data)
+{
+ l_free(rereg_unreg_agent_data);
+}
+
+static void btp_gap_set_io_capabilities(uint8_t index, const void *param,
+ uint16_t length, void *user_data)
+{
+ struct btp_adapter *adapter = find_adapter_by_index(index);
+ const struct btp_gap_set_io_capa_cp *cp = param;
+ uint8_t status = BTP_ERROR_FAIL;
+ struct rereg_unreg_agent_data *data;
+ bool prop;
+
+ switch (cp->capa) {
+ case BTP_GAP_IOCAPA_DISPLAY_ONLY:
+ case BTP_GAP_IOCAPA_DISPLAY_YESNO:
+ case BTP_GAP_IOCAPA_KEYBOARD_ONLY:
+ case BTP_GAP_IOCAPA_NO_INPUT_NO_OUTPUT:
+ case BTP_GAP_IOCAPA_KEYBOARD_DISPLAY:
+ break;
+ default:
+ l_error("Wrong iocapa given!");
+
+ goto failed;
+ }
+
+ if (!adapter) {
+ status = BTP_ERROR_INVALID_INDEX;
+ goto failed;
+ }
+
+ /* Adapter needs to be powered to be able to set io cap */
+ if (!l_dbus_proxy_get_property(adapter->proxy, "Powered", "b", &prop) ||
+ !prop)
+ goto failed;
+
+ if (ag.registered) {
+ data = l_new(struct rereg_unreg_agent_data, 1);
+ data->adapter = adapter;
+ data->capa = cp->capa;
+ data->cb = set_io_capabilities_reply;
+
+ if (!l_dbus_proxy_method_call(ag.proxy, "UnregisterAgent",
+ unreg_agent_setup,
+ rereg_unreg_agent_reply, data,
+ rereg_unreg_agent_destroy))
+ goto failed;
+
+ return;
+ }
+
+ if (!register_default_agent(adapter, cp->capa,
+ set_io_capabilities_reply))
+ goto failed;
+
+ return;
+
+failed:
+ btp_send_error(btp, BTP_GAP_SERVICE, index, status);
+}
+
static void btp_gap_device_found_ev(struct l_dbus_proxy *proxy)
{
struct btp_device_found_ev ev;
@@ -1639,6 +2179,9 @@ static void register_gap_service(void)

btp_register(btp, BTP_GAP_SERVICE, BTP_OP_GAP_DISCONNECT,
btp_gap_disconnect, NULL, NULL);
+
+ btp_register(btp, BTP_GAP_SERVICE, BTP_OP_GAP_SET_IO_CAPA,
+ btp_gap_set_io_capabilities, NULL, NULL);
}

static void btp_core_read_commands(uint8_t index, const void *param,
@@ -1698,9 +2241,12 @@ static void btp_core_register(uint8_t index, const void *param,
if (gap_service_registered)
goto failed;

- register_gap_service();
- gap_service_registered = true;
- break;
+ if (!register_default_agent(NULL,
+ BTP_GAP_IOCAPA_NO_INPUT_NO_OUTPUT,
+ register_default_agent_reply))
+ goto failed;
+
+ return;
case BTP_GATT_SERVICE:
case BTP_L2CAP_SERVICE:
case BTP_MESH_NODE_SERVICE:
@@ -1896,6 +2442,12 @@ static void proxy_added(struct l_dbus_proxy *proxy, void *user_data)

return;
}
+
+ if (!strcmp(interface, "org.bluez.AgentManager1")) {
+ ag.proxy = proxy;
+
+ return;
+ }
}

static bool device_match_by_proxy(const void *a, const void *b)
--
2.13.6