@@ -503,6 +503,10 @@ struct mgmt_rp_add_advertising {
#define MGMT_ADV_FLAG_SEC_1M (1 << 7)
#define MGMT_ADV_FLAG_SEC_2M (1 << 8)
#define MGMT_ADV_FLAG_SEC_CODED (1 << 9)
+#define MGMT_ADV_PARAM_DURATION (1 << 12)
+#define MGMT_ADV_PARAM_TIMEOUT (1 << 13)
+#define MGMT_ADV_PARAM_INTERVALS (1 << 14)
+#define MGMT_ADV_PARAM_TX_POWER (1 << 15)
#define MGMT_OP_REMOVE_ADVERTISING 0x003F
struct mgmt_cp_remove_advertising {
@@ -701,8 +705,32 @@ struct mgmt_rp_remove_adv_monitor {
} __packed;
#define MGMT_OP_ADD_EXT_ADV_PARAMS 0x0054
+struct mgmt_cp_add_ext_adv_params {
+ uint8_t instance;
+ uint32_t flags;
+ uint16_t duration;
+ uint16_t timeout;
+ uint32_t min_interval;
+ uint32_t max_interval;
+ int8_t tx_power;
+} __packed;
+struct mgmt_rp_add_ext_adv_params {
+ uint8_t instance;
+ int8_t tx_power;
+ uint8_t max_adv_data_len;
+ uint8_t max_scan_rsp_len;
+} __packed;
#define MGMT_OP_ADD_EXT_ADV_DATA 0x0055
+struct mgmt_cp_add_ext_adv_data {
+ uint8_t instance;
+ uint8_t adv_data_len;
+ uint8_t scan_rsp_len;
+ uint8_t data[0];
+} __packed;
+struct mgmt_rp_add_ext_adv_data {
+ uint8_t instance;
+} __packed;
#define MGMT_EV_CMD_COMPLETE 0x0001
struct mgmt_ev_cmd_complete {
@@ -6980,8 +6980,8 @@ static void le_set_ext_adv_params_cmd(const void *data, uint8_t size)
print_peer_addr_type("Peer address type", cmd->peer_addr_type);
print_addr("Peer address", cmd->peer_addr, cmd->peer_addr_type);
print_adv_filter_policy("Filter policy", cmd->filter_policy);
- if (cmd->tx_power == 0xff)
- print_field("TX power: Host has no preference (0xff)");
+ if (cmd->tx_power == 0x7f)
+ print_field("TX power: Host has no preference (0x7f)");
else
print_power_level(cmd->tx_power, NULL);
@@ -82,6 +82,7 @@ struct btd_adv_client {
uint32_t min_interval;
uint32_t max_interval;
int8_t tx_power;
+ mgmt_request_func_t refresh_done_func;
};
struct dbus_obj_match {
@@ -788,19 +789,9 @@ static uint8_t *generate_scan_rsp(struct btd_adv_client *client,
return bt_ad_generate(client->scan, len);
}
-static int refresh_adv(struct btd_adv_client *client, mgmt_request_func_t func,
- unsigned int *mgmt_id)
+static int get_adv_flags(struct btd_adv_client *client)
{
- struct mgmt_cp_add_advertising *cp;
- uint8_t param_len;
- uint8_t *adv_data;
- size_t adv_data_len;
- uint8_t *scan_rsp;
- size_t scan_rsp_len = -1;
uint32_t flags = 0;
- unsigned int mgmt_ret;
-
- DBG("Refreshing advertisement: %s", client->path);
if (client->type == AD_TYPE_PERIPHERAL) {
flags = MGMT_ADV_FLAG_CONNECTABLE;
@@ -812,6 +803,26 @@ static int refresh_adv(struct btd_adv_client *client, mgmt_request_func_t func,
flags |= client->flags;
+ return flags;
+}
+
+static int refresh_legacy_adv(struct btd_adv_client *client,
+ mgmt_request_func_t func,
+ unsigned int *mgmt_id)
+{
+ struct mgmt_cp_add_advertising *cp;
+ uint8_t param_len;
+ uint8_t *adv_data;
+ size_t adv_data_len;
+ uint8_t *scan_rsp;
+ size_t scan_rsp_len = -1;
+ uint32_t flags = 0;
+ unsigned int mgmt_ret;
+
+ DBG("Refreshing advertisement: %s", client->path);
+
+ flags = get_adv_flags(client);
+
adv_data = generate_adv_data(client, &flags, &adv_data_len);
if (!adv_data || (adv_data_len > calc_max_adv_len(client, flags))) {
error("Advertising data too long or couldn't be generated.");
@@ -864,6 +875,75 @@ static int refresh_adv(struct btd_adv_client *client, mgmt_request_func_t func,
return 0;
}
+static void add_adv_params_callback(uint8_t status, uint16_t length,
+ const void *param, void *user_data);
+
+static int refresh_extended_adv(struct btd_adv_client *client,
+ mgmt_request_func_t func, unsigned int *mgmt_id)
+{
+ struct mgmt_cp_add_ext_adv_params cp;
+ uint32_t flags = 0;
+ uint16_t included_params = 0;
+ unsigned int mgmt_ret = 0;
+
+ DBG("Refreshing advertisement parameters: %s", client->path);
+
+ flags = get_adv_flags(client);
+
+ memset(&cp, 0, sizeof(cp));
+ cp.instance = client->instance;
+
+ /* Not all advertising instances will use all possible parameters. The
+ * included_params bit field tells the kernel which parameters are
+ * relevant, and sensible defaults will be used for the rest
+ */
+
+ if (client->duration) {
+ cp.duration = client->duration;
+ flags |= MGMT_ADV_PARAM_DURATION;
+ }
+
+ if (client->min_interval && client->max_interval) {
+ cp.min_interval = client->min_interval;
+ cp.max_interval = client->max_interval;
+ flags |= MGMT_ADV_PARAM_INTERVALS;
+ }
+
+ if (client->tx_power != ADV_TX_POWER_NO_PREFERENCE) {
+ cp.tx_power = client->tx_power;
+ flags |= MGMT_ADV_PARAM_TX_POWER;
+ }
+
+ cp.flags = htobl(flags);
+
+ mgmt_ret = mgmt_send(client->manager->mgmt, MGMT_OP_ADD_EXT_ADV_PARAMS,
+ client->manager->mgmt_index, sizeof(cp), &cp,
+ add_adv_params_callback, client, NULL);
+
+ if (!mgmt_ret) {
+ error("Failed to request extended advertising parameters");
+ return -EINVAL;
+ }
+
+ /* Store callback, called after we set advertising data */
+ client->refresh_done_func = func;
+
+ if (mgmt_id)
+ *mgmt_id = mgmt_ret;
+
+
+ return 0;
+}
+
+static int refresh_advertisement(struct btd_adv_client *client,
+ mgmt_request_func_t func, unsigned int *mgmt_id)
+{
+ if (client->manager->extended_add_cmds)
+ return refresh_extended_adv(client, func, mgmt_id);
+
+ return refresh_legacy_adv(client, func, mgmt_id);
+}
+
static gboolean client_discoverable_timeout(void *user_data)
{
struct btd_adv_client *client = user_data;
@@ -874,7 +954,7 @@ static gboolean client_discoverable_timeout(void *user_data)
bt_ad_clear_flags(client->data);
- refresh_adv(client, NULL, NULL);
+ refresh_advertisement(client, NULL, NULL);
return FALSE;
}
@@ -1070,7 +1150,8 @@ static void properties_changed(GDBusProxy *proxy, const char *name,
continue;
if (parser->func(iter, client)) {
- refresh_adv(client, NULL, NULL);
+ refresh_advertisement(client, NULL, NULL);
+
break;
}
}
@@ -1133,6 +1214,112 @@ done:
add_client_complete(client, status);
}
+static void add_adv_params_callback(uint8_t status, uint16_t length,
+ const void *param, void *user_data)
+{
+ struct btd_adv_client *client = user_data;
+ const struct mgmt_rp_add_ext_adv_params *rp = param;
+ struct mgmt_cp_add_ext_adv_data *cp = NULL;
+ uint8_t param_len;
+ uint8_t *adv_data = NULL;
+ size_t adv_data_len;
+ uint8_t *scan_rsp = NULL;
+ size_t scan_rsp_len = -1;
+ uint32_t flags = 0;
+ unsigned int mgmt_ret;
+ dbus_int16_t tx_power;
+
+ if (status)
+ goto fail;
+
+ if (!param || length < sizeof(*rp)) {
+ status = MGMT_STATUS_FAILED;
+ goto fail;
+ }
+
+ DBG("Refreshing advertisement data: %s", client->path);
+
+ /* Update tx power held by client */
+ tx_power = rp->tx_power;
+ if (tx_power != ADV_TX_POWER_NO_PREFERENCE)
+ g_dbus_proxy_set_property_basic(client->proxy, "TxPower",
+ DBUS_TYPE_INT16, &tx_power, NULL, NULL, NULL);
+
+ client->instance = rp->instance;
+
+ flags = get_adv_flags(client);
+
+ adv_data = generate_adv_data(client, &flags, &adv_data_len);
+ if (!adv_data || (adv_data_len > rp->max_adv_data_len)) {
+ error("Advertising data too long or couldn't be generated.");
+ goto fail;
+ }
+
+ scan_rsp = generate_scan_rsp(client, &flags, &scan_rsp_len);
+ if ((!scan_rsp && scan_rsp_len) ||
+ scan_rsp_len > rp->max_scan_rsp_len) {
+ error("Scan data couldn't be generated.");
+ goto fail;
+ }
+
+ param_len = sizeof(struct mgmt_cp_add_advertising) + adv_data_len +
+ scan_rsp_len;
+
+ cp = malloc0(param_len);
+ if (!cp) {
+ error("Couldn't allocate for MGMT!");
+ goto fail;
+ }
+
+ cp->instance = client->instance;
+ cp->adv_data_len = adv_data_len;
+ cp->scan_rsp_len = scan_rsp_len;
+ memcpy(cp->data, adv_data, adv_data_len);
+ memcpy(cp->data + adv_data_len, scan_rsp, scan_rsp_len);
+
+ free(adv_data);
+ free(scan_rsp);
+ adv_data = NULL;
+ scan_rsp = NULL;
+
+ /* Submit request to update instance data */
+ mgmt_ret = mgmt_send(client->manager->mgmt, MGMT_OP_ADD_EXT_ADV_DATA,
+ client->manager->mgmt_index, param_len, cp,
+ client->refresh_done_func, client, NULL);
+
+ /* Clear the callback */
+ client->refresh_done_func = NULL;
+
+ if (!mgmt_ret) {
+ error("Failed to add Advertising Data");
+ goto fail;
+ }
+
+ if (client->add_adv_id)
+ client->add_adv_id = mgmt_ret;
+
+ free(cp);
+ cp = NULL;
+
+ return;
+
+fail:
+ if (adv_data)
+ free(adv_data);
+
+ if (scan_rsp)
+ free(scan_rsp);
+
+ if (cp)
+ free(cp);
+
+ if (!status)
+ status = -EINVAL;
+
+ /* Failure for any reason ends this advertising request */
+ add_client_complete(client, status);
+}
+
static DBusMessage *parse_advertisement(struct btd_adv_client *client)
{
struct adv_parser *parser;
@@ -1191,7 +1378,9 @@ static DBusMessage *parse_advertisement(struct btd_adv_client *client)
goto fail;
}
- err = refresh_adv(client, add_adv_callback, &client->add_adv_id);
+ err = refresh_advertisement(client, add_adv_callback,
+ &client->add_adv_id);
+
if (!err)
return NULL;
@@ -1270,6 +1459,8 @@ static struct btd_adv_client *client_create(struct btd_adv_manager *manager,
client->min_interval = 0;
client->max_interval = 0;
+ client->refresh_done_func = NULL;
+
return client;
fail:
@@ -1586,7 +1777,9 @@ void btd_adv_manager_destroy(struct btd_adv_manager *manager)
static void manager_refresh(void *data, void *user_data)
{
- refresh_adv(data, user_data, NULL);
+ struct btd_adv_client *client = data;
+
+ refresh_advertisement(client, user_data, NULL);
}
void btd_adv_manager_refresh(struct btd_adv_manager *manager)