@@ -713,6 +713,37 @@ struct mgmt_rp_remove_adv_monitor {
uint16_t monitor_handle;
} __packed;
+#define MGMT_ADV_PARAM_DURATION (1 << 0)
+#define MGMT_ADV_PARAM_TIMEOUT (1 << 1)
+#define MGMT_ADV_PARAM_INTERVALS (1 << 2)
+#define MGMT_ADV_PARAM_TX_POWER (1 << 3)
+
+#define MGMT_OP_ADD_EXT_ADV_PARAMS 0x0054
+struct mgmt_cp_add_ext_adv_params {
+ uint8_t instance;
+ uint32_t flags;
+ uint16_t params;
+ 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;
+} __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 {
uint16_t opcode;
@@ -91,6 +91,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 {
@@ -797,19 +798,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;
@@ -821,6 +812,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.");
@@ -873,6 +884,76 @@ 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.flags = htobl(flags);
+ 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;
+ included_params |= MGMT_ADV_PARAM_DURATION;
+ }
+
+ if (client->min_interval && client->max_interval) {
+ cp.min_interval = client->min_interval;
+ cp.max_interval = client->max_interval;
+ included_params |= MGMT_ADV_PARAM_INTERVALS;
+ }
+
+ if (client->tx_power != ADV_TX_POWER_NO_PREFERENCE) {
+ cp.tx_power = client->tx_power;
+ included_params |= MGMT_ADV_PARAM_TX_POWER;
+ }
+
+ cp.params = included_params;
+
+ 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;
@@ -883,7 +964,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;
}
@@ -1057,7 +1138,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;
}
}
@@ -1120,6 +1202,104 @@ 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;
+
+ if (status)
+ goto fail;
+
+ if (!param || length < sizeof(*rp)) {
+ status = MGMT_STATUS_FAILED;
+ goto fail;
+ }
+
+ DBG("Refreshing advertisement data: %s", client->path);
+
+ 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 > calc_max_adv_len(client, flags))) {
+ 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) {
+ 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;
@@ -1178,7 +1358,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;
@@ -1257,6 +1439,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:
@@ -1573,7 +1757,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)