* [PATCH BlueZ v2 1/4] tools/btpclient: Fix setting/reseting connectable flag
@ 2018-01-12 14:10 Grzegorz Kolodziejczyk
2018-01-12 14:10 ` [PATCH BlueZ v2 2/4] tools/btpclient: Add start, stop advertising commands Grzegorz Kolodziejczyk
` (3 more replies)
0 siblings, 4 replies; 7+ messages in thread
From: Grzegorz Kolodziejczyk @ 2018-01-12 14:10 UTC (permalink / raw)
To: linux-bluetooth
Defined setting flag is presented as mask.
---
tools/btpclient.c | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/tools/btpclient.c b/tools/btpclient.c
index 806403f6a..a8a65fd51 100644
--- a/tools/btpclient.c
+++ b/tools/btpclient.c
@@ -368,9 +368,9 @@ static void btp_gap_set_connectable(uint8_t index, const void *param,
new_settings = adapter->current_settings;
if (cp->connectable)
- new_settings |= 1 << BTP_GAP_SETTING_CONNECTABLE;
+ new_settings |= BTP_GAP_SETTING_CONNECTABLE;
else
- new_settings &= ~(1 << BTP_GAP_SETTING_CONNECTABLE);
+ new_settings &= ~BTP_GAP_SETTING_CONNECTABLE;
update_current_settings(adapter, new_settings);
--
2.13.6
^ permalink raw reply related [flat|nested] 7+ messages in thread* [PATCH BlueZ v2 2/4] tools/btpclient: Add start, stop advertising commands 2018-01-12 14:10 [PATCH BlueZ v2 1/4] tools/btpclient: Fix setting/reseting connectable flag Grzegorz Kolodziejczyk @ 2018-01-12 14:10 ` Grzegorz Kolodziejczyk 2018-01-15 13:21 ` Szymon Janc 2018-01-12 14:10 ` [PATCH BlueZ v2 3/4] tools/btpclient: Add connect, disconnect commands Grzegorz Kolodziejczyk ` (2 subsequent siblings) 3 siblings, 1 reply; 7+ messages in thread From: Grzegorz Kolodziejczyk @ 2018-01-12 14:10 UTC (permalink / raw) To: linux-bluetooth This patch adds start and stop advertising commands for btp client. --- tools/btpclient.c | 651 +++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 650 insertions(+), 1 deletion(-) diff --git a/tools/btpclient.c b/tools/btpclient.c index a8a65fd51..3fb94b7f9 100644 --- a/tools/btpclient.c +++ b/tools/btpclient.c @@ -34,6 +34,19 @@ #include "src/shared/btp.h" +#define AD_PATH "/org/bluez/advertising" +#define AD_IFACE "org.bluez.LEAdvertisement1" + +/* List of assigned numbers for advetising data and scan response */ +#define AD_TYPE_FLAGS 0x01 +#define AD_TYPE_INCOMPLETE_UUID16_SERVICE_LIST 0x02 +#define AD_TYPE_SHORT_NAME 0x08 +#define AD_TYPE_SERVICE_DATA_UUID16 0x16 +#define AD_TYPE_APPEARANCE 0x19 +#define AD_TYPE_MANUFACTURER_DATA 0xff + +static struct l_dbus *dbus; + struct btp_adapter { struct l_dbus_proxy *proxy; struct l_dbus_proxy *ad_proxy; @@ -53,12 +66,61 @@ static struct btp *btp; static bool gap_service_registered; +struct ad_data { + uint8_t data[25]; + uint8_t len; +}; + +struct service_data { + char *uuid; + struct ad_data data; +}; + +struct manufacturer_data { + uint16_t id; + struct ad_data data; +}; + +static struct ad { + bool registered; + char *type; + char *local_name; + uint16_t local_appearance; + uint16_t duration; + uint16_t timeout; + struct l_queue *uuids; + struct l_queue *services; + struct l_queue *manufacturers; + bool tx_power; + bool name; + bool appearance; +} ad = { + .local_appearance = UINT16_MAX, +}; + static bool str2addr(const char *str, uint8_t *addr) { return sscanf(str, "%hhx:%hhx:%hhx:%hhx:%hhx:%hhx", &addr[5], &addr[4], &addr[3], &addr[2], &addr[1], &addr[0]) == 6; } +static char *dupuuid2str(const uint8_t *uuid, uint8_t len) +{ + switch (len) { + case 16: + return l_strdup_printf("%hhx%hhx", uuid[0], uuid[1]); + case 128: + return l_strdup_printf("%hhx%hhx%hhx%hhx%hhx%hhx%hhx%hhx%hhx" + "%hhx%hhx%hhx%hhx%hhx%hhx%hhx", uuid[0], + uuid[1], uuid[2], uuid[3], uuid[4], + uuid[5], uuid[6], uuid[6], uuid[8], + uuid[7], uuid[10], uuid[11], uuid[12], + uuid[13], uuid[14], uuid[15]); + default: + return NULL; + } +} + static struct btp_adapter *find_adapter_by_proxy(struct l_dbus_proxy *proxy) { const struct l_queue_entry *entry; @@ -123,6 +185,8 @@ static void btp_gap_read_commands(uint8_t index, const void *param, commands |= (1 << BTP_OP_GAP_SET_CONNECTABLE); commands |= (1 << BTP_OP_GAP_SET_DISCOVERABLE); commands |= (1 << BTP_OP_GAP_SET_BONDABLE); + commands |= (1 << BTP_OP_GAP_START_ADVERTISING); + commands |= (1 << BTP_OP_GAP_STOP_ADVERTISING); commands |= (1 << BTP_OP_GAP_START_DISCOVERY); commands |= (1 << BTP_OP_GAP_STOP_DISCOVERY); @@ -234,6 +298,46 @@ static void remove_device_reply(struct l_dbus_proxy *proxy, l_queue_remove(adapter->devices, device); } +static void unreg_advertising_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', AD_PATH); + l_dbus_message_builder_finalize(builder); + l_dbus_message_builder_destroy(builder); +} + +static void unreg_advertising_reply(struct l_dbus_proxy *proxy, + struct l_dbus_message *result, + void *user_data) +{ + const char *path = l_dbus_proxy_get_path(proxy); + struct btp_adapter *adapter = find_adapter_by_path(path); + + if (!adapter) + return; + + if (l_dbus_message_is_error(result)) { + const char *name; + + l_dbus_message_get_error(result, &name, NULL); + + l_error("Failed to stop advertising %s (%s)", + l_dbus_proxy_get_path(proxy), name); + return; + } + + if (!l_dbus_object_remove_interface(dbus, AD_PATH, AD_IFACE)) + l_info("Unable to remove ad instance"); + if (!l_dbus_object_remove_interface(dbus, AD_PATH, + L_DBUS_INTERFACE_PROPERTIES)) + l_info("Unable to remove propety instance"); + if (!l_dbus_unregister_interface(dbus, AD_IFACE)) + l_info("Unable to unregister ad interface"); +} + static void btp_gap_reset(uint8_t index, const void *param, uint16_t length, void *user_data) { @@ -264,6 +368,16 @@ static void btp_gap_reset(uint8_t index, const void *param, uint16_t length, NULL); } + if (adapter->ad_proxy) + if (!l_dbus_proxy_method_call(adapter->ad_proxy, + "UnregisterAdvertisement", + unreg_advertising_setup, + unreg_advertising_reply, + NULL, NULL)) { + status = BTP_ERROR_FAIL; + goto failed; + } + /* TODO for we assume all went well */ btp_send(btp, BTP_GAP_SERVICE, BTP_OP_GAP_RESET, index, 0, NULL); return; @@ -449,6 +563,529 @@ 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() +{ + 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); +} + +static struct l_dbus_message *ad_release_call(struct l_dbus *dbus, + struct l_dbus_message *message, + void *user_data) +{ + struct l_dbus_message *reply; + + l_dbus_unregister_object(dbus, AD_PATH); + l_dbus_unregister_interface(dbus, AD_IFACE); + + reply = l_dbus_message_new_method_return(message); + l_dbus_message_set_arguments(reply, ""); + + ad_cleanup(); + + return reply; +} + +static bool ad_type_getter(struct l_dbus *dbus, struct l_dbus_message *message, + struct l_dbus_message_builder *builder, + void *user_data) +{ + l_dbus_message_builder_append_basic(builder, 's', ad.type); + + return true; +} + +static bool ad_serviceuuids_getter(struct l_dbus *dbus, + struct l_dbus_message *message, + struct l_dbus_message_builder *builder, + void *user_data) +{ + const struct l_queue_entry *entry; + + if (l_queue_isempty(ad.uuids)) + return false; + + l_dbus_message_builder_enter_array(builder, "s"); + + for (entry = l_queue_get_entries(ad.uuids); entry; entry = entry->next) + l_dbus_message_builder_append_basic(builder, 's', entry->data); + + l_dbus_message_builder_leave_array(builder); + + return true; +} + +static bool ad_servicedata_getter(struct l_dbus *dbus, + struct l_dbus_message *message, + struct l_dbus_message_builder *builder, + void *user_data) +{ + const struct l_queue_entry *entry; + size_t i; + + if (l_queue_isempty(ad.services)) + return false; + + l_dbus_message_builder_enter_array(builder, "{sv}"); + + for (entry = l_queue_get_entries(ad.services); entry; + entry = entry->next) { + struct service_data *sd = entry->data; + + l_dbus_message_builder_enter_dict(builder, "sv"); + l_dbus_message_builder_append_basic(builder, 's', sd->uuid); + l_dbus_message_builder_enter_variant(builder, "ay"); + l_dbus_message_builder_enter_array(builder, "y"); + + for (i = 0; i < sd->data.len; i++) + l_dbus_message_builder_append_basic(builder, 'y', + &(sd->data.data[i])); + + l_dbus_message_builder_leave_array(builder); + l_dbus_message_builder_leave_variant(builder); + l_dbus_message_builder_leave_dict(builder); + } + l_dbus_message_builder_leave_array(builder); + + return true; +} + +static bool ad_manufacturerdata_getter(struct l_dbus *dbus, + struct l_dbus_message *message, + struct l_dbus_message_builder *builder, + void *user_data) +{ + const struct l_queue_entry *entry; + size_t i; + + if (l_queue_isempty(ad.manufacturers)) + return false; + + l_dbus_message_builder_enter_array(builder, "{qv}"); + + for (entry = l_queue_get_entries(ad.manufacturers); entry; + entry = entry->next) { + struct manufacturer_data *md = entry->data; + + l_dbus_message_builder_enter_dict(builder, "qv"); + l_dbus_message_builder_append_basic(builder, 'q', &md->id); + l_dbus_message_builder_enter_variant(builder, "ay"); + l_dbus_message_builder_enter_array(builder, "y"); + + for (i = 0; i < md->data.len; i++) + l_dbus_message_builder_append_basic(builder, 'y', + &(md->data.data[i])); + + l_dbus_message_builder_leave_array(builder); + l_dbus_message_builder_leave_variant(builder); + l_dbus_message_builder_leave_dict(builder); + } + l_dbus_message_builder_leave_array(builder); + + return true; +} + +static bool ad_includes_getter(struct l_dbus *dbus, + struct l_dbus_message *message, + struct l_dbus_message_builder *builder, + void *user_data) +{ + l_dbus_message_builder_enter_array(builder, "s"); + + if (!(ad.tx_power || ad.name || ad.appearance)) + return false; + + if (ad.tx_power) { + const char *str = "tx-power"; + + l_dbus_message_builder_append_basic(builder, 's', &str); + } + + if (ad.name) { + const char *str = "local-name"; + + l_dbus_message_builder_append_basic(builder, 's', &str); + } + + if (ad.appearance) { + const char *str = "appearance"; + + l_dbus_message_builder_append_basic(builder, 's', &str); + } + + l_dbus_message_builder_leave_array(builder); + + return true; +} + +static bool ad_localname_getter(struct l_dbus *dbus, + struct l_dbus_message *message, + struct l_dbus_message_builder *builder, + void *user_data) +{ + if (!ad.local_name) + return false; + + l_dbus_message_builder_append_basic(builder, 's', ad.local_name); + + return true; +} + +static bool ad_appearance_getter(struct l_dbus *dbus, + struct l_dbus_message *message, + struct l_dbus_message_builder *builder, + void *user_data) +{ + if (!ad.local_appearance) + return false; + + l_dbus_message_builder_append_basic(builder, 'q', &ad.local_appearance); + + return true; +} + +static bool ad_duration_getter(struct l_dbus *dbus, + struct l_dbus_message *message, + struct l_dbus_message_builder *builder, + void *user_data) +{ + if (!ad.duration) + return false; + + l_dbus_message_builder_append_basic(builder, 'q', &ad.duration); + + return true; +} + +static bool ad_timeout_getter(struct l_dbus *dbus, + struct l_dbus_message *message, + struct l_dbus_message_builder *builder, + void *user_data) +{ + if (!ad.timeout) + return false; + + l_dbus_message_builder_append_basic(builder, 'q', &ad.timeout); + + return true; +} + +static void setup_ad_interface(struct l_dbus_interface *interface) +{ + l_dbus_interface_method(interface, "Release", + L_DBUS_METHOD_FLAG_NOREPLY, + ad_release_call, "", ""); + l_dbus_interface_property(interface, "Type", 0, "s", ad_type_getter, + NULL); + l_dbus_interface_property(interface, "ServiceUUIDs", 0, "as", + ad_serviceuuids_getter, NULL); + l_dbus_interface_property(interface, "ServiceData", 0, "a{sv}", + ad_servicedata_getter, NULL); + l_dbus_interface_property(interface, "ManufacturerServiceData", 0, + "a{qv}", ad_manufacturerdata_getter, + NULL); + l_dbus_interface_property(interface, "Includes", 0, "as", + ad_includes_getter, NULL); + l_dbus_interface_property(interface, "LocalName", 0, "s", + ad_localname_getter, NULL); + l_dbus_interface_property(interface, "Appearance", 0, "q", + ad_appearance_getter, NULL); + l_dbus_interface_property(interface, "Duration", 0, "q", + ad_duration_getter, NULL); + l_dbus_interface_property(interface, "Timeout", 0, "q", + ad_timeout_getter, NULL); +} + +static void start_advertising_reply(struct l_dbus_proxy *proxy, + struct l_dbus_message *result, + void *user_data) +{ + const char *path = l_dbus_proxy_get_path(proxy); + struct btp_adapter *adapter = find_adapter_by_path(path); + uint32_t new_settings; + + 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 start advertising (%s), %s", name, desc); + + btp_send_error(btp, BTP_GAP_SERVICE, adapter->index, + BTP_ERROR_FAIL); + return; + } + + new_settings = adapter->current_settings; + new_settings |= BTP_GAP_SETTING_ADVERTISING; + update_current_settings(adapter, new_settings); + + ad.registered = true; + + btp_send(btp, BTP_GAP_SERVICE, BTP_OP_GAP_START_ADVERTISING, + adapter->index, sizeof(new_settings), + &new_settings); +} + +static void create_advertising_data(uint8_t adv_data_len, const uint8_t *data) +{ + const uint8_t *ad_data; + uint8_t ad_type, ad_len; + uint8_t remaining_data_len = adv_data_len; + + while (remaining_data_len) { + ad_type = data[adv_data_len - remaining_data_len]; + ad_len = data[adv_data_len - remaining_data_len + 1]; + ad_data = &data[adv_data_len - remaining_data_len + 2]; + + switch (ad_type) { + case AD_TYPE_INCOMPLETE_UUID16_SERVICE_LIST: + { + char *uuid = dupuuid2str(ad_data, 16); + l_queue_push_tail(ad.uuids, uuid); + + break; + } + case AD_TYPE_SHORT_NAME: + ad.local_name = malloc(ad_len + 1); + memcpy(ad.local_name, ad_data, ad_len); + ad.local_name[ad_len] = '\0'; + + break; + case AD_TYPE_SERVICE_DATA_UUID16: + { + struct service_data *sd; + + sd = l_new(struct service_data, 1); + sd->uuid = dupuuid2str(ad_data, 16); + sd->data.len = ad_len - 2; + memcpy(sd->data.data, ad_data + 2, sd->data.len); + + l_queue_push_tail(ad.services, sd); + + break; + } + case AD_TYPE_APPEARANCE: + memcpy(&ad.local_appearance, ad_data, ad_len); + + break; + case AD_TYPE_MANUFACTURER_DATA: + { + struct manufacturer_data *md; + + md = l_new(struct manufacturer_data, 1); + /* The first 2 octets contain the Company Identifier + * Code followed by additional manufacturer specific + * data. + */ + memcpy(&md->id, ad_data, 2); + md->data.len = ad_len - 2; + memcpy(md->data.data, ad_data + 2, md->data.len); + + l_queue_push_tail(ad.manufacturers, md); + + break; + } + default: + l_info("Unsupported advertising data type"); + + break; + } + /* Advertising entity data len + advertising entity header + * (type, len) + */ + remaining_data_len -= ad_len + 2; + } +} + +static void create_scan_response(uint8_t scan_rsp_len, const uint8_t *data) +{ + /* TODO */ +} + +static void start_advertising_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', AD_PATH); + l_dbus_message_builder_enter_array(builder, "{sv}"); + l_dbus_message_builder_enter_dict(builder, "sv"); + l_dbus_message_builder_leave_dict(builder); + l_dbus_message_builder_leave_array(builder); + l_dbus_message_builder_finalize(builder); + l_dbus_message_builder_destroy(builder); +} + +static void btp_gap_start_advertising(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_start_adv_cp *cp = param; + 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 remove devices */ + if (!l_dbus_proxy_get_property(adapter->proxy, "Powered", "b", &prop) || + !prop || ad.registered) + goto failed; + + if (!l_dbus_register_interface(dbus, AD_IFACE, setup_ad_interface, NULL, + false)) { + l_info("Unable to register ad interface"); + goto failed; + } + + if (!l_dbus_object_add_interface(dbus, AD_PATH, AD_IFACE, NULL)) { + l_info("Unable to instantiate ad interface"); + + if (!l_dbus_unregister_interface(dbus, AD_IFACE)) + l_info("Unable to unregister ad interface"); + + goto failed; + } + + if (!l_dbus_object_add_interface(dbus, AD_PATH, + L_DBUS_INTERFACE_PROPERTIES, + NULL)) { + l_info("Unable to instantiate the properties interface"); + + if (!l_dbus_object_remove_interface(dbus, AD_PATH, AD_IFACE)) + l_info("Unable to remove ad instance"); + if (!l_dbus_unregister_interface(dbus, AD_IFACE)) + l_info("Unable to unregister ad interface"); + + goto failed; + } + + ad.uuids = l_queue_new(); + ad.services = l_queue_new(); + ad.manufacturers = l_queue_new(); + + if (adapter->current_settings & BTP_GAP_SETTING_CONNECTABLE) + ad.type = l_strdup("peripheral"); + else + ad.type = l_strdup("broadcast"); + + if (cp->adv_data_len > 0) + create_advertising_data(cp->adv_data_len, cp->data); + if (cp->scan_rsp_len > 0) + create_scan_response(cp->scan_rsp_len, + cp->data + cp->scan_rsp_len); + + if (!l_dbus_proxy_method_call(adapter->ad_proxy, + "RegisterAdvertisement", + start_advertising_setup, + start_advertising_reply, + NULL, NULL)) { + if (!l_dbus_object_remove_interface(dbus, AD_PATH, AD_IFACE)) + l_info("Unable to remove ad instance"); + if (!l_dbus_unregister_interface(dbus, AD_IFACE)) + l_info("Unable to unregister ad interface"); + + goto failed; + } + + return; + +failed: + btp_send_error(btp, BTP_GAP_SERVICE, index, status); +} + +static void stop_advertising_reply(struct l_dbus_proxy *proxy, + struct l_dbus_message *result, + void *user_data) +{ + const char *path = l_dbus_proxy_get_path(proxy); + struct btp_adapter *adapter = find_adapter_by_path(path); + uint32_t new_settings; + + if (!adapter) + return; + + if (l_dbus_message_is_error(result)) { + const char *name; + + l_dbus_message_get_error(result, &name, NULL); + + l_error("Failed to stop advertising %s (%s)", + l_dbus_proxy_get_path(proxy), name); + return; + } + + if (!l_dbus_object_remove_interface(dbus, AD_PATH, AD_IFACE)) + l_info("Unable to remove ad instance"); + if (!l_dbus_object_remove_interface(dbus, AD_PATH, + L_DBUS_INTERFACE_PROPERTIES)) + l_info("Unable to remove propety instance"); + if (!l_dbus_unregister_interface(dbus, AD_IFACE)) + l_info("Unable to unregister ad interface"); + + new_settings = adapter->current_settings; + new_settings &= ~BTP_GAP_SETTING_ADVERTISING; + update_current_settings(adapter, new_settings); + + ad_cleanup(); + + btp_send(btp, BTP_GAP_SERVICE, BTP_OP_GAP_STOP_ADVERTISING, + adapter->index, sizeof(new_settings), + &new_settings); +} + +static void btp_gap_stop_advertising(uint8_t index, const void *param, + uint16_t length, void *user_data) +{ + struct btp_adapter *adapter = find_adapter_by_index(index); + uint8_t status = BTP_ERROR_FAIL; + bool prop; + + if (!adapter) { + status = BTP_ERROR_INVALID_INDEX; + goto failed; + } + + if (!l_dbus_proxy_get_property(adapter->proxy, "Powered", "b", &prop) || + !prop || !ad.registered) + goto failed; + + if (adapter->ad_proxy) { + if (!l_dbus_proxy_method_call(adapter->ad_proxy, + "UnregisterAdvertisement", + unreg_advertising_setup, + stop_advertising_reply, + NULL, NULL)) { + status = BTP_ERROR_FAIL; + goto failed; + } + } + + return; + +failed: + btp_send_error(btp, BTP_GAP_SERVICE, index, status); +} + static void start_discovery_reply(struct l_dbus_proxy *proxy, struct l_dbus_message *result, void *user_data) @@ -728,6 +1365,12 @@ static void register_gap_service(void) btp_register(btp, BTP_GAP_SERVICE, BTP_OP_GAP_SET_BONDABLE, btp_gap_set_bondable, NULL, NULL); + btp_register(btp, BTP_GAP_SERVICE, BTP_OP_GAP_START_ADVERTISING, + btp_gap_start_advertising, NULL, NULL); + + btp_register(btp, BTP_GAP_SERVICE, BTP_OP_GAP_STOP_ADVERTISING, + btp_gap_stop_advertising, NULL, NULL); + btp_register(btp, BTP_GAP_SERVICE, BTP_OP_GAP_START_DISCOVERY, btp_gap_start_discovery, NULL, NULL); @@ -1124,6 +1767,12 @@ static void client_ready(struct l_dbus_client *client, void *user_data) BTP_INDEX_NON_CONTROLLER, 0, NULL); } +static void ready_callback(void *user_data) +{ + if (!l_dbus_object_manager_enable(dbus)) + l_info("Unable to register the ObjectManager"); +} + static void usage(void) { l_info("btpclient - Bluetooth tester"); @@ -1148,7 +1797,6 @@ int main(int argc, char *argv[]) { struct l_dbus_client *client; struct l_signal *signal; - struct l_dbus *dbus; sigset_t mask; int opt; @@ -1192,6 +1840,7 @@ int main(int argc, char *argv[]) signal = l_signal_create(&mask, signal_handler, NULL, NULL); dbus = l_dbus_new_default(L_DBUS_SYSTEM_BUS); + l_dbus_set_ready_handler(dbus, ready_callback, NULL, NULL); client = l_dbus_client_new(dbus, "org.bluez", "/org/bluez"); l_dbus_client_set_connect_handler(client, client_connected, NULL, NULL); -- 2.13.6 ^ permalink raw reply related [flat|nested] 7+ messages in thread
* Re: [PATCH BlueZ v2 2/4] tools/btpclient: Add start, stop advertising commands 2018-01-12 14:10 ` [PATCH BlueZ v2 2/4] tools/btpclient: Add start, stop advertising commands Grzegorz Kolodziejczyk @ 2018-01-15 13:21 ` Szymon Janc 2018-01-16 9:26 ` Grzegorz Kołodziejczyk 0 siblings, 1 reply; 7+ messages in thread From: Szymon Janc @ 2018-01-15 13:21 UTC (permalink / raw) To: Grzegorz Kolodziejczyk; +Cc: linux-bluetooth Hi Grzegorz, On Friday, 12 January 2018 15:10:10 CET Grzegorz Kolodziejczyk wrote: > This patch adds start and stop advertising commands for btp client. > --- > tools/btpclient.c | 651 > +++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 650 > insertions(+), 1 deletion(-) > > diff --git a/tools/btpclient.c b/tools/btpclient.c > index a8a65fd51..3fb94b7f9 100644 > --- a/tools/btpclient.c > +++ b/tools/btpclient.c > @@ -34,6 +34,19 @@ > > #include "src/shared/btp.h" > > +#define AD_PATH "/org/bluez/advertising" > +#define AD_IFACE "org.bluez.LEAdvertisement1" > + > +/* List of assigned numbers for advetising data and scan response */ > +#define AD_TYPE_FLAGS 0x01 > +#define AD_TYPE_INCOMPLETE_UUID16_SERVICE_LIST 0x02 > +#define AD_TYPE_SHORT_NAME 0x08 > +#define AD_TYPE_SERVICE_DATA_UUID16 0x16 > +#define AD_TYPE_APPEARANCE 0x19 > +#define AD_TYPE_MANUFACTURER_DATA 0xff > + > +static struct l_dbus *dbus; > + > struct btp_adapter { > struct l_dbus_proxy *proxy; > struct l_dbus_proxy *ad_proxy; > @@ -53,12 +66,61 @@ static struct btp *btp; > > static bool gap_service_registered; > > +struct ad_data { > + uint8_t data[25]; > + uint8_t len; > +}; > + > +struct service_data { > + char *uuid; > + struct ad_data data; > +}; > + > +struct manufacturer_data { > + uint16_t id; > + struct ad_data data; > +}; > + > +static struct ad { > + bool registered; > + char *type; > + char *local_name; > + uint16_t local_appearance; > + uint16_t duration; > + uint16_t timeout; > + struct l_queue *uuids; > + struct l_queue *services; > + struct l_queue *manufacturers; > + bool tx_power; > + bool name; > + bool appearance; > +} ad = { > + .local_appearance = UINT16_MAX, > +}; > + > static bool str2addr(const char *str, uint8_t *addr) > { > return sscanf(str, "%hhx:%hhx:%hhx:%hhx:%hhx:%hhx", &addr[5], &addr[4], > &addr[3], &addr[2], &addr[1], &addr[0]) == 6; > } > > +static char *dupuuid2str(const uint8_t *uuid, uint8_t len) > +{ > + switch (len) { > + case 16: > + return l_strdup_printf("%hhx%hhx", uuid[0], uuid[1]); > + case 128: > + return l_strdup_printf("%hhx%hhx%hhx%hhx%hhx%hhx%hhx%hhx%hhx" > + "%hhx%hhx%hhx%hhx%hhx%hhx%hhx", uuid[0], > + uuid[1], uuid[2], uuid[3], uuid[4], > + uuid[5], uuid[6], uuid[6], uuid[8], > + uuid[7], uuid[10], uuid[11], uuid[12], > + uuid[13], uuid[14], uuid[15]); > + default: > + return NULL; > + } > +} > + > static struct btp_adapter *find_adapter_by_proxy(struct l_dbus_proxy > *proxy) { > const struct l_queue_entry *entry; > @@ -123,6 +185,8 @@ static void btp_gap_read_commands(uint8_t index, const > void *param, commands |= (1 << BTP_OP_GAP_SET_CONNECTABLE); > commands |= (1 << BTP_OP_GAP_SET_DISCOVERABLE); > commands |= (1 << BTP_OP_GAP_SET_BONDABLE); > + commands |= (1 << BTP_OP_GAP_START_ADVERTISING); > + commands |= (1 << BTP_OP_GAP_STOP_ADVERTISING); > commands |= (1 << BTP_OP_GAP_START_DISCOVERY); > commands |= (1 << BTP_OP_GAP_STOP_DISCOVERY); > > @@ -234,6 +298,46 @@ static void remove_device_reply(struct l_dbus_proxy > *proxy, l_queue_remove(adapter->devices, device); > } > > +static void unreg_advertising_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', AD_PATH); > + l_dbus_message_builder_finalize(builder); > + l_dbus_message_builder_destroy(builder); > +} > + > +static void unreg_advertising_reply(struct l_dbus_proxy *proxy, > + struct l_dbus_message *result, > + void *user_data) > +{ > + const char *path = l_dbus_proxy_get_path(proxy); > + struct btp_adapter *adapter = find_adapter_by_path(path); > + > + if (!adapter) > + return; > + > + if (l_dbus_message_is_error(result)) { > + const char *name; > + > + l_dbus_message_get_error(result, &name, NULL); > + > + l_error("Failed to stop advertising %s (%s)", > + l_dbus_proxy_get_path(proxy), name); > + return; > + } > + > + if (!l_dbus_object_remove_interface(dbus, AD_PATH, AD_IFACE)) > + l_info("Unable to remove ad instance"); > + if (!l_dbus_object_remove_interface(dbus, AD_PATH, > + L_DBUS_INTERFACE_PROPERTIES)) > + l_info("Unable to remove propety instance"); > + if (!l_dbus_unregister_interface(dbus, AD_IFACE)) > + l_info("Unable to unregister ad interface"); > +} > + > static void btp_gap_reset(uint8_t index, const void *param, uint16_t > length, void *user_data) > { > @@ -264,6 +368,16 @@ static void btp_gap_reset(uint8_t index, const void > *param, uint16_t length, NULL); > } > > + if (adapter->ad_proxy) > + if (!l_dbus_proxy_method_call(adapter->ad_proxy, > + "UnregisterAdvertisement", > + unreg_advertising_setup, > + unreg_advertising_reply, > + NULL, NULL)) { > + status = BTP_ERROR_FAIL; > + goto failed; > + } > + > /* TODO for we assume all went well */ > btp_send(btp, BTP_GAP_SERVICE, BTP_OP_GAP_RESET, index, 0, NULL); > return; > @@ -449,6 +563,529 @@ 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) is missing > +{ > + 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); type free is missing here, and I'd also memset ad here. > +} > + > +static struct l_dbus_message *ad_release_call(struct l_dbus *dbus, > + struct l_dbus_message *message, > + void *user_data) > +{ > + struct l_dbus_message *reply; > + > + l_dbus_unregister_object(dbus, AD_PATH); > + l_dbus_unregister_interface(dbus, AD_IFACE); > + > + reply = l_dbus_message_new_method_return(message); > + l_dbus_message_set_arguments(reply, ""); > + > + ad_cleanup(); > + > + return reply; > +} > + > +static bool ad_type_getter(struct l_dbus *dbus, struct l_dbus_message > *message, + struct l_dbus_message_builder *builder, > + void *user_data) > +{ > + l_dbus_message_builder_append_basic(builder, 's', ad.type); > + > + return true; > +} > + > +static bool ad_serviceuuids_getter(struct l_dbus *dbus, > + struct l_dbus_message *message, > + struct l_dbus_message_builder *builder, > + void *user_data) > +{ > + const struct l_queue_entry *entry; > + > + if (l_queue_isempty(ad.uuids)) > + return false; > + > + l_dbus_message_builder_enter_array(builder, "s"); > + > + for (entry = l_queue_get_entries(ad.uuids); entry; entry = entry->next) > + l_dbus_message_builder_append_basic(builder, 's', entry->data); > + > + l_dbus_message_builder_leave_array(builder); > + > + return true; > +} > + > +static bool ad_servicedata_getter(struct l_dbus *dbus, > + struct l_dbus_message *message, > + struct l_dbus_message_builder *builder, > + void *user_data) > +{ > + const struct l_queue_entry *entry; > + size_t i; > + > + if (l_queue_isempty(ad.services)) > + return false; > + > + l_dbus_message_builder_enter_array(builder, "{sv}"); > + > + for (entry = l_queue_get_entries(ad.services); entry; > + entry = entry->next) { > + struct service_data *sd = entry->data; > + > + l_dbus_message_builder_enter_dict(builder, "sv"); > + l_dbus_message_builder_append_basic(builder, 's', sd->uuid); > + l_dbus_message_builder_enter_variant(builder, "ay"); > + l_dbus_message_builder_enter_array(builder, "y"); > + > + for (i = 0; i < sd->data.len; i++) > + l_dbus_message_builder_append_basic(builder, 'y', > + &(sd->data.data[i])); > + > + l_dbus_message_builder_leave_array(builder); > + l_dbus_message_builder_leave_variant(builder); > + l_dbus_message_builder_leave_dict(builder); > + } > + l_dbus_message_builder_leave_array(builder); > + > + return true; > +} > + > +static bool ad_manufacturerdata_getter(struct l_dbus *dbus, > + struct l_dbus_message *message, > + struct l_dbus_message_builder *builder, > + void *user_data) > +{ > + const struct l_queue_entry *entry; > + size_t i; > + > + if (l_queue_isempty(ad.manufacturers)) > + return false; > + > + l_dbus_message_builder_enter_array(builder, "{qv}"); > + > + for (entry = l_queue_get_entries(ad.manufacturers); entry; > + entry = entry->next) { > + struct manufacturer_data *md = entry->data; > + > + l_dbus_message_builder_enter_dict(builder, "qv"); > + l_dbus_message_builder_append_basic(builder, 'q', &md->id); > + l_dbus_message_builder_enter_variant(builder, "ay"); > + l_dbus_message_builder_enter_array(builder, "y"); > + > + for (i = 0; i < md->data.len; i++) > + l_dbus_message_builder_append_basic(builder, 'y', > + &(md->data.data[i])); > + > + l_dbus_message_builder_leave_array(builder); > + l_dbus_message_builder_leave_variant(builder); > + l_dbus_message_builder_leave_dict(builder); > + } > + l_dbus_message_builder_leave_array(builder); > + > + return true; > +} > + > +static bool ad_includes_getter(struct l_dbus *dbus, > + struct l_dbus_message *message, > + struct l_dbus_message_builder *builder, > + void *user_data) > +{ > + l_dbus_message_builder_enter_array(builder, "s"); > + > + if (!(ad.tx_power || ad.name || ad.appearance)) > + return false; > + > + if (ad.tx_power) { > + const char *str = "tx-power"; > + > + l_dbus_message_builder_append_basic(builder, 's', &str); > + } > + > + if (ad.name) { > + const char *str = "local-name"; > + > + l_dbus_message_builder_append_basic(builder, 's', &str); > + } > + > + if (ad.appearance) { > + const char *str = "appearance"; > + > + l_dbus_message_builder_append_basic(builder, 's', &str); > + } > + > + l_dbus_message_builder_leave_array(builder); > + > + return true; > +} > + > +static bool ad_localname_getter(struct l_dbus *dbus, > + struct l_dbus_message *message, > + struct l_dbus_message_builder *builder, > + void *user_data) > +{ > + if (!ad.local_name) > + return false; > + > + l_dbus_message_builder_append_basic(builder, 's', ad.local_name); > + > + return true; > +} > + > +static bool ad_appearance_getter(struct l_dbus *dbus, > + struct l_dbus_message *message, > + struct l_dbus_message_builder *builder, > + void *user_data) > +{ > + if (!ad.local_appearance) > + return false; > + > + l_dbus_message_builder_append_basic(builder, 'q', &ad.local_appearance); > + > + return true; > +} > + > +static bool ad_duration_getter(struct l_dbus *dbus, > + struct l_dbus_message *message, > + struct l_dbus_message_builder *builder, > + void *user_data) > +{ > + if (!ad.duration) > + return false; > + > + l_dbus_message_builder_append_basic(builder, 'q', &ad.duration); > + > + return true; > +} > + > +static bool ad_timeout_getter(struct l_dbus *dbus, > + struct l_dbus_message *message, > + struct l_dbus_message_builder *builder, > + void *user_data) > +{ > + if (!ad.timeout) > + return false; > + > + l_dbus_message_builder_append_basic(builder, 'q', &ad.timeout); > + > + return true; > +} > + > +static void setup_ad_interface(struct l_dbus_interface *interface) > +{ > + l_dbus_interface_method(interface, "Release", > + L_DBUS_METHOD_FLAG_NOREPLY, > + ad_release_call, "", ""); > + l_dbus_interface_property(interface, "Type", 0, "s", ad_type_getter, > + NULL); > + l_dbus_interface_property(interface, "ServiceUUIDs", 0, "as", > + ad_serviceuuids_getter, NULL); > + l_dbus_interface_property(interface, "ServiceData", 0, "a{sv}", > + ad_servicedata_getter, NULL); > + l_dbus_interface_property(interface, "ManufacturerServiceData", 0, > + "a{qv}", ad_manufacturerdata_getter, > + NULL); > + l_dbus_interface_property(interface, "Includes", 0, "as", > + ad_includes_getter, NULL); > + l_dbus_interface_property(interface, "LocalName", 0, "s", > + ad_localname_getter, NULL); > + l_dbus_interface_property(interface, "Appearance", 0, "q", > + ad_appearance_getter, NULL); > + l_dbus_interface_property(interface, "Duration", 0, "q", > + ad_duration_getter, NULL); > + l_dbus_interface_property(interface, "Timeout", 0, "q", > + ad_timeout_getter, NULL); > +} > + > +static void start_advertising_reply(struct l_dbus_proxy *proxy, > + struct l_dbus_message *result, > + void *user_data) > +{ > + const char *path = l_dbus_proxy_get_path(proxy); > + struct btp_adapter *adapter = find_adapter_by_path(path); > + uint32_t new_settings; > + > + 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 start advertising (%s), %s", name, desc); > + > + btp_send_error(btp, BTP_GAP_SERVICE, adapter->index, > + BTP_ERROR_FAIL); > + return; > + } > + > + new_settings = adapter->current_settings; > + new_settings |= BTP_GAP_SETTING_ADVERTISING; > + update_current_settings(adapter, new_settings); > + > + ad.registered = true; > + > + btp_send(btp, BTP_GAP_SERVICE, BTP_OP_GAP_START_ADVERTISING, > + adapter->index, sizeof(new_settings), > + &new_settings); > +} > + > +static void create_advertising_data(uint8_t adv_data_len, const uint8_t > *data) +{ > + const uint8_t *ad_data; > + uint8_t ad_type, ad_len; > + uint8_t remaining_data_len = adv_data_len; > + > + while (remaining_data_len) { > + ad_type = data[adv_data_len - remaining_data_len]; > + ad_len = data[adv_data_len - remaining_data_len + 1]; > + ad_data = &data[adv_data_len - remaining_data_len + 2]; > + > + switch (ad_type) { > + case AD_TYPE_INCOMPLETE_UUID16_SERVICE_LIST: > + { > + char *uuid = dupuuid2str(ad_data, 16); Empty line here. > + l_queue_push_tail(ad.uuids, uuid); > + > + break; > + } > + case AD_TYPE_SHORT_NAME: > + ad.local_name = malloc(ad_len + 1); > + memcpy(ad.local_name, ad_data, ad_len); > + ad.local_name[ad_len] = '\0'; > + > + break; > + case AD_TYPE_SERVICE_DATA_UUID16: > + { > + struct service_data *sd; > + > + sd = l_new(struct service_data, 1); > + sd->uuid = dupuuid2str(ad_data, 16); > + sd->data.len = ad_len - 2; > + memcpy(sd->data.data, ad_data + 2, sd->data.len); > + > + l_queue_push_tail(ad.services, sd); > + > + break; > + } > + case AD_TYPE_APPEARANCE: > + memcpy(&ad.local_appearance, ad_data, ad_len); > + > + break; > + case AD_TYPE_MANUFACTURER_DATA: > + { > + struct manufacturer_data *md; > + > + md = l_new(struct manufacturer_data, 1); > + /* The first 2 octets contain the Company Identifier > + * Code followed by additional manufacturer specific > + * data. > + */ > + memcpy(&md->id, ad_data, 2); > + md->data.len = ad_len - 2; > + memcpy(md->data.data, ad_data + 2, md->data.len); > + > + l_queue_push_tail(ad.manufacturers, md); > + > + break; > + } > + default: > + l_info("Unsupported advertising data type"); > + > + break; > + } > + /* Advertising entity data len + advertising entity header > + * (type, len) > + */ > + remaining_data_len -= ad_len + 2; > + } > +} > + > +static void create_scan_response(uint8_t scan_rsp_len, const uint8_t *data) > +{ > + /* TODO */ > +} > + > +static void start_advertising_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', AD_PATH); > + l_dbus_message_builder_enter_array(builder, "{sv}"); > + l_dbus_message_builder_enter_dict(builder, "sv"); > + l_dbus_message_builder_leave_dict(builder); > + l_dbus_message_builder_leave_array(builder); > + l_dbus_message_builder_finalize(builder); > + l_dbus_message_builder_destroy(builder); > +} > + > +static void btp_gap_start_advertising(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_start_adv_cp *cp = param; > + 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 remove devices */ > + if (!l_dbus_proxy_get_property(adapter->proxy, "Powered", "b", &prop) || > + !prop || ad.registered) > + goto failed; > + > + if (!l_dbus_register_interface(dbus, AD_IFACE, setup_ad_interface, NULL, > + false)) { > + l_info("Unable to register ad interface"); > + goto failed; > + } > + > + if (!l_dbus_object_add_interface(dbus, AD_PATH, AD_IFACE, NULL)) { > + l_info("Unable to instantiate ad interface"); > + > + if (!l_dbus_unregister_interface(dbus, AD_IFACE)) > + l_info("Unable to unregister ad interface"); > + > + goto failed; > + } > + > + if (!l_dbus_object_add_interface(dbus, AD_PATH, > + L_DBUS_INTERFACE_PROPERTIES, > + NULL)) { > + l_info("Unable to instantiate the properties interface"); > + > + if (!l_dbus_object_remove_interface(dbus, AD_PATH, AD_IFACE)) > + l_info("Unable to remove ad instance"); > + if (!l_dbus_unregister_interface(dbus, AD_IFACE)) > + l_info("Unable to unregister ad interface"); > + > + goto failed; > + } > + > + ad.uuids = l_queue_new(); > + ad.services = l_queue_new(); > + ad.manufacturers = l_queue_new(); Put this into helper eg ad_init(), also set appearance to default value. > + > + if (adapter->current_settings & BTP_GAP_SETTING_CONNECTABLE) > + ad.type = l_strdup("peripheral"); > + else > + ad.type = l_strdup("broadcast"); No need to dup those string, just assing them with constants. > + > + if (cp->adv_data_len > 0) > + create_advertising_data(cp->adv_data_len, cp->data); > + if (cp->scan_rsp_len > 0) > + create_scan_response(cp->scan_rsp_len, > + cp->data + cp->scan_rsp_len); > + > + if (!l_dbus_proxy_method_call(adapter->ad_proxy, > + "RegisterAdvertisement", > + start_advertising_setup, > + start_advertising_reply, > + NULL, NULL)) { > + if (!l_dbus_object_remove_interface(dbus, AD_PATH, AD_IFACE)) > + l_info("Unable to remove ad instance"); > + if (!l_dbus_unregister_interface(dbus, AD_IFACE)) > + l_info("Unable to unregister ad interface"); > + > + goto failed; > + } > + > + return; > + > +failed: > + btp_send_error(btp, BTP_GAP_SERVICE, index, status); > +} > + > +static void stop_advertising_reply(struct l_dbus_proxy *proxy, > + struct l_dbus_message *result, > + void *user_data) > +{ > + const char *path = l_dbus_proxy_get_path(proxy); > + struct btp_adapter *adapter = find_adapter_by_path(path); > + uint32_t new_settings; > + > + if (!adapter) > + return; > + > + if (l_dbus_message_is_error(result)) { > + const char *name; > + > + l_dbus_message_get_error(result, &name, NULL); > + > + l_error("Failed to stop advertising %s (%s)", > + l_dbus_proxy_get_path(proxy), name); > + return; > + } > + > + if (!l_dbus_object_remove_interface(dbus, AD_PATH, AD_IFACE)) > + l_info("Unable to remove ad instance"); > + if (!l_dbus_object_remove_interface(dbus, AD_PATH, > + L_DBUS_INTERFACE_PROPERTIES)) > + l_info("Unable to remove propety instance"); > + if (!l_dbus_unregister_interface(dbus, AD_IFACE)) > + l_info("Unable to unregister ad interface"); > + > + new_settings = adapter->current_settings; > + new_settings &= ~BTP_GAP_SETTING_ADVERTISING; > + update_current_settings(adapter, new_settings); > + > + ad_cleanup(); > + > + btp_send(btp, BTP_GAP_SERVICE, BTP_OP_GAP_STOP_ADVERTISING, > + adapter->index, sizeof(new_settings), > + &new_settings); > +} > + > +static void btp_gap_stop_advertising(uint8_t index, const void *param, > + uint16_t length, void *user_data) > +{ > + struct btp_adapter *adapter = find_adapter_by_index(index); > + uint8_t status = BTP_ERROR_FAIL; > + bool prop; > + > + if (!adapter) { > + status = BTP_ERROR_INVALID_INDEX; > + goto failed; > + } > + > + if (!l_dbus_proxy_get_property(adapter->proxy, "Powered", "b", &prop) || > + !prop || !ad.registered) > + goto failed; > + > + if (adapter->ad_proxy) { > + if (!l_dbus_proxy_method_call(adapter->ad_proxy, > + "UnregisterAdvertisement", > + unreg_advertising_setup, > + stop_advertising_reply, > + NULL, NULL)) { > + status = BTP_ERROR_FAIL; > + goto failed; > + } > + } > + > + return; > + > +failed: > + btp_send_error(btp, BTP_GAP_SERVICE, index, status); > +} > + > static void start_discovery_reply(struct l_dbus_proxy *proxy, > struct l_dbus_message *result, > void *user_data) > @@ -728,6 +1365,12 @@ static void register_gap_service(void) > btp_register(btp, BTP_GAP_SERVICE, BTP_OP_GAP_SET_BONDABLE, > btp_gap_set_bondable, NULL, NULL); > > + btp_register(btp, BTP_GAP_SERVICE, BTP_OP_GAP_START_ADVERTISING, > + btp_gap_start_advertising, NULL, NULL); > + > + btp_register(btp, BTP_GAP_SERVICE, BTP_OP_GAP_STOP_ADVERTISING, > + btp_gap_stop_advertising, NULL, NULL); > + > btp_register(btp, BTP_GAP_SERVICE, BTP_OP_GAP_START_DISCOVERY, > btp_gap_start_discovery, NULL, NULL); > > @@ -1124,6 +1767,12 @@ static void client_ready(struct l_dbus_client > *client, void *user_data) BTP_INDEX_NON_CONTROLLER, 0, NULL); > } > > +static void ready_callback(void *user_data) > +{ > + if (!l_dbus_object_manager_enable(dbus)) > + l_info("Unable to register the ObjectManager"); > +} > + > static void usage(void) > { > l_info("btpclient - Bluetooth tester"); > @@ -1148,7 +1797,6 @@ int main(int argc, char *argv[]) > { > struct l_dbus_client *client; > struct l_signal *signal; > - struct l_dbus *dbus; > sigset_t mask; > int opt; > > @@ -1192,6 +1840,7 @@ int main(int argc, char *argv[]) > signal = l_signal_create(&mask, signal_handler, NULL, NULL); > > dbus = l_dbus_new_default(L_DBUS_SYSTEM_BUS); > + l_dbus_set_ready_handler(dbus, ready_callback, NULL, NULL); > client = l_dbus_client_new(dbus, "org.bluez", "/org/bluez"); > > l_dbus_client_set_connect_handler(client, client_connected, NULL, NULL); -- pozdrawiam Szymon Janc ^ permalink raw reply [flat|nested] 7+ messages in thread
* Re: [PATCH BlueZ v2 2/4] tools/btpclient: Add start, stop advertising commands 2018-01-15 13:21 ` Szymon Janc @ 2018-01-16 9:26 ` Grzegorz Kołodziejczyk 0 siblings, 0 replies; 7+ messages in thread From: Grzegorz Kołodziejczyk @ 2018-01-16 9:26 UTC (permalink / raw) To: Szymon Janc; +Cc: linux-bluetooth Hi Szymon, 2018-01-15 14:21 GMT+01:00 Szymon Janc <szymon.janc@codecoup.pl>: > Hi Grzegorz, > > On Friday, 12 January 2018 15:10:10 CET Grzegorz Kolodziejczyk wrote: >> This patch adds start and stop advertising commands for btp client. >> --- >> tools/btpclient.c | 651 >> +++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 6= 50 >> insertions(+), 1 deletion(-) >> >> diff --git a/tools/btpclient.c b/tools/btpclient.c >> index a8a65fd51..3fb94b7f9 100644 >> --- a/tools/btpclient.c >> +++ b/tools/btpclient.c >> @@ -34,6 +34,19 @@ >> >> #include "src/shared/btp.h" >> >> +#define AD_PATH "/org/bluez/advertising" >> +#define AD_IFACE "org.bluez.LEAdvertisement1" >> + >> +/* List of assigned numbers for advetising data and scan response */ >> +#define AD_TYPE_FLAGS 0x01 >> +#define AD_TYPE_INCOMPLETE_UUID16_SERVICE_LIST 0x02 >> +#define AD_TYPE_SHORT_NAME 0x08 >> +#define AD_TYPE_SERVICE_DATA_UUID16 0x16 >> +#define AD_TYPE_APPEARANCE 0x19 >> +#define AD_TYPE_MANUFACTURER_DATA 0xff >> + >> +static struct l_dbus *dbus; >> + >> struct btp_adapter { >> struct l_dbus_proxy *proxy; >> struct l_dbus_proxy *ad_proxy; >> @@ -53,12 +66,61 @@ static struct btp *btp; >> >> static bool gap_service_registered; >> >> +struct ad_data { >> + uint8_t data[25]; >> + uint8_t len; >> +}; >> + >> +struct service_data { >> + char *uuid; >> + struct ad_data data; >> +}; >> + >> +struct manufacturer_data { >> + uint16_t id; >> + struct ad_data data; >> +}; >> + >> +static struct ad { >> + bool registered; >> + char *type; >> + char *local_name; >> + uint16_t local_appearance; >> + uint16_t duration; >> + uint16_t timeout; >> + struct l_queue *uuids; >> + struct l_queue *services; >> + struct l_queue *manufacturers; >> + bool tx_power; >> + bool name; >> + bool appearance; >> +} ad =3D { >> + .local_appearance =3D UINT16_MAX, >> +}; >> + >> static bool str2addr(const char *str, uint8_t *addr) >> { >> return sscanf(str, "%hhx:%hhx:%hhx:%hhx:%hhx:%hhx", &addr[5], &add= r[4], >> &addr[3], &addr[2], &addr[1], &addr[0]) = =3D=3D 6; >> } >> >> +static char *dupuuid2str(const uint8_t *uuid, uint8_t len) >> +{ >> + switch (len) { >> + case 16: >> + return l_strdup_printf("%hhx%hhx", uuid[0], uuid[1]); >> + case 128: >> + return l_strdup_printf("%hhx%hhx%hhx%hhx%hhx%hhx%hhx%hhx%h= hx" >> + "%hhx%hhx%hhx%hhx%hhx%hhx%hhx", uu= id[0], >> + uuid[1], uuid[2], uuid[3], uuid[4]= , >> + uuid[5], uuid[6], uuid[6], uuid[8]= , >> + uuid[7], uuid[10], uuid[11], uuid[= 12], >> + uuid[13], uuid[14], uuid[15]); >> + default: >> + return NULL; >> + } >> +} >> + >> static struct btp_adapter *find_adapter_by_proxy(struct l_dbus_proxy >> *proxy) { >> const struct l_queue_entry *entry; >> @@ -123,6 +185,8 @@ static void btp_gap_read_commands(uint8_t index, con= st >> void *param, commands |=3D (1 << BTP_OP_GAP_SET_CONNECTABLE); >> commands |=3D (1 << BTP_OP_GAP_SET_DISCOVERABLE); >> commands |=3D (1 << BTP_OP_GAP_SET_BONDABLE); >> + commands |=3D (1 << BTP_OP_GAP_START_ADVERTISING); >> + commands |=3D (1 << BTP_OP_GAP_STOP_ADVERTISING); >> commands |=3D (1 << BTP_OP_GAP_START_DISCOVERY); >> commands |=3D (1 << BTP_OP_GAP_STOP_DISCOVERY); >> >> @@ -234,6 +298,46 @@ static void remove_device_reply(struct l_dbus_proxy >> *proxy, l_queue_remove(adapter->devices, device); >> } >> >> +static void unreg_advertising_setup(struct l_dbus_message *message, >> + void *user= _data) >> +{ >> + struct l_dbus_message_builder *builder; >> + >> + builder =3D l_dbus_message_builder_new(message); >> + l_dbus_message_builder_append_basic(builder, 'o', AD_PATH); >> + l_dbus_message_builder_finalize(builder); >> + l_dbus_message_builder_destroy(builder); >> +} >> + >> +static void unreg_advertising_reply(struct l_dbus_proxy *proxy, >> + struct l_dbus_message *res= ult, >> + void *user_data) >> +{ >> + const char *path =3D l_dbus_proxy_get_path(proxy); >> + struct btp_adapter *adapter =3D find_adapter_by_path(path); >> + >> + if (!adapter) >> + return; >> + >> + if (l_dbus_message_is_error(result)) { >> + const char *name; >> + >> + l_dbus_message_get_error(result, &name, NULL); >> + >> + l_error("Failed to stop advertising %s (%s)", >> + l_dbus_proxy_get_path(proxy), name= ); >> + return; >> + } >> + >> + if (!l_dbus_object_remove_interface(dbus, AD_PATH, AD_IFACE)) >> + l_info("Unable to remove ad instance"); >> + if (!l_dbus_object_remove_interface(dbus, AD_PATH, >> + L_DBUS_INTERFACE_PROPERTIE= S)) >> + l_info("Unable to remove propety instance"); >> + if (!l_dbus_unregister_interface(dbus, AD_IFACE)) >> + l_info("Unable to unregister ad interface"); >> +} >> + >> static void btp_gap_reset(uint8_t index, const void *param, uint16_t >> length, void *user_data) >> { >> @@ -264,6 +368,16 @@ static void btp_gap_reset(uint8_t index, const void >> *param, uint16_t length, NULL); >> } >> >> + if (adapter->ad_proxy) >> + if (!l_dbus_proxy_method_call(adapter->ad_proxy, >> + "UnregisterAdvertisement", >> + unreg_advertising_setup, >> + unreg_advertising_reply, >> + NULL, NULL)) { >> + status =3D BTP_ERROR_FAIL; >> + goto failed; >> + } >> + >> /* TODO for we assume all went well */ >> btp_send(btp, BTP_GAP_SERVICE, BTP_OP_GAP_RESET, index, 0, NULL); >> return; >> @@ -449,6 +563,529 @@ failed: >> btp_send_error(btp, BTP_GAP_SERVICE, index, status); >> } >> >> +static void ad_cleanup_service(void *service) >> +{ >> + struct service_data *s =3D service; >> + >> + l_free(s->uuid); >> + l_free(s); >> +} >> + >> +static void ad_cleanup() > > (void) is missing > >> +{ >> + 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); > > type free is missing here, and I'd also memset ad here. Right, I'll fix it. > >> +} >> + >> +static struct l_dbus_message *ad_release_call(struct l_dbus *dbus, >> + struct l_dbus_message *mes= sage, >> + void *user_data) >> +{ >> + struct l_dbus_message *reply; >> + >> + l_dbus_unregister_object(dbus, AD_PATH); >> + l_dbus_unregister_interface(dbus, AD_IFACE); >> + >> + reply =3D l_dbus_message_new_method_return(message); >> + l_dbus_message_set_arguments(reply, ""); >> + >> + ad_cleanup(); >> + >> + return reply; >> +} >> + >> +static bool ad_type_getter(struct l_dbus *dbus, struct l_dbus_message >> *message, + struct l_dbus_message_builder *bui= lder, >> + void *user_data) >> +{ >> + l_dbus_message_builder_append_basic(builder, 's', ad.type); >> + >> + return true; >> +} >> + >> +static bool ad_serviceuuids_getter(struct l_dbus *dbus, >> + struct l_dbus_message *message, >> + struct l_dbus_message_builder *bui= lder, >> + void *user_data) >> +{ >> + const struct l_queue_entry *entry; >> + >> + if (l_queue_isempty(ad.uuids)) >> + return false; >> + >> + l_dbus_message_builder_enter_array(builder, "s"); >> + >> + for (entry =3D l_queue_get_entries(ad.uuids); entry; entry =3D ent= ry->next) >> + l_dbus_message_builder_append_basic(builder, 's', entry->d= ata); >> + >> + l_dbus_message_builder_leave_array(builder); >> + >> + return true; >> +} >> + >> +static bool ad_servicedata_getter(struct l_dbus *dbus, >> + struct l_dbus_message *message, >> + struct l_dbus_message_builder *bui= lder, >> + void *user_data) >> +{ >> + const struct l_queue_entry *entry; >> + size_t i; >> + >> + if (l_queue_isempty(ad.services)) >> + return false; >> + >> + l_dbus_message_builder_enter_array(builder, "{sv}"); >> + >> + for (entry =3D l_queue_get_entries(ad.services); entry; >> + entry =3D entry->n= ext) { >> + struct service_data *sd =3D entry->data; >> + >> + l_dbus_message_builder_enter_dict(builder, "sv"); >> + l_dbus_message_builder_append_basic(builder, 's', sd->uuid= ); >> + l_dbus_message_builder_enter_variant(builder, "ay"); >> + l_dbus_message_builder_enter_array(builder, "y"); >> + >> + for (i =3D 0; i < sd->data.len; i++) >> + l_dbus_message_builder_append_basic(builder, 'y', >> + &(sd->data.data[i]= )); >> + >> + l_dbus_message_builder_leave_array(builder); >> + l_dbus_message_builder_leave_variant(builder); >> + l_dbus_message_builder_leave_dict(builder); >> + } >> + l_dbus_message_builder_leave_array(builder); >> + >> + return true; >> +} >> + >> +static bool ad_manufacturerdata_getter(struct l_dbus *dbus, >> + struct l_dbus_message *message, >> + struct l_dbus_message_builder *bui= lder, >> + void *user_data) >> +{ >> + const struct l_queue_entry *entry; >> + size_t i; >> + >> + if (l_queue_isempty(ad.manufacturers)) >> + return false; >> + >> + l_dbus_message_builder_enter_array(builder, "{qv}"); >> + >> + for (entry =3D l_queue_get_entries(ad.manufacturers); entry; >> + entry =3D entry->n= ext) { >> + struct manufacturer_data *md =3D entry->data; >> + >> + l_dbus_message_builder_enter_dict(builder, "qv"); >> + l_dbus_message_builder_append_basic(builder, 'q', &md->id)= ; >> + l_dbus_message_builder_enter_variant(builder, "ay"); >> + l_dbus_message_builder_enter_array(builder, "y"); >> + >> + for (i =3D 0; i < md->data.len; i++) >> + l_dbus_message_builder_append_basic(builder, 'y', >> + &(md->data.data[i]= )); >> + >> + l_dbus_message_builder_leave_array(builder); >> + l_dbus_message_builder_leave_variant(builder); >> + l_dbus_message_builder_leave_dict(builder); >> + } >> + l_dbus_message_builder_leave_array(builder); >> + >> + return true; >> +} >> + >> +static bool ad_includes_getter(struct l_dbus *dbus, >> + struct l_dbus_message *message, >> + struct l_dbus_message_builder *bui= lder, >> + void *user_data) >> +{ >> + l_dbus_message_builder_enter_array(builder, "s"); >> + >> + if (!(ad.tx_power || ad.name || ad.appearance)) >> + return false; >> + >> + if (ad.tx_power) { >> + const char *str =3D "tx-power"; >> + >> + l_dbus_message_builder_append_basic(builder, 's', &str); >> + } >> + >> + if (ad.name) { >> + const char *str =3D "local-name"; >> + >> + l_dbus_message_builder_append_basic(builder, 's', &str); >> + } >> + >> + if (ad.appearance) { >> + const char *str =3D "appearance"; >> + >> + l_dbus_message_builder_append_basic(builder, 's', &str); >> + } >> + >> + l_dbus_message_builder_leave_array(builder); >> + >> + return true; >> +} >> + >> +static bool ad_localname_getter(struct l_dbus *dbus, >> + struct l_dbus_message *message, >> + struct l_dbus_message_builder *bui= lder, >> + void *user_data) >> +{ >> + if (!ad.local_name) >> + return false; >> + >> + l_dbus_message_builder_append_basic(builder, 's', ad.local_name); >> + >> + return true; >> +} >> + >> +static bool ad_appearance_getter(struct l_dbus *dbus, >> + struct l_dbus_message *message, >> + struct l_dbus_message_builder *bui= lder, >> + void *user_data) >> +{ >> + if (!ad.local_appearance) >> + return false; >> + >> + l_dbus_message_builder_append_basic(builder, 'q', &ad.local_appear= ance); >> + >> + return true; >> +} >> + >> +static bool ad_duration_getter(struct l_dbus *dbus, >> + struct l_dbus_message *message, >> + struct l_dbus_message_builder *bui= lder, >> + void *user_data) >> +{ >> + if (!ad.duration) >> + return false; >> + >> + l_dbus_message_builder_append_basic(builder, 'q', &ad.duration); >> + >> + return true; >> +} >> + >> +static bool ad_timeout_getter(struct l_dbus *dbus, >> + struct l_dbus_message *message, >> + struct l_dbus_message_builder *bui= lder, >> + void *user_data) >> +{ >> + if (!ad.timeout) >> + return false; >> + >> + l_dbus_message_builder_append_basic(builder, 'q', &ad.timeout); >> + >> + return true; >> +} >> + >> +static void setup_ad_interface(struct l_dbus_interface *interface) >> +{ >> + l_dbus_interface_method(interface, "Release", >> + L_DBUS_METHOD_FLAG_NOREPLY= , >> + ad_release_call, "", ""); >> + l_dbus_interface_property(interface, "Type", 0, "s", ad_type_gette= r, >> + NU= LL); >> + l_dbus_interface_property(interface, "ServiceUUIDs", 0, "as", >> + ad_serviceuuids_getter, NU= LL); >> + l_dbus_interface_property(interface, "ServiceData", 0, "a{sv}", >> + ad_servicedata_getter, NUL= L); >> + l_dbus_interface_property(interface, "ManufacturerServiceData", 0, >> + "a{qv}", ad_manufacturerdata_gette= r, >> + NULL); >> + l_dbus_interface_property(interface, "Includes", 0, "as", >> + ad_includes_getter, NULL); >> + l_dbus_interface_property(interface, "LocalName", 0, "s", >> + ad_localname_getter, NULL)= ; >> + l_dbus_interface_property(interface, "Appearance", 0, "q", >> + ad_appearance_getter, NULL= ); >> + l_dbus_interface_property(interface, "Duration", 0, "q", >> + ad_duration_getter, NULL); >> + l_dbus_interface_property(interface, "Timeout", 0, "q", >> + ad_timeout_getter, NULL); >> +} >> + >> +static void start_advertising_reply(struct l_dbus_proxy *proxy, >> + struct l_dbus_message *res= ult, >> + void *user_data) >> +{ >> + const char *path =3D l_dbus_proxy_get_path(proxy); >> + struct btp_adapter *adapter =3D find_adapter_by_path(path); >> + uint32_t new_settings; >> + >> + if (!adapter) { >> + btp_send_error(btp, BTP_GAP_SERVICE, BTP_INDEX_NON_CONTROL= LER, >> + 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 start advertising (%s), %s", name, desc= ); >> + >> + btp_send_error(btp, BTP_GAP_SERVICE, adapter->index, >> + BTP_ERROR_= FAIL); >> + return; >> + } >> + >> + new_settings =3D adapter->current_settings; >> + new_settings |=3D BTP_GAP_SETTING_ADVERTISING; >> + update_current_settings(adapter, new_settings); >> + >> + ad.registered =3D true; >> + >> + btp_send(btp, BTP_GAP_SERVICE, BTP_OP_GAP_START_ADVERTISING, >> + adapter->index, sizeof(new_setting= s), >> + &new_settings); >> +} >> + >> +static void create_advertising_data(uint8_t adv_data_len, const uint8_t >> *data) +{ >> + const uint8_t *ad_data; >> + uint8_t ad_type, ad_len; >> + uint8_t remaining_data_len =3D adv_data_len; >> + >> + while (remaining_data_len) { >> + ad_type =3D data[adv_data_len - remaining_data_len]; >> + ad_len =3D data[adv_data_len - remaining_data_len + 1]; >> + ad_data =3D &data[adv_data_len - remaining_data_len + 2]; >> + >> + switch (ad_type) { >> + case AD_TYPE_INCOMPLETE_UUID16_SERVICE_LIST: >> + { >> + char *uuid =3D dupuuid2str(ad_data, 16); > > Empty line here. Ok. > >> + l_queue_push_tail(ad.uuids, uuid); >> + >> + break; >> + } >> + case AD_TYPE_SHORT_NAME: >> + ad.local_name =3D malloc(ad_len + 1); >> + memcpy(ad.local_name, ad_data, ad_len); >> + ad.local_name[ad_len] =3D '\0'; >> + >> + break; >> + case AD_TYPE_SERVICE_DATA_UUID16: >> + { >> + struct service_data *sd; >> + >> + sd =3D l_new(struct service_data, 1); >> + sd->uuid =3D dupuuid2str(ad_data, 16); >> + sd->data.len =3D ad_len - 2; >> + memcpy(sd->data.data, ad_data + 2, sd->data.len); >> + >> + l_queue_push_tail(ad.services, sd); >> + >> + break; >> + } >> + case AD_TYPE_APPEARANCE: >> + memcpy(&ad.local_appearance, ad_data, ad_len); >> + >> + break; >> + case AD_TYPE_MANUFACTURER_DATA: >> + { >> + struct manufacturer_data *md; >> + >> + md =3D l_new(struct manufacturer_data, 1); >> + /* The first 2 octets contain the Company Identifi= er >> + * Code followed by additional manufacturer specif= ic >> + * data. >> + */ >> + memcpy(&md->id, ad_data, 2); >> + md->data.len =3D ad_len - 2; >> + memcpy(md->data.data, ad_data + 2, md->data.len); >> + >> + l_queue_push_tail(ad.manufacturers, md); >> + >> + break; >> + } >> + default: >> + l_info("Unsupported advertising data type"); >> + >> + break; >> + } >> + /* Advertising entity data len + advertising entity header >> + * (type, len) >> + */ >> + remaining_data_len -=3D ad_len + 2; >> + } >> +} >> + >> +static void create_scan_response(uint8_t scan_rsp_len, const uint8_t *d= ata) >> +{ >> + /* TODO */ >> +} >> + >> +static void start_advertising_setup(struct l_dbus_message *message, >> + void *user_data) >> +{ >> + struct l_dbus_message_builder *builder; >> + >> + builder =3D l_dbus_message_builder_new(message); >> + l_dbus_message_builder_append_basic(builder, 'o', AD_PATH); >> + l_dbus_message_builder_enter_array(builder, "{sv}"); >> + l_dbus_message_builder_enter_dict(builder, "sv"); >> + l_dbus_message_builder_leave_dict(builder); >> + l_dbus_message_builder_leave_array(builder); >> + l_dbus_message_builder_finalize(builder); >> + l_dbus_message_builder_destroy(builder); >> +} >> + >> +static void btp_gap_start_advertising(uint8_t index, const void *param, >> + uint16_t length, void *user_data) >> +{ >> + struct btp_adapter *adapter =3D find_adapter_by_index(index); >> + const struct btp_gap_start_adv_cp *cp =3D param; >> + uint8_t status =3D BTP_ERROR_FAIL; >> + bool prop; >> + >> + if (!adapter) { >> + status =3D BTP_ERROR_INVALID_INDEX; >> + goto failed; >> + } >> + >> + /* Adapter needs to be powered to be able to remove devices */ >> + if (!l_dbus_proxy_get_property(adapter->proxy, "Powered", "b", &pr= op) || >> + !prop || ad.regist= ered) >> + goto failed; >> + >> + if (!l_dbus_register_interface(dbus, AD_IFACE, setup_ad_interface,= NULL, >> + fa= lse)) { >> + l_info("Unable to register ad interface"); >> + goto failed; >> + } >> + >> + if (!l_dbus_object_add_interface(dbus, AD_PATH, AD_IFACE, NULL)) { >> + l_info("Unable to instantiate ad interface"); >> + >> + if (!l_dbus_unregister_interface(dbus, AD_IFACE)) >> + l_info("Unable to unregister ad interface"); >> + >> + goto failed; >> + } >> + >> + if (!l_dbus_object_add_interface(dbus, AD_PATH, >> + L_DBUS_INTERFACE_PROPERTIE= S, >> + NULL)) { >> + l_info("Unable to instantiate the properties interface"); >> + >> + if (!l_dbus_object_remove_interface(dbus, AD_PATH, AD_IFAC= E)) >> + l_info("Unable to remove ad instance"); >> + if (!l_dbus_unregister_interface(dbus, AD_IFACE)) >> + l_info("Unable to unregister ad interface"); >> + >> + goto failed; >> + } >> + >> + ad.uuids =3D l_queue_new(); >> + ad.services =3D l_queue_new(); >> + ad.manufacturers =3D l_queue_new(); > > Put this into helper eg ad_init(), also set appearance to default value. Ok. > >> + >> + if (adapter->current_settings & BTP_GAP_SETTING_CONNECTABLE) >> + ad.type =3D l_strdup("peripheral"); >> + else >> + ad.type =3D l_strdup("broadcast"); > > No need to dup those string, just assing them with constants. Right. > >> + >> + if (cp->adv_data_len > 0) >> + create_advertising_data(cp->adv_data_len, cp->data); >> + if (cp->scan_rsp_len > 0) >> + create_scan_response(cp->scan_rsp_len, >> + cp->data + cp->scan_rsp_le= n); >> + >> + if (!l_dbus_proxy_method_call(adapter->ad_proxy, >> + "RegisterAdvertise= ment", >> + start_advertising_= setup, >> + start_advertising_= reply, >> + NULL, NULL)) { >> + if (!l_dbus_object_remove_interface(dbus, AD_PATH, AD_IFAC= E)) >> + l_info("Unable to remove ad instance"); >> + if (!l_dbus_unregister_interface(dbus, AD_IFACE)) >> + l_info("Unable to unregister ad interface"); >> + >> + goto failed; >> + } >> + >> + return; >> + >> +failed: >> + btp_send_error(btp, BTP_GAP_SERVICE, index, status); >> +} >> + >> +static void stop_advertising_reply(struct l_dbus_proxy *proxy, >> + struct l_dbus_message *res= ult, >> + void *user_data) >> +{ >> + const char *path =3D l_dbus_proxy_get_path(proxy); >> + struct btp_adapter *adapter =3D find_adapter_by_path(path); >> + uint32_t new_settings; >> + >> + if (!adapter) >> + return; >> + >> + if (l_dbus_message_is_error(result)) { >> + const char *name; >> + >> + l_dbus_message_get_error(result, &name, NULL); >> + >> + l_error("Failed to stop advertising %s (%s)", >> + l_dbus_proxy_get_path(proxy), name= ); >> + return; >> + } >> + >> + if (!l_dbus_object_remove_interface(dbus, AD_PATH, AD_IFACE)) >> + l_info("Unable to remove ad instance"); >> + if (!l_dbus_object_remove_interface(dbus, AD_PATH, >> + L_DBUS_INTERFACE_PROPERTIE= S)) >> + l_info("Unable to remove propety instance"); >> + if (!l_dbus_unregister_interface(dbus, AD_IFACE)) >> + l_info("Unable to unregister ad interface"); >> + >> + new_settings =3D adapter->current_settings; >> + new_settings &=3D ~BTP_GAP_SETTING_ADVERTISING; >> + update_current_settings(adapter, new_settings); >> + >> + ad_cleanup(); >> + >> + btp_send(btp, BTP_GAP_SERVICE, BTP_OP_GAP_STOP_ADVERTISING, >> + adapter->index, sizeof(new_setting= s), >> + &new_settings); >> +} >> + >> +static void btp_gap_stop_advertising(uint8_t index, const void *param, >> + uint16_t length, void *user_data) >> +{ >> + struct btp_adapter *adapter =3D find_adapter_by_index(index); >> + uint8_t status =3D BTP_ERROR_FAIL; >> + bool prop; >> + >> + if (!adapter) { >> + status =3D BTP_ERROR_INVALID_INDEX; >> + goto failed; >> + } >> + >> + if (!l_dbus_proxy_get_property(adapter->proxy, "Powered", "b", &pr= op) || >> + !prop || !ad.regis= tered) >> + goto failed; >> + >> + if (adapter->ad_proxy) { >> + if (!l_dbus_proxy_method_call(adapter->ad_proxy, >> + "UnregisterAdvertisement", >> + unreg_advertising_setup, >> + stop_advertising_reply, >> + NULL, NULL)) { >> + status =3D BTP_ERROR_FAIL; >> + goto failed; >> + } >> + } >> + >> + return; >> + >> +failed: >> + btp_send_error(btp, BTP_GAP_SERVICE, index, status); >> +} >> + >> static void start_discovery_reply(struct l_dbus_proxy *proxy, >> struct l_dbus_message *res= ult, >> void *user_data) >> @@ -728,6 +1365,12 @@ static void register_gap_service(void) >> btp_register(btp, BTP_GAP_SERVICE, BTP_OP_GAP_SET_BONDABLE, >> btp_gap_set_bondable, NULL, NULL); >> >> + btp_register(btp, BTP_GAP_SERVICE, BTP_OP_GAP_START_ADVERTISING, >> + btp_gap_start_advertising, NULL, N= ULL); >> + >> + btp_register(btp, BTP_GAP_SERVICE, BTP_OP_GAP_STOP_ADVERTISING, >> + btp_gap_stop_advertising, NULL, NU= LL); >> + >> btp_register(btp, BTP_GAP_SERVICE, BTP_OP_GAP_START_DISCOVERY, >> btp_gap_start_discovery, NULL, NUL= L); >> >> @@ -1124,6 +1767,12 @@ static void client_ready(struct l_dbus_client >> *client, void *user_data) BTP_INDEX_NON_CONTROLLER, 0, NULL); >> } >> >> +static void ready_callback(void *user_data) >> +{ >> + if (!l_dbus_object_manager_enable(dbus)) >> + l_info("Unable to register the ObjectManager"); >> +} >> + >> static void usage(void) >> { >> l_info("btpclient - Bluetooth tester"); >> @@ -1148,7 +1797,6 @@ int main(int argc, char *argv[]) >> { >> struct l_dbus_client *client; >> struct l_signal *signal; >> - struct l_dbus *dbus; >> sigset_t mask; >> int opt; >> >> @@ -1192,6 +1840,7 @@ int main(int argc, char *argv[]) >> signal =3D l_signal_create(&mask, signal_handler, NULL, NULL); >> >> dbus =3D l_dbus_new_default(L_DBUS_SYSTEM_BUS); >> + l_dbus_set_ready_handler(dbus, ready_callback, NULL, NULL); >> client =3D l_dbus_client_new(dbus, "org.bluez", "/org/bluez"); >> >> l_dbus_client_set_connect_handler(client, client_connected, NULL, = NULL); > > > -- > pozdrawiam > Szymon Janc > > pozdrawiam, Grzegorz Ko=C5=82odziejczyk ^ permalink raw reply [flat|nested] 7+ messages in thread
* [PATCH BlueZ v2 3/4] tools/btpclient: Add connect, disconnect commands 2018-01-12 14:10 [PATCH BlueZ v2 1/4] tools/btpclient: Fix setting/reseting connectable flag Grzegorz Kolodziejczyk 2018-01-12 14:10 ` [PATCH BlueZ v2 2/4] tools/btpclient: Add start, stop advertising commands Grzegorz Kolodziejczyk @ 2018-01-12 14:10 ` Grzegorz Kolodziejczyk 2018-01-12 14:10 ` [PATCH BlueZ v2 4/4] tools/btpclient: Add connected, disconnected event Grzegorz Kolodziejczyk 2018-01-15 13:50 ` [PATCH BlueZ v2 1/4] tools/btpclient: Fix setting/reseting connectable flag Szymon Janc 3 siblings, 0 replies; 7+ messages in thread From: Grzegorz Kolodziejczyk @ 2018-01-12 14:10 UTC (permalink / raw) To: linux-bluetooth This patch adds start and stop connect, disconnect commands for btp client. --- tools/btpclient.c | 175 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 175 insertions(+) diff --git a/tools/btpclient.c b/tools/btpclient.c index 3fb94b7f9..7492872c0 100644 --- a/tools/btpclient.c +++ b/tools/btpclient.c @@ -104,6 +104,13 @@ static bool str2addr(const char *str, uint8_t *addr) &addr[3], &addr[2], &addr[1], &addr[0]) == 6; } +static bool addr2str(const uint8_t *addr, char *str) +{ + return snprintf(str, 18, "%02hhX:%02hhX:%02hhX:%02hhX:%02hhX:%02hhX", + addr[5], addr[4], addr[3], addr[2], addr[1], addr[0]) + == 17; +} + static char *dupuuid2str(const uint8_t *uuid, uint8_t len) { switch (len) { @@ -121,6 +128,17 @@ static char *dupuuid2str(const uint8_t *uuid, uint8_t len) } } +static bool match_dev_addr_type(const char *addr_type_str, uint8_t addr_type) +{ + if (addr_type == BTP_GAP_ADDR_PUBLIC && strcmp(addr_type_str, "public")) + return false; + + if (addr_type == BTP_GAP_ADDR_RANDOM && strcmp(addr_type_str, "random")) + return false; + + return true; +} + static struct btp_adapter *find_adapter_by_proxy(struct l_dbus_proxy *proxy) { const struct l_queue_entry *entry; @@ -166,6 +184,34 @@ static struct btp_adapter *find_adapter_by_path(const char *path) return NULL; } +static struct btp_device *find_device_by_address(struct btp_adapter *adapter, + const uint8_t *addr, + uint8_t addr_type) +{ + const struct l_queue_entry *entry; + const char *str; + char addr_str[18]; + + if (!addr2str(addr, addr_str)) + return NULL; + + for (entry = l_queue_get_entries(adapter->devices); entry; + entry = entry->next) { + struct btp_device *device = entry->data; + + l_dbus_proxy_get_property(device->proxy, "Address", "s", &str); + if (strcmp(str, addr_str)) + continue; + + l_dbus_proxy_get_property(device->proxy, "AddressType", "s", + &str); + if (match_dev_addr_type(str, addr_type)) + return device; + } + + return NULL; +} + static void btp_gap_read_commands(uint8_t index, const void *param, uint16_t length, void *user_data) { @@ -189,6 +235,8 @@ static void btp_gap_read_commands(uint8_t index, const void *param, commands |= (1 << BTP_OP_GAP_STOP_ADVERTISING); commands |= (1 << BTP_OP_GAP_START_DISCOVERY); commands |= (1 << BTP_OP_GAP_STOP_DISCOVERY); + commands |= (1 << BTP_OP_GAP_CONNECT); + commands |= (1 << BTP_OP_GAP_DISCONNECT); commands = L_CPU_TO_LE16(commands); @@ -1304,6 +1352,127 @@ static void btp_gap_stop_discovery(uint8_t index, const void *param, stop_discovery_reply, NULL, NULL); } +static void connect_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) { + 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 connect (%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_CONNECT, adapter_index, 0, + NULL); +} + +static void btp_gap_connect(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_connect_cp *cp = param; + struct btp_device *device; + bool prop; + uint8_t status = BTP_ERROR_FAIL; + + if (!adapter) { + status = BTP_ERROR_INVALID_INDEX; + goto failed; + } + + /* Adapter needs to be powered to be able to connect */ + 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; + + l_dbus_proxy_method_call(device->proxy, "Connect", NULL, connect_reply, + L_UINT_TO_PTR(adapter->index), NULL); + + return; + +failed: + btp_send_error(btp, BTP_GAP_SERVICE, index, status); +} + +static void disconnect_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) { + 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 disconnect (%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_DISCONNECT, adapter_index, 0, + NULL); +} + +static void btp_gap_disconnect(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_disconnect_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 connect */ + 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; + + l_dbus_proxy_method_call(device->proxy, "Disconnect", NULL, + disconnect_reply, + L_UINT_TO_PTR(adapter->index), 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; @@ -1376,6 +1545,12 @@ static void register_gap_service(void) btp_register(btp, BTP_GAP_SERVICE, BTP_OP_GAP_STOP_DISCOVERY, btp_gap_stop_discovery, NULL, NULL); + + btp_register(btp, BTP_GAP_SERVICE, BTP_OP_GAP_CONNECT, btp_gap_connect, + NULL, NULL); + + btp_register(btp, BTP_GAP_SERVICE, BTP_OP_GAP_DISCONNECT, + btp_gap_disconnect, NULL, NULL); } static void btp_core_read_commands(uint8_t index, const void *param, -- 2.13.6 ^ permalink raw reply related [flat|nested] 7+ messages in thread
* [PATCH BlueZ v2 4/4] tools/btpclient: Add connected, disconnected event 2018-01-12 14:10 [PATCH BlueZ v2 1/4] tools/btpclient: Fix setting/reseting connectable flag Grzegorz Kolodziejczyk 2018-01-12 14:10 ` [PATCH BlueZ v2 2/4] tools/btpclient: Add start, stop advertising commands Grzegorz Kolodziejczyk 2018-01-12 14:10 ` [PATCH BlueZ v2 3/4] tools/btpclient: Add connect, disconnect commands Grzegorz Kolodziejczyk @ 2018-01-12 14:10 ` Grzegorz Kolodziejczyk 2018-01-15 13:50 ` [PATCH BlueZ v2 1/4] tools/btpclient: Fix setting/reseting connectable flag Szymon Janc 3 siblings, 0 replies; 7+ messages in thread From: Grzegorz Kolodziejczyk @ 2018-01-12 14:10 UTC (permalink / raw) To: linux-bluetooth This patch adds conntected, disconnected events for btp client. --- tools/btpclient.c | 70 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 70 insertions(+) diff --git a/tools/btpclient.c b/tools/btpclient.c index 7492872c0..c0ecb3f06 100644 --- a/tools/btpclient.c +++ b/tools/btpclient.c @@ -212,6 +212,26 @@ static struct btp_device *find_device_by_address(struct btp_adapter *adapter, return NULL; } +static bool match_proxies(const void *proxy_a, const void *proxy_b) +{ + return proxy_a == proxy_b; +} + +static struct btp_adapter *find_adapter_by_dev_proxy(struct l_dbus_proxy *proxy) +{ + const struct l_queue_entry *entry; + + for (entry = l_queue_get_entries(adapters); entry; + entry = entry->next) { + struct btp_adapter *adapter = entry->data; + + if (l_queue_find(adapter->devices, match_proxies, proxy)) + return adapter; + } + + return NULL; +} + static void btp_gap_read_commands(uint8_t index, const void *param, uint16_t length, void *user_data) { @@ -1507,6 +1527,49 @@ static void btp_gap_device_found_ev(struct l_dbus_proxy *proxy) sizeof(ev) + ev.eir_len, &ev); } +static void btp_gap_device_connection_ev(struct l_dbus_proxy *proxy, + bool connected) +{ + struct btp_adapter *adapter; + const char *str_addr, *str_addr_type; + uint8_t address_type; + + + adapter = find_adapter_by_dev_proxy(proxy); + + if (!adapter) + return; + + if (!l_dbus_proxy_get_property(proxy, "Address", "s", &str_addr)) + return; + + + if (!l_dbus_proxy_get_property(proxy, "AddressType", "s", + &str_addr_type)) + return; + + address_type = strcmp(str_addr_type, "public") ? BTP_GAP_ADDR_RANDOM : + BTP_GAP_ADDR_PUBLIC; + + if (connected) { + struct btp_gap_device_connected_ev ev; + + str2addr(str_addr, ev.address); + ev.address_type = address_type; + + btp_send(btp, BTP_GAP_SERVICE, BTP_EV_GAP_DEVICE_CONNECTED, + adapter->index, sizeof(ev), &ev); + } else { + struct btp_gap_device_disconnected_ev ev; + + str2addr(str_addr, ev.address); + ev.address_type = address_type; + + btp_send(btp, BTP_GAP_SERVICE, BTP_EV_GAP_DEVICE_DISCONNECTED, + adapter->index, sizeof(ev), &ev); + } +} + static void register_gap_service(void) { btp_register(btp, BTP_GAP_SERVICE, BTP_OP_GAP_READ_SUPPORTED_COMMANDS, @@ -1910,6 +1973,13 @@ static void property_changed(struct l_dbus_proxy *proxy, const char *name, return; btp_gap_device_found_ev(proxy); + } else if (!strcmp(name, "Connected")) { + bool prop; + + if (!l_dbus_message_get_arguments(msg, "b", &prop)) + return; + + btp_gap_device_connection_ev(proxy, prop); } } } -- 2.13.6 ^ permalink raw reply related [flat|nested] 7+ messages in thread
* Re: [PATCH BlueZ v2 1/4] tools/btpclient: Fix setting/reseting connectable flag 2018-01-12 14:10 [PATCH BlueZ v2 1/4] tools/btpclient: Fix setting/reseting connectable flag Grzegorz Kolodziejczyk ` (2 preceding siblings ...) 2018-01-12 14:10 ` [PATCH BlueZ v2 4/4] tools/btpclient: Add connected, disconnected event Grzegorz Kolodziejczyk @ 2018-01-15 13:50 ` Szymon Janc 3 siblings, 0 replies; 7+ messages in thread From: Szymon Janc @ 2018-01-15 13:50 UTC (permalink / raw) To: Grzegorz Kolodziejczyk; +Cc: linux-bluetooth Hi Grzegorz, On Friday, 12 January 2018 15:10:09 CET Grzegorz Kolodziejczyk wrote: > Defined setting flag is presented as mask. > --- > tools/btpclient.c | 4 ++-- > 1 file changed, 2 insertions(+), 2 deletions(-) > > diff --git a/tools/btpclient.c b/tools/btpclient.c > index 806403f6a..a8a65fd51 100644 > --- a/tools/btpclient.c > +++ b/tools/btpclient.c > @@ -368,9 +368,9 @@ static void btp_gap_set_connectable(uint8_t index, const > void *param, new_settings = adapter->current_settings; > > if (cp->connectable) > - new_settings |= 1 << BTP_GAP_SETTING_CONNECTABLE; > + new_settings |= BTP_GAP_SETTING_CONNECTABLE; > else > - new_settings &= ~(1 << BTP_GAP_SETTING_CONNECTABLE); > + new_settings &= ~BTP_GAP_SETTING_CONNECTABLE; > > update_current_settings(adapter, new_settings); This patch is now applied, thanks. -- pozdrawiam Szymon Janc ^ permalink raw reply [flat|nested] 7+ messages in thread
end of thread, other threads:[~2018-01-16 9:26 UTC | newest] Thread overview: 7+ messages (download: mbox.gz follow: Atom feed -- links below jump to the message on this page -- 2018-01-12 14:10 [PATCH BlueZ v2 1/4] tools/btpclient: Fix setting/reseting connectable flag Grzegorz Kolodziejczyk 2018-01-12 14:10 ` [PATCH BlueZ v2 2/4] tools/btpclient: Add start, stop advertising commands Grzegorz Kolodziejczyk 2018-01-15 13:21 ` Szymon Janc 2018-01-16 9:26 ` Grzegorz Kołodziejczyk 2018-01-12 14:10 ` [PATCH BlueZ v2 3/4] tools/btpclient: Add connect, disconnect commands Grzegorz Kolodziejczyk 2018-01-12 14:10 ` [PATCH BlueZ v2 4/4] tools/btpclient: Add connected, disconnected event Grzegorz Kolodziejczyk 2018-01-15 13:50 ` [PATCH BlueZ v2 1/4] tools/btpclient: Fix setting/reseting connectable flag Szymon Janc
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox; as well as URLs for NNTP newsgroup(s).