uint32_t min_interval;
uint32_t max_interval;
int8_t tx_power;
+ mgmt_request_func_t refresh_done_func;
};
struct dbus_obj_match {
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;
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.");
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;
+ 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;
bt_ad_clear_flags(client->data);
- refresh_adv(client, NULL, NULL);
+ refresh_advertisement(client, NULL, NULL);
return FALSE;
}
continue;
if (parser->func(iter, client)) {
- refresh_adv(client, NULL, NULL);
+ refresh_advertisement(client, NULL, NULL);
+
break;
}
}
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;
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;
client->min_interval = 0;
client->max_interval = 0;
+ client->refresh_done_func = NULL;
+
return client;
fail:
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)