// SPDX-License-Identifier: Apache-2.0 /* * Copyright (C) 2014 Intel Corporation * */ #define _GNU_SOURCE #include #include #include #include #include #include #include "lib/bluetooth.h" #include "lib/mgmt.h" #include "src/shared/util.h" #include "src/shared/tester.h" #include "src/shared/mgmt.h" #include "src/shared/queue.h" #include "emulator/bthost.h" #include "monitor/bt.h" #include "tester-main.h" static char exec_dir[PATH_MAX + 1]; static gint scheduled_cbacks_num; #define EMULATOR_SIGNAL_TIMEOUT 2 /* in seconds */ #define EMULATOR_SIGNAL "emulator_started" #define BT_TRANSPORT_UNKNOWN 0x00 static struct { uint16_t cb_num; const char *str; } cb_table[] = { DBG_CB(CB_BT_NONE), DBG_CB(CB_BT_ADAPTER_STATE_CHANGED), DBG_CB(CB_BT_ADAPTER_PROPERTIES), DBG_CB(CB_BT_REMOTE_DEVICE_PROPERTIES), DBG_CB(CB_BT_DEVICE_FOUND), DBG_CB(CB_BT_DISCOVERY_STATE_CHANGED), DBG_CB(CB_BT_PIN_REQUEST), DBG_CB(CB_BT_SSP_REQUEST), DBG_CB(CB_BT_BOND_STATE_CHANGED), DBG_CB(CB_BT_ACL_STATE_CHANGED), DBG_CB(CB_BT_THREAD_EVT), DBG_CB(CB_BT_DUT_MODE_RECV), DBG_CB(CB_BT_LE_TEST_MODE), /* Hidhost cb */ DBG_CB(CB_HH_CONNECTION_STATE), DBG_CB(CB_HH_HID_INFO), DBG_CB(CB_HH_PROTOCOL_MODE), DBG_CB(CB_HH_IDLE_TIME), DBG_CB(CB_HH_GET_REPORT), DBG_CB(CB_HH_VIRTUAL_UNPLUG), /* PAN cb */ DBG_CB(CB_PAN_CONTROL_STATE), DBG_CB(CB_PAN_CONNECTION_STATE), /* HDP cb */ DBG_CB(CB_HDP_APP_REG_STATE), DBG_CB(CB_HDP_CHANNEL_STATE), /* A2DP cb */ DBG_CB(CB_A2DP_CONN_STATE), DBG_CB(CB_A2DP_AUDIO_STATE), /* AVRCP */ DBG_CB(CB_AVRCP_PLAY_STATUS_REQ), DBG_CB(CB_AVRCP_PLAY_STATUS_RSP), DBG_CB(CB_AVRCP_REG_NOTIF_REQ), DBG_CB(CB_AVRCP_REG_NOTIF_RSP), DBG_CB(CB_AVRCP_GET_ATTR_REQ), DBG_CB(CB_AVRCP_GET_ATTR_RSP), /* Gatt client */ DBG_CB(CB_GATTC_REGISTER_CLIENT), DBG_CB(CB_GATTC_SCAN_RESULT), DBG_CB(CB_GATTC_OPEN), DBG_CB(CB_GATTC_CLOSE), DBG_CB(CB_GATTC_SEARCH_COMPLETE), DBG_CB(CB_GATTC_SEARCH_RESULT), DBG_CB(CB_GATTC_GET_CHARACTERISTIC), DBG_CB(CB_GATTC_GET_DESCRIPTOR), DBG_CB(CB_GATTC_GET_INCLUDED_SERVICE), DBG_CB(CB_GATTC_REGISTER_FOR_NOTIFICATION), DBG_CB(CB_GATTC_NOTIFY), DBG_CB(CB_GATTC_READ_CHARACTERISTIC), DBG_CB(CB_GATTC_WRITE_CHARACTERISTIC), DBG_CB(CB_GATTC_READ_DESCRIPTOR), DBG_CB(CB_GATTC_WRITE_DESCRIPTOR), DBG_CB(CB_GATTC_EXECUTE_WRITE), DBG_CB(CB_GATTC_READ_REMOTE_RSSI), DBG_CB(CB_GATTC_LISTEN), /* Gatt server */ DBG_CB(CB_GATTS_REGISTER_SERVER), DBG_CB(CB_GATTS_CONNECTION), DBG_CB(CB_GATTS_SERVICE_ADDED), DBG_CB(CB_GATTS_INCLUDED_SERVICE_ADDED), DBG_CB(CB_GATTS_CHARACTERISTIC_ADDED), DBG_CB(CB_GATTS_DESCRIPTOR_ADDED), DBG_CB(CB_GATTS_SERVICE_STARTED), DBG_CB(CB_GATTS_SERVICE_STOPPED), DBG_CB(CB_GATTS_SERVICE_DELETED), DBG_CB(CB_GATTS_REQUEST_READ), DBG_CB(CB_GATTS_REQUEST_WRITE), DBG_CB(CB_GATTS_REQUEST_EXEC_WRITE), DBG_CB(CB_GATTS_RESPONSE_CONFIRMATION), DBG_CB(CB_GATTS_INDICATION_SEND), /* Map client */ DBG_CB(CB_MAP_CLIENT_REMOTE_MAS_INSTANCES), /* Emulator callbacks */ DBG_CB(CB_EMU_CONFIRM_SEND_DATA), DBG_CB(CB_EMU_ENCRYPTION_ENABLED), DBG_CB(CB_EMU_ENCRYPTION_DISABLED), DBG_CB(CB_EMU_CONNECTION_REJECTED), DBG_CB(CB_EMU_VALUE_INDICATION), DBG_CB(CB_EMU_VALUE_NOTIFICATION), DBG_CB(CB_EMU_READ_RESPONSE), DBG_CB(CB_EMU_WRITE_RESPONSE), DBG_CB(CB_EMU_ATT_ERROR), }; static gboolean check_callbacks_called(gpointer user_data) { /* * Wait for all callbacks scheduled in current test context to execute * in main loop. This will avoid late callback calls after test case has * already failed or timed out. */ if (g_atomic_int_get(&scheduled_cbacks_num) == 0) { tester_teardown_complete(); return FALSE; } else if (scheduled_cbacks_num < 0) { tester_warn("Unscheduled callback called!"); return FALSE; } return TRUE; } static void check_daemon_term(void) { int status; pid_t pid; struct test_data *data = tester_get_data(); if (!data) return; pid = waitpid(data->bluetoothd_pid, &status, WNOHANG); if (pid != data->bluetoothd_pid) return; data->bluetoothd_pid = 0; if (WIFEXITED(status) && (WEXITSTATUS(status) == EXIT_SUCCESS)) { g_idle_add(check_callbacks_called, NULL); return; } tester_warn("Unexpected Daemon shutdown with status %d", status); } static gboolean signal_handler(GIOChannel *channel, GIOCondition cond, gpointer user_data) { struct signalfd_siginfo si; ssize_t result; int fd; if (cond & (G_IO_NVAL | G_IO_ERR | G_IO_HUP)) return FALSE; fd = g_io_channel_unix_get_fd(channel); result = read(fd, &si, sizeof(si)); if (result != sizeof(si)) return FALSE; switch (si.ssi_signo) { case SIGCHLD: check_daemon_term(); break; } return TRUE; } static guint setup_signalfd(void) { GIOChannel *channel; guint source; sigset_t mask; int fd; sigemptyset(&mask); sigaddset(&mask, SIGCHLD); if (sigprocmask(SIG_BLOCK, &mask, NULL) < 0) return 0; fd = signalfd(-1, &mask, 0); if (fd < 0) return 0; channel = g_io_channel_unix_new(fd); g_io_channel_set_close_on_unref(channel, TRUE); g_io_channel_set_encoding(channel, NULL, NULL); g_io_channel_set_buffered(channel, FALSE); source = g_io_add_watch(channel, G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL, signal_handler, NULL); g_io_channel_unref(channel); return source; } static void test_post_teardown(const void *test_data) { struct test_data *data = tester_get_data(); /* remove hook for encryption change */ hciemu_del_hook(data->hciemu, HCIEMU_HOOK_POST_EVT, 0x08); hciemu_unref(data->hciemu); data->hciemu = NULL; g_source_remove(data->signalfd); data->signalfd = 0; } static void bluetoothd_start(int hci_index) { char prg_name[PATH_MAX + 1 + 11]; char index[8]; char *prg_argv[5]; snprintf(prg_name, sizeof(prg_name), "%s/%s", exec_dir, "bluetoothd"); snprintf(index, sizeof(index), "%d", hci_index); prg_argv[0] = prg_name; prg_argv[1] = "-i"; prg_argv[2] = index; prg_argv[3] = "-d"; prg_argv[4] = NULL; if (!tester_use_debug()) fclose(stderr); execve(prg_argv[0], prg_argv, NULL); } static void emulator(int pipe, int hci_index) { static const char SYSTEM_SOCKET_PATH[] = "\0android_system"; char buf[1024]; struct sockaddr_un addr; struct timeval tv; int fd; ssize_t len; fd = socket(PF_LOCAL, SOCK_DGRAM | SOCK_CLOEXEC, 0); if (fd < 0) goto failed; tv.tv_sec = EMULATOR_SIGNAL_TIMEOUT; tv.tv_usec = 0; setsockopt(fd, SOL_SOCKET, SO_RCVTIMEO, (char *)&tv, sizeof(tv)); memset(&addr, 0, sizeof(addr)); addr.sun_family = AF_UNIX; memcpy(addr.sun_path, SYSTEM_SOCKET_PATH, sizeof(SYSTEM_SOCKET_PATH)); if (bind(fd, (struct sockaddr *) &addr, sizeof(addr)) < 0) { perror("Failed to bind system socket"); goto failed; } len = write(pipe, EMULATOR_SIGNAL, sizeof(EMULATOR_SIGNAL)); if (len != sizeof(EMULATOR_SIGNAL)) goto failed; memset(buf, 0, sizeof(buf)); len = read(fd, buf, sizeof(buf)); if (len <= 0 || strcmp(buf, "bluetooth.start=daemon")) goto failed; close(pipe); close(fd); return bluetoothd_start(hci_index); failed: close(pipe); if (fd >= 0) close(fd); } static void mgmt_debug(const char *str, void *user_data) { const char *prefix = user_data; tester_print("%s%s", prefix, str); } static bool hciemu_post_encr_hook(const void *data, uint16_t len, void *user_data) { struct step *step; /* * Expected data: status (1 octet) + conn. handle (2 octets) + * encryption flag (1 octet) */ if (len < 4) return true; step = g_new0(struct step, 1); step->callback = ((uint8_t *)data)[3] ? CB_EMU_ENCRYPTION_ENABLED : CB_EMU_ENCRYPTION_DISABLED; schedule_callback_verification(step); return true; } static void read_info_callback(uint8_t status, uint16_t length, const void *param, void *user_data) { struct test_data *data = tester_get_data(); const struct mgmt_rp_read_info *rp = param; char addr[18]; uint16_t manufacturer; uint32_t supported_settings, current_settings; tester_print("Read Info callback"); tester_print(" Status: 0x%02x", status); if (status || !param) { tester_pre_setup_failed(); return; } ba2str(&rp->bdaddr, addr); manufacturer = btohs(rp->manufacturer); supported_settings = btohl(rp->supported_settings); current_settings = btohl(rp->current_settings); tester_print(" Address: %s", addr); tester_print(" Version: 0x%02x", rp->version); tester_print(" Manufacturer: 0x%04x", manufacturer); tester_print(" Supported settings: 0x%08x", supported_settings); tester_print(" Current settings: 0x%08x", current_settings); tester_print(" Class: 0x%02x%02x%02x", rp->dev_class[2], rp->dev_class[1], rp->dev_class[0]); tester_print(" Name: %s", rp->name); tester_print(" Short name: %s", rp->short_name); if (strcmp(hciemu_get_address(data->hciemu), addr)) { tester_pre_setup_failed(); return; } /* set hook for encryption change */ hciemu_add_hook(data->hciemu, HCIEMU_HOOK_POST_EVT, 0x08, hciemu_post_encr_hook, NULL); tester_pre_setup_complete(); } static void index_added_callback(uint16_t index, uint16_t length, const void *param, void *user_data) { struct test_data *data = tester_get_data(); tester_print("Index Added callback"); tester_print(" Index: 0x%04x", index); data->mgmt_index = index; mgmt_send(data->mgmt, MGMT_OP_READ_INFO, data->mgmt_index, 0, NULL, read_info_callback, NULL, NULL); } static void index_removed_callback(uint16_t index, uint16_t length, const void *param, void *user_data) { struct test_data *data = tester_get_data(); tester_print("Index Removed callback"); tester_print(" Index: 0x%04x", index); if (index != data->mgmt_index) return; mgmt_unregister_index(data->mgmt, data->mgmt_index); mgmt_unref(data->mgmt); data->mgmt = NULL; tester_post_teardown_complete(); } static void read_index_list_callback(uint8_t status, uint16_t length, const void *param, void *user_data) { struct test_data *data = tester_get_data(); tester_print("Read Index List callback"); tester_print(" Status: 0x%02x", status); if (status || !param) { tester_pre_setup_failed(); return; } mgmt_register(data->mgmt, MGMT_EV_INDEX_ADDED, MGMT_INDEX_NONE, index_added_callback, NULL, NULL); mgmt_register(data->mgmt, MGMT_EV_INDEX_REMOVED, MGMT_INDEX_NONE, index_removed_callback, NULL, NULL); data->hciemu = hciemu_new(data->hciemu_type); if (!data->hciemu) { tester_warn("Failed to setup HCI emulation"); tester_pre_setup_failed(); return; } tester_print("New hciemu instance created"); } static void test_pre_setup(const void *test_data) { struct test_data *data = tester_get_data(); data->signalfd = setup_signalfd(); if (!data->signalfd) { tester_warn("Failed to setup signalfd"); tester_pre_setup_failed(); return; } data->mgmt = mgmt_new_default(); if (!data->mgmt) { tester_warn("Failed to setup management interface"); tester_pre_setup_failed(); return; } if (!tester_use_debug()) fclose(stderr); else mgmt_set_debug(data->mgmt, mgmt_debug, "mgmt: ", NULL); mgmt_send(data->mgmt, MGMT_OP_READ_INDEX_LIST, MGMT_INDEX_NONE, 0, NULL, read_index_list_callback, NULL, NULL); } static bool match_property(bt_property_t *exp_prop, bt_property_t *rec_prop, int prop_num) { if (exp_prop->type && (exp_prop->type != rec_prop->type)) return 0; if (exp_prop->len && (exp_prop->len != rec_prop->len)) { tester_debug("Property [%d] len don't match! received=%d, " "expected=%d", prop_num, rec_prop->len, exp_prop->len); return 0; } if (exp_prop->val && memcmp(exp_prop->val, rec_prop->val, exp_prop->len)) { tester_debug("Property [%d] value don't match!", prop_num); return 0; } return 1; } static bool match_mas_inst(btmce_mas_instance_t *exp_inst, btmce_mas_instance_t *rec_inst, int inst_num) { if (exp_inst->id && (exp_inst->id != rec_inst->id)) { tester_debug("MAS inst. [%d] id missmatch %d vs %d", inst_num, rec_inst->id, exp_inst->id); return 0; } if (exp_inst->scn && (exp_inst->scn != rec_inst->scn)) { tester_debug("MAS inst. [%d] scn missmatch %d vs %d", inst_num, rec_inst->scn, exp_inst->scn); return 0; } if (exp_inst->msg_types && (exp_inst->msg_types != rec_inst->msg_types)) { tester_debug("Mas inst. [%d] mesg type missmatch %d vs %d", inst_num, rec_inst->scn, exp_inst->scn); return 0; } if (exp_inst->p_name && memcmp(exp_inst->p_name, rec_inst->p_name, strlen(exp_inst->p_name))) { tester_debug("Mas inst. [%d] name don't match!", inst_num); return 0; } return 1; } static int verify_property(bt_property_t *exp_props, int exp_num_props, bt_property_t *rec_props, int rec_num_props) { int i, j; int exp_prop_to_find = exp_num_props; if (rec_num_props == 0) return 1; if (exp_num_props == 0) { tester_debug("Wrong number of expected properties given"); tester_test_failed(); return 1; } /* Get first exp prop to match and search for it */ for (i = 0; i < exp_num_props; i++) { for (j = 0; j < rec_num_props; j++) { if (match_property(&exp_props[i], &rec_props[j], i)) { exp_prop_to_find--; break; } } } return exp_prop_to_find; } static int verify_mas_inst(btmce_mas_instance_t *exp_inst, int exp_num_inst, btmce_mas_instance_t *rec_inst, int rec_num_inst) { int i, j; int exp_inst_to_find = exp_num_inst; if (rec_num_inst == 0) return 1; if (exp_num_inst == 0) { tester_debug("Wrong number of expected MAS instances given"); tester_test_failed(); return 1; } for (i = 0; i < exp_num_inst; i++) { for (j = 0; j < rec_num_inst; j++) { if (match_mas_inst(&exp_inst[i], &rec_inst[i], i)) { exp_inst_to_find--; break; } } } return exp_inst_to_find; } /* * Check each test case step if test case expected * data is set and match it with expected result. */ static bool verify_gatt_ids(btgatt_gatt_id_t *a, btgatt_gatt_id_t *b) { if (memcmp(&a->uuid, &b->uuid, sizeof(bt_uuid_t))) return false; if (a->inst_id != b->inst_id) return false; return true; } static bool verify_services(btgatt_srvc_id_t *a, btgatt_srvc_id_t *b) { if (a->is_primary != b->is_primary) return false; return verify_gatt_ids(&a->id, &b->id); } static bool match_data(struct step *step) { struct test_data *data = tester_get_data(); const struct step *exp; exp = queue_peek_head(data->steps); if (!exp) { /* Can occure while test passed already */ tester_debug("Cannot get step to match"); return false; } if (exp->action_status != step->action_status) { tester_debug("Action status don't match"); return false; } if (!exp->callback && !step->callback) return true; if (exp->callback != step->callback) { tester_debug("Callback type mismatch: %s vs %s", cb_table[step->callback].str, cb_table[exp->callback].str); return false; } if (exp->callback_result.state != step->callback_result.state) { tester_debug("Callback state mismatch: %d vs %d", step->callback_result.state, exp->callback_result.state); return false; } if (exp->callback_result.status != step->callback_result.status) { tester_debug("Callback status mismatch: %d vs %d", step->callback_result.status, exp->callback_result.status); return false; } if (exp->callback_result.mode != step->callback_result.mode) { tester_debug("Callback mode mismatch: %02x vs %02x", step->callback_result.mode, exp->callback_result.mode); return false; } if (exp->callback_result.report_size != step->callback_result.report_size) { tester_debug("Callback report size mismatch: %d vs %d", step->callback_result.report_size, exp->callback_result.report_size); return false; } if (exp->callback_result.ctrl_state != step->callback_result.ctrl_state) { tester_debug("Callback ctrl state mismatch: %d vs %d", step->callback_result.ctrl_state, exp->callback_result.ctrl_state); return false; } if (exp->callback_result.conn_state != step->callback_result.conn_state) { tester_debug("Callback connection state mismatch: %d vs %d", step->callback_result.conn_state, exp->callback_result.conn_state); return false; } if (exp->callback_result.local_role != step->callback_result.local_role) { tester_debug("Callback local_role mismatch: %d vs %d", step->callback_result.local_role, exp->callback_result.local_role); return false; } if (exp->callback_result.remote_role != step->callback_result.remote_role) { tester_debug("Callback remote_role mismatch: %d vs %d", step->callback_result.remote_role, exp->callback_result.remote_role); return false; } if (exp->callback_result.app_id != step->callback_result.app_id) { tester_debug("Callback app_id mismatch: %d vs %d", step->callback_result.app_id, exp->callback_result.app_id); return false; } if (exp->callback_result.channel_id != step->callback_result.channel_id) { tester_debug("Callback channel_id mismatch: %d vs %d", step->callback_result.channel_id, exp->callback_result.channel_id); return false; } if (exp->callback_result.mdep_cfg_index != step->callback_result.mdep_cfg_index) { tester_debug("Callback mdep_cfg_index mismatch: %d vs %d", step->callback_result.mdep_cfg_index, exp->callback_result.mdep_cfg_index); return false; } if (exp->callback_result.app_state != step->callback_result.app_state) { tester_debug("Callback app_state mismatch: %d vs %d", step->callback_result.app_state, exp->callback_result.app_state); return false; } if (exp->callback_result.channel_state != step->callback_result.channel_state) { tester_debug("Callback channel_state mismatch: %d vs %d", step->callback_result.channel_state, exp->callback_result.channel_state); return false; } if (exp->callback_result.av_conn_state != step->callback_result.av_conn_state) { tester_debug("Callback av conn state mismatch: 0x%x vs 0x%x", step->callback_result.av_conn_state, exp->callback_result.av_conn_state); return false; } if (exp->callback_result.av_audio_state != step->callback_result.av_audio_state) { tester_debug("Callback av audio state mismatch: 0x%x vs 0x%x", step->callback_result.av_audio_state, exp->callback_result.av_audio_state); return false; } if (exp->callback_result.song_length != step->callback_result.song_length) { tester_debug("Callback song_length mismatch: 0x%x vs 0x%x", step->callback_result.song_length, exp->callback_result.song_length); return false; } if (exp->callback_result.song_position != step->callback_result.song_position) { tester_debug("Callback song_position mismatch: 0x%x vs 0x%x", step->callback_result.song_position, exp->callback_result.song_position); return false; } if (exp->callback_result.play_status != step->callback_result.play_status) { tester_debug("Callback play_status mismatch: 0x%x vs 0x%x", step->callback_result.play_status, exp->callback_result.play_status); return false; } if (exp->callback_result.rc_index != step->callback_result.rc_index) { tester_debug("Callback rc_index mismatch"); return false; } if (exp->callback_result.num_of_attrs != step->callback_result.num_of_attrs) { tester_debug("Callback rc num of attrs mismatch"); return false; } if (exp->callback_result.attrs) { if (memcmp(step->callback_result.attrs, exp->callback_result.attrs, exp->callback_result.num_of_attrs * sizeof(btrc_element_attr_val_t))) { tester_debug("Callback rc element attributes doesn't match"); return false; } } if (exp->callback_result.pairing_variant != step->callback_result.pairing_variant) { tester_debug("Callback pairing result mismatch: %d vs %d", step->callback_result.pairing_variant, exp->callback_result.pairing_variant); return false; } if (exp->callback_result.adv_data != step->callback_result.adv_data) { tester_debug("Callback adv. data status mismatch: %d vs %d", step->callback_result.adv_data, exp->callback_result.adv_data); return false; } if (exp->callback_result.conn_id != step->callback_result.conn_id) { tester_debug("Callback conn_id mismatch: %d vs %d", step->callback_result.conn_id, exp->callback_result.conn_id); return false; } if (exp->callback_result.gatt_app_id != step->callback_result.gatt_app_id) { tester_debug("Callback gatt_app_id mismatch: %d vs %d", step->callback_result.gatt_app_id, exp->callback_result.gatt_app_id); return false; } if (exp->callback_result.properties && verify_property(exp->callback_result.properties, exp->callback_result.num_properties, step->callback_result.properties, step->callback_result.num_properties)) { tester_debug("Gatt properties don't match"); return false; } if (exp->callback_result.service && !verify_services(step->callback_result.service, exp->callback_result.service)) { tester_debug("Gatt service doesn't match"); return false; } if (exp->callback_result.characteristic) { btgatt_gatt_id_t *a; btgatt_gatt_id_t *b; a = step->callback_result.characteristic; b = exp->callback_result.characteristic; if (!verify_gatt_ids(a, b)) { tester_debug("Gatt char doesn't match"); return false; } } if (exp->callback_result.char_prop != step->callback_result.char_prop) { tester_debug("Gatt char prop mismatch: %d vs %d", step->callback_result.char_prop, exp->callback_result.char_prop); return false; } if (exp->callback_result.descriptor) { btgatt_gatt_id_t *a; btgatt_gatt_id_t *b; a = step->callback_result.descriptor; b = exp->callback_result.descriptor; if (!verify_gatt_ids(a, b)) { tester_debug("Gatt desc doesn't match"); return false; } } if (exp->callback_result.included) { if (!verify_services(step->callback_result.included, exp->callback_result.included)) { tester_debug("Gatt include srvc doesn't match"); return false; } } if (exp->callback_result.read_params) { if (memcmp(step->callback_result.read_params, exp->callback_result.read_params, sizeof(btgatt_read_params_t))) { tester_debug("Gatt read_param doesn't match"); return false; } } if (exp->callback_result.write_params) { if (memcmp(step->callback_result.write_params, exp->callback_result.write_params, sizeof(btgatt_write_params_t))) { tester_debug("Gatt write_param doesn't match"); return false; } if (exp->callback_result.notification_registered != step->callback_result.notification_registered) { tester_debug("Gatt registered flag mismatch"); return false; } if (exp->callback_result.notify_params) { if (memcmp(step->callback_result.notify_params, exp->callback_result.notify_params, sizeof(btgatt_notify_params_t))) { tester_debug("Gatt notify_param doesn't match"); return false; } } } if (exp->callback_result.connected != step->callback_result.connected) { tester_debug("Gatt server conn status mismatch: %d vs %d", step->callback_result.connected, exp->callback_result.connected); return false; } if (exp->callback_result.attr_handle && step->callback_result.attr_handle) if (*exp->callback_result.attr_handle != *step->callback_result.attr_handle) { tester_debug("Gatt attribute handle mismatch: %d vs %d", *step->callback_result.attr_handle, *exp->callback_result.attr_handle); return false; } if (exp->callback_result.srvc_handle && step->callback_result.srvc_handle) if (*exp->callback_result.srvc_handle != *step->callback_result.srvc_handle) { tester_debug("Gatt service handle mismatch: %d vs %d", *step->callback_result.srvc_handle, *exp->callback_result.srvc_handle); return false; } if (exp->callback_result.inc_srvc_handle && step->callback_result.inc_srvc_handle) if (*exp->callback_result.inc_srvc_handle != *step->callback_result.inc_srvc_handle) { tester_debug("Gatt inc. srvc handle mismatch: %d vs %d", *step->callback_result.inc_srvc_handle, *exp->callback_result.inc_srvc_handle); return false; } if (exp->callback_result.uuid && step->callback_result.uuid) if (memcmp(exp->callback_result.uuid, step->callback_result.uuid, sizeof(*exp->callback_result.uuid))) { tester_debug("Uuid mismatch"); return false; } if (exp->callback_result.trans_id != step->callback_result.trans_id) { tester_debug("Gatt trans id mismatch: %d vs %d", exp->callback_result.trans_id, step->callback_result.trans_id); return false; } if (exp->callback_result.offset != step->callback_result.offset) { tester_debug("Gatt offset mismatch: %d vs %d", exp->callback_result.offset, step->callback_result.offset); return false; } if (exp->callback_result.is_long != step->callback_result.is_long) { tester_debug("Gatt is long attr value flag mismatch: %d vs %d", exp->callback_result.is_long, step->callback_result.is_long); return false; } if (exp->callback_result.length > 0) { if (exp->callback_result.length != step->callback_result.length) { tester_debug("Gatt attr length mismatch: %d vs %d", exp->callback_result.length, step->callback_result.length); return false; } if (!exp->callback_result.value || !step->callback_result.value) { tester_debug("Gatt attr values are wrong set"); return false; } if (!memcmp(exp->callback_result.value, step->callback_result.value, exp->callback_result.length)) { tester_debug("Gatt attr value mismatch"); return false; } } if (exp->callback_result.need_rsp != step->callback_result.need_rsp) { tester_debug("Gatt need response value flag mismatch: %d vs %d", exp->callback_result.need_rsp, step->callback_result.need_rsp); return false; } if (exp->callback_result.is_prep != step->callback_result.is_prep) { tester_debug("Gatt is prepared value flag mismatch: %d vs %d", exp->callback_result.is_prep, step->callback_result.is_prep); return false; } if (exp->callback_result.num_mas_instances != step->callback_result.num_mas_instances) { tester_debug("Mas instance count mismatch: %d vs %d", exp->callback_result.num_mas_instances, step->callback_result.num_mas_instances); return false; } if (exp->callback_result.mas_instances && verify_mas_inst(exp->callback_result.mas_instances, exp->callback_result.num_mas_instances, step->callback_result.mas_instances, step->callback_result.num_mas_instances)) { tester_debug("Mas instances don't match"); return false; } if (exp->callback_result.error != step->callback_result.error) { tester_debug("Err mismatch: %d vs %d", exp->callback_result.error, step->callback_result.error); return false; } if (exp->store_srvc_handle) memcpy(exp->store_srvc_handle, step->callback_result.srvc_handle, sizeof(*exp->store_srvc_handle)); if (exp->store_char_handle) memcpy(exp->store_char_handle, step->callback_result.char_handle, sizeof(*exp->store_char_handle)); return true; } static void init_test_steps(struct test_data *data) { const struct test_case *test_steps = data->test_data; int i = 0; for (i = 0; i < test_steps->step_num; i++) queue_push_tail(data->steps, (void *) &(test_steps->step[i])); tester_print("tester: Number of test steps=%d", queue_length(data->steps)); } /* * Each test case step should be verified, if match with * expected result tester should go to next test step. */ static void verify_step(struct step *step, queue_destroy_func_t cleanup_cb) { struct test_data *data = tester_get_data(); const struct test_case *test_steps = data->test_data; struct step *next_step; tester_debug("tester: STEP[%d] check", test_steps->step_num-queue_length(data->steps) + 1); if (step && !match_data(step)) { if (cleanup_cb) cleanup_cb(step); return; } queue_pop_head(data->steps); if (cleanup_cb) cleanup_cb(step); tester_debug("tester: STEP[%d] pass", test_steps->step_num-queue_length(data->steps)); if (queue_isempty(data->steps)) { tester_print("tester: All steps done, passing"); tester_test_passed(); return; } /* goto next step action if declared in step */ next_step = queue_peek_head(data->steps); if (next_step->action) next_step->action(); } /* * NOTICE: * Its mandatory for callback to set proper step.callback value so that * step verification could pass and move to next test step */ static void free_properties(struct step *step) { bt_property_t *properties = step->callback_result.properties; int num_properties = step->callback_result.num_properties; int i; for (i = 0; i < num_properties; i++) g_free(properties[i].val); g_free(properties); } static void free_mas_instances(struct step *step) { btmce_mas_instance_t *mas_instances; int num_instances; int i; mas_instances = step->callback_result.mas_instances; num_instances = step->callback_result.num_mas_instances; for (i = 0; i < num_instances; i++) g_free(mas_instances[i].p_name); g_free(mas_instances); } static void destroy_callback_step(void *data) { struct step *step = data; if (step->callback_result.properties) free_properties(step); if (step->callback_result.service) free(step->callback_result.service); if (step->callback_result.characteristic) free(step->callback_result.characteristic); if (step->callback_result.descriptor) free(step->callback_result.descriptor); if (step->callback_result.included) free(step->callback_result.included); if (step->callback_result.read_params) free(step->callback_result.read_params); if (step->callback_result.write_params) free(step->callback_result.write_params); if (step->callback_result.notify_params) free(step->callback_result.notify_params); if (step->callback_result.srvc_handle) free(step->callback_result.srvc_handle); if (step->callback_result.inc_srvc_handle) free(step->callback_result.inc_srvc_handle); if (step->callback_result.uuid) free(step->callback_result.uuid); if (step->callback_result.char_handle) free(step->callback_result.char_handle); if (step->callback_result.desc_handle) free(step->callback_result.desc_handle); if (step->callback_result.attr_handle) free(step->callback_result.attr_handle); if (step->callback_result.value) free(step->callback_result.value); if (step->callback_result.mas_instances) free_mas_instances(step); g_free(step); g_atomic_int_dec_and_test(&scheduled_cbacks_num); } static gboolean verify_action(gpointer user_data) { struct step *step = user_data; verify_step(step, g_free); return FALSE; } static gboolean verify_callback(gpointer user_data) { struct test_data *data = tester_get_data(); struct step *step = user_data; /* Return if callback came when all steps are already verified */ if (queue_isempty(data->steps)) { destroy_callback_step(step); return FALSE; } /* * TODO: This may call action from next step before callback data * from previous step was freed. */ verify_step(step, destroy_callback_step); return FALSE; } void schedule_callback_verification(struct step *step) { g_atomic_int_inc(&scheduled_cbacks_num); g_idle_add(verify_callback, step); } void schedule_action_verification(struct step *step) { g_idle_add_full(G_PRIORITY_HIGH_IDLE, verify_action, step, NULL); } static void adapter_state_changed_cb(bt_state_t state) { struct step *step = g_new0(struct step, 1); step->callback_result.state = state; step->callback = CB_BT_ADAPTER_STATE_CHANGED; schedule_callback_verification(step); } static bt_property_t *copy_properties(int num_properties, bt_property_t *properties) { int i; bt_property_t *props = g_new0(bt_property_t, num_properties); for (i = 0; i < num_properties; i++) { props[i].type = properties[i].type; props[i].len = properties[i].len; props[i].val = util_memdup(properties[i].val, properties[i].len); } return props; } static bt_property_t *repack_properties(int num_properties, bt_property_t **properties) { int i; bt_property_t *props = g_new0(bt_property_t, num_properties); for (i = 0; i < num_properties; i++) { props[i].type = properties[i]->type; props[i].len = properties[i]->len; props[i].val = util_memdup(properties[i]->val, properties[i]->len); } return props; } static bt_property_t *create_property(bt_property_type_t type, void *val, int len) { bt_property_t *prop = g_new0(bt_property_t, 1); prop->type = type; prop->len = len; prop->val = util_memdup(val, len); return prop; } static void adapter_properties_cb(bt_status_t status, int num_properties, bt_property_t *properties) { struct step *step = g_new0(struct step, 1); step->callback_result.status = status; step->callback_result.num_properties = num_properties; step->callback_result.properties = copy_properties(num_properties, properties); step->callback = CB_BT_ADAPTER_PROPERTIES; schedule_callback_verification(step); } static void discovery_state_changed_cb(bt_discovery_state_t state) { struct step *step = g_new0(struct step, 1); step->callback = CB_BT_DISCOVERY_STATE_CHANGED; step->callback_result.state = state; schedule_callback_verification(step); } static void device_found_cb(int num_properties, bt_property_t *properties) { struct step *step = g_new0(struct step, 1); step->callback_result.num_properties = num_properties; step->callback_result.properties = copy_properties(num_properties, properties); step->callback = CB_BT_DEVICE_FOUND; schedule_callback_verification(step); } static void remote_device_properties_cb(bt_status_t status, bt_bdaddr_t *bd_addr, int num_properties, bt_property_t *properties) { struct step *step = g_new0(struct step, 1); step->callback_result.num_properties = num_properties; step->callback_result.properties = copy_properties(num_properties, properties); step->callback = CB_BT_REMOTE_DEVICE_PROPERTIES; schedule_callback_verification(step); } static void bond_state_changed_cb(bt_status_t status, bt_bdaddr_t *remote_bd_addr, bt_bond_state_t state) { struct step *step = g_new0(struct step, 1); step->callback_result.status = status; step->callback_result.state = state; /* Utilize property verification mechanism for bdaddr */ step->callback_result.num_properties = 1; step->callback_result.properties = create_property(BT_PROPERTY_BDADDR, remote_bd_addr, sizeof(*remote_bd_addr)); step->callback = CB_BT_BOND_STATE_CHANGED; schedule_callback_verification(step); } static void pin_request_cb(bt_bdaddr_t *remote_bd_addr, bt_bdname_t *bd_name, uint32_t cod) { struct step *step = g_new0(struct step, 1); bt_property_t *props[3]; step->callback = CB_BT_PIN_REQUEST; /* Utilize property verification mechanism for those */ props[0] = create_property(BT_PROPERTY_BDADDR, remote_bd_addr, sizeof(*remote_bd_addr)); props[1] = create_property(BT_PROPERTY_BDNAME, bd_name->name, strlen((char *) bd_name->name)); props[2] = create_property(BT_PROPERTY_CLASS_OF_DEVICE, &cod, sizeof(cod)); step->callback_result.num_properties = 3; step->callback_result.properties = repack_properties(3, props); g_free(props[0]->val); g_free(props[0]); g_free(props[1]->val); g_free(props[1]); g_free(props[2]->val); g_free(props[2]); schedule_callback_verification(step); } static void ssp_request_cb(bt_bdaddr_t *remote_bd_addr, bt_bdname_t *bd_name, uint32_t cod, bt_ssp_variant_t pairing_variant, uint32_t pass_key) { struct step *step = g_new0(struct step, 1); bt_property_t *props[3]; step->callback = CB_BT_SSP_REQUEST; /* Utilize property verification mechanism for those */ props[0] = create_property(BT_PROPERTY_BDADDR, remote_bd_addr, sizeof(*remote_bd_addr)); props[1] = create_property(BT_PROPERTY_BDNAME, bd_name->name, strlen((char *) bd_name->name)); props[2] = create_property(BT_PROPERTY_CLASS_OF_DEVICE, &cod, sizeof(cod)); step->callback_result.num_properties = 3; step->callback_result.properties = repack_properties(3, props); g_free(props[0]->val); g_free(props[0]); g_free(props[1]->val); g_free(props[1]); g_free(props[2]->val); g_free(props[2]); schedule_callback_verification(step); } static void acl_state_changed_cb(bt_status_t status, bt_bdaddr_t *remote_bd_addr, bt_acl_state_t state) { struct step *step = g_new0(struct step, 1); step->callback = CB_BT_ACL_STATE_CHANGED; step->callback_result.status = status; step->callback_result.state = state; schedule_callback_verification(step); } static bt_callbacks_t bt_callbacks = { .size = sizeof(bt_callbacks), .adapter_state_changed_cb = adapter_state_changed_cb, .adapter_properties_cb = adapter_properties_cb, .remote_device_properties_cb = remote_device_properties_cb, .device_found_cb = device_found_cb, .discovery_state_changed_cb = discovery_state_changed_cb, .pin_request_cb = pin_request_cb, .ssp_request_cb = ssp_request_cb, .bond_state_changed_cb = bond_state_changed_cb, .acl_state_changed_cb = acl_state_changed_cb, .thread_evt_cb = NULL, .dut_mode_recv_cb = NULL, .le_test_mode_cb = NULL, .energy_info_cb = NULL, }; static void hidhost_connection_state_cb(bt_bdaddr_t *bd_addr, bthh_connection_state_t state) { struct step *step = g_new0(struct step, 1); step->callback = CB_HH_CONNECTION_STATE; step->callback_result.state = state; schedule_callback_verification(step); } static void hidhost_virtual_unplug_cb(bt_bdaddr_t *bd_addr, bthh_status_t status) { struct step *step = g_new0(struct step, 1); step->callback = CB_HH_VIRTUAL_UNPLUG; step->callback_result.status = status; schedule_callback_verification(step); } static void hidhost_protocol_mode_cb(bt_bdaddr_t *bd_addr, bthh_status_t status, bthh_protocol_mode_t mode) { struct step *step = g_new0(struct step, 1); step->callback = CB_HH_PROTOCOL_MODE; step->callback_result.status = status; step->callback_result.mode = mode; /* TODO: add bdaddr to verify? */ schedule_callback_verification(step); } static void hidhost_hid_info_cb(bt_bdaddr_t *bd_addr, bthh_hid_info_t hid) { struct step *step = g_new0(struct step, 1); step->callback = CB_HH_HID_INFO; schedule_callback_verification(step); } static void hidhost_get_report_cb(bt_bdaddr_t *bd_addr, bthh_status_t status, uint8_t *report, int size) { struct step *step = g_new0(struct step, 1); step->callback = CB_HH_GET_REPORT; step->callback_result.status = status; step->callback_result.report_size = size; schedule_callback_verification(step); } static bthh_callbacks_t bthh_callbacks = { .size = sizeof(bthh_callbacks), .connection_state_cb = hidhost_connection_state_cb, .hid_info_cb = hidhost_hid_info_cb, .protocol_mode_cb = hidhost_protocol_mode_cb, .idle_time_cb = NULL, .get_report_cb = hidhost_get_report_cb, .virtual_unplug_cb = hidhost_virtual_unplug_cb, .handshake_cb = NULL, }; static void gattc_register_client_cb(int status, int client_if, bt_uuid_t *app_uuid) { struct step *step = g_new0(struct step, 1); step->callback = CB_GATTC_REGISTER_CLIENT; step->callback_result.status = status; schedule_callback_verification(step); } static void gattc_scan_result_cb(bt_bdaddr_t *bda, int rssi, uint8_t *adv_data) { struct step *step = g_new0(struct step, 1); bt_property_t *props[2]; step->callback = CB_GATTC_SCAN_RESULT; step->callback_result.adv_data = adv_data ? TRUE : FALSE; /* Utilize property verification mechanism for those */ props[0] = create_property(BT_PROPERTY_BDADDR, bda, sizeof(*bda)); props[1] = create_property(BT_PROPERTY_REMOTE_RSSI, &rssi, sizeof(rssi)); step->callback_result.num_properties = 2; step->callback_result.properties = repack_properties(2, props); g_free(props[0]->val); g_free(props[0]); g_free(props[1]->val); g_free(props[1]); schedule_callback_verification(step); } static void gattc_connect_cb(int conn_id, int status, int client_if, bt_bdaddr_t *bda) { struct step *step = g_new0(struct step, 1); bt_property_t *props[1]; step->callback = CB_GATTC_OPEN; step->callback_result.status = status; step->callback_result.conn_id = conn_id; step->callback_result.gatt_app_id = client_if; /* Utilize property verification mechanism for bdaddr */ props[0] = create_property(BT_PROPERTY_BDADDR, bda, sizeof(*bda)); step->callback_result.num_properties = 1; step->callback_result.properties = repack_properties(1, props); g_free(props[0]->val); g_free(props[0]); schedule_callback_verification(step); } static void gattc_disconnect_cb(int conn_id, int status, int client_if, bt_bdaddr_t *bda) { struct step *step = g_new0(struct step, 1); bt_property_t *props[1]; step->callback = CB_GATTC_CLOSE; step->callback_result.status = status; step->callback_result.conn_id = conn_id; step->callback_result.gatt_app_id = client_if; /* Utilize property verification mechanism for bdaddr */ props[0] = create_property(BT_PROPERTY_BDADDR, bda, sizeof(*bda)); step->callback_result.num_properties = 1; step->callback_result.properties = repack_properties(1, props); g_free(props[0]->val); g_free(props[0]); schedule_callback_verification(step); } static void gattc_listen_cb(int status, int server_if) { struct step *step = g_new0(struct step, 1); step->callback = CB_GATTC_LISTEN; step->callback_result.status = status; schedule_callback_verification(step); } static void gattc_search_result_cb(int conn_id, btgatt_srvc_id_t *srvc_id) { struct step *step = g_new0(struct step, 1); step->callback = CB_GATTC_SEARCH_RESULT; step->callback_result.conn_id = conn_id; step->callback_result.service = util_memdup(srvc_id, sizeof(*srvc_id)); schedule_callback_verification(step); } static void gattc_search_complete_cb(int conn_id, int status) { struct step *step = g_new0(struct step, 1); step->callback = CB_GATTC_SEARCH_COMPLETE; step->callback_result.conn_id = conn_id; schedule_callback_verification(step); } static void gattc_get_characteristic_cb(int conn_id, int status, btgatt_srvc_id_t *srvc_id, btgatt_gatt_id_t *char_id, int char_prop) { struct step *step = g_new0(struct step, 1); step->callback = CB_GATTC_GET_CHARACTERISTIC; step->callback_result.status = status; step->callback_result.conn_id = conn_id; step->callback_result.service = util_memdup(srvc_id, sizeof(*srvc_id)); step->callback_result.characteristic = util_memdup(char_id, sizeof(*char_id)); step->callback_result.char_prop = char_prop; schedule_callback_verification(step); } static void gattc_get_descriptor_cb(int conn_id, int status, btgatt_srvc_id_t *srvc_id, btgatt_gatt_id_t *char_id, btgatt_gatt_id_t *descr_id) { struct step *step = g_new0(struct step, 1); step->callback = CB_GATTC_GET_DESCRIPTOR; step->callback_result.status = status; step->callback_result.conn_id = conn_id; step->callback_result.service = util_memdup(srvc_id, sizeof(*srvc_id)); step->callback_result.characteristic = util_memdup(char_id, sizeof(*char_id)); step->callback_result.descriptor = util_memdup(descr_id, sizeof(*descr_id)); schedule_callback_verification(step); } static void gattc_get_included_service_cb(int conn_id, int status, btgatt_srvc_id_t *srvc_id, btgatt_srvc_id_t *incl_srvc_id) { struct step *step = g_new0(struct step, 1); step->callback = CB_GATTC_GET_INCLUDED_SERVICE; step->callback_result.status = status; step->callback_result.conn_id = conn_id; step->callback_result.service = util_memdup(srvc_id, sizeof(*srvc_id)); step->callback_result.included = util_memdup(incl_srvc_id, sizeof(*srvc_id)); schedule_callback_verification(step); } static void gattc_read_characteristic_cb(int conn_id, int status, btgatt_read_params_t *p_data) { struct step *step = g_new0(struct step, 1); step->callback = CB_GATTC_READ_CHARACTERISTIC; step->callback_result.status = status; step->callback_result.conn_id = conn_id; step->callback_result.read_params = util_memdup(p_data, sizeof(*p_data)); schedule_callback_verification(step); } static void gattc_read_descriptor_cb(int conn_id, int status, btgatt_read_params_t *p_data) { struct step *step = g_new0(struct step, 1); step->callback = CB_GATTC_READ_DESCRIPTOR; step->callback_result.status = status; step->callback_result.conn_id = conn_id; step->callback_result.read_params = util_memdup(p_data, sizeof(*p_data)); schedule_callback_verification(step); } static void gattc_write_characteristic_cb(int conn_id, int status, btgatt_write_params_t *p_data) { struct step *step = g_new0(struct step, 1); step->callback = CB_GATTC_WRITE_CHARACTERISTIC; step->callback_result.status = status; step->callback_result.conn_id = conn_id; step->callback_result.write_params = util_memdup(p_data, sizeof(*p_data)); schedule_callback_verification(step); } static void gattc_write_descriptor_cb(int conn_id, int status, btgatt_write_params_t *p_data) { struct step *step = g_new0(struct step, 1); step->callback = CB_GATTC_WRITE_DESCRIPTOR; step->callback_result.status = status; step->callback_result.conn_id = conn_id; step->callback_result.write_params = util_memdup(p_data, sizeof(*p_data)); schedule_callback_verification(step); } static void gattc_register_for_notification_cb(int conn_id, int registered, int status, btgatt_srvc_id_t *srvc_id, btgatt_gatt_id_t *char_id) { struct step *step = g_new0(struct step, 1); step->callback = CB_GATTC_REGISTER_FOR_NOTIFICATION; step->callback_result.status = status; step->callback_result.conn_id = conn_id; step->callback_result.service = util_memdup(srvc_id, sizeof(*srvc_id)); step->callback_result.characteristic = util_memdup(char_id, sizeof(*char_id)); step->callback_result.notification_registered = registered; schedule_callback_verification(step); } static void gattc_notif_cb(int conn_id, btgatt_notify_params_t *p_data) { struct step *step = g_new0(struct step, 1); step->callback = CB_GATTC_NOTIFY; step->callback_result.conn_id = conn_id; step->callback_result.notify_params = util_memdup(p_data, sizeof(*p_data)); schedule_callback_verification(step); } static const btgatt_client_callbacks_t btgatt_client_callbacks = { .register_client_cb = gattc_register_client_cb, .scan_result_cb = gattc_scan_result_cb, .open_cb = gattc_connect_cb, .close_cb = gattc_disconnect_cb, .search_complete_cb = gattc_search_complete_cb, .search_result_cb = gattc_search_result_cb, .get_characteristic_cb = gattc_get_characteristic_cb, .get_descriptor_cb = gattc_get_descriptor_cb, .get_included_service_cb = gattc_get_included_service_cb, .register_for_notification_cb = gattc_register_for_notification_cb, .notify_cb = gattc_notif_cb, .read_characteristic_cb = gattc_read_characteristic_cb, .write_characteristic_cb = gattc_write_characteristic_cb, .read_descriptor_cb = gattc_read_descriptor_cb, .write_descriptor_cb = gattc_write_descriptor_cb, .execute_write_cb = NULL, .read_remote_rssi_cb = NULL, .listen_cb = gattc_listen_cb }; static void gatts_register_server_cb(int status, int server_if, bt_uuid_t *app_uuid) { struct step *step = g_new0(struct step, 1); step->callback = CB_GATTS_REGISTER_SERVER; step->callback_result.status = status; schedule_callback_verification(step); } static void gatts_connection_cb(int conn_id, int server_if, int connected, bt_bdaddr_t *bda) { struct step *step = g_new0(struct step, 1); bt_property_t *props[1]; step->callback = CB_GATTS_CONNECTION; step->callback_result.conn_id = conn_id; step->callback_result.gatt_app_id = server_if; step->callback_result.connected = connected; /* Utilize property verification mechanism for bdaddr */ props[0] = create_property(BT_PROPERTY_BDADDR, bda, sizeof(*bda)); step->callback_result.num_properties = 1; step->callback_result.properties = repack_properties(1, props); g_free(props[0]->val); g_free(props[0]); schedule_callback_verification(step); } static void gatts_service_added_cb(int status, int server_if, btgatt_srvc_id_t *srvc_id, int srvc_handle) { struct step *step = g_new0(struct step, 1); step->callback = CB_GATTS_SERVICE_ADDED; step->callback_result.status = status; step->callback_result.gatt_app_id = server_if; step->callback_result.service = util_memdup(srvc_id, sizeof(*srvc_id)); step->callback_result.srvc_handle = util_memdup(&srvc_handle, sizeof(srvc_handle)); schedule_callback_verification(step); } static void gatts_included_service_added_cb(int status, int server_if, int srvc_handle, int inc_srvc_handle) { struct step *step = g_new0(struct step, 1); step->callback = CB_GATTS_INCLUDED_SERVICE_ADDED; step->callback_result.status = status; step->callback_result.gatt_app_id = server_if; step->callback_result.srvc_handle = util_memdup(&srvc_handle, sizeof(srvc_handle)); step->callback_result.inc_srvc_handle = util_memdup(&inc_srvc_handle, sizeof(inc_srvc_handle)); schedule_callback_verification(step); } static void gatts_characteristic_added_cb(int status, int server_if, bt_uuid_t *uuid, int srvc_handle, int char_handle) { struct step *step = g_new0(struct step, 1); step->callback = CB_GATTS_CHARACTERISTIC_ADDED; step->callback_result.status = status; step->callback_result.gatt_app_id = server_if; step->callback_result.srvc_handle = util_memdup(&srvc_handle, sizeof(srvc_handle)); step->callback_result.uuid = util_memdup(uuid, sizeof(*uuid)); step->callback_result.char_handle = util_memdup(&char_handle, sizeof(char_handle)); schedule_callback_verification(step); } static void gatts_descriptor_added_cb(int status, int server_if, bt_uuid_t *uuid, int srvc_handle, int desc_handle) { struct step *step = g_new0(struct step, 1); step->callback = CB_GATTS_DESCRIPTOR_ADDED; step->callback_result.status = status; step->callback_result.gatt_app_id = server_if; step->callback_result.srvc_handle = util_memdup(&srvc_handle, sizeof(srvc_handle)); step->callback_result.uuid = util_memdup(uuid, sizeof(*uuid)); step->callback_result.desc_handle = util_memdup(&desc_handle, sizeof(desc_handle)); schedule_callback_verification(step); } static void gatts_service_started_cb(int status, int server_if, int srvc_handle) { struct step *step = g_new0(struct step, 1); step->callback = CB_GATTS_SERVICE_STARTED; step->callback_result.status = status; step->callback_result.gatt_app_id = server_if; step->callback_result.srvc_handle = util_memdup(&srvc_handle, sizeof(srvc_handle)); schedule_callback_verification(step); } static void gatts_service_stopped_cb(int status, int server_if, int srvc_handle) { struct step *step = g_new0(struct step, 1); step->callback = CB_GATTS_SERVICE_STOPPED; step->callback_result.status = status; step->callback_result.gatt_app_id = server_if; step->callback_result.srvc_handle = util_memdup(&srvc_handle, sizeof(srvc_handle)); schedule_callback_verification(step); } static void gatts_service_deleted_cb(int status, int server_if, int srvc_handle) { struct step *step = g_new0(struct step, 1); step->callback = CB_GATTS_SERVICE_DELETED; step->callback_result.status = status; step->callback_result.gatt_app_id = server_if; step->callback_result.srvc_handle = util_memdup(&srvc_handle, sizeof(srvc_handle)); schedule_callback_verification(step); } static void gatts_request_read_cb(int conn_id, int trans_id, bt_bdaddr_t *bda, int attr_handle, int offset, bool is_long) { struct step *step = g_new0(struct step, 1); bt_property_t *props[1]; step->callback = CB_GATTS_REQUEST_READ; step->callback_result.conn_id = conn_id; step->callback_result.trans_id = trans_id; step->callback_result.attr_handle = util_memdup(&attr_handle, sizeof(attr_handle)); step->callback_result.offset = offset; step->callback_result.is_long = is_long; /* Utilize property verification mechanism for bdaddr */ props[0] = create_property(BT_PROPERTY_BDADDR, bda, sizeof(*bda)); step->callback_result.num_properties = 1; step->callback_result.properties = repack_properties(1, props); g_free(props[0]->val); g_free(props[0]); schedule_callback_verification(step); } static void gatts_request_write_cb(int conn_id, int trans_id, bt_bdaddr_t *bda, int attr_handle, int offset, int length, bool need_rsp, bool is_prep, uint8_t *value) { struct step *step = g_new0(struct step, 1); bt_property_t *props[1]; step->callback = CB_GATTS_REQUEST_WRITE; step->callback_result.conn_id = conn_id; step->callback_result.trans_id = trans_id; step->callback_result.attr_handle = util_memdup(&attr_handle, sizeof(attr_handle)); step->callback_result.offset = offset; step->callback_result.length = length; step->callback_result.need_rsp = need_rsp; step->callback_result.is_prep = is_prep; step->callback_result.value = util_memdup(&value, length); /* Utilize property verification mechanism for bdaddr */ props[0] = create_property(BT_PROPERTY_BDADDR, bda, sizeof(*bda)); step->callback_result.num_properties = 1; step->callback_result.properties = repack_properties(1, props); g_free(props[0]->val); g_free(props[0]); schedule_callback_verification(step); } static void gatts_indication_send_cb(int conn_id, int status) { struct step *step = g_new0(struct step, 1); step->callback = CB_GATTS_INDICATION_SEND; step->callback_result.conn_id = conn_id; step->callback_result.status = status; schedule_callback_verification(step); } static const btgatt_server_callbacks_t btgatt_server_callbacks = { .register_server_cb = gatts_register_server_cb, .connection_cb = gatts_connection_cb, .service_added_cb = gatts_service_added_cb, .included_service_added_cb = gatts_included_service_added_cb, .characteristic_added_cb = gatts_characteristic_added_cb, .descriptor_added_cb = gatts_descriptor_added_cb, .service_started_cb = gatts_service_started_cb, .service_stopped_cb = gatts_service_stopped_cb, .service_deleted_cb = gatts_service_deleted_cb, .request_read_cb = gatts_request_read_cb, .request_write_cb = gatts_request_write_cb, .request_exec_write_cb = NULL, .response_confirmation_cb = NULL, .indication_sent_cb = gatts_indication_send_cb, .congestion_cb = NULL, }; static const btgatt_callbacks_t btgatt_callbacks = { .size = sizeof(btgatt_callbacks), .client = &btgatt_client_callbacks, .server = &btgatt_server_callbacks }; static void pan_control_state_cb(btpan_control_state_t state, int local_role, bt_status_t error, const char *ifname) { struct step *step = g_new0(struct step, 1); step->callback = CB_PAN_CONTROL_STATE; step->callback_result.state = error; step->callback_result.ctrl_state = state; step->callback_result.local_role = local_role; schedule_callback_verification(step); } static void pan_connection_state_cb(btpan_connection_state_t state, bt_status_t error, const bt_bdaddr_t *bd_addr, int local_role, int remote_role) { struct step *step = g_new0(struct step, 1); step->callback = CB_PAN_CONNECTION_STATE; step->callback_result.state = error; step->callback_result.conn_state = state; step->callback_result.local_role = local_role; step->callback_result.remote_role = remote_role; schedule_callback_verification(step); } static btpan_callbacks_t btpan_callbacks = { .size = sizeof(btpan_callbacks), .control_state_cb = pan_control_state_cb, .connection_state_cb = pan_connection_state_cb, }; static void hdp_app_reg_state_cb(int app_id, bthl_app_reg_state_t state) { struct step *step = g_new0(struct step, 1); step->callback = CB_HDP_APP_REG_STATE; step->callback_result.app_id = app_id; step->callback_result.app_state = state; schedule_callback_verification(step); } static void hdp_channel_state_cb(int app_id, bt_bdaddr_t *bd_addr, int mdep_cfg_index, int channel_id, bthl_channel_state_t state, int fd) { struct step *step = g_new0(struct step, 1); step->callback = CB_HDP_CHANNEL_STATE; step->callback_result.app_id = app_id; step->callback_result.channel_id = channel_id; step->callback_result.mdep_cfg_index = mdep_cfg_index; step->callback_result.channel_state = state; schedule_callback_verification(step); } static bthl_callbacks_t bthl_callbacks = { .size = sizeof(bthl_callbacks), .app_reg_state_cb = hdp_app_reg_state_cb, .channel_state_cb = hdp_channel_state_cb, }; static void a2dp_connection_state_cb(btav_connection_state_t state, bt_bdaddr_t *bd_addr) { struct step *step = g_new0(struct step, 1); step->callback = CB_A2DP_CONN_STATE; step->callback_result.av_conn_state = state; schedule_callback_verification(step); } static void a2dp_audio_state_cb(btav_audio_state_t state, bt_bdaddr_t *bd_addr) { struct step *step = g_new0(struct step, 1); step->callback = CB_A2DP_AUDIO_STATE; step->callback_result.av_audio_state = state; schedule_callback_verification(step); } static btav_callbacks_t bta2dp_callbacks = { .size = sizeof(bta2dp_callbacks), .connection_state_cb = a2dp_connection_state_cb, .audio_state_cb = a2dp_audio_state_cb, }; static void avrcp_get_play_status_cb(void) { struct step *step = g_new0(struct step, 1); step->callback = CB_AVRCP_PLAY_STATUS_REQ; schedule_callback_verification(step); } static void avrcp_register_notification_cb(btrc_event_id_t event_id, uint32_t param) { struct step *step = g_new0(struct step, 1); step->callback = CB_AVRCP_REG_NOTIF_REQ; schedule_callback_verification(step); } static void avrcp_get_element_attr_cb(uint8_t num_attr, btrc_media_attr_t *p_attrs) { struct step *step = g_new0(struct step, 1); step->callback = CB_AVRCP_GET_ATTR_REQ; schedule_callback_verification(step); } static btrc_callbacks_t btavrcp_callbacks = { .size = sizeof(btavrcp_callbacks), .get_play_status_cb = avrcp_get_play_status_cb, .register_notification_cb = avrcp_register_notification_cb, .get_element_attr_cb = avrcp_get_element_attr_cb, }; static btmce_mas_instance_t *copy_mas_instances(int num_instances, btmce_mas_instance_t *instances) { int i; btmce_mas_instance_t *inst; inst = g_new0(btmce_mas_instance_t, num_instances); for (i = 0; i < num_instances; i++) { inst[i].id = instances[i].id; inst[i].scn = instances[i].scn; inst[i].msg_types = instances[i].msg_types; inst[i].p_name = util_memdup(instances[i].p_name, strlen(instances[i].p_name)); } return inst; } static void map_client_get_remote_mas_instances_cb(bt_status_t status, bt_bdaddr_t *bd_addr, int num_instances, btmce_mas_instance_t *instances) { struct step *step = g_new0(struct step, 1); step->callback_result.status = status; step->callback_result.num_mas_instances = num_instances; step->callback_result.mas_instances = copy_mas_instances(num_instances, instances); step->callback = CB_MAP_CLIENT_REMOTE_MAS_INSTANCES; schedule_callback_verification(step); } static btmce_callbacks_t btmap_client_callbacks = { .size = sizeof(btmap_client_callbacks), .remote_mas_instances_cb = map_client_get_remote_mas_instances_cb, }; static bool setup_base(struct test_data *data) { const hw_module_t *module; hw_device_t *device; int signal_fd[2]; char buf[1024]; pid_t pid; int len; int err; if (pipe(signal_fd)) return false; pid = fork(); if (pid < 0) { close(signal_fd[0]); close(signal_fd[1]); return false; } if (pid == 0) { if (!tester_use_debug()) fclose(stderr); close(signal_fd[0]); emulator(signal_fd[1], data->mgmt_index); exit(0); } close(signal_fd[1]); data->bluetoothd_pid = pid; len = read(signal_fd[0], buf, sizeof(buf)); if (len <= 0 || strcmp(buf, EMULATOR_SIGNAL)) { close(signal_fd[0]); return false; } close(signal_fd[0]); err = hw_get_module_by_class(AUDIO_HARDWARE_MODULE_ID, AUDIO_HARDWARE_MODULE_ID_A2DP, &module); if (err) return false; err = audio_hw_device_open(module, &data->audio); if (err) return false; err = hw_get_module(BT_HARDWARE_MODULE_ID, &module); if (err) return false; err = module->methods->open(module, BT_HARDWARE_MODULE_ID, &device); if (err) return false; data->device = device; data->if_bluetooth = ((bluetooth_device_t *) device)->get_bluetooth_interface(); if (!data->if_bluetooth) return false; if (!(data->steps = queue_new())) return false; data->pdus = queue_new(); if (!data->pdus) { queue_destroy(data->steps, NULL); return false; } return true; } static void setup(const void *test_data) { struct test_data *data = tester_get_data(); bt_status_t status; if (!setup_base(data)) { tester_setup_failed(); return; } status = data->if_bluetooth->init(&bt_callbacks); if (status != BT_STATUS_SUCCESS) { data->if_bluetooth = NULL; tester_setup_failed(); return; } tester_setup_complete(); } static void setup_socket(const void *test_data) { struct test_data *data = tester_get_data(); bt_status_t status; const void *sock; if (!setup_base(data)) { tester_setup_failed(); return; } status = data->if_bluetooth->init(&bt_callbacks); if (status != BT_STATUS_SUCCESS) { data->if_bluetooth = NULL; tester_setup_failed(); return; } sock = data->if_bluetooth->get_profile_interface(BT_PROFILE_SOCKETS_ID); if (!sock) { tester_setup_failed(); return; } data->if_sock = sock; tester_setup_complete(); } static void setup_hidhost(const void *test_data) { struct test_data *data = tester_get_data(); bt_status_t status; const void *hid; if (!setup_base(data)) { tester_setup_failed(); return; } status = data->if_bluetooth->init(&bt_callbacks); if (status != BT_STATUS_SUCCESS) { data->if_bluetooth = NULL; tester_setup_failed(); return; } hid = data->if_bluetooth->get_profile_interface(BT_PROFILE_HIDHOST_ID); if (!hid) { tester_setup_failed(); return; } data->if_hid = hid; status = data->if_hid->init(&bthh_callbacks); if (status != BT_STATUS_SUCCESS) { data->if_hid = NULL; tester_setup_failed(); return; } tester_setup_complete(); } static void setup_pan(const void *test_data) { struct test_data *data = tester_get_data(); bt_status_t status; const void *pan; if (!setup_base(data)) { tester_setup_failed(); return; } status = data->if_bluetooth->init(&bt_callbacks); if (status != BT_STATUS_SUCCESS) { data->if_bluetooth = NULL; tester_setup_failed(); return; } pan = data->if_bluetooth->get_profile_interface(BT_PROFILE_PAN_ID); if (!pan) { tester_setup_failed(); return; } data->if_pan = pan; status = data->if_pan->init(&btpan_callbacks); if (status != BT_STATUS_SUCCESS) { data->if_pan = NULL; tester_setup_failed(); return; } tester_setup_complete(); } static void setup_hdp(const void *test_data) { struct test_data *data = tester_get_data(); bt_status_t status; const void *hdp; if (!setup_base(data)) { tester_setup_failed(); return; } status = data->if_bluetooth->init(&bt_callbacks); if (status != BT_STATUS_SUCCESS) { data->if_bluetooth = NULL; tester_setup_failed(); return; } hdp = data->if_bluetooth->get_profile_interface(BT_PROFILE_HEALTH_ID); if (!hdp) { tester_setup_failed(); return; } data->if_hdp = hdp; status = data->if_hdp->init(&bthl_callbacks); if (status != BT_STATUS_SUCCESS) { data->if_hdp = NULL; tester_setup_failed(); return; } tester_setup_complete(); } static void setup_a2dp(const void *test_data) { struct test_data *data = tester_get_data(); const bt_interface_t *if_bt; bt_status_t status; const void *a2dp; if (!setup_base(data)) { tester_setup_failed(); return; } if_bt = data->if_bluetooth; status = if_bt->init(&bt_callbacks); if (status != BT_STATUS_SUCCESS) { data->if_bluetooth = NULL; tester_setup_failed(); return; } a2dp = if_bt->get_profile_interface(BT_PROFILE_ADVANCED_AUDIO_ID); if (!a2dp) { tester_setup_failed(); return; } data->if_a2dp = a2dp; status = data->if_a2dp->init(&bta2dp_callbacks); if (status != BT_STATUS_SUCCESS) { data->if_a2dp = NULL; tester_setup_failed(); return; } tester_setup_complete(); } static void setup_avrcp(const void *test_data) { struct test_data *data = tester_get_data(); const bt_interface_t *if_bt; bt_status_t status; const void *a2dp, *avrcp; if (!setup_base(data)) { tester_setup_failed(); return; } if_bt = data->if_bluetooth; status = if_bt->init(&bt_callbacks); if (status != BT_STATUS_SUCCESS) { data->if_bluetooth = NULL; tester_setup_failed(); return; } a2dp = if_bt->get_profile_interface(BT_PROFILE_ADVANCED_AUDIO_ID); if (!a2dp) { tester_setup_failed(); return; } data->if_a2dp = a2dp; status = data->if_a2dp->init(&bta2dp_callbacks); if (status != BT_STATUS_SUCCESS) { data->if_a2dp = NULL; tester_setup_failed(); return; } avrcp = if_bt->get_profile_interface(BT_PROFILE_AV_RC_ID); if (!avrcp) { tester_setup_failed(); return; } data->if_avrcp = avrcp; status = data->if_avrcp->init(&btavrcp_callbacks); if (status != BT_STATUS_SUCCESS) { data->if_avrcp = NULL; tester_setup_failed(); return; } tester_setup_complete(); } static void setup_gatt(const void *test_data) { struct test_data *data = tester_get_data(); bt_status_t status; const void *gatt; if (!setup_base(data)) { tester_setup_failed(); return; } status = data->if_bluetooth->init(&bt_callbacks); if (status != BT_STATUS_SUCCESS) { data->if_bluetooth = NULL; tester_setup_failed(); return; } gatt = data->if_bluetooth->get_profile_interface(BT_PROFILE_GATT_ID); if (!gatt) { tester_setup_failed(); return; } data->if_gatt = gatt; status = data->if_gatt->init(&btgatt_callbacks); if (status != BT_STATUS_SUCCESS) { data->if_gatt = NULL; tester_setup_failed(); return; } tester_setup_complete(); } static void setup_map_client(const void *test_data) { struct test_data *data = tester_get_data(); bt_status_t status; const void *map_client; if (!setup_base(data)) { tester_setup_failed(); return; } status = data->if_bluetooth->init(&bt_callbacks); if (status != BT_STATUS_SUCCESS) { data->if_bluetooth = NULL; tester_setup_failed(); return; } map_client = data->if_bluetooth->get_profile_interface( BT_PROFILE_MAP_CLIENT_ID); if (!map_client) { tester_setup_failed(); return; } data->if_map_client = map_client; status = data->if_map_client->init(&btmap_client_callbacks); if (status != BT_STATUS_SUCCESS) { data->if_map_client = NULL; tester_setup_failed(); return; } tester_setup_complete(); } static void teardown(const void *test_data) { struct test_data *data = tester_get_data(); queue_destroy(data->steps, NULL); data->steps = NULL; queue_destroy(data->pdus, NULL); data->pdus = NULL; /* no cleanup for map_client */ data->if_map_client = NULL; if (data->if_gatt) { data->if_gatt->cleanup(); data->if_gatt = NULL; } if (data->if_hid) { data->if_hid->cleanup(); data->if_hid = NULL; } if (data->if_pan) { data->if_pan->cleanup(); data->if_pan = NULL; } if (data->if_hdp) { data->if_hdp->cleanup(); data->if_hdp = NULL; } if (data->if_stream) { data->audio->close_output_stream(data->audio, data->if_stream); data->if_stream = NULL; } if (data->if_a2dp) { data->if_a2dp->cleanup(); data->if_a2dp = NULL; } if (data->if_avrcp) { data->if_avrcp->cleanup(); data->if_avrcp = NULL; } if (data->if_bluetooth) { data->if_bluetooth->cleanup(); data->if_bluetooth = NULL; } data->device->close(data->device); audio_hw_device_close(data->audio); /* * Ssp_request_cb pointer can be set do default_ssp_req_cb. * Set it back to ssp_request_cb */ bt_callbacks.ssp_request_cb = ssp_request_cb; if (!data->bluetoothd_pid) tester_teardown_complete(); } static void emu_connectable_complete(uint16_t opcode, uint8_t status, const void *param, uint8_t len, void *user_data) { struct step *step; struct test_data *data = user_data; switch (opcode) { case BT_HCI_CMD_WRITE_SCAN_ENABLE: break; case BT_HCI_CMD_LE_SET_ADV_ENABLE: /* * For BREDRLE emulator we want to verify step after scan * enable and not after le_set_adv_enable */ if (data->hciemu_type == HCIEMU_TYPE_BREDRLE) return; break; default: return; } step = g_new0(struct step, 1); if (status) { tester_warn("Emulated remote setup failed."); step->action_status = BT_STATUS_FAIL; } else { tester_debug("Emulated remote setup done."); step->action_status = BT_STATUS_SUCCESS; } schedule_action_verification(step); } void emu_setup_powered_remote_action(void) { struct test_data *data = tester_get_data(); struct bthost *bthost; bthost = hciemu_client_get_host(data->hciemu); bthost_set_cmd_complete_cb(bthost, emu_connectable_complete, data); if ((data->hciemu_type == HCIEMU_TYPE_LE) || (data->hciemu_type == HCIEMU_TYPE_BREDRLE)) { uint8_t adv[3]; adv[0] = 0x02; /* Field length */ adv[1] = 0x01; /* Flags */ adv[2] = 0x02; /* Flags value */ bthost_set_adv_data(bthost, adv, sizeof(adv)); bthost_set_adv_enable(bthost, 0x01); } if (data->hciemu_type != HCIEMU_TYPE_LE) bthost_write_scan_enable(bthost, 0x03); } void emu_set_pin_code_action(void) { struct test_data *data = tester_get_data(); struct step *current_data_step = queue_peek_head(data->steps); struct bt_action_data *action_data = current_data_step->set_data; struct bthost *bthost; struct step *step = g_new0(struct step, 1); bthost = hciemu_client_get_host(data->hciemu); bthost_set_pin_code(bthost, action_data->pin->pin, action_data->pin_len); step->action_status = BT_STATUS_SUCCESS; tester_print("Setting emu pin done."); schedule_action_verification(step); } void emu_set_ssp_mode_action(void) { struct test_data *data = tester_get_data(); struct bthost *bthost; struct step *step = g_new0(struct step, 1); bthost = hciemu_client_get_host(data->hciemu); bthost_write_ssp_mode(bthost, 0x01); step->action_status = BT_STATUS_SUCCESS; schedule_action_verification(step); } void emu_set_connect_cb_action(void) { struct test_data *data = tester_get_data(); struct bthost *bthost = hciemu_client_get_host(data->hciemu); struct step *current_data_step = queue_peek_head(data->steps); void *cb = current_data_step->set_data; struct step *step = g_new0(struct step, 1); bthost_set_connect_cb(bthost, cb, data); step->action_status = BT_STATUS_SUCCESS; schedule_action_verification(step); } void emu_remote_connect_hci_action(void) { struct test_data *data = tester_get_data(); struct bthost *bthost = hciemu_client_get_host(data->hciemu); struct step *current_data_step = queue_peek_head(data->steps); struct bt_action_data *action_data = current_data_step->set_data; struct step *step = g_new0(struct step, 1); const uint8_t *master_addr; master_addr = hciemu_get_central_bdaddr(data->hciemu); tester_print("Trying to connect hci"); if (action_data) bthost_hci_connect(bthost, master_addr, action_data->bearer_type); else bthost_hci_connect(bthost, master_addr, BDADDR_BREDR); step->action_status = BT_STATUS_SUCCESS; schedule_action_verification(step); } void emu_remote_disconnect_hci_action(void) { struct test_data *data = tester_get_data(); struct bthost *bthost = hciemu_client_get_host(data->hciemu); struct step *current_data_step = queue_peek_head(data->steps); uint16_t *handle = current_data_step->set_data; struct step *step = g_new0(struct step, 1); if (handle) { bthost_hci_disconnect(bthost, *handle, 0x13); step->action_status = BT_STATUS_SUCCESS; } else { step->action_status = BT_STATUS_FAIL; } schedule_action_verification(step); } void emu_set_io_cap(void) { struct test_data *data = tester_get_data(); struct bthost *bthost; struct step *current_data_step = queue_peek_head(data->steps); struct bt_action_data *action_data = current_data_step->set_data; struct step *step = g_new0(struct step, 1); bthost = hciemu_client_get_host(data->hciemu); if (action_data) bthost_set_io_capability(bthost, action_data->io_cap); else bthost_set_io_capability(bthost, 0x01); step->action_status = BT_STATUS_SUCCESS; schedule_action_verification(step); } void emu_add_l2cap_server_action(void) { struct test_data *data = tester_get_data(); struct step *current_data_step = queue_peek_head(data->steps); struct emu_set_l2cap_data *l2cap_data = current_data_step->set_data; struct bthost *bthost; struct step *step = g_new0(struct step, 1); if (!l2cap_data) { tester_warn("Invalid l2cap_data params"); step->action_status = BT_STATUS_FAIL; goto done; } bthost = hciemu_client_get_host(data->hciemu); bthost_add_l2cap_server(bthost, l2cap_data->psm, l2cap_data->func, NULL, l2cap_data->user_data); step->action_status = BT_STATUS_SUCCESS; done: schedule_action_verification(step); } static void print_data(const char *str, void *user_data) { tester_debug("tester: %s", str); } static void emu_generic_cid_hook_cb(const void *data, uint16_t len, void *user_data) { struct test_data *t_data = tester_get_data(); struct emu_l2cap_cid_data *cid_data = user_data; const struct pdu_set *pdus = cid_data->pdu; struct bthost *bthost = hciemu_client_get_host(t_data->hciemu); int i; for (i = 0; pdus[i].rsp.iov_base; i++) { if (pdus[i].req.iov_base) { if (pdus[i].req.iov_len != len) continue; if (memcmp(pdus[i].req.iov_base, data, len)) continue; } if (pdus[i].rsp.iov_base) { util_hexdump('>', pdus[i].rsp.iov_base, pdus[i].rsp.iov_len, print_data, NULL); /* if its sdp pdu use transaction ID from request */ if (cid_data->is_sdp) { struct iovec rsp[3]; rsp[0].iov_base = pdus[i].rsp.iov_base; rsp[0].iov_len = 1; rsp[1].iov_base = ((uint8_t *) data) + 1; rsp[1].iov_len = 2; rsp[2].iov_base = pdus[i].rsp.iov_base + 3; rsp[2].iov_len = pdus[i].rsp.iov_len - 3; bthost_send_cid_v(bthost, cid_data->handle, cid_data->cid, rsp, 3); } else { bthost_send_cid_v(bthost, cid_data->handle, cid_data->cid, &pdus[i].rsp, 1); } } } } void tester_handle_l2cap_data_exchange(struct emu_l2cap_cid_data *cid_data) { struct test_data *t_data = tester_get_data(); struct bthost *bthost = hciemu_client_get_host(t_data->hciemu); bthost_add_cid_hook(bthost, cid_data->handle, cid_data->cid, emu_generic_cid_hook_cb, cid_data); } void tester_generic_connect_cb(uint16_t handle, uint16_t cid, void *user_data) { struct emu_l2cap_cid_data *cid_data = user_data; cid_data->handle = handle; cid_data->cid = cid; tester_handle_l2cap_data_exchange(cid_data); } static void rfcomm_connect_cb(uint16_t handle, uint16_t cid, void *user_data, bool status) { struct step *step = g_new0(struct step, 1); tester_print("Connect handle %d, cid %d cb status: %d", handle, cid, status); step->action_status = BT_STATUS_SUCCESS; schedule_action_verification(step); } void emu_add_rfcomm_server_action(void) { struct test_data *data = tester_get_data(); struct step *current_data_step = queue_peek_head(data->steps); struct bt_action_data *rfcomm_data = current_data_step->set_data; struct bthost *bthost; struct step *step; if (!rfcomm_data) { tester_warn("Invalid l2cap_data params"); return; } step = g_new0(struct step, 1); bthost = hciemu_client_get_host(data->hciemu); bthost_add_rfcomm_server(bthost, rfcomm_data->channel, rfcomm_connect_cb, data); step->action_status = BT_STATUS_SUCCESS; schedule_action_verification(step); } void dummy_action(void) { struct step *step = g_new0(struct step, 1); step->action = dummy_action; schedule_action_verification(step); } void bluetooth_enable_action(void) { struct test_data *data = tester_get_data(); struct step *step = g_new0(struct step, 1); step->action_status = data->if_bluetooth->enable(); schedule_action_verification(step); } void bluetooth_disable_action(void) { struct test_data *data = tester_get_data(); struct step *step = g_new0(struct step, 1); step->action_status = data->if_bluetooth->disable(); schedule_action_verification(step); } void bt_set_property_action(void) { struct test_data *data = tester_get_data(); struct step *step; struct step *current_data_step = queue_peek_head(data->steps); bt_property_t *prop; if (!current_data_step->set_data) { tester_debug("BT property not set for step"); tester_test_failed(); return; } step = g_new0(struct step, 1); prop = (bt_property_t *)current_data_step->set_data; step->action_status = data->if_bluetooth->set_adapter_property(prop); schedule_action_verification(step); } void bt_get_property_action(void) { struct test_data *data = tester_get_data(); struct step *step; struct step *current_data_step = queue_peek_head(data->steps); bt_property_t *prop; if (!current_data_step->set_data) { tester_debug("BT property to get not defined"); tester_test_failed(); return; } step = g_new0(struct step, 1); prop = (bt_property_t *)current_data_step->set_data; step->action_status = data->if_bluetooth->get_adapter_property( prop->type); schedule_action_verification(step); } void bt_start_discovery_action(void) { struct test_data *data = tester_get_data(); struct step *step = g_new0(struct step, 1); step->action_status = data->if_bluetooth->start_discovery(); schedule_action_verification(step); } void bt_cancel_discovery_action(void) { struct test_data *data = tester_get_data(); struct step *step = g_new0(struct step, 1); step->action_status = data->if_bluetooth->cancel_discovery(); schedule_action_verification(step); } void bt_get_device_props_action(void) { struct test_data *data = tester_get_data(); struct step *current_data_step = queue_peek_head(data->steps); struct step *step; if (!current_data_step->set_data) { tester_debug("bdaddr not defined"); tester_test_failed(); return; } step = g_new0(struct step, 1); step->action_status = data->if_bluetooth->get_remote_device_properties( current_data_step->set_data); schedule_action_verification(step); } void bt_get_device_prop_action(void) { struct test_data *data = tester_get_data(); struct step *current_data_step = queue_peek_head(data->steps); struct bt_action_data *action_data = current_data_step->set_data; struct step *step; if (!action_data) { tester_warn("No arguments for 'get remote device prop' req."); tester_test_failed(); return; } step = g_new0(struct step, 1); step->action_status = data->if_bluetooth->get_remote_device_property( action_data->addr, action_data->prop_type); schedule_action_verification(step); } void bt_set_device_prop_action(void) { struct test_data *data = tester_get_data(); struct step *current_data_step = queue_peek_head(data->steps); struct bt_action_data *action_data = current_data_step->set_data; struct step *step; if (!action_data) { tester_warn("No arguments for 'set remote device prop' req."); tester_test_failed(); return; } step = g_new0(struct step, 1); step->action_status = data->if_bluetooth->set_remote_device_property( action_data->addr, action_data->prop); schedule_action_verification(step); } void bt_create_bond_action(void) { struct test_data *data = tester_get_data(); struct step *current_data_step = queue_peek_head(data->steps); struct bt_action_data *action_data = current_data_step->set_data; struct step *step; if (!action_data || !action_data->addr) { tester_warn("Bad arguments for 'create bond' req."); tester_test_failed(); return; } step = g_new0(struct step, 1); step->action_status = data->if_bluetooth->create_bond(action_data->addr, action_data->transport_type ? action_data->transport_type : BT_TRANSPORT_UNKNOWN); schedule_action_verification(step); } void bt_pin_reply_accept_action(void) { struct test_data *data = tester_get_data(); struct step *current_data_step = queue_peek_head(data->steps); struct bt_action_data *action_data = current_data_step->set_data; struct step *step; if (!action_data || !action_data->addr || !action_data->pin) { tester_warn("Bad arguments for 'pin reply' req."); tester_test_failed(); return; } step = g_new0(struct step, 1); step->action_status = data->if_bluetooth->pin_reply(action_data->addr, TRUE, action_data->pin_len, action_data->pin); schedule_action_verification(step); } void bt_ssp_reply_accept_action(void) { struct test_data *data = tester_get_data(); struct step *current_data_step = queue_peek_head(data->steps); struct bt_action_data *action_data = current_data_step->set_data; struct step *step = g_new0(struct step, 1); step->action_status = data->if_bluetooth->ssp_reply(action_data->addr, action_data->ssp_variant, action_data->accept, 0); schedule_action_verification(step); } void bt_cancel_bond_action(void) { struct test_data *data = tester_get_data(); struct step *current_data_step = queue_peek_head(data->steps); bt_bdaddr_t *addr = current_data_step->set_data; struct step *step = g_new0(struct step, 1); step->action_status = data->if_bluetooth->cancel_bond(addr); schedule_action_verification(step); } void bt_remove_bond_action(void) { struct test_data *data = tester_get_data(); struct step *current_data_step = queue_peek_head(data->steps); bt_bdaddr_t *addr = current_data_step->set_data; struct step *step = g_new0(struct step, 1); step->action_status = data->if_bluetooth->remove_bond(addr); schedule_action_verification(step); } static void default_ssp_req_cb(bt_bdaddr_t *remote_bd_addr, bt_bdname_t *name, uint32_t cod, bt_ssp_variant_t pairing_variant, uint32_t pass_key) { struct test_data *t_data = tester_get_data(); t_data->if_bluetooth->ssp_reply(remote_bd_addr, pairing_variant, true, pass_key); } void set_default_ssp_request_handler(void) { struct step *step = g_new0(struct step, 1); bt_callbacks.ssp_request_cb = default_ssp_req_cb; step->action_status = BT_STATUS_SUCCESS; schedule_action_verification(step); } static void generic_test_function(const void *test_data) { struct test_data *data = tester_get_data(); struct step *first_step; init_test_steps(data); /* first step action */ first_step = queue_peek_head(data->steps); if (!first_step->action) { tester_print("tester: No initial action declared"); tester_test_failed(); return; } first_step->action(); } #define test(data, test_setup, test, test_teardown) \ do { \ struct test_data *user; \ user = g_malloc0(sizeof(struct test_data)); \ if (!user) \ break; \ user->hciemu_type = data->emu_type; \ user->test_data = data; \ tester_add_full(data->title, data, test_pre_setup, \ test_setup, test, test_teardown, \ test_post_teardown, 3, user, g_free); \ } while (0) static void tester_testcases_cleanup(void) { remove_bluetooth_tests(); remove_socket_tests(); remove_hidhost_tests(); remove_gatt_tests(); remove_a2dp_tests(); remove_avrcp_tests(); remove_hdp_tests(); remove_pan_tests(); } static void add_bluetooth_tests(void *data, void *user_data) { struct test_case *tc = data; test(tc, setup, generic_test_function, teardown); } static void add_socket_tests(void *data, void *user_data) { struct test_case *tc = data; test(tc, setup_socket, generic_test_function, teardown); } static void add_hidhost_tests(void *data, void *user_data) { struct test_case *tc = data; test(tc, setup_hidhost, generic_test_function, teardown); } static void add_pan_tests(void *data, void *user_data) { struct test_case *tc = data; test(tc, setup_pan, generic_test_function, teardown); } static void add_hdp_tests(void *data, void *user_data) { struct test_case *tc = data; test(tc, setup_hdp, generic_test_function, teardown); } static void add_a2dp_tests(void *data, void *user_data) { struct test_case *tc = data; test(tc, setup_a2dp, generic_test_function, teardown); } static void add_avrcp_tests(void *data, void *user_data) { struct test_case *tc = data; test(tc, setup_avrcp, generic_test_function, teardown); } static void add_gatt_tests(void *data, void *user_data) { struct test_case *tc = data; test(tc, setup_gatt, generic_test_function, teardown); } static void add_map_client_tests(void *data, void *user_data) { struct test_case *tc = data; test(tc, setup_map_client, generic_test_function, teardown); } int main(int argc, char *argv[]) { snprintf(exec_dir, sizeof(exec_dir), "%s", dirname(argv[0])); tester_init(&argc, &argv); queue_foreach(get_bluetooth_tests(), add_bluetooth_tests, NULL); queue_foreach(get_socket_tests(), add_socket_tests, NULL); queue_foreach(get_hidhost_tests(), add_hidhost_tests, NULL); queue_foreach(get_pan_tests(), add_pan_tests, NULL); queue_foreach(get_hdp_tests(), add_hdp_tests, NULL); queue_foreach(get_a2dp_tests(), add_a2dp_tests, NULL); queue_foreach(get_avrcp_tests(), add_avrcp_tests, NULL); queue_foreach(get_gatt_tests(), add_gatt_tests, NULL); queue_foreach(get_map_client_tests(), add_map_client_tests, NULL); if (tester_run()) return 1; tester_testcases_cleanup(); return 0; }