| /* |
| * Copyright (C) 2014 Intel Corporation |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| * |
| */ |
| #include <stdbool.h> |
| #include <unistd.h> |
| #include <libgen.h> |
| |
| #include <sys/un.h> |
| #include <sys/wait.h> |
| #include <sys/signalfd.h> |
| |
| #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]; |
| 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 = g_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 = g_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 = g_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 = g_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 = g_memdup(srvc_id, sizeof(*srvc_id)); |
| step->callback_result.characteristic = g_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 = g_memdup(srvc_id, sizeof(*srvc_id)); |
| step->callback_result.characteristic = g_memdup(char_id, |
| sizeof(*char_id)); |
| step->callback_result.descriptor = g_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 = g_memdup(srvc_id, sizeof(*srvc_id)); |
| step->callback_result.included = g_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 = g_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 = g_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 = g_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 = g_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 = g_memdup(srvc_id, sizeof(*srvc_id)); |
| step->callback_result.characteristic = g_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 = g_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 = g_memdup(srvc_id, sizeof(*srvc_id)); |
| step->callback_result.srvc_handle = g_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 = g_memdup(&srvc_handle, |
| sizeof(srvc_handle)); |
| step->callback_result.inc_srvc_handle = g_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 = g_memdup(&srvc_handle, |
| sizeof(srvc_handle)); |
| step->callback_result.uuid = g_memdup(uuid, sizeof(*uuid)); |
| step->callback_result.char_handle = g_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 = g_memdup(&srvc_handle, |
| sizeof(srvc_handle)); |
| step->callback_result.uuid = g_memdup(uuid, sizeof(*uuid)); |
| step->callback_result.desc_handle = g_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 = g_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 = g_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 = g_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 = g_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 = g_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 = g_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 = g_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_master_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, |
| 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; |
| } |