static gboolean l2cap_listen_cb(GIOChannel *io, GIOCondition cond, gpointer user_data) { struct test_data *data = tester_get_data(); const struct l2cap_data *l2data = data->test_data; int sk, new_sk; data->io_id = 0; sk = g_io_channel_unix_get_fd(io); new_sk = accept(sk, NULL, NULL); if (new_sk < 0) { tester_warn("accept failed: %s (%u)", strerror(errno), errno); tester_test_failed(); return FALSE; } if (l2data->read_data) { struct bthost *bthost; GIOChannel *new_io; new_io = g_io_channel_unix_new(new_sk); g_io_channel_set_close_on_unref(new_io, TRUE); bthost = hciemu_client_get_host(data->hciemu); g_io_add_watch(new_io, G_IO_IN, server_received_data, NULL); bthost_send_cid(bthost, data->handle, data->dcid, l2data->read_data, l2data->data_len); g_io_channel_unref(new_io); return FALSE; } else if (l2data->write_data) { struct bthost *bthost; ssize_t ret; bthost = hciemu_client_get_host(data->hciemu); bthost_add_cid_hook(bthost, data->handle, data->scid, server_bthost_received_data, NULL); ret = write(new_sk, l2data->write_data, l2data->data_len); close(new_sk); if (ret != l2data->data_len) { tester_warn("Unable to write all data"); tester_test_failed(); } return FALSE; } tester_print("Successfully connected"); close(new_sk); tester_test_passed(); return FALSE; }
static gboolean l2cap_connect_cb(GIOChannel *io, GIOCondition cond, gpointer user_data) { struct test_data *data = tester_get_data(); const struct l2cap_data *l2data = data->test_data; int err, sk_err, sk; socklen_t len = sizeof(sk_err); data->io_id = 0; sk = g_io_channel_unix_get_fd(io); if (getsockopt(sk, SOL_SOCKET, SO_ERROR, &sk_err, &len) < 0) err = -errno; else err = -sk_err; if (err < 0) tester_warn("Connect failed: %s (%d)", strerror(-err), -err); else tester_print("Successfully connected"); if (l2data->read_data) { struct bthost *bthost; bthost = hciemu_client_get_host(data->hciemu); g_io_add_watch(io, G_IO_IN, client_received_data, NULL); bthost_send_cid(bthost, data->handle, data->dcid, l2data->read_data, l2data->data_len); return FALSE; } else if (l2data->write_data) { struct bthost *bthost; ssize_t ret; bthost = hciemu_client_get_host(data->hciemu); bthost_add_cid_hook(bthost, data->handle, data->dcid, bthost_received_data, NULL); ret = write(sk, l2data->write_data, l2data->data_len); if (ret != l2data->data_len) { tester_warn("Unable to write all data"); tester_test_failed(); } return FALSE; } if (-err != l2data->expect_err) tester_test_failed(); else tester_test_passed(); return FALSE; }
static void test_connect(const void *test_data) { struct test_data *data = tester_get_data(); const struct l2cap_client_data *l2data = data->test_data; GIOChannel *io; int sk; if (l2data->server_psm) { struct bthost *bthost = hciemu_client_get_host(data->hciemu); bthost_set_server_psm(bthost, l2data->server_psm); } sk = create_l2cap_sock(data, 0); if (sk < 0) { tester_test_failed(); return; } if (connect_l2cap_sock(data, sk, l2data->client_psm) < 0) { close(sk); tester_test_failed(); return; } io = g_io_channel_unix_new(sk); g_io_channel_set_close_on_unref(io, TRUE); data->io_id = g_io_add_watch(io, G_IO_OUT, l2cap_connect_cb, NULL); g_io_channel_unref(io); tester_print("Connect in progress"); }
static void test_connect_two_sockets(const void *test_data) { struct test_data *data = tester_get_data(); const struct l2cap_data *l2data = data->test_data; const uint8_t *client_bdaddr; test_two_sockets_connect_cb_cnt = 0; test_scan_enable_counter = 0; hciemu_add_master_post_command_hook(data->hciemu, test_connect_two_sockets_router, data); if (l2data->server_psm) { struct bthost *bthost = hciemu_client_get_host(data->hciemu); if (!l2data->data_len) bthost_add_l2cap_server(bthost, l2data->server_psm, NULL, NULL); } client_bdaddr = hciemu_get_client_bdaddr(data->hciemu); if (l2data->close_one_socket) connect_socket(client_bdaddr, &data->sk, NULL); else connect_socket(client_bdaddr, &data->sk, test_two_sockets_connect_cb); }
static void test_client(const void *test_data) { struct test_data *data = tester_get_data(); const struct smp_data *smp = data->test_data; struct mgmt_cp_pair_device cp; struct bthost *bthost; init_bdaddr(data); bthost = hciemu_client_get_host(data->hciemu); bthost_set_connect_cb(bthost, smp_new_conn, data); test_add_condition(data); if (smp->expect_hci_command) { tester_print("Registering HCI command callback"); hciemu_add_master_post_command_hook(data->hciemu, command_hci_callback, data); test_add_condition(data); } memcpy(&cp.addr.bdaddr, data->ra, sizeof(data->ra)); cp.addr.type = BDADDR_LE_PUBLIC; if (smp->mitm) cp.io_cap = 0x04; /* KeyboardDisplay */ else cp.io_cap = 0x03; /* NoInputNoOutput */ mgmt_send(data->mgmt, MGMT_OP_PAIR_DEVICE, data->mgmt_index, sizeof(cp), &cp, pair_device_complete, NULL, NULL); tester_print("Pairing in progress"); }
static void setup_powered_client_callback(uint8_t status, uint16_t length, const void *param, void *user_data) { struct test_data *data = tester_get_data(); const struct l2cap_data *l2data = data->test_data; struct bthost *bthost; if (status != MGMT_STATUS_SUCCESS) { tester_setup_failed(); return; } tester_print("Controller powered on"); bthost = hciemu_client_get_host(data->hciemu); bthost_set_cmd_complete_cb(bthost, client_cmd_complete, user_data); if (data->hciemu_type == HCIEMU_TYPE_LE) { if (!l2data || !l2data->server_not_advertising) bthost_set_adv_enable(bthost, 0x01); else tester_setup_complete(); } else { bthost_write_scan_enable(bthost, 0x03); } }
static void client_l2cap_rsp(uint8_t code, const void *data, uint16_t len, void *user_data) { struct test_data *t_data = tester_get_data(); struct bthost *bthost = hciemu_client_get_host(t_data->hciemu); struct emu_l2cap_cid_data *cid_data = user_data; const uint16_t *psm = data; const struct iovec con_req = raw_pdu(0x13, 0x00, /* PSM */ 0x41, 0x00); /* Source CID */ if (len < sizeof(*psm)) { tester_warn("Invalid l2cap response."); return; } switch (*psm) { case 0x40: bthost_add_cid_hook(bthost, cid_data->handle, 0x40, hid_ctrl_cid_hook_cb, cid_data); bthost_l2cap_req(bthost, cid_data->handle, 0x02, con_req.iov_base, con_req.iov_len, client_l2cap_rsp, cid_data); break; case 0x41: bthost_add_cid_hook(bthost, cid_data->handle, 0x41, hid_intr_cid_hook_cb, cid_data); break; default: break; } }
static void setup_powered_common(void) { struct test_data *data = tester_get_data(); const struct l2cap_data *test = data->test_data; struct bthost *bthost = hciemu_client_get_host(data->hciemu); unsigned char param[] = { 0x01 }; mgmt_register(data->mgmt, MGMT_EV_USER_CONFIRM_REQUEST, data->mgmt_index, user_confirm_request_callback, NULL, NULL); if (test && (test->pin || test->expect_pin)) mgmt_register(data->mgmt, MGMT_EV_PIN_CODE_REQUEST, data->mgmt_index, pin_code_request_callback, data, NULL); if (test && test->client_pin) bthost_set_pin_code(bthost, test->client_pin, test->client_pin_len); if (test && test->reject_ssp) bthost_set_reject_user_confirm(bthost, true); if (data->hciemu_type == HCIEMU_TYPE_LE) mgmt_send(data->mgmt, MGMT_OP_SET_LE, data->mgmt_index, sizeof(param), param, NULL, NULL, NULL); if (test && test->enable_ssp) mgmt_send(data->mgmt, MGMT_OP_SET_SSP, data->mgmt_index, sizeof(param), param, NULL, NULL, NULL); mgmt_send(data->mgmt, MGMT_OP_SET_PAIRABLE, data->mgmt_index, sizeof(param), param, NULL, NULL, NULL); }
static void client_cmd_complete(uint16_t opcode, uint8_t status, const void *param, uint8_t len, void *user_data) { struct test_data *data = tester_get_data(); const struct l2cap_data *test = data->test_data; struct bthost *bthost; bthost = hciemu_client_get_host(data->hciemu); switch (opcode) { case BT_HCI_CMD_WRITE_SCAN_ENABLE: case BT_HCI_CMD_LE_SET_ADV_ENABLE: tester_print("Client set connectable status 0x%02x", status); if (!status && test && test->enable_ssp) { bthost_write_ssp_mode(bthost, 0x01); return; } break; case BT_HCI_CMD_WRITE_SIMPLE_PAIRING_MODE: tester_print("Client enable SSP status 0x%02x", status); break; default: return; } if (status) tester_setup_failed(); else tester_setup_complete(); }
static bool verify_random(const uint8_t rnd[16]) { struct test_data *data = tester_get_data(); uint8_t confirm[16]; if (!bt_crypto_c1(data->crypto, data->tk, data->rrnd, data->prsp, data->preq, data->ia_type, data->ia, data->ra_type, data->ra, confirm)) return false; if (memcmp(data->pcnf, confirm, sizeof(data->pcnf) != 0)) { tester_warn("Confirmation values don't match"); return false; } if (data->out) { struct bthost *bthost = hciemu_client_get_host(data->hciemu); bt_crypto_s1(data->crypto, data->tk, data->rrnd, data->prnd, data->ltk); bthost_le_start_encrypt(bthost, data->handle, data->ltk); } else { bt_crypto_s1(data->crypto, data->tk, data->prnd, data->rrnd, data->ltk); } return true; }
static void test_connect(const void *test_data) { struct test_data *data = tester_get_data(); struct bthost *bthost = hciemu_client_get_host(data->hciemu); const struct rfcomm_client_data *client_data = data->test_data; const uint8_t *client_addr, *master_addr; GIOChannel *io; int sk; bthost_add_l2cap_server(bthost, 0x0003, NULL, NULL); bthost_add_rfcomm_server(bthost, client_data->server_channel, rfcomm_connect_cb, NULL); master_addr = hciemu_get_master_bdaddr(data->hciemu); client_addr = hciemu_get_client_bdaddr(data->hciemu); sk = create_rfcomm_sock((bdaddr_t *) master_addr, 0); if (connect_rfcomm_sock(sk, (const bdaddr_t *) client_addr, client_data->client_channel) < 0) { close(sk); tester_test_failed(); return; } io = g_io_channel_unix_new(sk); g_io_channel_set_close_on_unref(io, TRUE); data->io_id = g_io_add_watch(io, G_IO_OUT, rc_connect_cb, NULL); g_io_channel_unref(io); tester_print("Connect in progress %d", sk); }
static void smp_new_conn(uint16_t handle, void *user_data) { struct test_data *data = user_data; const struct smp_data *smp = data->test_data; struct bthost *bthost = hciemu_client_get_host(data->hciemu); const struct smp_req_rsp *req; const void *pdu; tester_print("New SMP client connection with handle 0x%04x", handle); data->handle = handle; bthost_add_cid_hook(bthost, handle, SMP_CID, smp_server, data); if (smp->req_count == data->counter) return; req = &smp->req[data->counter]; if (!req->send) return; tester_print("Sending SMP PDU"); pdu = get_pdu(req->send); bthost_send_cid(bthost, handle, SMP_CID, pdu, req->send_len); if (!req->expect) test_condition_complete(data); }
static void send_req_new_conn(uint16_t handle, void *user_data) { struct test_data *data = user_data; const struct l2cap_data *l2data = data->test_data; struct bthost *bthost; tester_print("New client connection with handle 0x%04x", handle); data->handle = handle; if (l2data->send_cmd) { bthost_l2cap_rsp_cb cb; if (l2data->expect_cmd_code) cb = client_l2cap_rsp; else cb = NULL; tester_print("Sending L2CAP Request from client"); bthost = hciemu_client_get_host(data->hciemu); bthost_l2cap_req(bthost, handle, l2data->send_cmd_code, l2data->send_cmd, l2data->send_cmd_len, cb, data); } }
static gboolean enable_advertising(gpointer args) { struct test_data *data = tester_get_data(); struct bthost *bthost = hciemu_client_get_host(data->hciemu); bthost_set_adv_enable(bthost, 0x01); return FALSE; }
static void hid_prepare_reply_report(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); const struct iovec pdu = raw_pdu(0xa2, 0x01, 0x00); bthost_send_cid_v(bthost, cid_data->handle, cid_data->cid, &pdu, 1); }
static void conn_cb(uint16_t handle, void *user_data) { struct test_data *data = tester_get_data(); struct bthost *bthost = hciemu_client_get_host(data->hciemu); tester_print("New connection with handle 0x%04x", handle); test_conn_handle = handle; bthost_request_auth(bthost, handle); }
static void avrcp_get_element_attributes_req(void) { struct test_data *data = tester_get_data(); struct bthost *bthost = hciemu_client_get_host(data->hciemu); const struct iovec pdu = raw_pdu(req_ele_attr); struct step *step = g_new0(struct step, 1); bthost_send_cid_v(bthost, avrcp_data.handle, avrcp_data.cid, &pdu, 1); step->action_status = BT_STATUS_SUCCESS; schedule_action_verification(step); }
static void avrcp_connect_request_cb(uint16_t handle, uint16_t cid, void *user_data) { struct test_data *data = tester_get_data(); struct bthost *bthost = hciemu_client_get_host(data->hciemu); struct emu_l2cap_cid_data *cid_data = user_data; cid_data->handle = handle; cid_data->cid = cid; bthost_add_cid_hook(bthost, handle, cid, avrcp_cid_hook_cb, cid_data); }
static void test_server(const void *test_data) { struct test_data *data = tester_get_data(); const struct l2cap_data *l2data = data->test_data; const uint8_t *master_bdaddr; uint8_t addr_type; struct bthost *bthost; GIOChannel *io; int sk; if (l2data->server_psm || l2data->cid) { sk = create_l2cap_sock(data, l2data->server_psm, l2data->cid, l2data->sec_level); if (sk < 0) { tester_test_failed(); return; } if (listen(sk, 5) < 0) { tester_warn("listening on socket failed: %s (%u)", strerror(errno), errno); tester_test_failed(); close(sk); return; } io = g_io_channel_unix_new(sk); g_io_channel_set_close_on_unref(io, TRUE); data->io_id = g_io_add_watch(io, G_IO_IN, l2cap_listen_cb, NULL); g_io_channel_unref(io); tester_print("Listening for connections"); } master_bdaddr = hciemu_get_master_bdaddr(data->hciemu); if (!master_bdaddr) { tester_warn("No master bdaddr"); tester_test_failed(); return; } bthost = hciemu_client_get_host(data->hciemu); bthost_set_connect_cb(bthost, send_req_new_conn, data); if (data->hciemu_type == HCIEMU_TYPE_BREDR) addr_type = BDADDR_BREDR; else addr_type = BDADDR_LE_PUBLIC; bthost_hci_connect(bthost, master_bdaddr, addr_type); }
static void client_new_conn(uint16_t handle, void *user_data) { struct test_data *data = tester_get_data(); const struct rfcomm_server_data *server_data = data->test_data; struct bthost *bthost; bthost = hciemu_client_get_host(data->hciemu); bthost_add_rfcomm_chan_hook(bthost, handle, server_data->client_channel, server_hook_func, NULL); bthost_connect_rfcomm(bthost, handle, server_data->client_channel, connection_cb, NULL); }
static void rfcomm_connect_cb(uint16_t handle, uint16_t cid, void *user_data, bool status) { struct test_data *data = tester_get_data(); const struct rfcomm_client_data *client_data = data->test_data; struct bthost *bthost = hciemu_client_get_host(data->hciemu); if (client_data->send_data) bthost_add_rfcomm_chan_hook(bthost, handle, client_data->client_channel, client_hook_func, NULL); else if (client_data->read_data) data->conn_handle = handle; }
static void test_server(const void *test_data) { struct test_data *data = tester_get_data(); struct bthost *bthost; data->out = true; init_bdaddr(data); bthost = hciemu_client_get_host(data->hciemu); bthost_set_connect_cb(bthost, smp_new_conn, data); bthost_hci_connect(bthost, data->ra, BDADDR_LE_PUBLIC); }
static gboolean rc_connect_cb(GIOChannel *io, GIOCondition cond, gpointer user_data) { struct test_data *data = tester_get_data(); const struct rfcomm_client_data *client_data = data->test_data; socklen_t len = sizeof(int); int sk, err, sk_err; data->io_id = 0; sk = g_io_channel_unix_get_fd(io); if (getsockopt(sk, SOL_SOCKET, SO_ERROR, &sk_err, &len) < 0) err = -errno; else err = -sk_err; if (client_data->expected_connect_err && err == client_data->expected_connect_err) { tester_test_passed(); return false; } if (client_data->send_data) { ssize_t ret; ret = write(sk, client_data->send_data, client_data->data_len); if (client_data->data_len != ret) tester_test_failed(); return false; } else if (client_data->read_data) { g_io_add_watch(io, G_IO_IN, client_received_data, NULL); bthost_send_rfcomm_data(hciemu_client_get_host(data->hciemu), data->conn_handle, client_data->client_channel, client_data->read_data, client_data->data_len); return false; } if (err < 0) tester_test_failed(); else tester_test_passed(); return false; }
static void setup_powered_client_callback(uint8_t status, uint16_t length, const void *param, void *user_data) { struct test_data *data = tester_get_data(); struct bthost *bthost; if (status != MGMT_STATUS_SUCCESS) { tester_setup_failed(); return; } tester_print("Controller powered on"); bthost = hciemu_client_get_host(data->hciemu); bthost_set_cmd_complete_cb(bthost, client_connectable_complete, data); bthost_set_adv_enable(bthost, 0x01, 0x00); }
static void map_client_conn_cb(uint16_t handle, void *user_data) { struct test_data *data = tester_get_data(); struct bthost *bthost = hciemu_client_get_host(data->hciemu); tester_print("New connection with handle 0x%04x", handle); if (data->hciemu_type == HCIEMU_TYPE_BREDR) { tester_warn("Not handled device type."); return; } cid_data.cid = 0x0040; cid_data.handle = handle; bthost_add_cid_hook(bthost, handle, cid_data.cid, map_client_cid_hook_cb, &cid_data); }
static void send_rsp_new_conn(uint16_t handle, void *user_data) { struct test_data *data = user_data; struct bthost *bthost; tester_print("New connection with handle 0x%04x", handle); data->handle = handle; if (data->hciemu_type == HCIEMU_TYPE_LE) data->dcid = 0x0005; else data->dcid = 0x0001; bthost = hciemu_client_get_host(data->hciemu); bthost_add_cid_hook(bthost, data->handle, data->dcid, bthost_send_rsp, NULL); }
static gboolean test_close_socket_2_part_2(gpointer arg) { struct test_data *data = tester_get_data(); struct bthost *bthost = hciemu_client_get_host(data->hciemu); /* Make sure CMD_LE_CREATE_CONN will not immediately result in * BT_HCI_EVT_CONN_COMPLETE. */ hciemu_add_hook(data->hciemu, HCIEMU_HOOK_PRE_EVT, BT_HCI_CMD_LE_CREATE_CONN, test_close_socket_cc_hook, NULL); /* Advertise once. After that, kernel should stop scanning, and trigger * BT_HCI_CMD_LE_CREATE_CONN_CANCEL. */ bthost_set_adv_enable(bthost, 0x01); bthost_set_adv_enable(bthost, 0x00); return FALSE; }
static void test_connect(const void *test_data) { struct test_data *data = tester_get_data(); const struct l2cap_data *l2data = data->test_data; GIOChannel *io; int sk; if (l2data->server_psm) { struct bthost *bthost = hciemu_client_get_host(data->hciemu); if (!l2data->data_len) bthost_add_l2cap_server(bthost, l2data->server_psm, NULL, NULL); else bthost_add_l2cap_server(bthost, l2data->server_psm, client_l2cap_connect_cb, data); } if (l2data->direct_advertising) hciemu_add_master_post_command_hook(data->hciemu, direct_adv_cmd_complete, NULL); sk = create_l2cap_sock(data, 0, l2data->cid, l2data->sec_level); if (sk < 0) { tester_test_failed(); return; } if (connect_l2cap_sock(data, sk, l2data->client_psm, l2data->cid) < 0) { close(sk); tester_test_failed(); return; } io = g_io_channel_unix_new(sk); g_io_channel_set_close_on_unref(io, TRUE); data->io_id = g_io_add_watch(io, G_IO_OUT, l2cap_connect_cb, NULL); g_io_channel_unref(io); tester_print("Connect in progress"); }
static void setup_powered_client(const void *test_data) { struct test_data *data = tester_get_data(); const struct l2cap_data *test = data->test_data; unsigned char param[] = { 0x01 }; setup_powered_common(); tester_print("Powering on controller"); if (test && (test->expect_cmd || test->send_cmd)) { struct bthost *bthost = hciemu_client_get_host(data->hciemu); bthost_set_connect_cb(bthost, send_rsp_new_conn, data); } mgmt_send(data->mgmt, MGMT_OP_SET_POWERED, data->mgmt_index, sizeof(param), param, setup_powered_client_callback, NULL, NULL); }
static void test_server(const void *test_data) { struct test_data *data = tester_get_data(); const struct rfcomm_server_data *server_data = data->test_data; const uint8_t *master_addr; struct bthost *bthost; GIOChannel *io; int sk; master_addr = hciemu_get_master_bdaddr(data->hciemu); sk = create_rfcomm_sock((bdaddr_t *) master_addr, server_data->server_channel); if (sk < 0) { tester_test_failed(); return; } if (listen(sk, 5) < 0) { tester_warn("listening on socket failed: %s (%u)", strerror(errno), errno); tester_test_failed(); close(sk); return; } io = g_io_channel_unix_new(sk); g_io_channel_set_close_on_unref(io, TRUE); data->io_id = g_io_add_watch(io, G_IO_IN, rfcomm_listen_cb, NULL); g_io_channel_unref(io); tester_print("Listening for connections"); bthost = hciemu_client_get_host(data->hciemu); bthost_set_connect_cb(bthost, client_new_conn, data); bthost_hci_connect(bthost, master_addr, BDADDR_BREDR); }