| // SPDX-License-Identifier: GPL-2.0-or-later |
| /* |
| * |
| * BlueZ - Bluetooth protocol stack for Linux |
| * |
| * Copyright (C) 2020 Google LLC |
| * Copyright 2024 NXP |
| * |
| * |
| */ |
| |
| #ifdef HAVE_CONFIG_H |
| #include <config.h> |
| #endif |
| |
| #define _GNU_SOURCE |
| #include <stdio.h> |
| #include <stdlib.h> |
| #include <stdint.h> |
| #include <stdbool.h> |
| #include <string.h> |
| |
| #include "gdbus/gdbus.h" |
| #include "src/shared/ad.h" |
| #include "src/shared/util.h" |
| #include "src/shared/shell.h" |
| #include "adv_monitor.h" |
| |
| #define ADV_MONITOR_APP_PATH "/org/bluez/adv_monitor_app" |
| #define ADV_MONITOR_INTERFACE "org.bluez.AdvertisementMonitor1" |
| |
| #define RSSI_UNSET_THRESHOLD 127 |
| #define RSSI_UNSET_TIMEOUT 0 |
| #define RSSI_UNSET_SAMPLING_PERIOD 256 |
| |
| struct rssi_setting { |
| int16_t high_threshold; |
| uint16_t high_timeout; |
| int16_t low_threshold; |
| uint16_t low_timeout; |
| uint16_t sampling_period; |
| }; |
| |
| struct pattern { |
| uint8_t start_pos; |
| uint8_t ad_data_type; |
| uint8_t content_len; |
| uint8_t content[BT_AD_MAX_DATA_LEN]; |
| }; |
| |
| struct adv_monitor { |
| uint8_t idx; |
| char *path; |
| char *type; |
| struct rssi_setting *rssi; |
| GSList *patterns; |
| }; |
| |
| static struct adv_monitor_manager { |
| GSList *supported_types; |
| GSList *supported_features; |
| GDBusProxy *proxy; |
| gboolean app_registered; |
| } manager = { NULL, NULL, NULL, FALSE }; |
| |
| static uint8_t adv_mon_idx; |
| static GSList *adv_mons; |
| static struct rssi_setting *current_rssi; |
| |
| static void remove_adv_monitor(void *data, void *user_data); |
| |
| static DBusMessage *release_adv_monitor(DBusConnection *conn, |
| DBusMessage *msg, void *user_data) |
| { |
| struct adv_monitor *adv_monitor = user_data; |
| |
| bt_shell_printf("Advertisement monitor %d released\n", |
| adv_monitor->idx); |
| remove_adv_monitor(adv_monitor, conn); |
| |
| return dbus_message_new_method_return(msg); |
| } |
| |
| static DBusMessage *activate_adv_monitor(DBusConnection *conn, |
| DBusMessage *msg, void *user_data) |
| { |
| struct adv_monitor *adv_monitor = user_data; |
| |
| bt_shell_printf("Advertisement monitor %d activated\n", |
| adv_monitor->idx); |
| return dbus_message_new_method_return(msg); |
| } |
| |
| static DBusMessage *device_found_adv_monitor(DBusConnection *conn, |
| DBusMessage *msg, void *user_data) |
| { |
| struct adv_monitor *adv_monitor = user_data; |
| const char *device; |
| |
| dbus_message_get_args(msg, NULL, DBUS_TYPE_OBJECT_PATH, &device, |
| DBUS_TYPE_INVALID); |
| bt_shell_printf("Advertisement monitor %d found device %s\n", |
| adv_monitor->idx, device); |
| return dbus_message_new_method_return(msg); |
| } |
| |
| static DBusMessage *device_lost_adv_monitor(DBusConnection *conn, |
| DBusMessage *msg, void *user_data) |
| { |
| struct adv_monitor *adv_monitor = user_data; |
| const char *device; |
| |
| dbus_message_get_args(msg, NULL, DBUS_TYPE_OBJECT_PATH, &device, |
| DBUS_TYPE_INVALID); |
| bt_shell_printf("Advertisement monitor %d lost device %s\n", |
| adv_monitor->idx, device); |
| return dbus_message_new_method_return(msg); |
| } |
| |
| static const GDBusMethodTable adv_monitor_methods[] = { |
| { GDBUS_ASYNC_METHOD("Release", NULL, NULL, release_adv_monitor) }, |
| { GDBUS_ASYNC_METHOD("Activate", NULL, NULL, activate_adv_monitor) }, |
| { GDBUS_ASYNC_METHOD("DeviceFound", GDBUS_ARGS({ "device", "o" }), |
| NULL, device_found_adv_monitor) }, |
| { GDBUS_ASYNC_METHOD("DeviceLost", GDBUS_ARGS({ "device", "o" }), |
| NULL, device_lost_adv_monitor) }, |
| { } |
| }; |
| |
| |
| static gboolean get_type(const GDBusPropertyTable *property, |
| DBusMessageIter *iter, void *user_data) |
| { |
| struct adv_monitor *adv_monitor = user_data; |
| |
| dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, |
| &adv_monitor->type); |
| return TRUE; |
| } |
| |
| static gboolean get_low_threshold(const GDBusPropertyTable *property, |
| DBusMessageIter *iter, void *user_data) |
| { |
| struct adv_monitor *adv_monitor = user_data; |
| struct rssi_setting *rssi = adv_monitor->rssi; |
| |
| dbus_message_iter_append_basic(iter, DBUS_TYPE_INT16, |
| &rssi->low_threshold); |
| return TRUE; |
| } |
| |
| static gboolean get_high_threshold(const GDBusPropertyTable *property, |
| DBusMessageIter *iter, void *user_data) |
| { |
| struct adv_monitor *adv_monitor = user_data; |
| struct rssi_setting *rssi = adv_monitor->rssi; |
| |
| dbus_message_iter_append_basic(iter, DBUS_TYPE_INT16, |
| &rssi->high_threshold); |
| return TRUE; |
| } |
| |
| static gboolean get_low_timeout(const GDBusPropertyTable *property, |
| DBusMessageIter *iter, void *user_data) |
| { |
| struct adv_monitor *adv_monitor = user_data; |
| struct rssi_setting *rssi = adv_monitor->rssi; |
| |
| dbus_message_iter_append_basic(iter, DBUS_TYPE_UINT16, |
| &rssi->low_timeout); |
| return TRUE; |
| } |
| |
| static gboolean get_high_timeout(const GDBusPropertyTable *property, |
| DBusMessageIter *iter, void *user_data) |
| { |
| struct adv_monitor *adv_monitor = user_data; |
| struct rssi_setting *rssi = adv_monitor->rssi; |
| |
| dbus_message_iter_append_basic(iter, DBUS_TYPE_UINT16, |
| &rssi->high_timeout); |
| return TRUE; |
| } |
| |
| static gboolean get_sampling_period(const GDBusPropertyTable *property, |
| DBusMessageIter *iter, void *user_data) |
| { |
| struct adv_monitor *adv_monitor = user_data; |
| struct rssi_setting *rssi = adv_monitor->rssi; |
| |
| dbus_message_iter_append_basic(iter, DBUS_TYPE_UINT16, |
| &rssi->sampling_period); |
| return TRUE; |
| } |
| |
| static gboolean low_threshold_exists(const GDBusPropertyTable *property, |
| void *data) |
| { |
| struct adv_monitor *adv_monitor = data; |
| |
| return adv_monitor->rssi != NULL && |
| adv_monitor->rssi->low_threshold != RSSI_UNSET_THRESHOLD; |
| } |
| |
| static gboolean high_threshold_exists(const GDBusPropertyTable *property, |
| void *data) |
| { |
| struct adv_monitor *adv_monitor = data; |
| |
| return adv_monitor->rssi != NULL && |
| adv_monitor->rssi->high_threshold != RSSI_UNSET_THRESHOLD; |
| } |
| |
| static gboolean low_timeout_exists(const GDBusPropertyTable *property, |
| void *data) |
| { |
| struct adv_monitor *adv_monitor = data; |
| |
| return adv_monitor->rssi != NULL && |
| adv_monitor->rssi->low_timeout != RSSI_UNSET_TIMEOUT; |
| } |
| |
| static gboolean high_timeout_exists(const GDBusPropertyTable *property, |
| void *data) |
| { |
| struct adv_monitor *adv_monitor = data; |
| |
| return adv_monitor->rssi != NULL && |
| adv_monitor->rssi->high_timeout != RSSI_UNSET_TIMEOUT; |
| } |
| |
| static gboolean sampling_period_exists(const GDBusPropertyTable *property, |
| void *data) |
| { |
| struct adv_monitor *adv_monitor = data; |
| |
| return adv_monitor->rssi != NULL && |
| adv_monitor->rssi->sampling_period != |
| RSSI_UNSET_SAMPLING_PERIOD; |
| } |
| |
| static void append_pattern_content_to_dbus(DBusMessageIter *iter, |
| struct pattern *pattern) |
| { |
| DBusMessageIter data_iter; |
| int idx; |
| |
| dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY, |
| DBUS_TYPE_BYTE_AS_STRING, &data_iter); |
| for (idx = 0; idx < pattern->content_len; idx++) |
| dbus_message_iter_append_basic(&data_iter, DBUS_TYPE_BYTE, |
| &pattern->content[idx]); |
| dbus_message_iter_close_container(iter, &data_iter); |
| } |
| |
| static void append_pattern_to_dbus(void *data, void *user_data) |
| { |
| struct pattern *pattern = data; |
| DBusMessageIter *array_iter = user_data; |
| DBusMessageIter data_iter; |
| |
| dbus_message_iter_open_container(array_iter, DBUS_TYPE_STRUCT, |
| NULL, &data_iter); |
| dbus_message_iter_append_basic(&data_iter, DBUS_TYPE_BYTE, |
| &pattern->start_pos); |
| dbus_message_iter_append_basic(&data_iter, DBUS_TYPE_BYTE, |
| &pattern->ad_data_type); |
| append_pattern_content_to_dbus(&data_iter, pattern); |
| dbus_message_iter_close_container(array_iter, &data_iter); |
| } |
| |
| static gboolean get_patterns(const GDBusPropertyTable *property, |
| DBusMessageIter *iter, void *user_data) |
| { |
| struct adv_monitor *adv_monitor = user_data; |
| DBusMessageIter array_iter; |
| |
| dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY, "(yyay)", |
| &array_iter); |
| g_slist_foreach(adv_monitor->patterns, append_pattern_to_dbus, |
| &array_iter); |
| dbus_message_iter_close_container(iter, &array_iter); |
| return TRUE; |
| } |
| |
| static gboolean pattern_exists(const GDBusPropertyTable *property, void *data) |
| { |
| struct adv_monitor *adv_monitor = data; |
| |
| return adv_monitor->patterns != NULL; |
| } |
| |
| static const GDBusPropertyTable adv_monitor_props[] = { |
| { "Type", "s", get_type }, |
| { "RSSILowThreshold", "n", get_low_threshold, NULL, |
| low_threshold_exists }, |
| { "RSSIHighThreshold", "n", get_high_threshold, NULL, |
| high_threshold_exists }, |
| { "RSSILowTimeout", "q", get_low_timeout, NULL, low_timeout_exists }, |
| { "RSSIHighTimeout", "q", get_high_timeout, NULL, high_timeout_exists }, |
| { "RSSISamplingPeriod", "q", get_sampling_period, NULL, |
| sampling_period_exists }, |
| { "Patterns", "a(yyay)", get_patterns, NULL, pattern_exists }, |
| { } |
| }; |
| |
| static void set_supported_list(GSList **list, DBusMessageIter *iter) |
| { |
| char *str; |
| DBusMessageIter subiter; |
| |
| dbus_message_iter_recurse(iter, &subiter); |
| while (dbus_message_iter_get_arg_type(&subiter) == |
| DBUS_TYPE_STRING) { |
| dbus_message_iter_get_basic(&subiter, &str); |
| *list = g_slist_append(*list, str); |
| dbus_message_iter_next(&subiter); |
| } |
| } |
| |
| void adv_monitor_add_manager(DBusConnection *conn, GDBusProxy *proxy) |
| { |
| DBusMessageIter iter; |
| |
| if (manager.proxy != NULL || manager.supported_types != NULL || |
| manager.supported_features != NULL) { |
| bt_shell_printf("advertisement monitor manager already " |
| "added\n"); |
| return; |
| } |
| |
| manager.proxy = proxy; |
| |
| if (g_dbus_proxy_get_property(proxy, "SupportedMonitorTypes", &iter)) |
| set_supported_list(&(manager.supported_types), &iter); |
| |
| if (g_dbus_proxy_get_property(proxy, "SupportedFeatures", &iter)) |
| set_supported_list(&(manager.supported_features), &iter); |
| |
| } |
| |
| void adv_monitor_remove_manager(DBusConnection *conn) |
| { |
| if (manager.supported_types != NULL) { |
| g_slist_free(manager.supported_types); |
| manager.supported_types = NULL; |
| } |
| |
| if (manager.supported_features != NULL) { |
| g_slist_free(manager.supported_features); |
| manager.supported_features = NULL; |
| } |
| |
| manager.proxy = NULL; |
| manager.app_registered = FALSE; |
| |
| g_free(current_rssi); |
| current_rssi = NULL; |
| } |
| |
| static void register_setup(DBusMessageIter *iter, void *user_data) |
| { |
| const char *path = "/"; |
| |
| dbus_message_iter_append_basic(iter, DBUS_TYPE_OBJECT_PATH, &path); |
| } |
| |
| static void register_reply(DBusMessage *message, void *user_data) |
| { |
| DBusError error; |
| |
| dbus_error_init(&error); |
| |
| if (!dbus_set_error_from_message(&error, message)) { |
| bt_shell_printf("AdvertisementMonitor path registered\n"); |
| return; |
| } |
| |
| bt_shell_printf("Failed to register path: %s\n", error.name); |
| dbus_error_free(&error); |
| } |
| |
| static void unregister_setup(DBusMessageIter *iter, void *user_data) |
| { |
| const char *path = "/"; |
| |
| dbus_message_iter_append_basic(iter, DBUS_TYPE_OBJECT_PATH, &path); |
| } |
| |
| static void unregister_reply(DBusMessage *message, void *user_data) |
| { |
| DBusError error; |
| |
| dbus_error_init(&error); |
| |
| if (!dbus_set_error_from_message(&error, message)) { |
| bt_shell_printf("AdvertisementMonitor path unregistered\n"); |
| return bt_shell_noninteractive_quit(EXIT_SUCCESS); |
| } |
| |
| bt_shell_printf("Failed to unregister Advertisement Monitor:" |
| " %s\n", error.name); |
| dbus_error_free(&error); |
| return bt_shell_noninteractive_quit(EXIT_FAILURE); |
| } |
| |
| void adv_monitor_register_app(DBusConnection *conn) |
| { |
| if (manager.app_registered) { |
| bt_shell_printf("Advertisement Monitor already registered\n"); |
| return; |
| } else if (manager.supported_types == NULL || |
| !g_dbus_proxy_method_call(manager.proxy, "RegisterMonitor", |
| register_setup, register_reply, |
| NULL, NULL)) { |
| bt_shell_printf("Failed to register Advertisement Monitor\n"); |
| return; |
| } |
| manager.app_registered = TRUE; |
| } |
| |
| void adv_monitor_unregister_app(DBusConnection *conn) |
| { |
| if (!manager.app_registered) { |
| bt_shell_printf("Advertisement Monitor not registered\n"); |
| return bt_shell_noninteractive_quit(EXIT_FAILURE); |
| } else if (!g_dbus_proxy_method_call(manager.proxy, "UnregisterMonitor", |
| unregister_setup, unregister_reply, |
| NULL, NULL)) { |
| bt_shell_printf("Failed to unregister Advertisement Monitor\n"); |
| return bt_shell_noninteractive_quit(EXIT_FAILURE); |
| } |
| manager.app_registered = FALSE; |
| } |
| |
| static void free_pattern(void *user_data) |
| { |
| struct pattern *p = user_data; |
| |
| g_free(p); |
| } |
| |
| static void free_adv_monitor(void *user_data) |
| { |
| struct adv_monitor *adv_monitor = user_data; |
| |
| g_free(adv_monitor->path); |
| g_free(adv_monitor->type); |
| g_free(adv_monitor->rssi); |
| g_slist_free_full(adv_monitor->patterns, free_pattern); |
| g_free(adv_monitor); |
| } |
| |
| static uint8_t str2bytearray(char *str, uint8_t *arr) |
| { |
| int idx, len = strlen(str), arr_len = 0; |
| |
| if (len%2 != 0) |
| return 0; |
| |
| for (idx = 0; idx < len; idx += 2) { |
| if (sscanf(str+idx, "%2hhx", &arr[arr_len++]) < 1) |
| return 0; |
| } |
| return arr_len; |
| } |
| |
| static struct pattern *parse_pattern(char *parameter_list[]) |
| { |
| struct pattern *pat; |
| |
| pat = g_malloc0(sizeof(struct pattern)); |
| |
| if (!pat) { |
| bt_shell_printf("Failed to allocate pattern\n"); |
| bt_shell_noninteractive_quit(EXIT_FAILURE); |
| return NULL; |
| } |
| |
| pat->start_pos = atoi(parameter_list[0]); |
| pat->ad_data_type = atoi(parameter_list[1]); |
| pat->content_len = str2bytearray(parameter_list[2], pat->content); |
| if (pat->content_len == 0) { |
| free_pattern(pat); |
| return NULL; |
| } |
| |
| return pat; |
| } |
| |
| static GSList *parse_patterns(char *pattern_list[], int num) |
| { |
| GSList *patterns = NULL; |
| int cnt; |
| |
| if (num == 0) { |
| bt_shell_printf("No pattern provided\n"); |
| return NULL; |
| } |
| |
| if (num%3) { |
| bt_shell_printf("Expected %d more arguments\n", 3 - num%3); |
| return NULL; |
| } |
| |
| for (cnt = 0; cnt < num; cnt += 3) { |
| struct pattern *pattern; |
| |
| pattern = parse_pattern(pattern_list+cnt); |
| if (pattern == NULL) { |
| g_slist_free_full(patterns, free_pattern); |
| return NULL; |
| } |
| patterns = g_slist_append(patterns, pattern); |
| } |
| |
| return patterns; |
| } |
| |
| static void remove_adv_monitor(void *data, void *user_data) |
| { |
| struct adv_monitor *adv_monitor = data; |
| DBusConnection *conn = user_data; |
| |
| adv_mons = g_slist_remove(adv_mons, adv_monitor); |
| g_dbus_unregister_interface(conn, adv_monitor->path, |
| ADV_MONITOR_INTERFACE); |
| } |
| |
| static gint cmp_adv_monitor_with_idx(gconstpointer a, gconstpointer b) |
| { |
| const struct adv_monitor *adv_monitor = a; |
| uint8_t idx = *(uint8_t *)b; |
| |
| return adv_monitor->idx != idx; |
| } |
| |
| static struct adv_monitor *find_adv_monitor_with_idx(uint8_t monitor_idx) |
| { |
| GSList *list; |
| |
| list = g_slist_find_custom(adv_mons, &monitor_idx, |
| cmp_adv_monitor_with_idx); |
| |
| if (list) |
| return (struct adv_monitor *)list->data; |
| return NULL; |
| } |
| |
| static void print_bytearray(char *prefix, uint8_t *arr, uint8_t len) |
| { |
| int idx; |
| |
| bt_shell_printf("%s", prefix); |
| for (idx = 0; idx < len; idx++) |
| bt_shell_printf("%02hhx", arr[idx]); |
| bt_shell_printf("\n"); |
| } |
| |
| static void print_adv_monitor(struct adv_monitor *adv_monitor) |
| { |
| GSList *l; |
| |
| bt_shell_printf("Advertisement Monitor %d\n", adv_monitor->idx); |
| bt_shell_printf("\tpath: %s\n", adv_monitor->path); |
| bt_shell_printf("\ttype: %s\n", adv_monitor->type); |
| if (adv_monitor->rssi) { |
| bt_shell_printf("\trssi:\n"); |
| bt_shell_printf("\t\thigh threshold: %hd\n", |
| adv_monitor->rssi->high_threshold); |
| bt_shell_printf("\t\thigh threshold timeout: %hu\n", |
| adv_monitor->rssi->high_timeout); |
| bt_shell_printf("\t\tlow threshold: %hd\n", |
| adv_monitor->rssi->low_threshold); |
| bt_shell_printf("\t\tlow threshold timeout: %hu\n", |
| adv_monitor->rssi->low_timeout); |
| bt_shell_printf("\t\tsampling period: %hu\n", |
| adv_monitor->rssi->sampling_period); |
| } |
| |
| if (adv_monitor->patterns) { |
| int idx = 1; |
| |
| for (l = adv_monitor->patterns; l; l = g_slist_next(l), idx++) { |
| struct pattern *pattern = l->data; |
| |
| bt_shell_printf("\tpattern %d:\n", idx); |
| bt_shell_printf("\t\tstart position: %hhu\n", |
| pattern->start_pos); |
| bt_shell_printf("\t\tAD data type: %hhu\n", |
| pattern->ad_data_type); |
| print_bytearray("\t\tcontent: ", pattern->content, |
| pattern->content_len); |
| } |
| } |
| } |
| |
| static struct rssi_setting *get_current_rssi(void) |
| { |
| if (current_rssi) |
| return current_rssi; |
| |
| current_rssi = g_malloc0(sizeof(struct rssi_setting)); |
| |
| if (!current_rssi) |
| bt_shell_printf("Failed to allocate rssi setting"); |
| |
| current_rssi->low_threshold = RSSI_UNSET_THRESHOLD; |
| current_rssi->high_threshold = RSSI_UNSET_THRESHOLD; |
| current_rssi->low_timeout = RSSI_UNSET_TIMEOUT; |
| current_rssi->high_timeout = RSSI_UNSET_TIMEOUT; |
| current_rssi->sampling_period = RSSI_UNSET_SAMPLING_PERIOD; |
| |
| return current_rssi; |
| } |
| |
| void adv_monitor_set_rssi_threshold(int16_t low_threshold, |
| int16_t high_threshold) |
| { |
| struct rssi_setting *rssi = get_current_rssi(); |
| |
| if (!rssi) |
| return; |
| |
| rssi->low_threshold = low_threshold; |
| rssi->high_threshold = high_threshold; |
| } |
| |
| void adv_monitor_set_rssi_timeout(uint16_t low_timeout, uint16_t high_timeout) |
| { |
| struct rssi_setting *rssi = get_current_rssi(); |
| |
| if (!rssi) |
| return; |
| |
| rssi->low_timeout = low_timeout; |
| rssi->high_timeout = high_timeout; |
| } |
| |
| void adv_monitor_set_rssi_sampling_period(uint16_t sampling) |
| { |
| struct rssi_setting *rssi = get_current_rssi(); |
| |
| if (!rssi) |
| return; |
| |
| rssi->sampling_period = sampling; |
| } |
| |
| void adv_monitor_add_monitor(DBusConnection *conn, char *type, |
| int argc, char *argv[]) |
| { |
| struct adv_monitor *adv_monitor; |
| GSList *patterns = NULL; |
| |
| if (g_slist_length(adv_mons) >= UINT8_MAX) { |
| bt_shell_printf("Number of advertisement monitor exceeds " |
| "the limit"); |
| return; |
| } |
| |
| while (find_adv_monitor_with_idx(adv_mon_idx)) |
| adv_mon_idx += 1; |
| |
| patterns = parse_patterns(argv+1, argc-1); |
| if (patterns == NULL) { |
| bt_shell_printf("pattern-list malformed\n"); |
| return; |
| } |
| |
| adv_monitor = g_malloc0(sizeof(struct adv_monitor)); |
| |
| if (!adv_monitor) { |
| bt_shell_printf("Failed to allocate adv_monitor"); |
| return bt_shell_noninteractive_quit(EXIT_FAILURE); |
| } |
| |
| adv_monitor->idx = adv_mon_idx; |
| adv_monitor->type = g_strdup(type); |
| adv_monitor->rssi = current_rssi; |
| adv_monitor->patterns = patterns; |
| adv_monitor->path = g_strdup_printf("%s/%hhu", ADV_MONITOR_APP_PATH, |
| adv_mon_idx); |
| current_rssi = NULL; |
| |
| if (g_dbus_register_interface(conn, adv_monitor->path, |
| ADV_MONITOR_INTERFACE, |
| adv_monitor_methods, NULL, |
| adv_monitor_props, adv_monitor, |
| free_adv_monitor) == FALSE) { |
| bt_shell_printf("Failed to register advertisement monitor\n"); |
| free_adv_monitor(adv_monitor); |
| return bt_shell_noninteractive_quit(EXIT_FAILURE); |
| } |
| |
| adv_mons = g_slist_append(adv_mons, adv_monitor); |
| bt_shell_printf("Advertisement Monitor %d added\n", adv_monitor->idx); |
| } |
| |
| void adv_monitor_print_monitor(DBusConnection *conn, int monitor_idx) |
| { |
| struct adv_monitor *adv_monitor; |
| GSList *l; |
| |
| if (monitor_idx < 0) { |
| for (l = adv_mons; l; l = g_slist_next(l)) { |
| adv_monitor = l->data; |
| print_adv_monitor(adv_monitor); |
| } |
| return; |
| } |
| |
| adv_monitor = find_adv_monitor_with_idx(monitor_idx); |
| |
| if (adv_monitor == NULL) { |
| bt_shell_printf("Can't find monitor with index %d\n", |
| monitor_idx); |
| return; |
| } |
| |
| print_adv_monitor(adv_monitor); |
| } |
| |
| void adv_monitor_remove_monitor(DBusConnection *conn, int monitor_idx) |
| { |
| struct adv_monitor *adv_monitor; |
| |
| if (monitor_idx < 0) { |
| g_slist_foreach(adv_mons, remove_adv_monitor, conn); |
| return; |
| } |
| |
| adv_monitor = find_adv_monitor_with_idx(monitor_idx); |
| if (adv_monitor == NULL) { |
| bt_shell_printf("Can't find monitor with index %d\n", |
| monitor_idx); |
| return; |
| } |
| |
| remove_adv_monitor(adv_monitor, conn); |
| bt_shell_printf("Monitor %d deleted\n", monitor_idx); |
| } |
| |
| static void print_supported_list(GSList *list) |
| { |
| GSList *iter; |
| |
| for (iter = list; iter; iter = g_slist_next(iter)) { |
| char *data = iter->data; |
| |
| printf(" %s", data); |
| } |
| } |
| |
| void adv_monitor_get_supported_info(void) |
| { |
| bt_shell_printf("Supported Features:"); |
| print_supported_list(manager.supported_features); |
| bt_shell_printf("\n"); |
| |
| bt_shell_printf("Supported Monitor Types:"); |
| print_supported_list(manager.supported_types); |
| bt_shell_printf("\n"); |
| } |