@@ -79,6 +79,7 @@ enum monitor_state {
MONITOR_STATE_FAILED, /* Failed to be init'ed */
MONITOR_STATE_INITED, /* Init'ed but not yet sent to kernel */
MONITOR_STATE_ACTIVE, /* Accepted by kernel */
+ MONITOR_STATE_REMOVING, /* Removing from kernel */
};
struct adv_monitor {
@@ -87,6 +88,7 @@ struct adv_monitor {
char *path;
enum monitor_state state; /* MONITOR_STATE_* */
+ uint16_t monitor_handle; /* Kernel Monitor Handle */
int8_t high_rssi; /* High RSSI threshold */
uint16_t high_rssi_timeout; /* High RSSI threshold timeout */
@@ -567,6 +569,7 @@ static void add_adv_patterns_monitor_cb(uint8_t status, uint16_t length,
return;
}
+ monitor->monitor_handle = le16_to_cpu(rp->monitor_handle);
monitor->state = MONITOR_STATE_ACTIVE;
DBG("Calling Activate() on Adv Monitor of owner %s at path %s",
@@ -575,8 +578,7 @@ static void add_adv_patterns_monitor_cb(uint8_t status, uint16_t length,
g_dbus_proxy_method_call(monitor->proxy, "Activate", NULL, NULL, NULL,
NULL);
- DBG("Adv Monitor with handle:0x%04x added",
- le16_to_cpu(rp->monitor_handle));
+ DBG("Adv monitor with handle:0x%04x added", monitor->monitor_handle);
}
static void monitor_copy_patterns(void *data, void *user_data)
@@ -650,20 +652,77 @@ done:
free(cp);
}
+/* Handles the callback of Remove Adv Monitor command */
+static void remove_adv_monitor_cb(uint8_t status, uint16_t length,
+ const void *param, void *user_data)
+{
+ struct adv_monitor *monitor = user_data;
+ const struct mgmt_rp_remove_adv_monitor *rp = param;
+ uint16_t adapter_id = monitor->app->manager->adapter_id;
+
+ if (status != MGMT_STATUS_SUCCESS || !param) {
+ btd_error(adapter_id, "Failed to Remove Adv Monitor with "
+ "status 0x%02x", status);
+ goto done;
+ }
+
+ if (length < sizeof(*rp)) {
+ btd_error(adapter_id, "Wrong size of Remove Adv Monitor "
+ "response");
+ goto done;
+ }
+
+done:
+ queue_remove(monitor->app->monitors, monitor);
+
+ DBG("Adv Monitor removed with handle:0x%04x, path %s",
+ monitor->monitor_handle, monitor->path);
+
+ monitor_free(monitor);
+}
+
+
/* Handles the removal of an Adv Monitor D-Bus proxy */
static void monitor_proxy_removed_cb(GDBusProxy *proxy, void *user_data)
{
struct adv_monitor *monitor;
+ struct mgmt_cp_remove_adv_monitor cp;
struct adv_monitor_app *app = user_data;
+ uint16_t adapter_id = app->manager->adapter_id;
- monitor = queue_remove_if(app->monitors, monitor_match, proxy);
- if (monitor) {
- DBG("Adv Monitor removed for the object at path %s",
- monitor->path);
+ monitor = queue_find(app->monitors, monitor_match, proxy);
- /* The object was gone, so we don't need to call Release() */
- monitor_free(monitor);
+ /* A monitor removed event from kernel can remove a monitor and notify
+ * the app on Release() where this callback can be invoked, so we
+ * simply skip here.
+ */
+ if (!monitor)
+ return;
+
+ if (monitor->state != MONITOR_STATE_ACTIVE)
+ goto done;
+
+ monitor->state = MONITOR_STATE_REMOVING;
+
+ cp.monitor_handle = cpu_to_le16(monitor->monitor_handle);
+
+ if (!mgmt_send(app->manager->mgmt, MGMT_OP_REMOVE_ADV_MONITOR,
+ adapter_id, sizeof(cp), &cp, remove_adv_monitor_cb,
+ monitor, NULL)) {
+ btd_error(adapter_id, "Unable to send Remove Advt Monitor "
+ "command");
+ goto done;
}
+
+ return;
+
+done:
+ queue_remove(app->monitors, monitor);
+
+ DBG("Adv Monitor removed in state %02x with path %s", monitor->state,
+ monitor->path);
+
+ monitor_free(monitor);
}
/* Creates an app object, initiates it and sets D-Bus event handlers */
@@ -872,6 +931,59 @@ static const GDBusPropertyTable adv_monitor_properties[] = {
{ }
};
+/* Matches a monitor based on its handle */
+static bool removed_monitor_match(const void *data, const void *user_data)
+{
+ const uint16_t *handle = user_data;
+ const struct adv_monitor *monitor = data;
+
+ if (!data || !handle)
+ return false;
+
+ return monitor->monitor_handle == *handle;
+}
+
+/* Remove the matched monitor and reports the removal to the app */
+static void app_remove_monitor(void *data, void *user_data)
+{
+ struct adv_monitor_app *app = data;
+ struct adv_monitor *monitor;
+
+ monitor = queue_find(app->monitors, removed_monitor_match, user_data);
+ if (monitor) {
+ if (monitor->state == MONITOR_STATE_ACTIVE)
+ monitor_release(monitor, NULL);
+
+ queue_remove(app->monitors, monitor);
+
+ DBG("Adv Monitor at path %s removed", monitor->path);
+
+ monitor_free(monitor);
+ }
+}
+
+/* Processes Adv Monitor removed event from kernel */
+static void adv_monitor_removed_callback(uint16_t index, uint16_t length,
+ const void *param, void *user_data)
+{
+ struct btd_adv_monitor_manager *manager = user_data;
+ const struct mgmt_ev_adv_monitor_removed *ev = param;
+ uint16_t handle = ev->monitor_handle;
+ const uint16_t adapter_id = manager->adapter_id;
+
+ if (length < sizeof(*ev)) {
+ btd_error(adapter_id, "Wrong size of Adv Monitor Removed "
+ "event");
+ return;
+ }
+
+ /* Traverse the apps to find the monitor */
+ queue_foreach(manager->apps, app_remove_monitor, &handle);
+
+ DBG("Adv Monitor removed event with handle 0x%04x processed",
+ ev->monitor_handle);
+}
+
/* Allocates a manager object */
static struct btd_adv_monitor_manager *manager_new(
struct btd_adapter *adapter,
@@ -891,6 +1003,10 @@ static struct btd_adv_monitor_manager *manager_new(
manager->adapter_id = btd_adapter_get_index(adapter);
manager->apps = queue_new();
+ mgmt_register(manager->mgmt, MGMT_EV_ADV_MONITOR_REMOVED,
+ manager->adapter_id, adv_monitor_removed_callback,
+ manager, NULL);
+
return manager;
}